Skip to main content

Offramp Integration

The ZKP2P Client SDK is a TypeScript SDK for liquidity providers who want to offer fiat off-ramp services on Base. Use it to create and manage USDC deposits, configure payment methods and currencies, and query on-chain state with RPC-first reads.

Who is this for?

This SDK is designed for liquidity providers (peers) who want to:

  • Create and manage USDC deposits that accept fiat payments
  • Configure payment methods, currencies, and conversion rates
  • Monitor deposit utilization and manage liquidity
  • Earn fees by providing off-ramp services

Installation

npm install @zkp2p/sdk viem
# or
yarn add @zkp2p/sdk viem
# or
pnpm add @zkp2p/sdk viem

Quick start

Initialize the client

import { OfframpClient } 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 OfframpClient({
walletClient,
chainId: base.id,
});

Core operations

Create a deposit

import { Currency } from '@zkp2p/sdk';

await client.createDeposit({
token: '0xUSDC_ADDRESS',
amount: 10000000000n, // 10,000 USDC (6 decimals)
intentAmountRange: { min: 100000n, max: 1000000000n },
processorNames: ['wise', 'revolut'],
depositData: [
{ wisetag: 'maker' }, // Wise payee details
{ revolutUsername: 'maker' }, // Revolut payee details
],
conversionRates: [
[{ currency: Currency.USD, conversionRate: '1020000000000000000' }], // 1.02 (18 decimals)
[{ currency: Currency.EUR, conversionRate: '950000000000000000' }], // 0.95 (18 decimals)
],
onSuccess: ({ hash }) => console.log('Deposit created:', hash),
});

Manage deposit settings

await client.setAcceptingIntents({ depositId: 1n, accepting: true });

await client.setIntentRange({ depositId: 1n, min: 50000n, max: 5000000n });

await client.setCurrencyMinRate({
depositId: 1n,
paymentMethod: '0x...',
fiatCurrency: '0x...',
minConversionRate: 1020000n,
});

Fund management

await client.addFunds({ depositId: 1n, amount: 5000000n });
await client.removeFunds({ depositId: 1n, amount: 1000000n });
await client.withdrawDeposit({ depositId: 1n });

Querying on-chain data (RPC-first)

const deposits = await client.getDeposits();
const ownerDeposits = await client.getAccountDeposits('0xOwnerAddress');
const deposit = await client.getDeposit(42n);
const batch = await client.getDepositsById([1n, 2n, 3n]);

const intents = await client.getIntents();
const ownerIntents = await client.getAccountIntents('0xOwnerAddress');
const intent = await client.getIntent('0xIntentHash...');

Indexer queries

const deposits = await client.indexer.getDeposits(
{ status: 'ACTIVE', minLiquidity: '1000000', depositor: '0xYourAddress' },
{ limit: 50, orderBy: 'remainingDeposits', orderDirection: 'desc' }
);

const depositsWithRelations = await client.indexer.getDepositsWithRelations(
{ status: 'ACTIVE' },
{ limit: 50 },
{ includeIntents: true, intentStatuses: ['SIGNALED'] }
);

const fulfillments = await client.indexer.getFulfilledIntentEvents(['0x...']);

Payment methods

Supported payment platforms and keys:

PlatformKey
Wisewise
Venmovenmo
Revolutrevolut
CashAppcashapp
PayPalpaypal
Zellezelle
Monzomonzo
MercadoPagomercadopago
Chimechime
Luxonluxon
N26n26

Each item in depositData must line up by index with processorNames. If you pass payeeDetailsHashes to createDeposit(), the SDK uses those hashes directly and skips the curator call. Otherwise the SDK forwards each depositData object to curator's public /v1/makers/create endpoint to obtain the hashes, so the key names need to match the processor exactly. Either path works without an API key — you can also call registerPayeeDetails() standalone to get the hashes ahead of time.

The expected depositData shape for each platform is:

