Skip to main content

Client Reference

Reference for Zkp2pClient and the standalone helpers in @zkp2p/sdk. For end-to-end flows, start with Offramp Integration or Onramp Integration.

Published SDK compatibility

SDK 0.15.0-rc.11 supports OrchestratorV4 routing and deferred payouts with matching deployed contracts. It can still prepare OrchestratorV3 transactions; preparation does not establish that the retired deployment supports execution. Use OrchestratorV4 for current writes.

Constructor​

import { Zkp2pClient } from "@zkp2p/sdk";

const client = new Zkp2pClient({
walletClient,
chainId: 8453,
baseApiUrl: "https://api.zkp2p.xyz",
});
FieldRequiredDescription
walletClientYesviem WalletClient with an account
chainIdYes8453 (Base)
baseApiUrlNoRoot of Peer's API: https://api.zkp2p.xyz. Set it so signalIntent() can fetch gating signatures; other API calls already default to it. Don't append /v1, /v2, or /v3
rpcUrlNoRPC URL for reads. Defaults to the wallet chain's RPC
rpcTransportNoviem Transport for reads; takes precedence over rpcUrl
runtimeEnvNoproduction (default), preproduction, or staging. Selects the contract deployment and default endpoints
indexerUrlNoIndexer GraphQL endpoint. Defaults to https://indexer.zkp2p.xyz/v1/graphql in production
timeouts.apiNoAPI timeout in milliseconds. Defaults to 15000

You don't need an API key. Quotes, gating signatures, payee registration, seller credential uploads, and payment verification all work without credentials. Peer's API accepts browser requests only from allowed origins, except public referral reads; if your origin isn't allowed, call it from your server.

OfframpClient is an alias of Zkp2pClient.

Prepared transactions​

Write methods simulate the call, send it, and return the transaction hash without waiting for confirmation. Most also have .prepare(), which returns a PreparedTransaction ({ to, data, value, chainId }) for relayers, smart accounts, batching, or gas estimation.

const prepared = await client.signalIntent.prepare({
depositId: 42n,
amount: 100_000000n,
toAddress: "0xYourRecipientAddress",
processorName: "wise",
payeeDetails:
"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
fiatCurrencyCode: "USD",
conversionRate: 1_020000000000000000n,
});

await relayer.submit({
to: prepared.to,
data: prepared.data,
value: prepared.value,
});

createDeposit() has no .prepare(). Use prepareCreateDeposit(), which takes the same params and returns { depositDetails, prepared }.

Deposits​

Create a deposit​

Approve the token, then create the deposit. ensureAllowance({ token, amount, spender?, maxApprove? }) returns { hadAllowance, hash? }; when it sends an approval, wait for hash to confirm first.

const usdc = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
const amount = 1_000_000000n; // 1,000 USDC

await client.ensureAllowance({ token: usdc, amount });

const { hash, depositDetails } = await client.createDeposit({
token: usdc,
amount,
intentAmountRange: { min: 10_000000n, max: 500_000000n },
processorNames: ["wise"],
payeeData: [{ offchainId: "maker@example.com" }],
conversionRates: [
[{ currency: "USD", conversionRate: "1020000000000000000" }],
],
});
ParameterRequiredDescription
tokenYesDeposit token, such as USDC on Base
amountYesTotal deposit in token base units (USDC has 6 decimals)
intentAmountRangeYes{ min, max } amount per intent, in base units
processorNamesYesPayment platforms, such as ["wise", "revolut"]
conversionRatesYesOne list of { currency, conversionRate } per platform. Rates have 18 decimals
payeeDataUnless payeeDetailsHashes is setOne payee per platform; see Payee registration
payeeDetailsHashesNoHashes from registerPayeeDetails(). Skips registration
delegateNoAddress that may manage the deposit
intentGuardianNoDefaults to the deployed IntentGuardian, which lets payers buy more time for a live intent
retainOnEmptyNoKeep the deposit open when its balance reaches zero
referralCodeNoPeer referral code for deposit attribution; see Attribution
escrowAddressNoEscrow override
txOverridesNoviem transaction overrides plus optional referrer attribution codes
paymentMethodsOverride, paymentMethodDataOverride, currenciesOverrideNoRaw on-chain values that replace the ones the SDK derives
depositDataNoDeprecated alias of payeeData

createDeposit() sends only the deposit transaction. It doesn't configure a whitelist.

SDK 0.14.11 and earlier

After a Venmo or PayPal deposit mines, these releases send one extra transaction per method to add the deposit to a retired access group. Pass enableTrustedGroupBypass: false to createDeposit() to skip it.

Payee registration​

createDeposit() registers payeeData with Peer's API. To reuse the hashes, register first with registerPayeeDetails():

const { hashedOnchainIds } = await client.registerPayeeDetails({
processorNames: ["wise", "revolut"],
payeeData: [{ offchainId: "maker@example.com" }, { offchainId: "maker" }],
});

await client.createDeposit({ ...params, payeeDetailsHashes: hashedOnchainIds });

Each payeeData entry is { offchainId, telegramUsername?, metadata?, identityAttestation? }, in the same order as processorNames. PayPal, Wise, and Alipay registrations take an identityAttestation; see Identity attestation. depositData is a deprecated alias.

The returned hash (hashedOnchainId) is the payeeDetails value that deposits, quotes, intents, and seller credentials use.

Manage a deposit​

MethodParametersEffect
addFundsdepositId, amountAdd tokens. Approve them first
removeFundsdepositId, amountWithdraw part of the available balance
withdrawDepositdepositIdReturn the remaining balance and stop new intents. Funds locked in live intents stay until they expire; call it again to withdraw them
setAcceptingIntentsdepositId, acceptingPause or resume new intents
setIntentRangedepositId, min, maxChange the per-intent limits
setCurrencyMinRatedepositId, paymentMethod, fiatCurrency, minConversionRateChange the minimum rate for one platform and currency
setRetainOnEmptydepositId, retainKeep the deposit open at a zero balance
setDelegate / removeDelegatedepositId, delegateLet another address manage the deposit
pruneExpiredIntentsdepositIdReturn liquidity held by expired intents. Anyone can call it

Each method accepts optional escrowAddress and txOverrides and has .prepare().

Payment methods and currencies​

Platforms and currencies are bytes32 hashes on-chain. Resolve them with resolvePaymentMethodHash("wise") and resolveFiatCurrencyBytes32("USD").

MethodParametersEffect
addPaymentMethodsdepositId, paymentMethods, paymentMethodData, currenciesAdd platforms to a deposit
setPaymentMethodActivedepositId, paymentMethod, isActiveTurn a platform on or off
removePaymentMethoddepositId, paymentMethodTurn a platform off
addCurrenciesdepositId, paymentMethod, currenciesAdd currencies to a platform
deactivateCurrency / removeCurrencydepositId, paymentMethod, currencyCodeTurn a currency off
setOracleRateConfig / removeOracleRateConfigdepositId, paymentMethodHash, currencyHash, configSet or remove oracle pricing; see Oracle helpers
setOracleRateConfigBatch, updateCurrencyConfigBatch, deactivateCurrenciesBatchdepositId, paymentMethods, and per-currency arraysBatch versions

Deposit hooks​

OrchestratorV3 has one pre-intent hook slot per deposit. The whitelist-hook methods target OrchestratorV2 deposits. OrchestratorV4 has no deposit hooks.

