Skip to Content
GuidesFor DevelopersMaking a Withdrawal

Making a Withdrawal

Withdrawals move USDC from the Lateo pool to any Stellar address. A Groth16 proof with nullifiers is generated and verified on-chain — the pool contract atomically verifies the proof, marks nullifiers as spent, and transfers USDC.

Via Dashboard

  1. Click Withdraw in the Agent Dashboard
  2. Enter amount and destination address
  3. The proxy generates a ZK proof with nullifiers from your unspent notes
  4. The operator signs and submits transact() with negative ext_amount
  5. USDC arrives at the destination after on-chain confirmation

No Freighter signing is required for withdrawals — the operator signs because the pool contract transfers from itself.

Via API

curl -X POST http://localhost:3002/api/withdraw \ -H "Content-Type: application/json" \ -H "Authorization: Bearer <jwt>" \ -d '{ "amount": "1.0000000", "destination": "GDEST..." }'

Response:

{ "status": "confirmed", "txHash": "dcb61c18...", "amount": "1.0000000", "destination": "GDEST...", "zkProof": { "generated": true, "onChain": true } }

Via MCP

lateo_withdraw(amount: "1", destination: "GDEST...")

What happens on-chain

The transact() call with negative ext_amount:

  1. Verifies the Groth16 proof (BN254 pairing check)
  2. Verifies nullifiers have NOT been previously spent (double-spend protection)
  3. Marks nullifiers as spent (stored permanently on-chain)
  4. Transfers USDC from pool to the specified recipient
  5. Inserts change commitments (if withdrawal < total input notes)

The transaction is atomic — if any step fails, no USDC moves and no nullifiers are marked.

Key difference from deposits

DepositWithdrawal
Who signsUser (Freighter)Operator
ext_amountPositiveNegative
USDC directionUser → PoolPool → Recipient
NullifiersNone (dummy inputs)Real (prevents double-spend)
Change noteNoneCreated if partial withdrawal
Last updated on