Skip to main content
In this page you create a shielded account, register it, deposit USDC, settle the deposit and withdraw part of it. You use the wallet path, so a wallet you control submits every transaction. The code on this page type-checks against SDK 1.4.3 (with the old package names). It is illustrative, not a script you can run as is. It needs a data source and circuit files that you supply, and values in square brackets that you fill in.

What you need

  • Node.js and a package manager.
  • A test wallet with some test ETH for gas and some test USDC on the test network you use.
  • The pool address, the paymaster address and the asset ids for the network. They are in Network reference.
  • The circuit files for register, transact21, transact22 and treeUpdate at least. Larger shapes (transact23, transact42, transact44) are needed when a transaction spends more notes or makes more outputs.
  • The pool ABI, from the protocol artifacts.

1. Install

2. Create the account

A shielded account comes from one wallet signature. You hash the signature, cut it down to the field, and generate the keys from it.
Anyone who gets the signature can rebuild the account, so treat it like a private key. See Shielded accounts.

3. Create Core

Core is the SDK’s entry point. It needs five services. You give it a contract source, a notes source, a commitment tree source and an address tree source. The SDK builds the fifth, the address resolver, itself, and does not let you replace it.
Two things to know about the services:
  • The two tree sources must read from the pool’s events or from an index of them, and keep a Merkle tree in step with the pool. The commitment tree has depth 25. The address tree has depth 20. The Palliora DeFi app reads both from a subgraph. The SimpleTreeSource in the services package does not implement the update method that Core calls, so write your own ITreeSource for now. See SDK reference.
  • deployedBlock is the block where the pool was deployed. It stops the address resolver scanning from the first block each time.
The circuit chain list in the SDK covers Ethereum mainnet and Sepolia. It does not include HyperEVM in the published build. For HyperEVM, use a build that adds it: [SDK_VERSION_WITH_HYPEREVM].

4. Register

Registration links your public address to your shielded address, so other people can send to you by address. You sign a typed message with your wallet, prove it in the register circuit, and send the proof to the pool.
The second argument to proveAddress must be the address that produced the signature. The proof binds to it, and the pool rejects a mismatch. On Ethereum the pool also screens the sender and the signer against a sanctions list.

5. Deposit

A deposit moves tokens from your wallet into the pool and creates a note. First approve the pool to pull the tokens.
The asset id 65538 is an example. Ids differ by network. Read them from Network reference or from pool.getAsset. The pool also enforces a minimum and a maximum deposit, and a cap on total value. Those figures are in Status and limits. createTransaction picks the notes to spend, resolves the recipient, reads the revoker and builds the transaction. signTransaction signs it with your shielded key. proveTransaction runs the circuit and returns a ZTransaction.

6. Settle

The new note is queued. It joins the commitment tree after the next tree update. Anyone can submit the update, and you can do it yourself.
getBalances returns two maps from asset id to amount. pending holds notes still in the queue. current holds notes you can spend. A note can be spent only after it is in the tree. See Notes and commitments.

7. Withdraw

A withdrawal spends notes and sends the tokens to a public address.
Your wallet submitted this one, so the public can see which address sent the transaction. They still cannot see which notes it spent. To hide the sender, use the relayed path below.

Send privately to another account

A transfer is the same call with TransactionType.TRANSFER. Set to to the recipient’s public address, which the SDK resolves from the registration, or to their 96-byte packed shielded address.

Submit through a relayer

For the relayed path, set viaBundler: true and name the fee asset. The fee is paid from your notes. The SDK builds and proves the transaction in both cases. It does not build the user operation or call the relayer for you. The Palliora DeFi app does that in its own code. quoteUserOpGasCostWithLatestBaseFee in the core package helps you compute userOpFeeQuoteEth. See Relaying and gas for how the fee is bound into the proof.

Next