AnonymousTransferClient, which hides the sender’s wallet address entirely. If you only need encrypted amounts and balances (with a visible sender), use the ConfidentialTransferClient covered in Building an App with the SDK.
Confidential vs. Anonymous: which client?
| Feature | ConfidentialTransferClient | AnonymousTransferClient |
|---|---|---|
| Identity | Onchain wallet address is visible | Account ID only; wallet address is hidden |
| Gas | User pays gas for all transactions | Fairycloak relay pays gas (except deposit) |
| Setup | Direct RPC connection | Requires a running Fairycloak relay server |
| Recipient | Must have a confidential account | Can receive to another anonymous account or a public address |
| Key management | Keys auto-derived from wallet signature | Keys derived per-account; store the private key yourself |
| Best for | Privacy over balances/amounts with known senders | Full sender anonymity |
AnonymousTransferClient, transactions are routed through the Fairycloak relay, which submits them onchain and pays gas on your behalf (except deposits). The sender’s wallet address is never revealed onchain: only a numeric anonymous account ID is associated with the transfer.
Access required. Anonymous transfers are available to teams building privacy-critical applications. To obtain a Fairycloak relay URL and API key, reach out to the Fairblock team at hello@fairblock.network.
Installation
Anonymous account ID rules
EveryaccountId (and senderAccountId / recipientId) you pass must follow these rules, enforced both onchain and by the Fairycloak relay:
- Non-empty string
- At most 20 characters long
- Alphanumeric characters only (
A-Z,a-z,0-9): no spaces, punctuation, or Unicode - Case-sensitive:
"MyAccount"and"myaccount"are different accounts; no normalization is applied
Error if they don’t follow the rules.
Initialization
Key derivation
Anonymous accounts use a per-account ElGamal keypair derived from a wallet signature. Store the returnedprivateKey securely: it cannot be recovered without the original wallet and account ID.
End-to-end anonymous flow
Key methods
Account management
createAccount(authWallet, accountId, elgamalPublicKey, options?): Creates a new anonymous account via the relay (relay pays gas).accountIdis a caller-chosen string following the account ID rules.ensureAnonymousAccount(authWallet, accountId, elgamalPublicKey, options?): Idempotent helper: creates the account if it doesn’t exist and (by default) waits until it has settled. Safe to call on every run with a stableaccountId. Returns{ accountId, accountInfo, created }. Options:deadlineOffset(default3600),waitUntilReady(defaulttrue),timeoutMs(default180000),pollIntervalMs(default2000).updateAuthKeys(authWallet, accountId, { add, remove }, options?): Adds or removes authorised signers.getAnonymousAccountInfo(accountId): Returns onchain state:exists,finalized,hasPendingAction,txId,elgamalPubkey,authNonce.isAuthorizedSigner(accountId, signerAddress): Checks whether an address is an authorised signer for the account.
Balances
getBalance(accountId, tokenAddress, elGamalPrivateKey): Returns decrypted balance totals:{ amount, available, pending }.getAnonymousBalance(accountId, tokenAddress, elGamalPrivateKey): Returns decrypted balances including raw ciphertexts (useful for generating proofs manually):{ available: { amount, ciphertext }, pending: { amount, ciphertext } }.
Moving funds
deposit(authWallet, accountId, tokenAddress, amount, options?): Deposits tokens into an anonymous account. The user pays gas. Handles ERC-20 approval automatically.transferToPublic(authWallet, accountId, params, options?): Transfers from an anonymous account to a public EVM address (relay pays gas).transferToAnonymous(authWallet, senderAccountId, params, options?): Transfers between two anonymous accounts (relay pays gas).applyPending(authWallet, accountId, options?): Moves a pending incoming balance into available. Must be called after receiving an anonymous-to-anonymous transfer before the recipient can spend.withdraw(authWallet, accountId, params, options?): Withdraws from an anonymous account to a public EVM address (relay pays gas).
Manual proof mode
transferToPublic supports an auto-proof mode (recommended: pass elGamalPrivateKey and amount) and a manual proof mode for advanced use:
Prepaid fees
EverytransferToPublic and transferToAnonymous call deducts a protocol fee from the sender account’s prepaid fee reserve. The SDK checks this balance before submitting and throws a descriptive error if it’s insufficient: top up with depositFees before transferring.
getFeeToken(): Returns the currently configured ERC-20 fee token address.depositFeesmust use this token. The fee token can be rotated by the protocol; existing balances in old fee tokens remain withdrawable.getFeeAmount(): Returns the per-transfer protocol fee (raw ERC-20 units), or0nif none is configured.getPrepaidFeeBalance(accountId, token): Returns the prepaid fee balance for an account and fee token.depositFees(authWallet, accountId, amount, options?): Deposits into the prepaid fee reserve. The user pays gas. Handles ERC-20 approval against the active fee token automatically.withdrawFees(authWallet, accountId, { token, destination, amount }, options?): Withdraws a specific amount from a given fee token’s reserve (relay pays gas).withdrawAllFees(authWallet, accountId, { destination }, options?): Withdraws all prepaid fee balances across all (including historical) fee tokens (relay pays gas).
Request tracking
All relay operations return a{ request_id, tx_hash, status } object. Track completion with:
Security considerations
- Private keys: Anonymous account
privateKeyvalues are as sensitive as wallet private keys. Never log them; store them in encrypted vaults or hardware-backed key stores. - One signer per account: An auth signer binds to a single anonymous account. Manage authorised signers with
updateAuthKeys. - Apply pending before spending: Anonymous-to-anonymous recipients must call
applyPending()before the received balance is spendable. - Network: Use HTTPS-only RPC endpoints and verify contract addresses before initialization.