Skip to main content

Types reference

Field-by-field shapes for the public types the PoolSession methods consume and return. Source of truth: v2-monorepo/packages/sdk/src/types/.

Amount fields across the SDK (amount on transfer/withdraw params, value on deposit params, feeAmount) are 0x-prefixed hex strings (Hex), not bigint, so convert with `0x${wei.toString(16)}` before calling. Relayer-quote money fields (feeAmount, txCost, gasPrice) come back as decimal strings; wrap those in BigInt(...) before formatting.

Notes and status​

type PendingNote = {
noteAddressHash: Hash;
commitment: Hash;
value: Hex;
tokenId: Address;
label: Label;
status: NoteStatus;
ownerAddress: Address; // zero address for out-of-band recipient notes
noteSecret: Secret;
isOwned: boolean;
depositSecret?: Secret; // only on the note that originated its lineage
};

type Note = PendingNote & {
createdAtBlock: Hex; // on-chain block.timestamp at insertion, despite the name
spentAtBlock: Hex | null;
txHash: Hash;
};

enum NoteStatus { INACTIVE, PENDING, ACTIVE, SPENT, EXITED, REJECTED } // runtime export

Spend parameters​

All spend params share PrepareTransactBase:

type PrepareTransactBase = {
inputCommitments: Hash[]; // all inputs must be ACTIVE
amount: Hex;
tokenId: Address;
relayAddress?: Address;
feeAmount?: Hex;
processorAddress?: Address; // default: owner address in prepareTransfer (self-submit works,
// sender visible); zero address in prepareWithdraw and
// prepareColdStartTransfer (self-submit reverts unless set)
nativeGas?: Hex; // default "0x0"
feeRecipient?: Address;
};

type TransferParams = PrepareTransactBase & (
| { recipientDiscoveryData: RecipientDiscoveryData }
| { recipientNoteAddressHashByLabel: Map<Label, Hash> } // blind path, keyed by input label
| { recipientNoteAddressHash: Hash[] } // deprecated positional blind path
);

type ColdStartTransferParams = PrepareTransactBase & { recipientEvmAddress: Address };
type WithdrawParams = PrepareTransactBase & { recipientAddress: Address };
type RageQuitParams = { commitment: Hash }; // must not be EXITED
type DepositParams = { tokenId: Address; value: Hex };

RecipientDiscoveryData is a three-variant union:

type RecipientDiscoveryData =
| { evmAddress: Address; noteSecretsMap?: Map<Hash, Secret>; allowOutOfBandFallback?: boolean }
| { publicViewingKey: PublicKey; noteSecretsMap: Map<Hash, Secret>; hint?: Hex }
| { publicViewingKey: PublicKey; noteAddressHashes: Hash[]; hint?: Hex }; // payment requests

On the second variant the caller guarantees every map key equals Poseidon(recipientEvmAddress, noteSecret); a malformed map produces notes nobody can spend.

Spend results​

type PrepareTransferResult = { executeOptions: ExecuteTransferParams; relayOptions: RelayTransferParams[] };

type ExecuteTransferParams = {
to: Address; callData: Hex;
spentNotes: Note[]; changePendingNotes: PendingNote[]; recipientPendingNotes: PendingNote[];
recipient: ResolvedTransferRecipient; recipientEvmAddress?: Address;
};

type RelayTransferParams = PrepareTransactBase & {
recipient: ResolvedTransferRecipient; recipientEvmAddress?: Address;
selectedQuote: { relayerInfo: RelayerInfo; quote: TransferRelayerQuote };
};

type TransferResult = { txReceipt: TxReceipt; recipientNotes?: Note[] }; // notes present when the
// variant carries secrets

type PrepareWithdrawResult = {
changePendingNotes: PendingNote[]; callData: Hex; to: Address; spentNotes: Note[];
relayerOptions: ExecuteWithdrawRelayerParams[];
};

type PrepareRageQuitResult = {
commitment: Hash; value: Hex; // always the full note value
tokenId: Address; nullifierHash: Hash;
recipientAddress: Address; // the note's ownerAddress, circuit-enforced
callData: Hex; to: Address;
};

type PrepareDepositResult = {
pendingNote: PendingNote; // self-deposits only
callData: Hex; to: Address; // to = Entrypoint
msgValue: Hex;
approvalTx: { to: Address; data: Hex; value: Hex } | null;
};

type TxReceipt = { status: boolean; blockNumber: Hex; txHash: Hex; logs: TxLog[] };

Payment requests​

type PaymentRequest = { // the public, shareable subset; carries no secrets
paymentId: Hex;
noteAddressHashes: Hash[];
amount: Hex; // informational, not enforced on-chain
tokenId: Address;
tag: Hex; // Poseidon([paymentId])
paymentPubKey: PublicKey;
};

type GeneratePaymentRequestParams = { amount: Hex; tokenId: Address; slots?: number }; // default 5
type FulfillPaymentRequestParams = {
paymentRequest: PaymentRequest;
inputCommitments: Hash[]; // length must not exceed noteAddressHashes.length
relayAddress?: Address; feeAmount?: Hex; processorAddress?: Address;
};

The recipient-local record behind a request additionally holds noteSecretsMap, recipientAddress, status, fulfilledByCommitments, and a paymentPrivKey; everything except the private key is persisted through the payment-request storage adapter (storing it would put a recoverable secret at rest), and the key is re-derived from the viewing key and paymentId on load. None of it is ever shared.

Keystore​

type RegisterKeystoreOptions = { includeViewingKey?: boolean }; // default true
type RegisterKeystoreResult = {
alreadyRegistered: boolean;
keystoreCalldata: PreparedTransaction | null;
viewingKeyCalldata: PreparedTransaction | null;
nullifyingKeyHash: Hash; authDigest: Hash;
};
type RotateRevocableKeyConfig = Omit<DeriveFromSignatureConfig, "revocableKeyIndex">;
type DeriveFromSignatureConfig = {
signature: Hex; // 65-byte ECDSA signature of the derivation payload
signerAddress: Address;
revocableKeyIndex: Hex; // "0x0" for fresh accounts
addressHash: Hex; // keccak256(signerAddress) from the EIP-712 payload
};
type RotateRevocableKeyOutcome = {
receipt: TxReceipt;
oldAuthorizerDigest: Hash; newAuthorizerDigest: Hash;
newRevocableKeyIndex: Hex; // persist this; future sessions must be built with it
};

Config and environment​

type ProtocolKeys = {
privateNullifyingKey: Secret; privateRevocableKey: Secret;
revocableKeyIndex: Hex;
viewingPrivateKey: Secret; viewingPublicKey: PublicKey;
};
type DeploymentAddresses = {
poolAddress: Address; entrypointAddress: Address;
keystoreAddress: Address; aspRegistryAddress: Address;
};
type RelayerInfo = {
url: string; name: string; chainId: number; chainType: string;
status: "active" | "inactive"; address: Address; processorAddress: Address;
};
type AccountExport = {
notes: Note[]; syncCursor: Hex;
owner?: Address; chainId?: number; // mismatch on import throws AccountExportMismatch
paymentRequests?: SerializedPaymentRequestRecord[];
};
type DiscoverNotesParams = { fromBlock?: Hex; contacts?: Contact[] };

DEPLOYMENTS (runtime export) currently contains only Sepolia (11155111).

Batch withdraw​

type BatchWithdrawSessionParams = { tokenId: Address; recipientAddress: Address }; // whole active balance
type SelfSubmitBatchParams = BatchWithdrawSessionParams & { relayContractAddress: Address };
type PrepareBatchWithdrawResult = BatchPrepareResult & { relayerInfo: RelayerInfo; quote: BatchWithdrawalRelayerQuote };

// Dependency-light, also on @privacy-pools-v2/sdk/batch-fee:
const MAX_BATCH = 10;
type ComputeBatchItemFeeArgs = { relayCost: bigint; feeBps: bigint; flatFee: bigint; amountOut: bigint; maxRelayFee: bigint /* 0 = no cap */ };

Swaps and reshield​

type PrepareSwapQuoteParams = {
relayerInfo: RelayerInfo;
amount: Hex; tokenId: Address; outputToken: Address;
recipientAddress: Address; fallbackAddress?: Address; dustRecipient?: Address;
extraGas?: boolean; slippage?: number; toChainId?: number; denyExchanges?: string[];
};
type PreparedSwapQuote = {
selectedQuote: PrepareSwapRelayerQuotes; swapCalldata: Hex;
minOutputAmount: Hex; destinationMinOutputAmount: Hex;
feeAmount: string; netAmount: Hex; estimatedOutput: string;
swapInputToken: Address; swapTarget: Address; approvalAddress: Address;
dustRecipient: Address; dustAmount: Hex; toChainId: number;
};
type ExecuteWithdrawAndSwapRelayerParams = {
inputCommitments: Hash[]; amount: Hex; tokenId: Address;
recipientAddress: Address; fallbackAddress?: Address; outputToken: Address;
minOutputAmount: Hex; swapCalldata: Hex; selectedQuote: PrepareSwapRelayerQuotes;
toChainId?: number; dustRecipient?: Address; extraGas?: boolean;
swapTarget?: Address; approvalAddress?: Address;
};

type EstimateReshieldParams = { relayerInfo: RelayerInfo; amount: Hex; tokenId: Address; outputToken: Address; dustRecipient?: Address; slippage?: number };
type ReshieldEstimate = { feeAmount: string; netSwapAmount: string; estimatedOutput: string; estimatedMinOutput: string; noteValue: Hex; vettingFee: string; totalDepositCost: string; swapInputToken: Address };
type PrepareReshieldQuoteParams = EstimateReshieldParams & { depositResult: PrepareDepositResult; totalDepositCost: string };
type PreparedReshieldQuote = { selectedQuote; swapCalldata: Hex; feeAmount: string; netAmount: Hex; swapTarget: Address; approvalAddress: Address; dustRecipient: Address; dustAmount: Hex; swapInputToken: Address; depositNote: PendingNote };
type ReshieldParams = { inputCommitments: Hash[]; amount: Hex; tokenId: Address; outputToken: Address; swapCalldata: Hex; selectedQuote; dustRecipient?: Address; swapTarget?: Address; approvalAddress?: Address; depositNote?: PendingNote };

