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.