@zkp2p/sdk
v0.3.1
Published
ZKP2P Client SDK - TypeScript SDK for deposit management, liquidity provision, and onramping
Readme
@zkp2p/sdk
Stable TypeScript SDK for trustless fiat-to-crypto on Base. ZKP2P combines escrowed on-chain settlement, TLS attestations for payment verification, and API/indexer helpers so makers, takers, wallets, and embedded ramps can ship production-grade fiat liquidity flows without building their own contract or indexing stack.
Current version: 0.3.1
Why This SDK
- Build maker, taker, vault, and embedded ramp flows from one client.
- Read live protocol state directly from ProtocolViewer instead of waiting on indexer sync.
- Route against the upgraded escrow/orchestrator stack while preserving historical deposit visibility.
- Use prepareable write methods for smart accounts, relayers, gas estimation, and batching.
- Keep advanced analytics, fund activity history, and vault stats behind
client.indexer.*.
Who This Is For
- maker and liquidity-provider applications
- wallets and embedded onramp/offramp products
- backends that need contract-safe deposit and intent operations
- dashboards and analytics services that query
client.indexer.* - React apps that want first-party hooks instead of writing transaction glue
Architecture
- RPC-first reads: primary reads use ProtocolViewer and on-chain fallbacks, so
getDeposits(),getDeposit(),getIntents(),getIntent(), and thegetPv*methods are not blocked on indexer lag. - Indexer for history and filtering: use
client.indexer.*for pagination, historical volumes, fund activities, daily snapshots, and vault analytics. - Current-stack routing: the client resolves against the upgraded escrow and orchestrator contracts, with historical escrow lookups available for reads and legacy deposits.
- Modular internals: intent, vault, and ProtocolViewer logic are extracted into
IntentOperations,VaultOperations, andProtocolViewerReader, keepingZkp2pClientfocused on orchestration. - App-level rollout control: the SDK is capability-based. Product gating and phase flags belong in your app layer, not inside transaction helpers.
Installation
npm install @zkp2p/sdk viem
# or
pnpm add @zkp2p/sdk viem
# or
bun add @zkp2p/sdk viemIf you use React hooks, add React as well:
pnpm add reactQuick Start
import { Zkp2pClient } from '@zkp2p/sdk';
import { createWalletClient, custom } from 'viem';
import { base } from 'viem/chains';
const walletClient = createWalletClient({
chain: base,
transport: custom(window.ethereum),
});
const client = new Zkp2pClient({
walletClient,
chainId: base.id,
});runtimeEnv defaults to 'production'. Use 'preproduction' or 'staging' when you want to point the SDK at those deployments.
Environment and Authentication
- Supported runtime environments:
'production','preproduction','staging' runtimeEnvdefault:'production'rpcUrl: optional RPC URL override (defaults to wallet's chain RPC)apiKey: optional curator-issued API key — used when the SDK auto-fetchessignalIntent()gating signatures and enables richer authenticatedgetQuote()responses with resolved maker payee details (offchainId,telegramUsername,metadata)authorizationToken/getAuthorizationToken: optional bearer auth for hybrid client and indexer flowsindexerApiKey: optionalx-api-keyfor indexer proxy authindexerUrlandbaseApiUrl: override defaults when you are targeting custom deploymentstimeouts:{ api?: number }— API timeout in milliseconds (default 15000)
No API key is required to get started. createDeposit, registerPayeeDetails, getQuote, getTakerTier, and the rest of the SDK work without apiKey or authorizationToken. The main tradeoff is that signalIntent() only auto-fetches a gating service signature from curator /v3/intent/sign when apiKey or authorizationToken is available; otherwise provide both gatingServiceSignature and signatureExpiration manually if your integration needs one. Authenticated quotes also include resolved maker payee details (offchainId, telegramUsername, metadata).
Indexer defaults by environment:
production:https://indexer.zkp2p.xyz/v1/graphqlpreproduction:https://indexer-preprod.zkp2p.xyz/v1/graphqlstaging:https://indexer-staging.zkp2p.xyz/v1/graphql
Core Capabilities
| Area | Stable surface |
| ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Deposits | createDeposit, addFunds, removeFunds, withdrawDeposit, ensureAllowance, setAcceptingIntents, setIntentRange, setCurrencyMinRate, setRetainOnEmpty |
| Payment methods and currencies | addPaymentMethods, removePaymentMethod, setPaymentMethodActive, addCurrencies, removeCurrency, deactivateCurrency, pruneExpiredIntents |
| Intents | signalIntent, fulfillIntent, cancelIntent, releaseFundsToPayer, getFulfillIntentInputs |
| Prepared transactions | client.prepareCreateDeposit(...), client.signalIntent.prepare(...), client.fulfillIntent.prepare(...), client.setVaultFee.prepare(...), and equivalent prepare flows across the rest of the prepareable write surface |
| Payee and quote APIs | registerPayeeDetails, resolvePayeeHash, getQuote, getQuotesBestByPlatform, getTakerTier |
| Delegation and hooks | setDelegate, removeDelegate, setRateManager, clearRateManager, setDepositRateManager, clearDepositRateManager, setDepositPreIntentHook, setDepositWhitelistHook |
| Vault / DRM | createRateManager, setVaultMinRate, setVaultMinRatesBatch, setVaultFee, setVaultConfig, getDepositRateManager, getManagerFee, getEffectiveRate |
| Oracle config | setOracleRateConfig, setOracleRateConfigBatch, removeOracleRateConfig, updateCurrencyConfigBatch, deactivateCurrenciesBatch, supportsInlineOracleRateConfig, validateOracleFeedsOnChain |
| RPC reads | getDeposits, getDeposit, getDepositsById, getIntents, getIntent, getPvDepositById, getPvDepositsFromIds, getPvAccountDeposits, getPvAccountIntents, getPvIntent |
| Indexer | client.indexer.getDeposits, getDepositsWithRelations, getDepositById, getDepositsByIds, getDepositsByIdsWithRelations, getDepositsByPayeeHash, getIntentsForDeposits, getOwnerIntents, getIntentsByRateManager, getIntentByHash, getExpiredIntents, getFulfilledIntentEvents, getIntentFulfillmentAmounts, getFulfillmentAndPayment, getDepositFundActivities, getMakerFundActivities, getDepositDailySnapshots, getProfitSnapshotsByDeposits, getRateManagers, getRateManagerDetail, getRateManagerDelegations, getDelegationForDeposit, getManagerDailySnapshots, getManualRateUpdates, getOracleConfigUpdates, query |
| React hooks | @zkp2p/sdk/react exports hooks for deposits, intents, delegation, vaults, payment methods, and taker tier |
| Attribution | ERC-8021 helpers like sendTransactionWithAttribution, encodeWithAttribution, and txOverrides.referrer support |
Create a Deposit
import { Currency } from '@zkp2p/sdk';
const result = await client.createDeposit({
token: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
amount: 10_000_000n,
intentAmountRange: { min: 100_000n, max: 5_000_000n },
processorNames: ['wise'],
depositData: [{ email: '[email protected]' }],
conversionRates: [[{ currency: Currency.USD, conversionRate: '1020000000000000000' }]],
});
console.log(result.hash);
console.log(result.depositDetails);If you do not pass payeeDetailsHashes, createDeposit() can register the payee details for you. If you want to pre-register or reuse hashes across deposits, use registerPayeeDetails() first.
Payee Registration, Quotes, and Taker Tier
import { resolvePaymentMethodHash } from '@zkp2p/sdk';
const { hashedOnchainIds } = await client.registerPayeeDetails({
processorNames: ['wise'],
depositData: [{ email: '[email protected]' }],
});
const quote = await client.getQuote({
paymentPlatforms: ['wise'],
fiatCurrency: 'USD',
user: '0xBuyer',
recipient: '0xBuyer',
destinationChainId: 8453,
destinationToken: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
amount: '250',
isExactFiat: true,
});
const takerTier = await client.getTakerTier({
owner: '0xBuyer',
chainId: 8453,
});
const payeeHash = await client.resolvePayeeHash(
42n,
resolvePaymentMethodHash('wise', { env: 'production' }),
);getQuote() returns available liquidity plus payee details when authenticated. Use getQuotesBestByPlatform() to fetch the best quote per supported payment platform in a single call (handy for cross-platform comparison UIs). getTakerTier() returns limits and cooldown data for taker UX.
Signal, Fulfill, and Release Intents
const intentHash = await client.signalIntent({
depositId: 42n,
amount: 250_000n,
toAddress: '0xBuyer',
processorName: 'wise',
payeeDetails: '0xPAYEE_DETAILS_HASH',
fiatCurrencyCode: 'USD',
conversionRate: 1_020_000_000_000_000_000n,
referralFees: [
{ recipient: '0xPlatformFeeRecipient', fee: 2_500n },
{ recipient: '0xPartnerFeeRecipient', fee: 1_250n },
],
});
await client.fulfillIntent({
intentHash,
proof: zkTlsProofJson,
});
await client.releaseFundsToPayer({
intentHash,
});Use referralFees[] for multi-recipient fee distribution on OrchestratorV2.
Prepared Transactions
Most write methods expose .prepare(params) and return a PreparedTransaction without sending the transaction. createDeposit() is the main exception and uses client.prepareCreateDeposit(params), which returns { depositDetails, prepared }. This is the stable pattern for smart accounts, batched user-ops, relayers, gas estimation, and custom senders.
const prepared = await client.signalIntent.prepare({
depositId: 42n,
amount: 250_000n,
toAddress: '0xBuyer',
processorName: 'wise',
payeeDetails: '0xPAYEE_DETAILS_HASH',
fiatCurrencyCode: 'USD',
conversionRate: 1_020_000_000_000_000_000n,
});
console.log(prepared);
// { to, data, value, chainId }React hooks mirror this where useful. For example, useSignalIntent() returns prepareSignalIntent() and prepared.
Error Handling
The SDK exports a normalized error model:
ZKP2PErrorValidationErrorNetworkErrorAPIErrorContractErrorErrorCode
import { APIError, ErrorCode, ValidationError, ZKP2PError } from '@zkp2p/sdk';
try {
await client.createDeposit({
token: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
amount: 10_000_000n,
intentAmountRange: { min: 100_000n, max: 5_000_000n },
processorNames: ['wise'],
depositData: [{ email: '[email protected]' }],
conversionRates: [[{ currency: 'USD', conversionRate: '1020000000000000000' }]],
});
} catch (error) {
if (error instanceof ValidationError) {
console.error('Invalid field:', error.field, error.message);
} else if (error instanceof APIError) {
console.error('HTTP status:', error.status, error.message);
} else if (error instanceof ZKP2PError) {
console.error('SDK error:', error.code ?? ErrorCode.UNKNOWN, error.details);
} else {
console.error('Unknown error:', error);
}
}You should still handle generic Error for lower-level transport or wallet failures that originate outside the normalized SDK wrappers.
Oracle Configuration
The SDK exports Chainlink and Pyth adapter helpers, plus a Chainlink-first getSpreadOracleConfig() resolver for bundled feed metadata.
import {
getSpreadOracleConfig,
resolveFiatCurrencyBytes32,
resolvePaymentMethodHash,
validateOracleFeedsOnChain,
} from '@zkp2p/sdk';
const paymentMethodHash = resolvePaymentMethodHash('wise', { env: 'production' });
const currencyHash = resolveFiatCurrencyBytes32('USD');
const oracle = getSpreadOracleConfig('USD');
if (!oracle) throw new Error('No bundled oracle config for USD');
await client.setOracleRateConfig({
depositId: 42n,
paymentMethodHash,
currencyHash,
config: {
adapter: oracle.adapter,
adapterConfig: oracle.adapterConfig,
spreadBps: -50,
maxStaleness: oracle.maxStaleness,
},
});
await client.updateCurrencyConfigBatch({
depositId: 42n,
paymentMethods: [paymentMethodHash],
updates: [
[
{
code: currencyHash,
minConversionRate: 1_020_000_000_000_000_000n,
updateOracle: true,
oracleRateConfig: {
adapter: oracle.adapter,
adapterConfig: oracle.adapterConfig,
spreadBps: -50,
maxStaleness: oracle.maxStaleness,
},
},
],
],
});
const availableFeeds = await validateOracleFeedsOnChain(client.publicClient);
console.log([...availableFeeds].sort());Useful helpers and methods in this area:
CHAINLINK_ORACLE_ADAPTERPYTH_ORACLE_ADAPTERencodeSpreadOracleAdapterConfigencodePythAdapterConfiggetSpreadOracleConfigsetOracleRateConfigBatchupdateCurrencyConfigBatchdeactivateCurrenciesBatch
Delegation and Hooks
await client.setDelegate({
depositId: 42n,
delegate: '0xDelegate',
});
await client.setRateManager({
depositId: 42n,
rateManagerAddress: '0xVaultAddress',
rateManagerId: '0xVaultId',
});
await client.setDepositPreIntentHook({
depositId: 42n,
preIntentHook: '0xHookAddress',
});
await client.setDepositWhitelistHook({
depositId: 42n,
whitelistHook: '0xHookAddress',
});Use removeDelegate() and clearRateManager() to unwind delegation. Phase-4 hook methods are available when the target deployment supports them.
Vault / DRM
const createHash = await client.createRateManager({
config: {
manager: '0xManager',
feeRecipient: '0xFeeRecipient',
maxFee: 50_000_000_000_000_000n,
fee: 10_000_000_000_000_000n,
name: 'My Vault',
uri: 'ipfs://vault-metadata',
},
});
await client.setVaultFee({
rateManagerId: '0xVaultId',
newFee: 20_000_000_000_000_000n,
});
await client.setVaultMinRate({
rateManagerId: '0xVaultId',
paymentMethodHash: resolvePaymentMethodHash('wise', { env: 'production' }),
currencyHash: resolveFiatCurrencyBytes32('USD'),
rate: 1_010_000_000_000_000_000n,
});
await client.setVaultConfig({
rateManagerId: '0xVaultId',
newManager: '0xNewManager',
newFeeRecipient: '0xNewFeeRecipient',
newName: 'Updated Vault',
newUri: 'ipfs://updated-vault-metadata',
});Protocol Viewer (RPC) Reads
ProtocolViewer methods bypass indexer lag and are the right default for primary user-facing reads.
const deposit = await client.getPvDepositById(42n);
const deposits = await client.getPvDepositsFromIds([42n, 43n]);
const makerDeposits = await client.getPvAccountDeposits('0xMaker');
const takerIntents = await client.getPvAccountIntents('0xTaker');
const intent = await client.getPvIntent('0xIntentHash');You can also use the convenience wrappers getDeposits(), getDeposit(), getIntents(), and getIntent() for common flows.
Indexer Queries
Use the indexer for filtered queries, pagination, fund activity history, snapshots, and vault analytics. All methods live on the flat client.indexer.* namespace.
// Deposit queries
const deposits = await client.indexer.getDeposits(
{ status: 'ACTIVE', depositor: '0xMaker' },
{ limit: 25, orderBy: 'updatedAt', orderDirection: 'desc' },
);
const depositsWithRelations = await client.indexer.getDepositsWithRelations(
{ status: 'ACTIVE' },
{ limit: 10 },
{ includeIntents: true },
);
const deposit = await client.indexer.getDepositById('8453_0xEscrowAddress_42', {
includeIntents: true,
});
const depositsByIds = await client.indexer.getDepositsByIds(['8453_0xEscrow_42']);
const depositsByIdsWithRelations = await client.indexer.getDepositsByIdsWithRelations(
['8453_0xEscrow_42'],
{ includeIntents: true },
);
const depositsByPayee = await client.indexer.getDepositsByPayeeHash('0xPayeeHash', {
paymentMethodHash: '0x...',
limit: 10,
includeIntents: true,
});
// Intent queries
const intentsForDeposits = await client.indexer.getIntentsForDeposits(
['8453_0xEscrow_42'],
['SIGNALED'],
);
const ownerIntents = await client.indexer.getOwnerIntents('0xTaker', ['SIGNALED', 'FULFILLED']);
const intentsByVault = await client.indexer.getIntentsByRateManager('0xVaultId');
const intent = await client.indexer.getIntentByHash('0xIntentHash');
const expiredIntents = await client.indexer.getExpiredIntents({
now: BigInt(Math.floor(Date.now() / 1000)),
depositIds: ['8453_0xEscrow_42'],
});
const fulfilledEvents = await client.indexer.getFulfilledIntentEvents(['0xIntentHash']);
const fulfillment = await client.indexer.getIntentFulfillmentAmounts('0xIntentHash');
const fulfillmentAndPayment = await client.indexer.getFulfillmentAndPayment('0xIntentHash');
// Fund activity and snapshots
const fundActivities = await client.indexer.getDepositFundActivities('8453_0xEscrowAddress_42');
const makerActivities = await client.indexer.getMakerFundActivities('0xMaker', 50);
const snapshots = await client.indexer.getDepositDailySnapshots('8453_0xEscrowAddress_42', 30);
const profitSnapshots = await client.indexer.getProfitSnapshotsByDeposits(['8453_0xEscrow_42']);
// Rate manager (vault) queries
const managers = await client.indexer.getRateManagers({
limit: 10,
orderBy: 'currentDelegatedBalance',
orderDirection: 'desc',
});
const managerDetail = await client.indexer.getRateManagerDetail('0xVaultId', {
rateManagerAddress: '0xVaultAddress',
statsLimit: 30,
});
const delegations = await client.indexer.getRateManagerDelegations('0xVaultId');
const delegation = await client.indexer.getDelegationForDeposit('8453_0xEscrow_42');
const managerSnapshots = await client.indexer.getManagerDailySnapshots('0xVaultId');
const manualRates = await client.indexer.getManualRateUpdates('0xVaultId');
const oracleUpdates = await client.indexer.getOracleConfigUpdates('0xVaultId');
// Raw GraphQL
const custom = await client.indexer.query<MyType>({
query: '{ deposits(limit: 5) { id } }',
});React Hooks
Install React and import hooks from @zkp2p/sdk/react. React is an optional peer dependency of the SDK.
Most mutation hooks return a named action plus status fields such as isLoading, error, and txHash. Hooks with prepare support expose dedicated prepare... helpers, and some of them also store the latest prepared transaction in hook state. Every hook exports a matching Use*Options type.
import {
useCreateDeposit,
useSignalIntent,
useCreateVault,
useVaultDelegation,
useGetTakerTier,
} from '@zkp2p/sdk/react';
const createDeposit = useCreateDeposit({ client });
const signalIntent = useSignalIntent({ client });
const createVault = useCreateVault({ client, sendTransaction });
const vaultDelegation = useVaultDelegation({ client, sendTransaction, sendBatch });
const takerTier = useGetTakerTier({ client, owner, chainId, autoFetch: true });Hook groups:
- Deposit lifecycle:
useCreateDeposit,useAddFunds,useRemoveFunds,useWithdrawDeposit,useSetAcceptingIntents,useSetIntentRange,useSetCurrencyMinRate,useSetRetainOnEmpty - Payment and currency management:
useAddPaymentMethods,useRemovePaymentMethod,useSetPaymentMethodActive,useAddCurrencies,useRemoveCurrency,useDeactivateCurrency - Intent lifecycle:
useSignalIntent,useFulfillIntent,useReleaseFundsToPayer,usePruneExpiredIntents - Delegation:
useSetDelegate,useRemoveDelegate - Vault / DRM:
useCreateVault,useVaultDelegation,useSetVaultFee,useSetVaultMinRate,useSetVaultConfig - Taker tier:
useGetTakerTier, plusgetTierDisplayInfo()andgetNextTierCap()helpers
useVaultDelegation() is the batching-oriented hook. It returns delegateDeposit, delegateDeposits, clearDelegation, and clearDelegations.
Contracts and Catalog Helpers
import {
getContracts,
getPaymentMethodsCatalog,
getRateManagerContracts,
resolveFiatCurrencyBytes32,
resolvePaymentMethodHash,
} from '@zkp2p/sdk';
const contracts = getContracts(8453, 'production');
const rateManagerContracts = getRateManagerContracts(8453, 'production');
const paymentMethods = getPaymentMethodsCatalog(8453, 'production');
const usdHash = resolveFiatCurrencyBytes32('USD');
const wiseHash = resolvePaymentMethodHash('wise', { env: 'production' });Supported Currencies
The SDK ships currency metadata for 34 fiat currencies:
AED, ARS, AUD, BRL, CAD, CHF, CNY, CZK, DKK, EUR, GBP, HKD, HUF, IDR, ILS, INR, JPY, KES, MXN, MYR, NOK, NZD, PHP, PLN, RON, SAR, SEK, SGD, THB, TRY, UGX, USD, VND, ZAR
Supported Payment Platforms
wise, venmo, revolut, cashapp, mercadopago, zelle, paypal, monzo, chime, luxon, n26, alipay
Attribution (ERC-8021)
The SDK includes builder-code attribution helpers and transaction overrides for partner attribution:
const hash = await client.signalIntent({
depositId: 42n,
amount: 250_000n,
toAddress: '0xBuyer',
processorName: 'wise',
payeeDetails: '0xPAYEE_DETAILS_HASH',
fiatCurrencyCode: 'USD',
conversionRate: 1_020_000_000_000_000_000n,
txOverrides: {
referrer: ['my-app', 'campaign-42'],
},
});Lower-level helpers are also exported: BASE_BUILDER_CODE, ZKP2P_IOS_REFERRER, ZKP2P_ANDROID_REFERRER, getAttributionDataSuffix(), appendAttributionToCalldata(), encodeWithAttribution(), and sendTransactionWithAttribution().
TypeDoc API Reference
The package already includes TypeDoc configuration. Generate the full API reference locally with:
pnpm --filter @zkp2p/sdk docsThis writes documentation to packages/sdk/docs and now includes the React entry point as well as the main SDK and extension surfaces.
Debug Logging
Use setLogLevel() when you want verbose SDK logging during local development or integration debugging.
import { setLogLevel } from '@zkp2p/sdk';
setLogLevel('debug');Supported log levels: 'error', 'info', 'debug'
Development
cd packages/sdk
pnpm build
pnpm test
pnpm typecheck
pnpm lint
pnpm docsLinks
- NPM: https://www.npmjs.com/package/@zkp2p/sdk
- Docs: https://docs.zkp2p.xyz
- Monorepo: https://github.com/zkp2p/zkp2p-clients
- TypeDoc output:
packages/sdk/docs