PlatformKeydepositData shapeNotes
Wisewise{ wisetag: 'your-wisetag' }Pass the Wisetag without @. Wise uses a manual approval flow in curator.
Venmovenmo{ venmoUsername: 'YourVenmoUsername' }Do not include @. Curator validates the exact Venmo username casing.
Revolutrevolut{ revolutUsername: 'your-revtag' }Do not include @.
Cash Appcashapp{ cashtag: 'yourcashtag' }Do not include $.
PayPalpaypal{ paypalMeUsername: 'yourpaypalmeusername' }Use the PayPal.me username without the paypal.me/ prefix. Requires PeerAuth extension v0.4.14+.
Zellezelle{ zelleEmail: 'maker@example.com' }Curator expects a lowercase email address.
Monzomonzo{ monzoMeUsername: 'your-monzo-me-name' }Use the Monzo.me username only.
Mercado Pagomercadopago{ cvu: '0000003100064367123868' }CVU must be a valid 22-digit Mercado Pago / bank CVU.
Chimechime{ chimesign: '$yourchimesign' }Include the leading $. Curator expects the value in lowercase.
Luxonluxon{ luxonUsername: 'maker@example.com' }Use a lowercase Luxon email address.
N26n26{ iban: 'DE89370400440532013000' }Pass a valid IBAN with spaces removed.
import { getPaymentMethodsCatalog, PLATFORM_METADATA, PAYMENT_PLATFORMS } from '@zkp2p/sdk';

console.log(PAYMENT_PLATFORMS);

const methods = getPaymentMethodsCatalog(8453, 'production');
const wiseHash = methods['wise'].paymentMethodHash;

const wiseInfo = PLATFORM_METADATA['wise'];
console.log(wiseInfo.displayName);

Currency utilities

import {
Currency,
currencyInfo,
getCurrencyInfoFromHash,
resolveFiatCurrencyBytes32,
} from '@zkp2p/sdk';

const usd = Currency.USD;
const info = currencyInfo[Currency.USD];
const usdBytes = resolveFiatCurrencyBytes32('USD');

Contract helpers

import { getContracts, getPaymentMethodsCatalog } from '@zkp2p/sdk';

const { addresses, abis } = getContracts(8453, 'production');
const catalog = getPaymentMethodsCatalog(8453, 'production');

Supported networks

NetworkChain IDEnvironment
Base Mainnet8453production
Base Mainnet8453preproduction
Base Mainnet8453staging

Token allowance management

import { getContracts } from '@zkp2p/sdk';

const { addresses } = getContracts(8453, 'production');

const result = await client.ensureAllowance({
token: '0xUSDC_ADDRESS',
amount: 10000000000n,
spender: addresses.escrow,
maxApprove: false,
});

if (result.hadAllowance) {
console.log('Already had sufficient allowance');
} else {
console.log('Approval transaction:', result.hash);
}

Error handling

import { ValidationError, NetworkError, ContractError } from '@zkp2p/sdk';

try {
await client.createDeposit({ /* ... */ });
} catch (error) {
if (error instanceof ValidationError) {
console.error('Invalid parameters:', error.message);
} else if (error instanceof NetworkError) {
console.error('Network issue:', error.message);
} else if (error instanceof ContractError) {
console.error('Contract error:', error.message);
}
}

Logging

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

setLogLevel('debug'); // 'debug' | 'info' | 'error'

React hooks

import {
useCreateDeposit,
useAddFunds,
useRemoveFunds,
useWithdrawDeposit,
useSetAcceptingIntents,
useSetIntentRange,
useSetCurrencyMinRate,
} from '@zkp2p/sdk/react';

function DepositManager({ client }) {
const { createDeposit, isLoading, error } = useCreateDeposit({ client });

const handleCreate = async () => {
const result = await createDeposit({
token: '0xUSDC_ADDRESS',
amount: 10000000000n,
intentAmountRange: { min: 100000n, max: 1000000000n },
processorNames: ['wise'],
depositData: [{ email: 'maker@example.com' }],
conversionRates: [[{ currency: 'USD', conversionRate: '1020000000000000000' }]],
});
console.log('Created deposit:', result.hash);
};

return (
<div>
<button disabled={isLoading} onClick={handleCreate}>
{isLoading ? 'Creating...' : 'Create Deposit'}
</button>
{error && <p>Error: {error.message}</p>}
</div>
);
}