Deposit
A deposit moves public ERC-20 tokens into the pool and mints a private note.
The pool pulls tokens with transfer_from while the proof executes, so the
ERC-20 approve must already be visible on-chain - it is a separate
transaction, submitted and waited on first.
Snippets assume transfers, account and provider from
Getting Started.
const poolAddress = process.env.POOL_ADDRESS!
const tokenAddress = "0x049d..." // any ERC-20
const amount = 100n
// Transaction 1 - approve the pool to pull tokens.
const approveTx = await account.execute(
{
contractAddress: tokenAddress,
entrypoint: "approve",
calldata: [poolAddress, amount.toString(), "0"],
},
{ tip: 0n },
)
await provider.waitForTransaction(approveTx.transaction_hash)
// Transaction 2 - the private deposit.
// Re-fetch provingBlockId AFTER the approve lands, so the proof base
// is not older than the approval.
const provingBlockId = (await provider.getBlockNumber()) - 10
const { callAndProof } = await transfers
.build({ autoSetup: true })
.with(tokenAddress, (t) => t.deposit({ amount }))
.surplusTo(account.address) // the deposited amount becomes my note
.execute({ provingBlockId })
const proofDetails = callAndProof.proof.proofFacts?.length
? { proofFacts: callAndProof.proof.proofFacts, proof: callAndProof.proof.data }
: {}
const tx = await account.execute(callAndProof.call, { tip: 0n, ...proofDetails })
await provider.waitForTransaction(tx.transaction_hash)
Things to notice
- Two transactions, never one. The pool's
apply_actionsentrypoint is reentrancy-guarded against sharing a transaction with other calls, so you cannot batchapproveand the deposit into a singleaccount.execute. - The deposit omits
recipient;surplusTo(account.address)directs the unassigned amount into a note owned by you. This is the shape that scales to deposit-and-transfer on the next page. - Amounts are bigint literals (
100n) in the token's smallest unit. autoSetup: trueopens your self-channel and the token subchannel if they do not exist yet - a first deposit needs both.- The new note matures 10 blocks after creation. Spending it earlier
produces a proof the contract rejects with
Note not mature. - Every deposit is screened. FPI (the screening provider) screens the depositing address and signs each deposit, and the pool verifies that signature onchain - enforcement is part of the protocol since the v0.14.3 upgrade. Wallet and hosted-proving flows handle this step for you; if a structurally valid deposit reverts, screening is the first thing to check. See Compliance & Auditing.
Next: Transfer a note privately to another account.