Shield (Deposit)
Shielding converts public tokens into private notes in the pool.
Basic Shield
After syncing, shield tokens to yourself:
typescript
await client.sync();
const txHash = await client.shield({
amount: parseEther('100'),
tokenId: BigInt(deployment.tokens.zkUSD), // Optional, 0n for ETH
});Shield to Another User
Use shieldTo() to deposit directly to someone else's ZK address:
typescript
import { shieldTo, DEPLOYMENTS } from '@zkprivacy/sdk';
await shieldTo(
{
rpcUrl: DEPLOYMENTS.remote.rpcUrl,
poolAddress: DEPLOYMENTS.remote.pool,
},
{
recipient: 'zks1...', // Their ZK address
amount: '100', // Human-readable amount
tokenAddress: DEPLOYMENTS.remote.tokens.zkUSD,
walletClient,
}
);This is perfect for:
- Payment links
- Deposit widgets
- Onramps
Shield Module (Lightweight)
For deposit-only apps, use the shield submodule (~150KB vs ~2MB):
typescript
import { shieldTo, encryptNoteForRecipient } from '@zkprivacy/sdk/shield';No prover initialization needed!
Token Approval
For ERC-20 tokens, the SDK handles approval automatically. But you can pre-approve:
typescript
import { EMT_ABI } from '@zkprivacy/sdk';
// Pre-approve pool to spend tokens
await walletClient.writeContract({
address: tokenAddress,
abi: EMT_ABI,
functionName: 'approve',
args: [poolAddress, maxUint256],
});
// Now shield without approval tx
await client.shield({ amount: parseEther('100') });Low-Level: Manual Shielding
For advanced use cases, you can construct the shield transaction manually:
typescript
import { encryptNoteForRecipient, EMT_ABI } from '@zkprivacy/sdk';
// 1. Encrypt note for recipient
const { encryptedNote, commitment } = encryptNoteForRecipient(
recipientZkAddress,
amount,
tokenId, // MUST include for correct commitment
);
// 2. Format commitment
const commitmentBytes = `0x${commitment.toString(16).padStart(64, '0')}`;
// 3. Call EMT token's shield function
await walletClient.writeContract({
address: tokenAddress,
abi: EMT_ABI,
functionName: 'shield',
args: [amount, commitmentBytes, encryptedNote],
});Token ID
Always pass tokenId to encryptNoteForRecipient(). Omitting it creates invalid notes.
After Shielding
After the transaction confirms, sync to see your new balance:
typescript
const txHash = await client.shield({ amount: parseEther('100') });
// Wait for confirmation
await publicClient.waitForTransactionReceipt({ hash: txHash });
// Sync to pick up the new note
await client.sync();
console.log('New balance:', client.getBalance());Gas Considerations
Shield transactions:
- Gas cost: ~150,000 gas (token approval + shield)
- Who pays: The sender's EOA
- No ZK proof: Shielding doesn't require proof generation
For gasless shielding, users would need to use a relayer that supports shield operations.