Skip to main content
peer-lp Manage USDC liquidity deposits on ZKP2P protocol (Base chain). Create deposits, add/remove funds, set conversion rates, monitor intents, and earn fees from fiat-to-crypto exchanges. Use when the user wants to provide liquidity, manage LP positions, or earn yield on ZKP2P.
Jump to install Skills Marketplace Discover and explore AI skills built by the community.
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Copy promptShow prompt details A direct command skips the review prompt. Inspect the source before running it.
npx skills add https://github.com/zkp2p/zkp2p-skills --skill peer-lpThe command stays on one line. Scroll horizontally to inspect it before copying.
Prefer a local copy? Download the files currently available to SkillsMP.
Download Zip Downloading... More from this repository Create or update Peer (ZKP2P) provider capture templates for buyer TEE, identity, and payment flows. Use when capturing an authenticated platform request, mapping transaction metadata and public parameters, adding a provider action type, or testing a provider through PeerAuth and the developer portal.
Accept fiat payments from humans and receive USDC on Base. Generate payment links for Venmo, Wise, Revolut, CashApp, PayPal, and more. No merchant account needed. No KYC. Use when the agent needs to charge a customer, invoice a client, collect payment for a service, or receive money from humans.
Generate ZKP2P Pay checkout links for receiving fiat payments as USDC. Create checkout sessions, send payment links to users, and handle webhook notifications for payment completion. Use when the user wants to accept payments, generate a payment link, or receive fiat payments as crypto.
SOC
Based on SOC occupation classification
name peer-lp description Manage USDC liquidity deposits on ZKP2P protocol (Base chain). Create deposits, add/remove funds, set conversion rates, monitor intents, and earn fees from fiat-to-crypto exchanges. Use when the user wants to provide liquidity, manage LP positions, or earn yield on ZKP2P.
ZKP2P Liquidity Provider Skill
Manage USDC liquidity on ZKP2P (Peer) protocol. Earn fees when buyers convert fiat to USDC through your deposits.
How It Works
You deposit USDC into an on-chain escrow on Base. Buyers signal intents against your deposit, send fiat payment to your specified payee account (Venmo, Wise, Revolut, etc.), prove the payment via zkTLS, and your USDC is released to them. You keep the fiat plus any conversion rate markup.
Setup
Install Dependencies
npm install @zkp2p/sdk viem
Initialize Client
import { OfframpClient } from '@zkp2p/sdk' ;
import { createWalletClient, http } from 'viem' ;
import { privateKeyToAccount } from 'viem/accounts' ;
import { base } from 'viem/chains' ;
const account = privateKeyToAccount (process.env .PRIVATE_KEY as `0x${string } ` );
walletClient = ({
account,
: base,
: ( ),
});
client = ({
walletClient,
: ,
: ,
: process. . ,
: ,
});
const
createWalletClient
chain
transport
http
'https://mainnet.base.org'
const
new
OfframpClient
chainId
8453
runtimeEnv
'production'
apiKey
env
ZKP2P_API_KEY
baseApiUrl
'https://api.zkp2p.xyz'
Key Addresses (Base Mainnet) Contract Address USDC 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913Escrow 0x2f121CDDCA6d652f35e8B3E560f9760898888888Orchestrator 0x88888883Ed048FF0a415271B28b2F52d431810D0ProtocolViewer 0x30B03De22328074Fbe8447C425ae988797146606
Create a Deposit Creating a deposit requires: token address, amount, intent range, payment methods, payee data, and conversion rates.
Step 1: Approve USDC const USDC = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' as `0x${string } ` ;
const amount = 1000_000000n ;
const { hadAllowance, hash : approvalHash } = await client.ensureAllowance ({
token : USDC ,
amount,
maxApprove : true ,
});
if (!hadAllowance) {
console .log ('Approval tx:' , approvalHash);
}
Step 2: Resolve Payment Method Hashes import {
resolvePaymentMethodHash,
resolveFiatCurrencyBytes32,
} from '@zkp2p/sdk' ;
const wiseHash = resolvePaymentMethodHash ('wise' );
const revolutHash = resolvePaymentMethodHash ('revolut' );
const usdHash = resolveFiatCurrencyBytes32 ('USD' );
const eurHash = resolveFiatCurrencyBytes32 ('EUR' );
Step 3: Create Deposit const { depositDetails, hash } = await client.createDeposit ({
token : USDC ,
amount : 1000_000000n ,
intentAmountRange : {
min : 10_000000n ,
max : 500_000000n ,
},
processorNames : ['wise' ],
depositData : [{
payeeDetails : 'your-wise-email@example.com' ,
}],
conversionRates : [[
{ currency : 'USD' , conversionRate : '1020000000000000000' },
{ currency : 'EUR' , conversionRate : '1080000000000000000' },
]],
});
console .log ('Deposit created, tx:' , hash);
Conversion Rate Format Rates use 18-decimal precision where 1e18 = 1.00x (no markup).
Desired Markup Rate Value BigInt 0% (par) 1.00 1000000000000000000n1% 1.01 1010000000000000000n2% 1.02 1020000000000000000n3% 1.03 1030000000000000000n5% 1.05 1050000000000000000n
For cross-currency deposits (e.g., USDC deposit with EUR payments), the rate must also account for the FX spread. If EUR/USD is 1.08 and you want a 2% markup: 1.08 * 1.02 = 1.1016, so use 1101600000000000000n.
Manage Funds
Add Funds to Existing Deposit await client.addFunds ({
depositId : 42n ,
amount : 500_000000n ,
});
Note: addFunds is permissionless -- anyone can add funds to any deposit. The USDC is still controlled by the deposit owner.
Remove Available Funds await client.removeFunds ({
depositId : 42n ,
amount : 200_000000n ,
});
Only removes from remainingDeposits (unlocked funds). Funds locked in active intents cannot be removed.
Withdraw Entire Deposit await client.withdrawDeposit ({
depositId : 42n ,
});
Withdraws all available funds and marks the deposit inactive. Fails if there are outstanding (locked) intents -- prune expired intents first.
Set Conversion Rates Update the minimum conversion rate for a specific payment method + currency pair on your deposit.
import {
resolvePaymentMethodHash,
resolveFiatCurrencyBytes32,
} from '@zkp2p/sdk' ;
await client.setCurrencyMinRate ({
depositId : 42n ,
paymentMethod : resolvePaymentMethodHash ('wise' ),
fiatCurrency : resolveFiatCurrencyBytes32 ('USD' ),
minConversionRate : 1015000000000000000n ,
});
The minConversionRate is a floor. Buyers can offer a higher rate (more fiat per USDC) but never lower. Setting to 0n effectively deactivates that currency.
Rate Update Examples
await client.setCurrencyMinRate ({
depositId : 42n ,
paymentMethod : resolvePaymentMethodHash ('revolut' ),
fiatCurrency : resolveFiatCurrencyBytes32 ('EUR' ),
minConversionRate : 1085000000000000000n ,
});
await client.setCurrencyMinRate ({
depositId : 42n ,
paymentMethod : resolvePaymentMethodHash ('revolut' ),
fiatCurrency : resolveFiatCurrencyBytes32 ('EUR' ),
minConversionRate : 1120000000000000000n ,
});
Toggle Intent Acceptance Pause or resume your deposit from accepting new intents. Existing intents are not affected.
await client.setAcceptingIntents ({
depositId : 42n ,
accepting : false ,
});
await client.setAcceptingIntents ({
depositId : 42n ,
accepting : true ,
});
Set Intent Amount Range Update the min/max USDC per intent.
await client.setIntentRange ({
depositId : 42n ,
min : 50_000000n ,
max : 1000_000000n ,
});
Monitor Intents
Get All Your Intents const intents = await client.getIntents ();
for (const intent of intents) {
console .log (`Intent ${intent.intentHash} :` );
console .log (` Amount: ${intent.amount} USDC` );
console .log (` Status: ${intent.status} ` );
console .log (` Deposit ID: ${intent.depositId} ` );
console .log (` Created: ${new Date (Number (intent.timestamp) * 1000 )} ` );
}
Get Single Intent const intent = await client.getIntent ('0xabc123...' as `0x${string } ` );
Polling Loop for Active Monitoring async function monitorIntents (client : OfframpClient , intervalMs = 30_000 ) {
setInterval (async () => {
try {
const intents = await client.getIntents ();
const active = intents.filter (i => i.status === 'active' );
if (active.length > 0 ) {
console .log (`${active.length} active intent(s):` );
for (const intent of active) {
const expiresIn = Number (intent.expiryTime ) - Math .floor (Date .now () / 1000 );
console .log (` ${intent.intentHash} - ${intent.amount} USDC - expires in ${expiresIn} s` );
}
}
} catch (err) {
console .error ('Intent monitoring error:' , err);
}
}, intervalMs);
}
Intent Lifecycle State Meaning Active Buyer has signaled intent, funds locked in escrow Fulfilled Buyer proved payment, USDC released to buyer Cancelled Buyer cancelled, funds unlocked back to deposit Expired Intent passed expiration period, can be pruned
Prune Expired Intents Expired intents lock funds unnecessarily. Pruning is permissionless -- anyone can prune any deposit.
await client.pruneExpiredIntents ({
depositId : 42n ,
});
This unlocks all expired intent amounts back to remainingDeposits.
View Deposit Status
Get All Your Deposits const deposits = await client.getDeposits ();
for (const d of deposits) {
console .log (`Deposit #${d.depositId} :` );
console .log (` Available: ${d.remainingDeposits} (raw units)` );
console .log (` Locked: ${d.outstandingIntentAmount} ` );
console .log (` Accepting: ${d.acceptingIntents} ` );
console .log (` Payment Methods: ${d.paymentMethods?.join(', ' )} ` );
}
Get Single Deposit const deposit = await client.getDeposit (42n );
console .log ('Remaining USDC:' , Number (deposit.remainingDeposits ) / 1e6 );
console .log ('Locked in intents:' , Number (deposit.outstandingIntentAmount ) / 1e6 );
Get Deposits for Any Address const deposits = await client.getAccountDeposits ('0x1234...' );
Advanced Deposit Management
Set Delegate Allow another address to manage your deposit (set rates, toggle acceptance, etc.) without transferring ownership.
await client.setDelegate ({
depositId : 42n ,
delegate : '0xDelegateAddress...' as `0x${string } ` ,
});
Remove Delegate await client.removeDelegate ({
depositId : 42n ,
});
Retain on Empty Keep deposit configuration alive when balance reaches zero. Useful if you plan to refill later without re-creating the deposit.
await client.setRetainOnEmpty ({
depositId : 42n ,
retain : true ,
});
Add Payment Methods to Existing Deposit await client.addPaymentMethods ({
depositId : 42n ,
paymentMethods : [resolvePaymentMethodHash ('revolut' )],
paymentMethodData : [{
payeeDetails : 'your-revolut-tag' ,
}],
});
Toggle Payment Method await client.setPaymentMethodActive ({
depositId : 42n ,
paymentMethod : resolvePaymentMethodHash ('wise' ),
isActive : false ,
});
Release Funds to Payer As a depositor, you can manually release locked funds back to the buyer (e.g., if the buyer's payment was reversed or disputed).
await client.releaseFundsToPayer ({
intentHash : '0xabc123...' as `0x${string } ` ,
});
Contract Resolution import { getContracts, getPaymentMethodsCatalog } from '@zkp2p/sdk' ;
const { addresses, abis } = getContracts (8453 , 'production' );
const catalog = getPaymentMethodsCatalog (8453 , 'production' );
Security Rules
Never expose private keys in code, logs, or responses. Use environment variables or secure key management.
Validate amounts before transactions. Always confirm deposit amounts, withdrawal amounts, and rates with the user before executing.
Check balances before operations. Verify the wallet has sufficient USDC before createDeposit or addFunds. Verify remainingDeposits before removeFunds.
Monitor intent expiry. Prune expired intents regularly to avoid funds being locked unnecessarily.
Use appropriate gas settings. Base L2 gas is cheap but spikes happen. Consider setting maxFeePerGas in txOverrides for time-sensitive operations.
Never set rates to zero unintentionally. A minConversionRate of 0n deactivates that currency pair.
Confirm before withdrawDeposit. This removes all available funds. If intents are active, those funds remain locked until fulfilled/cancelled/expired.
Rate precision matters. All conversion rates use 18 decimals. Double-check calculations. 1% markup = 1.01 * 1e18 = 1010000000000000000.
Monitor Deposit Health Track deposit utilization, detect stale deposits, and cross-reference with Peerlytics explorer data.
Utilization Check async function checkDepositHealth (client : OfframpClient ) {
const deposits = await client.getDeposits ();
for (const d of deposits) {
const available = Number (d.remainingDeposits );
const locked = Number (d.outstandingIntentAmount );
const total = available + locked;
const utilization = total > 0 ? locked / total : 0 ;
if (utilization > 0.8 ) {
console .log (`Deposit #${d.depositId} : ${(utilization * 100 ).toFixed(0 )} % utilized. Consider adding funds.` );
}
if (d.acceptingIntents && available > 0 && locked === 0 ) {
console .log (`Deposit #${d.depositId} : Active but no locked intents. Check rates are competitive.` );
}
if (available < 100_000000 ) {
console .log (`Deposit #${d.depositId} : Low balance (${available / 1e6 } USDC available).` );
}
}
}
Cross-Reference with Peerlytics Explorer Use the @peerlytics/sdk to get richer deposit analytics:
import { Peerlytics } from '@peerlytics/sdk' ;
const peerlytics = new Peerlytics ({ apiKey : process.env .PEERLYTICS_API_KEY });
const detail = await peerlytics.getDeposit (depositId);
const portfolio = await peerlytics.getMaker (account.address );
Common Patterns
Pattern: Conservative LP Setup Create a deposit with moderate markup, single payment method, and tight intent range.
await client.ensureAllowance ({ token : USDC , amount : 500_000000n , maxApprove : true });
const { hash } = await client.createDeposit ({
token : USDC ,
amount : 500_000000n ,
intentAmountRange : { min : 20_000000n , max : 200_000000n },
processorNames : ['wise' ],
depositData : [{ payeeDetails : 'user@email.com' }],
conversionRates : [[
{ currency : 'USD' , conversionRate : '1030000000000000000' },
]],
});
Pattern: Multi-Platform LP Accept payments via multiple platforms to maximize fill rate.
const { hash } = await client.createDeposit ({
token : USDC ,
amount : 5000_000000n ,
intentAmountRange : { min : 10_000000n , max : 1000_000000n },
processorNames : ['wise' , 'revolut' , 'venmo' ],
depositData : [
{ payeeDetails : 'wise-email@example.com' },
{ payeeDetails : '@revolut-tag' },
{ payeeDetails : '@venmo-username' },
],
conversionRates : [
[{ currency : 'USD' , conversionRate : '1020000000000000000' }, { currency : 'EUR' , conversionRate : '1100000000000000000' }],
[{ currency : 'EUR' , conversionRate : '1090000000000000000' }, { currency : 'GBP' , conversionRate : '1270000000000000000' }],
[{ currency : 'USD' , conversionRate : '1025000000000000000' }],
],
});
Pattern: Dynamic Rate Adjustment Adjust rates based on utilization. When most of your deposit is locked in intents, widen the spread.
async function adjustRates (client : OfframpClient , depositId : bigint ) {
const deposit = await client.getDeposit (depositId);
const total = Number (deposit.remainingDeposits ) + Number (deposit.outstandingIntentAmount );
const utilization = Number (deposit.outstandingIntentAmount ) / total;
let markup : bigint ;
if (utilization > 0.8 ) {
markup = 1050000000000000000n ;
} else if (utilization > 0.5 ) {
markup = 1030000000000000000n ;
} else {
markup = 1015000000000000000n ;
}
await client.setCurrencyMinRate ({
depositId,
paymentMethod : resolvePaymentMethodHash ('wise' ),
fiatCurrency : resolveFiatCurrencyBytes32 ('USD' ),
minConversionRate : markup,
});
}
Pattern: Rebalancing Check Monitor and rebalance deposits when utilization or balance drops.
async function checkRebalance (client : OfframpClient ) {
const deposits = await client.getDeposits ();
for (const d of deposits) {
const available = Number (d.remainingDeposits ) / 1e6 ;
const locked = Number (d.outstandingIntentAmount ) / 1e6 ;
if (available < 100 ) {
console .log (`Deposit #${d.depositId} : Low balance (${available} USDC available). Consider adding funds.` );
}
if (locked > 0 ) {
await client.pruneExpiredIntents ({ depositId : BigInt (d.depositId ) });
}
}
}
Supported Payment Platforms Platform Processor Name Currencies Wise wiseUSD, EUR, GBP, SGD, AUD, CAD, CHF, JPY, and more Revolut revolutEUR, GBP, USD, CHF, and more Venmo venmoUSD CashApp cashappUSD PayPal paypalUSD, EUR, GBP Monzo monzoGBP MercadoPago mercadopagoARS, BRL, MXN Zelle (Chase) zelle-chaseUSD Zelle (BofA) zelle-bofaUSD Zelle (Citi) zelle-citiUSD
Use getPaymentMethodsCatalog(8453, 'production') for the canonical list of supported methods and currencies.
Environment Production Staging Chain Base (8453) Base Sepolia (84532) Escrow 0x2f121CDDCA6d652f35e8B3E560f97608988888880x5C2a8B9246777eE4501B6C426a8B8C7635C7b5b5Orchestrator 0x88888883Ed048FF0a415271B28b2F52d431810D0- Core API https://api.zkp2p.xyzhttps://api-staging.zkp2p.xyzPeerlytics https://peerlytics.xyz- Indexer - https://indexer.hyperindex.xyz/00be13d/v1/graphql
API Reference See references/sdk-api.md for the complete method reference with parameter types and return types.