Skip to content

PolyhedraZK/privacy-swap

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Privacy Swap

Atomic privacy-preserving token swap combining OCash (ZK privacy pool) with PancakeSwap V3 (DEX).

Users swap tokens without exposing their on-chain identity. A single atomic transaction executes: OCash withdraw → PancakeSwap swap → OCash deposit. The user's EOA never appears on-chain; a relayer submits the transaction and is compensated via OCash's built-in relayerFee mechanism.

Architecture

User (off-chain)              Relayer               BSC
  │                              │                    │
  │ 1. getQuote()                │                    │
  │ ──[QuoterV2 eth_call]──────────────────────────>  │
  │ <─── quote ──────────────────────────────────────  │
  │                              │                    │
  │ 2. prepare()                 │                    │
  │  [select UTXOs]              │                    │
  │  [generate ZK proof with     │                    │
  │   recipient=PrivacySwap]     │                    │
  │                              │                    │
  │ 3. submit() or               │                    │
  │    buildTransaction()        │                    │
  │ ──POST /privacy-swap──────>  │                    │
  │                              │ 4. validate+sim    │
  │                              │ ──eth_call───────> │
  │                              │ 5. send tx         │
  │                              │ ──executeSwap────> │
  │                              │    [ATOMIC TX]     │
  │                              │    withdraw()      │
  │                              │    approve+swap()  │
  │                              │    approve+deposit()│
  │                              │    [END TX]        │
  │                              │ <── receipt ────── │
  │ <── txHash ────────────────  │                    │
  │                              │                    │
  │ 4. syncOnce() → new UTXO     │                    │

Project Structure

privacy-swap/
├── contracts/                 # Solidity (Foundry)
│   ├── src/
│   │   ├── PrivacySwap.sol           # Core atomic swap contract
│   │   └── interfaces/
│   │       ├── IOCash.sol            # OCash withdraw/deposit interface
│   │       └── ISmartRouter.sol      # PancakeSwap SmartRouter interface
│   ├── test/
│   │   ├── PrivacySwap.t.sol         # 16 unit tests
│   │   └── mocks/                    # MockOCash, MockSmartRouter, MockERC20
│   └── script/Deploy.s.sol           # Deployment script
├── sdk/                       # TypeScript SDK
│   └── src/
│       ├── PrivacySwapSDK.ts         # Main class (getQuote, prepare, buildTransaction, submit)
│       ├── types.ts                  # All type definitions
│       ├── quote.ts                  # PancakeSwap routing logic
│       └── abi/PrivacySwap.ts        # Contract ABI
└── relayer/                   # Reference relayer (Fastify)
    └── src/
        ├── handler.ts                # POST/GET endpoints
        ├── validator.ts              # Request validation
        ├── submitter.ts              # Tx simulation + submission
        └── config.ts                 # Environment configuration

Quick Start

Build & Test Contracts

cd contracts
forge install                  # Install dependencies (forge-std, OpenZeppelin)
forge build                    # Compile
forge test -vvv                # Run all 16 tests

Deploy Contract (BSC)

cd contracts

# Set environment variables
export OCASH_CONTRACT=0x...          # OCash contract address on BSC
export SMART_ROUTER=0x13f4EA83D0bd40E75C8222255bc855a974568Dd4  # PancakeSwap SmartRouter (BSC)
export OWNER=0x...                   # Contract owner (multisig recommended)
export RELAYER=0x...                 # Relayer EOA address

# Deploy
forge script script/Deploy.s.sol:Deploy \
  --rpc-url https://bsc-dataseed1.binance.org \
  --private-key $DEPLOYER_PRIVATE_KEY \
  --broadcast

Use the SDK

import { createSdk } from '@ocash/sdk';
import { PrivacySwapSDK } from '@privacy-swap/sdk';
import { createPublicClient, http } from 'viem';
import { bsc } from 'viem/chains';

const publicClient = createPublicClient({ chain: bsc, transport: http() });
const ocashSdk = createSdk({ chains: [{ chainId: 56, /* ... */ }] });
await ocashSdk.core.ready();

const privacySwap = new PrivacySwapSDK(ocashSdk, {
  privacySwapContract: '0x...',   // Deployed PrivacySwap address
  smartRouterAddress: '0x13f4EA83D0bd40E75C8222255bc855a974568Dd4',
  relayerUrl: 'https://relayer.example.com',
}, publicClient);

// 1. Quote
const quote = await privacySwap.getQuote({
  chainId: 56,
  tokenInAssetId: 'usdt',
  tokenOutAssetId: 'wbnb',
  amountIn: 1000_000000n,      // 1000 USDT (6 decimals)
  slippageBps: 50,             // 0.5%
});

// 2. Prepare (generates ZK proof, ~10-30s)
const keyPair = ocashSdk.keys.deriveKeyPair('my-secret-seed', '0');
const prepared = await privacySwap.prepare({
  chainId: 56,
  tokenInAssetId: 'usdt',
  tokenOutAssetId: 'wbnb',
  quote,
  ownerKeyPair: keyPair,
  depositSeed: 'fresh-deposit-seed',  // New key for deposit (better privacy)
  relayerAddress: '0x...',
  relayerFee: 2_000000n,              // 2 USDT relayer fee
});

// 3a. Submit via relayer
const result = await privacySwap.submit(prepared);
const txHash = await result.txHash;

// 3b. Or build calldata and submit yourself
const tx = privacySwap.buildTransaction(prepared);
// tx = { to, data, value: 0n, gasLimit: 700000n }

Run Relayer

cd relayer
pnpm install
cp .env.example .env           # Edit with your config

# Environment variables:
# RELAYER_PRIVATE_KEY=0x...
# PRIVACY_SWAP_CONTRACT=0x...
# RPC_URL=https://bsc-dataseed1.binance.org
# CHAIN_ID=56
# PORT=3000

pnpm dev                       # Development mode

Gas Costs

Operation Gas
OCash withdraw (ZK proof verification) ~200,000
ERC-20 approve + PancakeSwap swap ~195,000
ERC-20 approve + OCash deposit ~145,000
Contract overhead ~30,000
Total ~570,000

At 3 gwei on BSC: 0.0017 BNB ($1)

Privacy Model

Hidden: User's EOA address, link between user and swap

Visible: Swap amount, token pair, timing, PrivacySwap contract address, BabyJubjub deposit public key

Recommendations:

  • Use a fresh BabyJubjub key pair for the deposit (depositSeed parameter)
  • Vary swap amounts to reduce correlation
  • Use multiple relayers to avoid single-point censorship

Security

  • Atomicity: All three steps (withdraw, swap, deposit) in one transaction. Any failure reverts everything.
  • ZK Proof Binding: The OCash ZK proof binds recipient to the contract address. Relayer cannot redirect tokens.
  • Slippage Protection: amountOutMinimum enforced on-chain by PancakeSwap SmartRouter.
  • Relayer Authorization: Only whitelisted relayers can call executePrivacySwap.
  • Reentrancy Guard: OpenZeppelin ReentrancyGuard on the core function.
  • MEV Protection: Relayer can use Flashbots Protect or private mempools.

Dependencies

Component Dependency Purpose
Contract OpenZeppelin 5.x SafeERC20, ReentrancyGuard, Ownable
SDK @ocash/sdk ZK proof generation, UTXO management
SDK @pancakeswap/smart-router Swap routing and quoting
SDK viem Ethereum client, ABI encoding
Relayer fastify HTTP server
Relayer viem Transaction signing and submission

License

MIT

About

Atomic privacy swap: OCash ZK pool + PancakeSwap V3

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors