Smart Contracts
zDoge's core logic is implemented in Solidity smart contracts deployed on DogeOS.
Contract Overview
| Contract | Purpose |
|---|---|
| ShieldedPoolMultiToken | Main contract for shield/transfer/unshield/swap |
| ShieldVerifier | Groth16 proof verification for shields |
| TransferVerifier | Groth16 proof verification for transfers |
| UnshieldVerifier | Groth16 proof verification for unshields |
| SwapVerifier | Groth16 proof verification for swaps |
| Hasher | MiMC sponge hash function |
ShieldedPoolMultiToken
The main contract handling all shielded transaction types.
Key Functions
shield
function shield(
bytes calldata proof,
bytes32 commitment,
address token,
uint256 amount
) external
Shields tokens (converts public to private):
- Verifies the ZK proof
- Transfers tokens from user to contract
- Inserts commitment into Merkle tree
- Emits
Shieldevent
transfer
function transfer(
bytes calldata proof,
bytes32 root,
bytes32 nullifierHash,
bytes32[2] calldata outputCommitments,
bytes[2] calldata encryptedMemos,
address relayer,
uint256 fee
) external
Transfers tokens privately between shielded addresses:
- Verifies the ZK proof
- Checks nullifier hasn't been spent
- Marks nullifier as spent
- Adds new commitments to Merkle tree
- Emits
Transferevent with encrypted memos
unshield
function unshield(
bytes calldata proof,
bytes32 root,
bytes32 nullifierHash,
address recipient,
address token,
uint256 amount,
address relayer,
uint256 fee
) external
Unshields tokens (converts private to public):
- Verifies the ZK proof
- Checks nullifier hasn't been spent
- Marks nullifier as spent
- Transfers tokens to recipient (minus fee)
- Emits
Unshieldevent
swap
function swap(
bytes calldata proof,
bytes32 root,
bytes32 inputNullifier,
bytes32 outputCommitment,
address tokenIn,
address tokenOut,
uint256 amountIn,
uint256 amountOut,
bytes calldata encryptedMemo
) external
Swaps tokens within the shielded layer:
- Verifies the ZK proof
- Checks nullifier hasn't been spent
- Marks nullifier as spent
- Adds output commitment to Merkle tree
- Emits
Swapevent with encrypted memo
State Variables
// Verifiers
IShieldVerifier public immutable shieldVerifier;
ITransferVerifier public immutable transferVerifier;
IUnshieldVerifier public immutable unshieldVerifier;
ISwapVerifier public immutable swapVerifier;
// Merkle tree
uint32 public constant MERKLE_TREE_HEIGHT = 20;
MerkleTree public tree;
// Nullifier tracking
mapping(bytes32 => bool) public nullifierHashes;
// Commitment tracking
mapping(bytes32 => bool) public commitments;
Events
event Shield(
bytes32 indexed commitment,
uint256 indexed leafIndex,
address indexed token,
uint256 amount,
uint256 timestamp
);
event Transfer(
bytes32 indexed nullifierHash,
bytes32 outputCommitment1,
bytes32 outputCommitment2,
uint256 indexed leafIndex1,
uint256 indexed leafIndex2,
bytes encryptedMemo1,
bytes encryptedMemo2,
uint256 timestamp
);
event Unshield(
bytes32 indexed nullifierHash,
address indexed recipient,
address indexed token,
uint256 amount,
address relayer,
uint256 fee,
uint256 timestamp
);
event Swap(
bytes32 indexed inputNullifier,
bytes32 outputCommitment,
address indexed tokenIn,
address indexed tokenOut,
uint256 amountIn,
uint256 amountOut,
bytes encryptedMemo,
uint256 timestamp
);
Verifier Contracts
Auto-generated Groth16 verifiers from the circuit trusted setup.
ShieldVerifier
function verifyProof(
uint[2] memory a,
uint[2][2] memory b,
uint[2] memory c,
uint[2] memory input
) public view returns (bool)
Verifies shield proofs:
input[0]: Commitmentinput[1]: Token addressinput[2]: Amount
TransferVerifier
function verifyProof(
uint[2] memory a,
uint[2][2] memory b,
uint[2] memory c,
uint[6] memory input
) public view returns (bool)
Verifies transfer proofs:
input[0]: Merkle rootinput[1]: Nullifier hashinput[2]: Output commitment 1input[3]: Output commitment 2input[4]: Relayer addressinput[5]: Fee amount
UnshieldVerifier
function verifyProof(
uint[2] memory a,
uint[2][2] memory b,
uint[2] memory c,
uint[4] memory input
) public view returns (bool)
Verifies unshield proofs:
input[0]: Merkle rootinput[1]: Nullifier hashinput[2]: Recipient addressinput[3]: Amount
SwapVerifier
function verifyProof(
uint[2] memory a,
uint[2][2] memory b,
uint[2] memory c,
uint[5] memory input
) public view returns (bool)
Verifies swap proofs:
input[0]: Merkle rootinput[1]: Input nullifier hashinput[2]: Output commitmentinput[3]: Token in addressinput[4]: Token out address
Hasher Contract
Implements MiMC sponge hash for Merkle tree operations.
function MiMCSponge(
uint256 in_xL,
uint256 in_xR
) public pure returns (uint256 xL, uint256 xR)
Used for:
- Computing commitment hashes
- Building Merkle tree
- Computing nullifier hashes
Security Considerations
Reentrancy Protection
All external calls happen after state updates (checks-effects-interactions pattern).
Overflow Protection
Uses Solidity 0.8+ built-in overflow checks.
Access Control
- No admin functions
- No upgradability
- Immutable after deployment
Front-running Protection
- Commitments are hashed (can't predict shields)
- Nullifiers are hashed (can't predict spends)
- Encrypted memos prevent front-running transfers
Gas Costs
| Operation | Approximate Gas |
|---|---|
| Shield | ~200,000 gas |
| Transfer | ~400,000 gas |
| Unshield | ~300,000 gas |
| Swap | ~350,000 gas |
Next: Zero-Knowledge Proofs