MethodDescriptionKey parameters
setDepositPreIntentHook() / .prepare()Set the hook called before an intent is signaleddepositId, preIntentHook, escrowAddress?, orchestratorAddress?
getDepositPreIntentHook()Read the pre-intent hookdepositId, escrowAddress?, orchestratorAddress?
setDepositWhitelistHook() / .prepare()Set the OrchestratorV2 whitelist hookdepositId, whitelistHook, escrowAddress?, orchestratorAddress?
getDepositWhitelistHook()Read the OrchestratorV2 whitelist hookdepositId, escrowAddress?, orchestratorAddress?
cleanupOrphanedIntents() / .prepare()Clean up orphaned V2 or V3 intents; anyone can call itintentHashes, escrowAddress?, orchestratorAddress?

Identity attestation​

Some platforms need a verified account identity before payee registration. Request one from the attestation service with the Nitro client the SDK re-exports, or with the lower-level apiRequestIdentityAttestation().

PlatformAction typeEncrypted session materialPublic params
venmoregister_venmoCookie{ SENDER_ID }
paypalregister_paypal, register_business_paypalCookie{}
wiseregister_wiseCookie, X-Access-Token{ PROFILE_ID }
cashappregister_cashappsessionCookie, requestPayload, optional requestHeaders{}
alipayregister_alipayCookie{}

For Venmo, send only the Cookie header and params.SENDER_ID; don't include a captured URL.

import { createNitroAttestationClient } from "@zkp2p/sdk";

const nitro = createNitroAttestationClient({
environment: "production",
attestationServiceUrl: "https://attestation-service.zkp2p.xyz",
});

const identity = await nitro.requestIdentityAttestation({
platform: "venmo",
actionType: "register_venmo",
callerAddress: "0x0000000000000000000000000000000000000002",
sessionMaterial: {
Cookie: "venmo-session-cookie-header",
},
params: {
SENDER_ID: "123456789",
},
});

console.log(identity.identity.payeeIdHash);

If you encrypted the session material yourself, call the endpoint helper:

import { apiRequestIdentityAttestation } from "@zkp2p/sdk";

const response = await apiRequestIdentityAttestation(
{
callerAddress: "0x0000000000000000000000000000000000000002",
encryptedSessionMaterial,
params: { SENDER_ID: "123456789" },
},
"https://attestation-service.zkp2p.xyz",
"venmo",
"register_venmo",
);

callerAddress is required and signed into the attestation. Payee registration doesn't check it against the caller's wallet.

Intent operations​

signalIntent() / signalIntent.prepare()​

Reserves liquidity from a deposit and returns the transaction hash. Read the intent hash from the IntentSignaled event (example).

Without gatingServiceSignature and signatureExpiration, the SDK fetches them from Peer's API; baseApiUrl must be set. On OrchestratorV3, a deposit without a gating service is signaled with no signature. OrchestratorV4 requires a signature on every intent.

ParameterRequiredDescription
depositIdYesDeposit ID
amountYesToken amount in base units. From a quote, use signalIntentAmount
toAddressYesRecipient of the tokens
processorNameYesPayment platform, such as wise
payeeDetailsYesPayee hash of the deposit's payment method
fiatCurrencyCodeYesFiat code such as USD, or its bytes32 hash
conversionRateYesAgreed rate with 18 decimals
referralFeesNo{ recipient, fee }[]. fee is a fraction with 18 decimals (10n ** 16n is 1%)
referrerFeeConfigNoOne { recipient, feeBps }. Can't be combined with referralFees, referrer, or referrerFee
referrer / referrerFeeNoDeprecated single-referrer fields
gatingServiceSignature, signatureExpirationNoA gating signature you already have
payerCommitmentNoOrchestratorV4 only: bytes covered by your own gating signature. Defaults to 0x
groupIdNoOrchestratorV4 only: an allowed group the taker signals through. Defaults to zero
postIntentHook, preIntentHookData, dataNoOrchestratorV3 only. OrchestratorV4 rejects non-empty values
escrowAddress, orchestratorAddressNoRouting overrides. Must be the current deployment
txOverridesNoviem transaction overrides plus optional referrer attribution codes

Use OrchestratorV4 for new intents. OrchestratorV3 was retired on 2026-11-05, and its intents and events remain readable. SDK 0.15.0-rc.11 still routes existing intents to their owning orchestrator and can prepare O3 transactions; it does not reject them solely because O3 is retired.

cancelIntent() / cancelIntent.prepare()​

Cancels a signaled intent and returns its liquidity to the deposit. The intent owner can call it; on OrchestratorV4, so can the intent's arbitrator.

ParameterRequiredDescription
intentHashYes0x-prefixed 32-byte intent hash
orchestratorAddressNoOrchestrator override
txOverridesNoviem transaction overrides

fulfillIntent() / fulfillIntent.prepare()​

Fulfills an intent with a payment proof. The SDK reads the intent, gets a payment attestation from the attestation service, and submits it.

ParameterRequiredDescription
intentHashYes0x-prefixed 32-byte intent hash
proofUnless precomputedAttestation is setBuyer TEE proof: { proofType: "buyerTee", encryptedSessionMaterial, params }. Legacy zkTLS proofs are rejected
timestampBufferMsNoAllowed payment timestamp variance in milliseconds. Defaults to "300000"
attestationServiceUrlNoDefaults to https://attestation-service.zkp2p.xyz in production
attestationServiceFallbackUrlsNoOrigins to try after network failures. Omit to use the attestor's proxy; [] disables fallback
orchestratorAddressNoOrchestrator override
postIntentHookDataNoOrchestratorV3 only. OrchestratorV4 rejects non-empty data
txOverridesNoviem transaction overrides
callbacksNoonAttestationStart, onAttestationComplete, onTxSent, onTxMined
precomputedAttestationNo{ paymentProof, verificationData } you already encoded

To build a Buyer TEE proof in a web app, see Onramp Integration. getFulfillIntentInputs(intentHash) returns the intent fields the attestation uses.

releaseFundsToPayer() / releaseFundsToPayer.prepare()​

Releases an intent's funds to the payer without a payment proof, for example after the depositor confirms payment some other way. The depositor can call it; on OrchestratorV4, so can the intent's arbitrator.

ParameterRequiredDescription
intentHashYes0x-prefixed 32-byte intent hash
releaseAmountYesAmount to release. The depositor must release the full intent amount; an OrchestratorV4 arbitrator may release less, and the rest returns to the deposit. Ignored before OrchestratorV4
orchestratorAddressNoOrchestrator override
txOverridesNoviem transaction overrides

extendIntent() / extendIntent.prepare()​

OrchestratorV4 only. Lets the intent's arbitrator extend its expiry at no cost. Takes intentHash, additionalTime in seconds, and optional orchestratorAddress and txOverrides.

Intent lifetime extension​

The IntentGuardian lets any payer buy more time for a live intent. Check hasIntentGuardian() first. The fee can change at any time, so quote right before signing and pass the quote's cost as maxCost.

MethodPurpose
hasIntentGuardian()Whether a guardian is deployed; doesn't throw
getIntentGuardianPolicy(){ guardian, extensionsEnabled, extensionFeeBpsPerHour, maxExtensionFeeBpsPerHour, maxTotalIntentLifetime, ... }
quoteIntentExtension(){ intentAmount, additionalTime } in, { cost, ... } out
getIntentGuardianPayerFunding(){ payer, token } in, the payer's balance and allowance out
extendIntentLifetime() / .prepare()Pay for and apply the extension

extendIntentLifetime() requires escrow, depositId, intentHash, additionalTime in seconds, and maxCost. Approve the deposit token for policy.guardian first. The guardian charges the deposit's token, pays the deposit owner, and doesn't refund extensions.

classifyIntentGuardianError(error) maps a failure to EXTENSIONS_DISABLED, COST_EXCEEDS_MAX, FEE_EXCEEDS_INTENT_AMOUNT, INTENT_ALREADY_EXPIRED, ESCROW_NOT_WHITELISTED, ZERO_ADDRESS, or UNKNOWN.

Quote API​

Both quote methods call Peer's API without credentials. Request fields go in the first argument; the optional second argument only overrides transport settings.

getQuote(req, opts?)​

Finds liquidity for selected payment platforms. Unknown platform names throw before the request.

Request fieldTypeRequiredDescription
paymentPlatformsstring[]YesPlatforms to search, such as ["wise", "revolut"]
fiatCurrencystringYesFiat currency code, such as USD
userstringYesTaker address. Whitelist access is checked for this address
recipientstringYesAddress that receives the tokens
destinationChainIdnumberYes8453 (Base)
destinationTokenstringYesToken address
amountstringYesPositive integer string in the token's base units, in both modes. With USDC, "100000000" means 100 USD with isExactFiat: true, or 100 USDC with isExactFiat: false
isExactFiatbooleanNotrue (default) fixes the fiat amount and quotes tokens. false fixes the token amount and quotes fiat
quotePreferenceQuotePreferenceNoSeller Autopilot or Buyer TEE pool; see preferences
quotesToReturnnumberNoMaximum quotes, a positive integer. The API returns at most 20
escrowAddressesstring[]NoEscrows to search. Defaults to the client's escrow
excludedPayToValuesstring[]NoPayee hashes to skip, such as a maker already tried for this order. At most 100
includeNearbyQuotesbooleanNoWhen nothing matches exactly, also return quotes at nearby amounts. Defaults to false
nearbySearchRangenumberNoMaximum deviation for nearby quotes, in percent (1 to 100). No limit when omitted
nearbyQuotesCountnumberNoNearby quotes per direction, 1 to 10. Defaults to 3
referralFeesReferrerFeeConfig[]NoUp to five { recipient, feeBps }. Takes precedence over referrerFeeConfig, even when empty
referrerFeeConfigReferrerFeeConfigNoOne { recipient, feeBps }, sent as a one-entry referralFees
referrerstringNoReferrer attribution
useMultihopbooleanNoPeer's API rejects true; omit it

Signal with the same referral fees you quoted with. A quote doesn't reserve liquidity or stake; both can change before you signal.

Quote preferences​

quotePreference picks between Seller Autopilot (SAR) liquidity, where the seller's credential verifies the payment, and Buyer TEE liquidity, where the buyer proves it.

quotePreferenceSearch behavior
OmittedBuyer TEE pool only
EXCLUSIVE_SARSeller Autopilot pool only
PREFER_SARSeller Autopilot first, then Buyer TEE as fallback
PREFER_BUYER_TEEBuyer TEE first, then Seller Autopilot as fallback
EXCLUSIVE_BUYER_TEEBuyer TEE pool only

For getQuote, a pass with exact matches ends the search; the fallback pool doesn't top up the count. For getQuotesBestByPlatform, the fallback fills platforms that had no best quote.

Per-call options​

OptionTypeDescription
baseApiUrlstringAPI root for this call. Defaults to the client's baseApiUrl
timeoutMsnumberTimeout in milliseconds. Defaults to the client's timeouts.api
import type { QuoteRequest } from "@zkp2p/sdk";

const taker = "0x0000000000000000000000000000000000000001";

const request = {
paymentPlatforms: ["wise"],
fiatCurrency: "USD",
user: taker,
recipient: taker,
destinationChainId: 8453,
destinationToken: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
amount: "100000000", // 100 USD, in 6-decimal units
isExactFiat: true,
quotePreference: "PREFER_SAR",
quotesToReturn: 3,
} satisfies QuoteRequest;

const quote = await client.getQuote(request, { timeoutMs: 15_000 });
const [best] = quote.responseObject.quotes;

if (best) {
await client.signalIntent({
depositId: BigInt(best.intent.depositId),
amount: best.signalIntentAmount ?? best.intent.amount,
toAddress: taker,
processorName: best.intent.processorName,
payeeDetails: best.intent.payeeDetails,
fiatCurrencyCode: "USD",
conversionRate: best.conversionRate,
});
}

Quote responses​

getQuote() returns { success, message, statusCode, responseObject }:

FieldDescription
quotesMatching quotes. The SDK drops quotes whose payment method it doesn't recognize
nearbySuggestionsWith includeNearbyQuotes and no exact match: { below, above }, each entry a quote plus the suggested amount and its percentage difference
fiat, token, feesCurrency details, token metadata (including decimals), and the protocol fee
quoteExpiresAtISO 8601 time after which to fetch a new quote
orchestratorAddressOrchestrator the quoted intents are signaled on

Each quote includes:

  • intent: depositId, escrowAddress, processorName, amount, toAddress, payeeDetails, fiatCurrencyCode, and chainId.
  • signalIntentAmount: the gross token amount to pass to signalIntent().
  • fiatAmount, tokenAmount, and their Formatted variants; conversionRate with 18 decimals.
  • Fee details: serviceFeeBps, zkp2pFeeAmount, takerReferralFeeBps, takerReferralFeeAmount, managerFeeAmount, and totalFeeAmount, with Formatted variants.
  • whitelistEnabled and allowedGroupIds for the deposit's whitelist.
  • disputeProtectionRequiresStake: the payout may need taker stake or be held; see Taker staking.
  • sellerAutomatedReleaseAvailable: the maker's Seller Autopilot credential is live.

With referrerFeeConfig, the SDK also adds referrerFeeAmount, referrerFeeAmountFormatted, and referrerFeeBps. They aren't added for referralFees.

getQuotesBestByPlatform(req, opts?)​

Returns the best quote for each supported platform. Takes the same fields as getQuote except paymentPlatforms, quotesToReturn, useMultihop, and the nearby-quote options, and adds:

Request fieldTypeDescription
minDepositSuccessRateBpsnumberMinimum maker success rate, 0 to 10000 basis points
supportBusinessAccountsbooleanInclude business accounts. Defaults to false

The response has platformQuotes instead of quotes. Each entry is { platform, supported, available, bestQuote? }; check available before using bestQuote.

const best = await client.getQuotesBestByPlatform({
fiatCurrency: "USD",
user: "0x0000000000000000000000000000000000000001",
recipient: "0x0000000000000000000000000000000000000002",
destinationChainId: 8453,
destinationToken: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
amount: "100000000", // receive exactly 100 USDC
isExactFiat: false,
quotePreference: "PREFER_BUYER_TEE",
});

for (const { platform, available, bestQuote } of best.responseObject
.platformQuotes) {
if (available && bestQuote) {
console.log(platform, bestQuote.intent.depositId, bestQuote.fiatAmount);
}
}

Deposit access policy​

client.accessPolicy reads deposit whitelists and unwinds whitelists created before access groups were retired. It uses WhitelistPolicyV2 and AddressGroupRegistryV4 with OrchestratorV4, and WhitelistPolicy and AddressGroupRegistry with OrchestratorV3. Where they aren't deployed, calls throw AccessPolicyUnsupportedError; check accessPolicy.isSupported first.

MethodPurpose
isEnabled({ escrow, depositId, paymentMethod })Whether the whitelist is on. With OrchestratorV4 it covers the whole deposit
isGroupAllowed({ escrow, depositId, paymentMethod, groupId })Whether one group is allowed
isWhitelisted({ escrow, depositId, taker, groupId? })Whether a taker is admitted. A zero groupId checks a direct grant; a nonzero group must be allowed and contain the taker
groupExists(groupId), isMember({ groupId, account })Group registry reads
getGroup(groupId)OrchestratorV4 registry only: the group, including its arbitrator
getAllowedGroups(...), maxGroupsPerDepositPaymentMethod()OrchestratorV3 only. OrchestratorV4 groups can't be enumerated; use point reads or indexed events
prepareDisable, prepareRemoveAllowedGroups, prepareRemoveWhitelistedAddressesPrepared transactions that turn a whitelist off or remove entries. Only the depositor can send them

A group's arbitrator can release part of, cancel, or extend an intent signaled through that group. With the whitelist off, OrchestratorV4 admits any taker and attaches no arbitrator. Existing whitelists stay enforced until they're disabled or the deposit closes.

Vault and rate-manager operations​

The client exposes vaults as rate managers. Use them to delegate deposits or share pricing.

Create a vault​

Use createRateManager() to create a vault.

FieldRequiredDescription
config.managerYesManager address
config.feeRecipientYesAddress that receives manager fees
config.maxFeeYesMaximum allowed fee
config.feeYesCurrent fee
config.depositHookNoDeposit hook contract
config.minLiquidityNoMinimum USDC liquidity required for delegation
config.nameYesDisplay name
config.uriYesMetadata URI
txOverridesNoviem transaction overrides

Delegation methods​

MethodUse it whenKey parameters
setDepositRateManager()Delegating through the registry and controllerescrow, depositId, registry, rateManagerId
clearDepositRateManager()Clearing controller-based delegationescrow, depositId
setRateManager()Writing directly to EscrowV2depositId, rateManagerAddress, rateManagerId, escrowAddress?
clearRateManager()Clearing direct EscrowV2 delegationdepositId, escrowAddress?

Vault configuration​

MethodDescriptionKey parameters
setVaultFee()Update the manager feerateManagerId, newFee
setVaultMinRate()Set the floor rate for one platform and currencyrateManagerId, paymentMethodHash, currencyHash, rate
setVaultMinRatesBatch()Batch version of setVaultMinRate()rateManagerId, paymentMethods, currencies, rates
setVaultConfig()Update manager, fee recipient, hook, name, or URIrateManagerId, newManager, newFeeRecipient, newHook?, newName, newUri

Rate-manager reads​

MethodReturnsNotes
getDepositRateManager(escrow, depositId){ registry, rateManagerId }Current delegation
getManagerFee(escrow, depositId)bigintEffective manager fee
getEffectiveRate({ escrow, depositId, paymentMethod, fiatCurrency })bigintEscrowV2 rate after the manager's adjustments

Taker staking and dispute-protection risk​

Stake to Take protects sellers on payment methods where the buyer can reverse the fiat payment. On OrchestratorV4, an intent is protected when its payment method has a positive dispute window (14 days by default for Venmo and PayPal), the deposit token is USDC, and the deposit's whitelist is off. At fulfillment, the taker's stake owner locks stake for the window or, without enough free stake, the payout is held in the StakeVault until the window ends. Other intents pay out immediately. The attestor signs each payment's actual window.

DISPUTE_WINDOW_POLICY and getDefaultDisputeWindow(paymentMethod) expose the default windows in seconds. mayRequireDisputeProtection({ paymentMethod, depositToken, stakeToken, whitelistEnabled }) applies the rule above at quote time.

StakeVault collateral belongs to an effective stake owner: the taker itself, or an owner that has authorized the taker. Read getStakeOwner(taker) right before attributing capacity; an expired or revoked selection falls back to the taker's own stake.

Stake and policy reads​

MethodReturns or purpose
getStakeBalance(stakeOwner)Total stake owned
getLockedStake(stakeOwner)Stake locked by open protections
getFreeStake(stakeOwner)Stake available for new protected intents or withdrawal
getClaimable(beneficiary)USDC already released into the beneficiary's claim balance
getStakeOwner(taker)Effective owner after authorization checks
getSelectedStakeOwner(taker)Raw selection; the zero address means none
getTakerAuthorization(stakeOwner, taker)Whether the owner currently sponsors the taker
getStakeVaultState({ staker, taker? })Stake, ownership, authorization, and claimable balance in one read
getAdmissionsPaused()OrchestratorV3 stack only: whether new protected intents are paused
getRiskWindow(paymentMethodHash)OrchestratorV3 stack only: collateral lock window in seconds
isDisputeProtectionEnabled(escrow, depositId, paymentMethod)OrchestratorV3 stack only: whether the deposit method uses protection

Stake methods use the active vault. Pass stakeVaultAddress to read from or withdraw from an earlier known vault; getKnownStakeVaults() lists them and getActiveStakeVaultContract() returns the active one.

Stake and keeper writes​

MethodSigner and behavior
ensureStakeAllowance({ amount })Approves the StakeVault to spend USDC when needed; returns { hadAllowance, hash? }
depositStake({ amount })Deposits USDC; the sender becomes the stake owner
withdrawStake({ amount })Withdraws immediately, up to the free stake
withdrawStakeTo({ to, amount })StakeVaultV2 only: withdraws free stake to another address
claim()Withdraws the caller's whole claimable balance
setTakerAuthorization({ taker, authorized })Signed by the stake owner: sponsor a taker or stop
selectStakeOwner({ stakeOwner })Signed by the taker: use an owner that authorizes it
clearStakeOwner()Signed by the taker: go back to its own stake
setDisputeProtectionEnabled({ escrow, depositId, paymentMethod, enabled })O3-only write retained in SDK 0.15.0-rc.11; do not use for current integrations
releaseMaturedDisputeProtectionIntent() / releaseMaturedDisputeProtectionIntents()OrchestratorV3 stack only: release matured protections; anyone can call it. Takes a disputeProtectionPolicyAddress you pin
release({ intentHashes })OrchestratorV4: release matured protections; see Deferred payouts

Every write except ensureStakeAllowance() has .prepare(). There is no withdrawal request or cooldown.

Indexed state and risk helpers​

client.indexer.getStakingState({ chainId, environment, vaultAddress, disputeProtectionPolicyAddress, taker, stakeOwner }) returns indexed ownership, balances, claims, authorizations, risk windows, and per-row freshness. environment is base or base_staging. Pass a stakeOwner you just read on-chain; if the index hasn't caught up with it, the result reports zero capacity instead of another account's stake.

Pure helpers that match the contract's admission rules:

HelperResult
calculateRequiredCoverage(amount, disputeProtected)The full intent amount for protected methods, otherwise zero
calculateStakeBackedCapacity(freeStake)The largest protected amount the free stake supports
selectRiskMode(amount, freeStake, disputeProtected)UNBONDED, STAKE_BACKED, or an insufficient-stake error

Deployment readiness​

getDisputeProtectionReadiness({ verifyPassiveSuccessor? }) checks the OrchestratorV3 dispute-protection deployment against the manifest packaged with the SDK, with every read pinned to one block. It doesn't cover OrchestratorV4.

StateMeaning
active_successor_verifiedThe manifest's successor hook and its full policy and vault stack are active
recognized_predecessorThe manifest's predecessor hook is still active
passive_successorThe predecessor is active and the requested successor check passed
verified_no_hookOrchestratorV3 has no lifecycle hook
mismatchBuild identity, runtime code, wiring, or an RPC read failed verification

readyForDisputeProtection is true only when the orchestrator and policy also accept new protected intents. Local source builds always return mismatch. Published packages expose their build identity as SDK_BUILD_METADATA and @zkp2p/sdk/build-metadata.json.

For dispute evidence, findDisputeProtectionIntent({ publicClient, chainId, environment?, intentHash, blockNumber? }) finds the policy that holds an intent's protection, and readDisputeAttestationConfig({ publicClient, chainId, environment?, policyAddress? }) reads that policy's attestation witnesses and signature threshold at one block.

Deferred payouts​

When an OrchestratorV4 payout is held, the StakeVault keeps it under a lock keyed by the intent hash until the dispute window ends.

MethodPurpose
getCurrentOrchestratorContract()Orchestrator for new intents: V4 where deployed, otherwise V3
resolveIntentOrchestrator(intentHash)Orchestrator that owns an existing intent
getPayoutStatus(intentHash) / getPayoutStatuses(intentHashes)Payout state per intent
parseDeferredPayoutFromLogs(logs, intentHash)The IntentPayoutDeferred event from a fulfillment receipt, or null
release({ intentHashes, disputeProtectionPolicyAddress?, txOverrides? })Release matured protections; anyone can call it. Has .prepare()
getDisputeProtectionStacks(){ current, draining }: discovers the OrchestratorV4 and historical OrchestratorV3 stacks; either can be null if unavailable
Payout stateMeaning
PENDINGNo indexed fulfillment yet
PAIDPaid at fulfillment
HELDHeld until maturesAt
CLAIMABLEMatured; waiting for release()
RELEASEDThe lock closed: paid to the recipient by release(), or awarded to the depositor in a dispute

HELD and CLAIMABLE include vault, recipient, amount, and maturesAt.

release() pays each held payout to its recipient, whoever sends the transaction, and returns locked stake to its owner's free stake. It reverts with NotMatured before maturity and ProtectionNotFound after a release or dispute. Intents on the OrchestratorV3 stack use releaseMaturedDisputeProtectionIntent(s) instead.

Referral account APIs​

Referral codes belong to wallet addresses. Reads are public and accept requests from any browser origin. Writes are EIP-712 signatures from the client's walletClient, so no Peer account is needed.

MethodDescription
getReferralDashboard({ address })A wallet's code, redemption state, reward rates, referee counts, and lifetime fees
getReferralEarnings({ address })A wallet's earnings breakdown
lookupReferralCode(code){ code, referrerWalletAddress, isActive }
createReferralCodeWithSignature(opts?)Create the wallet's code, or return its existing one
redeemReferralCodeWithSignature(code, opts?)Redeem another wallet's code. Without referrerWalletAddress, looks the code up first and rejects it if inactive
updateReferralCodeWithSignature(code, { oldCode })Rename the wallet's code. oldCode prevents replaying a stale rename
const dashboard = await client.getReferralDashboard({
address: "0x1111111111111111111111111111111111111111",
});

const lookup = await client.lookupReferralCode("PEER42");

const { code: myCode } = await client.createReferralCodeWithSignature();

await client.redeemReferralCodeWithSignature("PEER42", {
referrerWalletAddress: lookup.referrerWalletAddress,
});

await client.updateReferralCodeWithSignature("MYCODE", {
oldCode: myCode,
});

The signature domain is { name: "ZKP2PReferral", version: "1" } (REFERRAL_SIGNATURE_DOMAIN and REFERRAL_SIGNATURE_TYPES). The SDK signs the current Unix time as issuedAt and the audience base_production in production; override them with the issuedAt and audience options. Sign right before sending, because Peer's API rejects stale signatures. Codes are six letters or digits and are uppercased before signing; isValidReferralCode() and normalizeReferralCode() apply the same rules.

Seller Autopilot​

Seller Autopilot verifies payments from the seller's side, so buyers don't have to prove them. The seller connects a payment account once. The attestation enclave seals the credential into an encrypted bundle that Peer's API stores but can't read, and later uses it to check payments. See Seller Autopilot for the trust model.

PlatformHow the seller connects
venmo, paypal, xmoneyA receipt inbox. Venmo and X Money can use Peer's hosted linking page. Any of the three can connect iCloud Mail with uploadICloudMailSellerCredential()
cashapp, upiSession capture in an extension, then uploadSellerCredentialBundle(), or uploadSellerCredential() from a trusted runtime
wiseA Wise Personal API Token, through uploadSellerCredential()

Every flow needs a registered payee: the payeeDetails hash from registerPayeeDetails() or createDeposit(). A connection is live when getSellerCredentialStatus() returns status: "active"; for an inbox, the credentialType must also pass isMailboxSellerCredentialType().

uploadSellerCredential()​

uploadSellerCredential(params, opts?) has the attestation service sign a credential bundle, registers the payee if needed, and stores the bundle with Peer's API. Run it only where the seller's session material is safe, such as an extension background or your backend. The response's responseObject is the credential status.

platformOther params
cashappoffchainId, payeeId, sessionMaterial, and optional telegramUsername, metadata, callerAddress
wisesessionMaterial: { apiToken, profileId? } and optional callerAddress. The enclave derives the payee hash from the token. If the token has several profiles and you omit profileId, handle the profile-selection response
upicallerAddress and sessionMaterial: { sessionCookie, userAgent } from Amazon Pay. The attestor reads the receiving UPI ID, and the SDK registers it

Cash App sessionMaterial is { recipientCashtag, customerId, sessionCookie, requestPayload, requestHeaders? }.

OptionDescription
baseApiUrlAPI root override
attestationServiceUrlDefaults to the attestor that matches baseApiUrl
attestationServiceFallbackUrlsOrigins to try after network failures. Omit to use the attestor's proxy; [] disables fallback
timeoutMsTimeout in milliseconds
attestationRuntimefetch, subtle, or getRandomValues overrides for runtimes without WebCrypto, such as React Native
const response = await client.uploadSellerCredential(
{
platform: "cashapp",
offchainId: "peer-seller",
payeeId: "123456789",
sessionMaterial: {
recipientCashtag: "peer-seller",
customerId: "123456789",
requestPayload: capturedRequestBody,
sessionCookie: "session_cookie",
requestHeaders: {
"user-agent": "Mozilla/5.0",
},
},
},
{ timeoutMs: 10_000 },
);

uploadSellerCredentialBundle()​

Use uploadSellerCredentialBundle(params, opts?) when an extension already created the encrypted bundle, for example with apiCreateSellerCredentialBundle(). This is the page side of the extension Seller Autopilot flow.

For cashapp, upi, and venmo, pass platform, offchainId, bundle, and optional telegramUsername and metadata. For wise, pass platform and bundle. opts takes baseApiUrl and timeoutMs.

The SDK registers the payee, checks that the registered hash equals bundle.payeeIdHash, then stores the bundle. The check stops a tampered capture from binding a credential to a different payee.

const response = await client.uploadSellerCredentialBundle({
platform: "cashapp",
offchainId: capture.offchainId,
bundle: capture.credentialBundle,
});

Hosted Venmo receipt linking​

Peer hosts a page where a seller links Gmail, personal Outlook, or iCloud Mail to a registered Venmo payee. The page runs the consent flow and collects the iCloud app-specific password; your app never receives OAuth codes, passwords, tokens, or email contents. For a cash-out product, @zkp2p/cash wraps this flow and also handles registration.

HelperUse
openVenmoGmailConnect({ payeeDetails, payeeUsername?, peerOrigin?, appearance?, popup? })Browsers. Call it directly from a click handler. Opens a popup on desktop and a tab on mobile, then resolves { payeeDetails }
getVenmoGmailConnectUrl({ payeeDetails, payeeUsername?, peerOrigin?, appearance?, returnUrl? })Native apps and custom windows. Returns the page URL; returnUrl sends the seller back to your app

payeeUsername prefills the iCloud Mail step. peerOrigin defaults to https://app.peer.xyz. For styling, see popup customization.

