This project implements a privacy-preserving token contract based on EIP-7945 (Confidential Transactions Supported Token) using Zether protocol on BabyJub with Circom proofs.
- EIP-7945 Compliance: Implements the standard interface for confidential token contracts (link)
- Zether Protocol: Based on paper (page 13) with BabyJub curve
- Zero-Knowledge Proofs: Circom circuits for confidential transfers, burns, and approvals
- Solidity contracts:
contracts/PrivacyToken.sol,contracts/BabyJub.sol,contracts/Verifier/ - TypeScript client:
client/Client.tswith simplified API - Test suite:
test/Test.tswith comprehensive EIP-7945 flow tests
- Install dependencies
npm install- Compile contracts
npx hardhat compile- Run tests
npx hardhat test- Node.js 18+
- npm (or pnpm/yarn)
- For circuit work:
circomandsnarkjs(seecircom/README)
- EIP-7945 Interface: Standard methods for confidential transactions (
confidentialTransfer,confidentialApprove,confidentialTransferFrom,confidentialBalanceOf) - Epoch-based accounting: Pending changes are applied when the next epoch starts (
epochLengthblocks) - Zero-Knowledge Proofs: Circom circuits attest to valid balance updates without revealing amounts
- Confidential Allowances: Support for third-party transfers with encrypted allowance tracking
The Client class provides a simplified interface for interacting with the PrivacyToken contract:
// Create client with wallet account
const client = new Client(walletAccount, MAX);
// Register account with Schnorr signature
await client.registerAccount(privacyToken);
// Mint tokens by sending ETH
await client.mint(privacyToken, "1.0"); // 1 ETH
// Confidential transfer
await client.confidentialTransfer(
privacyToken,
publicClient,
"1000",
receiverAddress
);
// Approve allowance
await client.confidentialApprove(
privacyToken,
publicClient,
"500",
spenderAddress
);
// Transfer from (spender)
await client.confidentialTransferFrom(
privacyToken,
fromAddress,
toAddress,
"100"
);
// Read balances and allowances
const balance = await client.getCurrentBalance(privacyToken, publicClient);
const allowanceData = await client.readSpenderAllowance(
privacyToken,
spenderAddress
);Pre-built artifacts for transfer/burn/transferFrom are included under circom/. To rebuild or modify circuits:
# inside ./circom
make compile name=circom-file-name
make power power=power name=circom-file-name
make solidity name=circom-file-namecontracts/— Solidity sources (PrivacyToken, BabyJub, Verifiers)contracts/interfaces/— EIP-7945 interface definitionscircom/— circuits, proving/verifying keys, wasm, Makefileclient/— TypeScript client with simplified APItest/— Hardhat tests for EIP-7945 flows (register, mint, transfer, approve, transferFrom)
# Compile contracts
npx hardhat compile
# Run tests with gas report
npx hardhat testThe test suite covers all EIP-7945 core flows:
- Account Registration: Schnorr signature-based account setup
- Minting: ETH → token conversion with epoch-based accounting
- Confidential Transfers: Private transfers between registered accounts
- Burning: Token → ETH conversion with balance reduction
- Metadata: EIP-7945 standard methods (name, symbol, decimals, confidentialBalanceOf)
- Registration Validation: Error handling for unregistered accounts
- Confidential Approvals: Allowance management with encrypted values
- TransferFrom: Third-party transfers using allowances
Key behaviors:
- Balances move from
pending→accat the next epoch - Transfers debit sender immediately; receiver credited after next epoch
- Burns schedule balance reduction for next epoch
- Approvals decrease owner balance immediately; allowances track encrypted amounts for both owner and spender