Release Funds

Called by a provisioned operator to release escrowed tokens to a user's Mainnet wallet, contingent on a valid Sparse Merkle Tree exclusion proof for the withdrawal nonce.

The escrow program is migrating from this Sparse Merkle Tree design to an on-chain nonce bitmap (tracked upstream). This page is accurate against the current escrow program and will be reworked once that change lands.

Accounts

AccountWritableSignerDescription
payerFee payer
operatorOperator signer
instanceEscrow instance PDA (root updated on-chain)
operator_pdaOperator PDA proving the signer is provisioned
mintSPL token mint
allowed_mintAllowedMint PDA - validates the mint is permitted on this instance
user_ataRecipient's associated token account (destination)
instance_ataEscrow's associated token account (source)
token_programSPL Token Program
associated_token_programAssociated Token Program
event_authorityEvent authority PDA for EmitEvent CPI
private_channel_escrow_programEscrow Program (self-reference for CPI)

Parameters

ParameterTypeDescription
amountu64Amount of tokens to release
userPubkeyRecipient wallet on Mainnet
new_withdrawal_root[u8; 32]Updated SMT root after including this withdrawal's nonce
transaction_nonceu64Unique nonce identifying this withdrawal leaf in the SMT
sibling_proofs[u8; 512]16 sibling hashes (32 bytes each) for the SMT exclusion proof

Returns

Returns a TransactionBuilder that can be used to build and send the transaction.

Example

import { getReleaseFundsInstruction } from "../private-channel-escrow-program/clients/typescript/src/generated";
const releaseIx = getReleaseFundsInstruction({
payer: operatorPayerSigner,
operator: operatorSigner,
instance: instancePda,
operatorPda: operatorPdaAddress,
mint: mintAddress,
allowedMint: allowedMintPda,
userAta: userAtaAddress,
instanceAta: instanceAtaAddress,
tokenProgram: TOKEN_PROGRAM_ID,
associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
eventAuthority: eventAuthorityPda,
privateChannelEscrowProgram: ESCROW_PROGRAM_ID,
amount: 1_000_000n,
user: recipientPublicKey,
newWithdrawalRoot: newSmtRoot, // [u8; 32]
transactionNonce: withdrawalNonce, // u64
siblingProofs: proofBytes // Uint8Array(512)
});

Important Notes

  • Caller must be a provisioned operator: operator_pda must exist (created by AddOperator); invalid PDA throws InvalidOperatorPda
  • sibling_proofs is exactly 512 bytes (16 × 32-byte hashes, matching the SMT tree height of 16)
  • new_withdrawal_root is the updated SMT root after including this withdrawal's nonce. Compute it off-chain before calling
  • transaction_nonce must be valid for Instance.current_tree_index; an invalid nonce throws InvalidTransactionNonceForCurrentTreeIndex
  • The program first runs verify_smt_exclusion_proof, proving the nonce does NOT yet exist against the current root, then runs a separate verify_smt_inclusion_proof proving the nonce DOES exist in the caller-supplied new_withdrawal_root. Either check failing throws InvalidSmtProof
  • Only after both checks pass is Instance.withdrawal_transactions_root updated atomically; the nonce cannot be reused
  • Emits an event via EmitEvent CPI

Is this page helpful?

Inhoudsopgave

Pagina Bewerken
© 2026 Solana Foundation. Alle rechten voorbehouden.