openVenmoGmailConnect() rejects with VenmoGmailConnectError. Its code is popup_blocked, connection_closed, or connection_timeout (after 10 minutes), or an error code from the page, such as venmo_google_oauth_receipt_not_found or icloud_receipt_not_found.

A closed window or a return to your app doesn't prove the link worked. Read getSellerCredentialStatus() afterwards: the inbox is linked only when status is active and isMailboxSellerCredentialType(credentialType) is true. Your browser origin needs access to Peer's API to read status; otherwise read it from your server.

Hosted X Money receipt linking​

openXMoneyConnect() and getXMoneyConnectUrl() (SDK 0.14.10 or later) open the same page for X Money and take the same options. payeeUsername is required: the trimmed, lowercased X username without @ must hash to payeeDetails, or the helper throws ValidationError. Failures reject with XMoneyConnectError. The seller needs X Money email notifications on and a received payment in the inbox.

import { openXMoneyConnect } from "@zkp2p/sdk";

// Call directly from the link button's click handler.
await openXMoneyConnect({ payeeDetails, payeeUsername: "yourhandle" });

Cash-out products can use Peer Cash's X Money helpers.

uploadGoogleOAuthSellerCredential()​

uploadGoogleOAuthSellerCredential(params, opts?) sends a one-time Google authorization code for a Gmail inbox to Peer's API, which exchanges it and stores the credential. The code must come from Peer's Google OAuth client and a redirect URI registered for it, so partner apps should use the hosted page.

FieldRequiredDescription
platformYespaypal, venmo, or xmoney
payeeDetailsYesRegistered payee hash
authorizationCodeYesOne-time Google OAuth code
redirectUriYesExact redirect URI from the authorization request
payeeEmailPayPal onlyPayPal receiving email

uploadMicrosoftOAuthSellerCredential() connects a personal Outlook inbox. It takes the same fields plus codeVerifier from the PKCE request. Both accept opts with baseApiUrl, timeoutMs, and signal.

Codes are single-use and the SDK doesn't retry. If the response is lost, start consent again. For Venmo, Peer's API resolves the account from the registered username, so don't send an account ID. For X Money, it uses the registered X username; don't send payeeEmail. XMONEY_EMAIL_RECEIPT_NOT_FOUND means the inbox has no received-payment email for that account: the seller should turn on X Money email notifications, receive a payment, and reconnect.

uploadICloudMailSellerCredential()​

Connects an iCloud Mail inbox for PayPal or Venmo (SDK 0.14.7 or later) or X Money (0.14.9 or later). iCloud has no OAuth, so the seller creates an Apple app-specific password and your app passes it with the payee hash. Returns the stored status, { platform, payeeIdHash, status: "active", credentialType: "icloud_mail" }.

FieldRequiredDescription
platformYespaypal, venmo, or xmoney
payeeDetailsYesRegistered payee hash
mailboxEmailYesPrimary iCloud Mail address that receives the receipts. Trimmed and lowercased
appSpecificPasswordYesApple app-specific password. Encrypted to the enclave; never retained
payeeEmailPayPal onlyPayPal receiving email. keccak256 of the lowercased email must equal payeeDetails
payeeUsernameVenmo, X MoneyTrimmed, with a leading @ removed, and lowercased. X Money: the X username, whose hash must equal payeeDetails. Venmo: the seller's Venmo username; its account ID's hash must equal payeeDetails
OptionDescription
baseApiUrlAPI root override
attestationServiceUrlDefaults to the attestor that matches baseApiUrl. Must be one of Peer's attestors; other hosts throw before any request
attestationRuntimefetch, subtle, or getRandomValues overrides for runtimes without WebCrypto, such as React Native

The method runs these steps once. Apart from the proxy fallback in step 2, it doesn't retry.

  1. Payee check. PayPal and X Money hashes are checked locally. For Venmo, Peer's API looks up the account ID for payeeUsername from Venmo's public profile, and the SDK checks its hash. A mismatch rejects before any mailbox work.
  2. Upload. The SDK verifies the enclave's attestation, encrypts the password to it, and sends it to the attestor. After a network failure, it retries once through that attestor's proxy, which serves the same enclave. The enclave signs in to the inbox and requires a received-payment receipt for this payee.
  3. Bundle check. The SDK verifies the returned bundle against the enclave's pinned signer, the platform, and payeeDetails, and requires credentialType: "icloud_mail".
  4. Store. Peer's API stores the unchanged bundle. The payee isn't registered again.

Only one connection per inbox runs at a time in a process. Failures reject with ICloudMailCredentialError; route on its reason:

reasonMeaning
payee_mismatchThe payee doesn't hash to payeeDetails; no mailbox work ran
payee_not_foundVenmo has no account for that username; no mailbox work ran
maker_not_foundPeer's API has no payee with this hash
mailbox_busyAnother connection for this inbox is running
rate_limitedTry again after retryAfterMs
receipt_not_foundNo verified received-payment receipt for this payee in the inbox
app_password_rejectedApple rejected the app-specific password; the seller creates a new one
upload_rejectedThe enclave rejected the upload for another reason; see cause
unavailableTemporary attestor, API, or network failure; a user-triggered retry may succeed
bundle_unverifiedThe attestation, signature, payee binding, or credential type failed verification; nothing was stored
auth_requiredPeer's API rejected the request's authorization
store_failedPeer's API didn't store an active iCloud credential for this payee

An empty mailboxEmail or appSpecificPassword, a missing payeeEmail or payeeUsername, or an empty Venmo payeeUsername throws ValidationError.

import { ICloudMailCredentialError } from "@zkp2p/sdk";

try {
const status = await client.uploadICloudMailSellerCredential({
platform: "venmo",
payeeUsername: "your-venmo-username",
payeeDetails: registeredPayeeHash,
mailboxEmail: "seller@icloud.com",
appSpecificPassword,
});
console.log(status.credentialType); // "icloud_mail"
} catch (error) {
if (!(error instanceof ICloudMailCredentialError)) throw error;
// Show copy for error.reason; error.retryAfterMs is set for rate_limited.
}

React Native has no WebCrypto by default. Pass { attestationRuntime: { subtle, getRandomValues } } as the second argument, plus fetch if your runtime needs one.

isMailboxSellerCredentialType()​

isMailboxSellerCredentialType(credentialType) (SDK 0.14.7 or later) returns true for google_oauth (Gmail), microsoft_oauth (Outlook), and icloud_mail (iCloud Mail). It accepts string | null | undefined.

import { isMailboxSellerCredentialType } from "@zkp2p/sdk";

const { responseObject } = await client.getSellerCredentialStatus({
processorName: "venmo",
payeeDetails,
});
const linked =
responseObject.status === "active" &&
isMailboxSellerCredentialType(responseObject.credentialType);

getSellerCredentialStatus()​

getSellerCredentialStatus({ processorName, payeeDetails }, opts?) reads a payee's public credential status. responseObject is { platform, payeeIdHash, status, credentialType }, where status is active, inactive, or missing. opts takes baseApiUrl and timeoutMs.

const response = await client.getSellerCredentialStatus(
{
processorName: "paypal",
payeeDetails:
"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
},
{ timeoutMs: 10_000 },
);

Standalone API and attestation helpers​

Low-level helpers for calling Peer's API or the attestation service without a client. Pass baseApiUrl as the API root, https://api.zkp2p.xyz.