// Cross-chain adds toChainId (+ fallbackAddress, denyExchanges) and, on the quote, an optional
// dustRescue: { recipientIdentifier: StealthRecipientIdentifier } for the destination dust deposit.

type SwapAndDepositParams = { fromChainId: number; inputToken: Address; depositToken: Address; depositAmount: Hex; fallbackAddress?: Address; inputAmount?: Hex };
type PrepareSwapAndDepositResult = {
pendingNote: PendingNote; inputToken: Address; estimatedInputAmount: Hex;
approvalAddress: Address; approvalTxs: PreparedTransaction[]; approvalTx: PreparedTransaction | null;
transactionData: Hex; transactionTo: Address; fromChainId: number;
depositCallData: Hex; depositTarget: Address; depositMsgValue?: Hex; msgValue: Hex; estimatedDuration: number;
};

Yield​

type YieldDeployment = {
ppRouter: Address; nativeMode?: boolean; zap?: Address;
ppUSDC: Address; ppUSDCDecimals: number; // the share token (18 decimals)
underlying: Address; underlyingDecimals: number; underlyingSymbol: string; apyLabel?: string;
};
type YieldSessions = { chainId: number; deployment: YieldDeployment; deposit: IPoolSessionYieldDeposit; withdraw: IPoolSessionYieldWithdraw; token: IPPYieldTokenInteractor; router: IPPRouterInteractor };

type YieldDepositParams = { fundingSource?: "wallet" | "executor"; value: Hex /* shares */; maxUnderlyingIn?: bigint };
type BudgetSizedYieldDeposit = { shares: bigint; underlyingNeeded: bigint; maxUnderlyingIn: bigint; headroom: bigint };
type PreparedYieldDeposit = { pendingNote: PendingNote; approvalTxs: PreparedTransaction[]; approvalTx: PreparedTransaction | null; depositTx: PreparedTransaction; sharesValue: bigint; underlyingNeeded: bigint; maxUnderlyingIn: bigint };

type YieldWithdrawParams = { inputCommitments: Hash[]; amount: Hex; amountOut: Hex; recipient: Address; feeRecipient?: Address; feeAmount?: Hex; nativeGas?: bigint; slippageBps?: bigint };
type PreparedYieldWithdraw = { withdrawTx: PreparedTransaction; changePendingNotes: PendingNote[]; spentNotes: Note[]; recipientShares: bigint; quotedUnderlyingOut: bigint; minUnderlyingOut: bigint };
type YieldWithdrawRelayParams = { inputCommitments: Hash[]; amount: Hex; amountOut: Hex; recipient: Address; signedFeeCommitment: WithdrawalFeeCommitment; extraGas?: boolean; slippageBps?: bigint };
type PreparedYieldWithdrawRelay = { relayParams: WithdrawalRelayParams; changePendingNotes: PendingNote[]; spentNotes: Note[]; recipientShares: bigint; quotedUnderlyingOut: bigint; minUnderlyingOut: bigint };

// Sizing constants (runtime exports)
const DEPOSIT_RATE_HEADROOM_PPM = 1n;
const CROSS_CHAIN_DEPOSIT_RATE_HEADROOM_PPM = 500n;
const WITHDRAW_SLIPPAGE_BPS = 100n;
const ZAP_SUPPLY_BUFFER_UNITS = 2n;

Yield amounts are bigint in the public API (the batch-fee precedent), except where the base sessions already speak Hex (value, amount, amountOut).

Errors​

The public entrypoint exports exactly seven error classes as runtime values, so instanceof works only for these: InvalidBuilderConfig, UnsupportedChainId, InvalidAddNoteParams, InvalidMarkNoteSpentParams, InvalidMarkNoteExitedParams, NoteCommitmentNotMined, and PoolSessionNoteManagerBaseError. Every other error class (including RecipientViewingKeyUnregistered, FeeCommitmentExpired, RelayerRejected, WitnessPreparationFailed, and the transact/deposit/ragequit/keystore families) reaches the package as a type only. All classes set a distinct name, so branch on error.name for anything outside the seven.

Most-likely-to-hit errors:

  • InvalidBuilderConfig: config validation failure.
  • WitnessPreparationFailed: witness construction failed; a "Leaf not found in tree" message on a spend or ragequit usually means the owner never registered on the keystore.
  • TransactNoteNotActive / InsufficientTransactValue: bad input selection.
  • NoteNotFoundError: the local NoteManager doesn't have the commitment.
  • InvalidStatusTransitionError: illegal note status transition.
  • ProofGenerationFailed: circuit error.
  • RelayerRejected: relayer returned 4xx (often wraps an on-chain revert).
  • RecipientViewingKeyUnregistered: recipient hasn't registered and allowOutOfBandFallback wasn't set.
  • FeeCommitmentExpired: quote expired before submit.
  • CircuitArtifactLoadFailed: couldn't fetch the wasm/zkey/vkey.

Per-method error conditions are listed on PoolSession.