HelperPurpose
apiGetQuote(req, { baseApiUrl, timeoutMs? }), apiGetQuotesBestByPlatform(req, { baseApiUrl, timeoutMs? })Quote calls
apiSignIntentV3(request, { baseApiUrl, timeoutMs? })Fetch a gating signature for a signal. Returns { signature, signatureExpiration, ... }
apiGetOrderbook(params, { baseApiUrl, timeoutMs? })Orderbook entries for a currency
apiGetOrderbookTable(params, { baseApiUrl, timeoutMs?, runtimeEnv? })Aggregated orderbook rows. With runtimeEnv, drops rows on escrows signalIntent() can't use
apiGetDepositBundle({ depositId, escrowAddress, dailySnapshotLimit? }, { baseApiUrl, timeoutMs? })One deposit with its intents, events, profit snapshots, fund activities, and daily snapshots
apiValidatePayeeDetails(req, baseApiUrl, timeoutMs?)Validate a payee before registration
apiPostDepositDetails(req, baseApiUrl, timeoutMs?)Register one payee; returns its hashedOnchainId
apiGetSellerCredentialStatus(processorName, payeeDetails, baseApiUrl, timeoutMs?)Seller credential status
apiUploadSellerCredentialBundle(params, baseApiUrl?, timeoutMs?)Register the payee if needed, check the bundle's payee hash, and store the bundle
apiUploadGoogleOAuthSellerCredential(...), apiUploadMicrosoftOAuthSellerCredential(...)Inbox code uploads behind the client methods
apiGetReferralDashboard({ baseApiUrl, address }), apiGetReferralEarnings({ baseApiUrl, address })Public referral reads
apiLookupReferralCode(code, { baseApiUrl })Resolve a referral code
apiCreateReferralCode, apiRedeemReferralCode, apiUpdateReferralCodeReferral writes; send the signed body the client's WithSignature methods build
createNitroAttestationClient(opts)Verify the Nitro enclave and request identity attestations, Buyer TEE attestations, or seller credential bundles
apiRequestIdentityAttestation(payload, attestationServiceUrl, platform, actionType, options?)Request an identity attestation with already-encrypted session material
createEncryptedBuyerTeeSessionMaterial(input)Encrypt Buyer TEE session material for a payment proof
apiVerifyBuyerTeePayment(payload, attestationServiceUrl, platform, actionType, options?)Request a Buyer TEE payment attestation
apiCreateSellerCredentialBundle(payload, attestationServiceUrl, platform, timeoutMs?, runtime?, options?)Create a signed seller credential bundle
getAmazonPaySellerIdentity(bundle)The receiving UPI ID inside an Amazon Pay bundle

apiGetOrderbook() and apiGetOrderbookTable() take { currency, paymentPlatform?, takerAddress?, sortBy?, sortDirection?, sellerAutomatedRelease?, limit?, offset?, amount?, showSmallOrders?, hideExtremeSpread?, chainId?, token? }. sortBy is price, available, limits, or apr. sellerAutomatedRelease is include, exclude, or only.

Querying on-chain data​

These reads go to the chain through ProtocolViewer, so they don't lag behind the indexer:

  • Deposits: getDeposits() (the connected wallet's), getAccountDeposits(owner), getDeposit(depositId), getDepositsById(ids)
  • Intents: getIntents() (the connected wallet's), getAccountIntents(owner), getIntent(intentHash)
  • Raw ProtocolViewer views: getPvDepositById(depositId), getPvDepositsFromIds(ids), getPvAccountDeposits(owner), getPvAccountIntents(owner), getPvIntent(intentHash)
  • Other: resolvePayeeHash(depositId, paymentMethodHash), getFulfillIntentInputs(intentHash), getDepositPreIntentHook(depositId, options?), getDepositWhitelistHook(depositId, options?), getDeployedAddresses(), getUsdcAddress()

Intent reads cover OrchestratorV2, V3, and V4 intents. For examples, see Offramp Integration.

Indexer​

Use client.indexer for history, filtering, and pagination across all deposits and intents. The production endpoint is https://indexer.zkp2p.xyz/v1/graphql. It is rate limited per caller; the client retries 429 responses after Retry-After. Indexed data can trail a confirmed transaction, so confirm with the receipt and on-chain reads.

Indexer deposit IDs have the form escrowAddress_depositId, with the escrow address in lowercase. Build one with createCompositeDepositId(escrowAddress, depositId).

const deposits = await client.indexer.getDeposits(
{ status: "ACTIVE", depositor: "0xMakerAddress" },
{ limit: 25, orderBy: "updatedAt", orderDirection: "desc" },
);

Deposit filters accept status (ACTIVE or CLOSED), depositor, delegate, delegateIsSet, chainId, escrowAddress, escrowAddresses, minLiquidity, and acceptingIntents. Pagination accepts limit, offset, orderBy, and orderDirection.

Deposit queries​

  • getDeposits(filter?, pagination?)
  • getDepositsWithRelations(filter?, pagination?, options?)
  • getDepositById(compositeId, options?)
  • getDepositsByIds(ids)
  • getDepositsByIdsWithRelations(ids, options?)
  • getDepositsByPayeeHash(payeeHash, options?)

Intent queries​

  • getIntentsForDeposits(depositIds, statuses?)
  • getOwnerIntents(owner, statuses?)
  • getIntentsByRateManager(rateManagerId, statuses?)
  • getIntentByHash(intentHash)
  • getExpiredIntents({ now, depositIds, limit? })
  • getFulfilledIntentEvents(intentHashes): fulfillment events
  • getIntentFulfillmentAmounts(intentHash): includes takerAmountNetFees, the net USDC the taker received
  • getFulfillmentAndPayment(intentHash)

Fund activity and snapshots​

  • getDepositFundActivities(depositId)
  • getMakerFundActivities(depositor, limit?)
  • getDepositDailySnapshots(depositId, limit?)
  • getProfitSnapshotsByDeposits(depositIds)

Rate manager (vault) queries​

  • getRateManagers(pagination?, filter?)
  • getRateManagerDetail(managerId, options?)
  • getRateManagerDelegations(managerId, pagination?)
  • getDelegationForDeposit(depositId, options?)
  • getManagerDailySnapshots(managerId, options?)
  • getManualRateUpdates(managerId, options?)
  • getOracleConfigUpdates(managerId, options?)

Staking queries​

  • getStakingState({ chainId, environment, vaultAddress, disputeProtectionPolicyAddress, taker, stakeOwner })
  • getStakeTotals({ chainId, vaultAddress }): totalStaked, totalLocked, and stakerCount
  • getStakeLocks({ chainId, vaultAddress, stakeOwner, status?, limit?, cursor? })
  • getTakerStakeAuthorizations({ chainId, vaultAddress, stakeOwner | taker, authorized?, limit?, cursor? }): the takers an owner authorized, or the owners that authorized a taker
  • getStakeActivity({ chainId, vaultAddress, merchant?, kind?, orderDirection?, limit?, cursor? }): merchant matches the stake owner or taker; omit it for the whole vault. orderDirection defaults to asc
  • getDisputeProtectionIntentByHash({ chainId, intentHash }): the indexed protection, including released and disputed ones, or null
  • getDisputeProtectionIntents({ chainId, policyAddress, throughBlockNumber, limit?, cursor? })

Page methods return { items, nextCursor, asOf }. Pass cursors back unchanged, and keep orderDirection the same across pages. Limits are integers from 1 to 100, and numbers are unsigned decimal strings. asOf is the latest row in the page, not an indexer head or consistency watermark.

getDisputeProtectionIntents needs throughBlockNumber, an inclusive upper bound on disputedAtBlockNumber; use one finalized block for every page of a pass. Its page also returns endCursor, the cursor of its last item (null only for an empty page). Follow nextCursor within a pass, then store the final endCursor for the next poll.

Raw access​

  • query<T>({ query, variables? }): raw GraphQL
  • client: the underlying IndexerClient

The package also exports IndexerClient, IndexerDepositService, IndexerRateManagerService, IndexerStakingService, and fetchIndexerFulfillmentAndPayment(client.indexer.client, intentHash).

Indexer converters​

These turn indexer payloads into the EscrowDepositView shape that RPC reads return.

HelperPurpose
convertIndexerDepositToEscrowView(deposit, chainId, escrowAddress)Convert one indexer deposit, with relations
convertDepositsForLiquidity(deposits, chainId, escrowAddress, options?)Keep active, funded deposits that accept intents. Pass { includePrivateOrderbooks: true } to keep deposits behind a whitelist hook
convertIndexerIntentsToEscrowViews(intents, depositViewsById)Convert indexer intents to EscrowIntentView[]

Oracle helpers​

With an oracle config, a deposit's floor rate for a currency is the greater of its minimum rate and the Chainlink rate after the spread. If the feed is stale or invalid, that currency stops accepting intents. The config is { adapter, adapterConfig, spreadBps, maxStaleness }. Take adapter, adapterConfig, and maxStaleness from getSpreadOracleConfig(currency). spreadBps is a signed spread on the oracle rate; -50 is 0.5% below it.

import {
getSpreadOracleConfig,
resolveFiatCurrencyBytes32,
resolvePaymentMethodHash,
} from "@zkp2p/sdk";

const oracle = getSpreadOracleConfig("USD");
if (!oracle) throw new Error("No bundled oracle config for USD");

await client.setOracleRateConfig({
depositId: 42n,
paymentMethodHash: resolvePaymentMethodHash("wise"),
currencyHash: resolveFiatCurrencyBytes32("USD"),
config: {
adapter: oracle.adapter,
adapterConfig: oracle.adapterConfig,
spreadBps: -50,
maxStaleness: oracle.maxStaleness,
},
});
HelperPurpose
getSpreadOracleConfig(currency, adapters?)The bundled Chainlink oracle config for a fiat currency, or null
encodeSpreadOracleAdapterConfig(config)Encode a Chainlink adapter config
validateOracleFeedsOnChain(publicClient)The currencies whose bundled feeds are available on-chain
supportsInlineOracleRateConfig({ escrowAddress? })Client method: whether the target escrow accepts inline oracle configs

Constants: CHAINLINK_ORACLE_ADAPTER, DEFAULT_ORACLE_MAX_STALENESS_SECONDS, CHAINLINK_ORACLE_FEEDS, and its deprecated alias SPREAD_ORACLE_FEEDS. PYTH_ORACLE_ADAPTER, PYTH_ORACLE_FEEDS, and encodePythAdapterConfig() remain only for compatibility; the bundled Pyth feed map is empty and isn't a fallback.

Referrer fees​

Helpers to validate referrer fee settings before getQuote() or signalIntent().

HelperPurpose
assertValidReferrerFeeConfig(config, context)Throws if the config is invalid for getQuote, getQuotesBestByPlatform, or signalIntent
isValidReferrerFeeRecipient(value)Whether the recipient is a valid address
isValidReferrerFeeBps(value)Whether the value is an integer from 0 to 10000
parseReferrerFeeConfig(recipient, feeBpsValue)Build a ReferrerFeeConfig from loose input, or null
referrerFeeConfigToPreciseUnits(config)Convert feeBps to the 18-decimal fee used on-chain

Attribution​

The SDK appends ERC-8021 builder codes to transaction calldata. Pass your own codes in txOverrides.referrer; the SDK adds BASE_BUILDER_CODE after them.

await client.signalIntent({
...params,
txOverrides: { referrer: ["my-app", "campaign-42"] },
});

createDeposit() and prepareCreateDeposit() also take referralCode, a Peer referral code for deposit attribution. It must be six letters or digits; the SDK trims and uppercases it, then adds the code peer-ref- plus the referral code before your other codes. It doesn't set a fee or redeem a referral. A peer-ref- code already in txOverrides.referrer throws, so send a referral through one option only.

HelperPurpose
getAttributionDataSuffix(referrer?)Build the attribution suffix
appendAttributionToCalldata(calldata, referrer?)Append attribution to existing calldata
encodeWithAttribution(request, referrer?)Encode calldata and append attribution
sendTransactionWithAttribution(walletClient, request, referrer?, overrides?)Send a transaction with attribution

Constants: BASE_BUILDER_CODE, ZKP2P_IOS_REFERRER, ZKP2P_ANDROID_REFERRER.

Contract helpers​

HelperDescription
getContracts(chainId, env?)Deployed addresses and ABIs for escrow, orchestrators, verifiers, ProtocolViewer, USDC, and others
getRateManagerContracts(chainId, env?)Rate-manager registry and controller
getStakeVaultContract(chainId, env?)StakeVault address, ABI, and stake token
getDisputeProtectionPolicyContract(chainId, env?)DisputeProtectionPolicy address and ABI
getOrchestratorV3Contract(chainId, env?)OrchestratorV3 address and ABI
getOrchestratorV4Contract(chainId, env?), getDisputeProtectionPolicyV2Contract(chainId, env?), getStakeVaultV2Contract(chainId, env?)OrchestratorV4 stack contracts; throw where not deployed
getIntentGuardianContract(chainId, env?), hasIntentGuardian(chainId, env?)IntentGuardian address and ABI, and whether one is deployed
getPaymentMethodsCatalog(chainId, env?)Platform-to-hash catalog for the deployment
getGatingServiceAddress(chainId, env?)Gating-service signer for OrchestratorV3 deposits

A client also has getStakeVaultContract(), getOrchestratorV3Contract(), and getDisputeProtectionPolicyContract(), resolved for its chain and environment.

Currency and platform helpers: Currency, currencyInfo, getCurrencyInfoFromHash(), getCurrencyInfoFromCountryCode(), resolveFiatCurrencyBytes32(), resolvePaymentMethodHash(), resolvePaymentMethodHashFromCatalog(), and resolvePaymentMethodNameFromHash().

Error handling​

SDK errors extend ZKP2PError:

ClassCodeExtra fieldsUse it for
ZKP2PErrorAny ErrorCodedetails?, field?Shared base class
ValidationErrorVALIDATIONfield?, details?Invalid input
NetworkErrorNETWORKdetails?RPC or network failures
APIErrorAPIstatus?, details?Failed API requests
ContractErrorCONTRACTdetails?Contract call or simulation failures

ErrorCode values: VALIDATION, NETWORK, API, CONTRACT, UNKNOWN. These errors extend Error instead: AccessPolicyUnsupportedError, ICloudMailCredentialError, VenmoGmailConnectError, and XMoneyConnectError. Wallet and transport failures can also surface as plain Error.

import {
APIError,
ContractError,
ValidationError,
ZKP2PError,
} from "@zkp2p/sdk";

try {
await client.createDeposit({
/* ... */
});
} catch (error) {
if (error instanceof ValidationError) {
console.error(error.field, error.message);
} else if (error instanceof APIError) {
console.error(error.status, error.message);
} else if (error instanceof ContractError) {
console.error(error.details);
} else if (error instanceof ZKP2PError) {
console.error(error.code, error.message);
}
}

Logging​

import { setLogLevel } from "@zkp2p/sdk";

setLogLevel("debug"); // 'debug' | 'info' | 'error'; defaults to 'info'

Help?​

For integration questions, join the Peer Telegram community. For order-specific help, email support@peer.xyz.