
Security News
Package Maintainers Call for Improvements to GitHub’s New npm Security Plan
Maintainers back GitHub’s npm security overhaul but raise concerns about CI/CD workflows, enterprise support, and token management.
@zkp2p/client-sdk
Advanced tools
Browser-first TypeScript SDK for ZKP2P with peerauth extension integration
Browser-first TypeScript SDK for integrating ZKP2P into web apps. Built on the proven core from the React Native SDK and extended with peerauth browser extension integration.
npm install @zkp2p/client-sdk viem
import { Zkp2pClient } from '@zkp2p/client-sdk';
const client = new Zkp2pClient({
walletClient, // viem wallet client
apiKey: 'YOUR_API_KEY',
chainId: 8453, // Base mainnet
});
// Fetch quotes
const quotes = await client.getQuote({
paymentPlatforms: ['wise'],
fiatCurrency: 'USD',
user: '0xYourAddress',
recipient: '0xPayeeAddress',
destinationChainId: 8453,
destinationToken: client.getUsdcAddress(),
amount: '100', // exact fiat by default
});
// Use the extension (optional)
import { PeerauthExtension } from '@zkp2p/client-sdk/extension';
const ext = new PeerauthExtension({ onVersion: v => console.log('Extension version', v) });
ext.fetchVersion();
// After generating proof via extension, fulfill intent (see Extension Flow section)
// await client.fulfillIntent({ intentHash, paymentProofs: [{ proof }], paymentMethod: 1 });
@zkp2p/client-sdk/extension
.wise
, venmo
, revolut
, cashapp
, mercadopago
, zelle
, paypal
, monzo
.examples/
folder for:
examples/node-scripts/get-quote.ts
)examples/e2e-browser/index.html
)This demo simulates the peerauth extension with a small in-page mock so you can see the end-to-end flow.
Steps:
npm run build
npx http-server -p 5174 .
or npx serve .
http://localhost:5174/examples/e2e-browser/
in your browser.What it shows:
postMessage
ReclaimProof
with parseExtensionProof
fulfillIntent
with the proofYou can pass any viem
WalletClient
(from wagmi or raw viem). Example with injected wallet on Base:
import { createWalletClient, custom } from 'viem'
import { base } from 'viem/chains'
const walletClient = createWalletClient({
chain: base,
transport: typeof window !== 'undefined' ? custom((window as any).ethereum) : undefined,
});
const client = new Zkp2pClient({ walletClient, apiKey: 'YOUR_API_KEY', chainId: base.id });
Override RPC URL if desired by passing rpcUrl
to the constructor.
const client = new Zkp2pClient({ walletClient, apiKey, chainId: base.id, rpcUrl: 'https://base-mainnet.g.alchemy.com/v2/<key>' });
Supported chains match DEPLOYED_ADDRESSES
. Use client.getDeployedAddresses()
and client.getUsdcAddress()
when needed.
Best practice: pass values into the SDK at initialization. The SDK does not read env directly.
VITE_ZKP2P_API_KEY=your_public_key
VITE_ZKP2P_RPC_URL=https://base-mainnet.g.alchemy.com/v2/xxx
Code:
const apiKey = import.meta.env.VITE_ZKP2P_API_KEY;
if (!apiKey) throw new Error('Missing VITE_ZKP2P_API_KEY');
const client = new Zkp2pClient({
walletClient,
apiKey,
chainId: 8453,
rpcUrl: import.meta.env.VITE_ZKP2P_RPC_URL,
// Optional overrides:
// baseApiUrl: import.meta.env.VITE_ZKP2P_BASE_API_URL,
// witnessUrl: import.meta.env.VITE_ZKP2P_WITNESS_URL,
});
NEXT_PUBLIC_ZKP2P_API_KEY=your_public_key
NEXT_PUBLIC_ZKP2P_RPC_URL=https://base-mainnet.g.alchemy.com/v2/xxx
Code:
const apiKey = process.env.NEXT_PUBLIC_ZKP2P_API_KEY;
if (!apiKey) throw new Error('Missing NEXT_PUBLIC_ZKP2P_API_KEY');
const client = new Zkp2pClient({
walletClient,
apiKey,
chainId: 8453,
rpcUrl: process.env.NEXT_PUBLIC_ZKP2P_RPC_URL,
});
Security: only use public runtime env vars in the browser (VITE_/NEXT_PUBLIC_). If keys must remain private, proxy via your server.
PAYMENT_PLATFORMS
is an exported as const
array and PaymentPlatformType
is the corresponding string union. Use it for extension calls to get autocomplete and type-safety.import { PAYMENT_PLATFORMS, type PaymentPlatformType } from '@zkp2p/client-sdk';
const platform: PaymentPlatformType = 'wise'; // from PAYMENT_PLATFORMS
CurrencyType
is the ISO-like currency code union (e.g., 'USD' | 'EUR' | …'
). Use it in signalIntent
.import { type CurrencyType } from '@zkp2p/client-sdk';
await client.signalIntent({
processorName: 'wise',
depositId: '1',
tokenAmount: '1000000',
payeeDetails: '{"email":"alice@example.com"}',
toAddress: '0xRecipient',
currency: 'USD' as CurrencyType,
});
The typical browser flow is:
intentHash
ReclaimProof
formatfulfillIntent
with the encoded proofimport { Zkp2pClient, assembleProofBytes, intentHashHexToDecimalString } from '@zkp2p/client-sdk';
import { PeerauthExtension, parseExtensionProof, ExtensionProofFlow, ExtensionMetadataFlow, metadataUtils } from '@zkp2p/client-sdk/extension';
// 1) Initialize the client
const client = new Zkp2pClient({ walletClient, apiKey, chainId: 8453 });
// 2) Set up the extension with callbacks
let cachedProofId: string | null = null;
const ext = new PeerauthExtension({
onVersion: (v) => console.log('extension version:', v),
onProofId: (id) => {
cachedProofId = id;
if (cachedProofId) ext.fetchProofById(); // 3) Request proof details once we have the id
},
onProof: async (notaryRequest) => {
if (!notaryRequest) return;
// 4) Convert extension proof → ReclaimProof shape expected by the contracts
const reclaimProof = parseExtensionProof(notaryRequest.proof);
// Submit proof on-chain
await client.fulfillIntent({
intentHash,
paymentProofs: [{ proof: reclaimProof }],
// optionally include a paymentMethod identifier (uint8) if needed by verifier
// paymentMethod: 1,
});
},
onError: (e) => console.error('extension error:', e),
});
// Kick off version check and proof generation
ext.fetchVersion();
ext.generateProof(
'wise', // platform identifier (e.g. 'wise', 'venmo', 'revolut', 'paypal', 'monzo', ...)
intentHash, // `0x…` intent hash to fulfill
0 // originalIndex for the selected transaction/metadata
);
// Helper: Convert extension proof payload → ReclaimProof
const reclaimProof = parseExtensionProof(notaryRequest.proof);
Use the ExtensionOrchestrator
to hide platform action strings and required proof counts. It requests metadata, lets you pick a transaction, generates the correct number of proofs, and builds the bytes for contract submission.
import { ExtensionOrchestrator } from '@zkp2p/client-sdk/extension';
const orch = new ExtensionOrchestrator({ debug: false, versionPollMs: 5000, metadataTimeoutMs: 60000 });
// 1) Request and render payments (internal action strings are abstracted)
const payments = await orch.requestAndGetPayments('revolut');
const selected = payments[0]; // render and let user pick in real apps
// 2) Generate the correct number of proofs, based on platform config
const proofs = await orch.generateProofs('revolut', intentHash, selected.originalIndex);
// 3) Submit on-chain
await client.fulfillIntent({
intentHash,
paymentProofs: proofs.map((p) => ({ proof: p })),
});
If you prefer manual control, but still want a helper to handle polling/timeout and parsing, use ExtensionProofFlow
directly and assembleProofBytes
to build the final payload:
const flow = new ExtensionProofFlow();
try {
const proofs = await flow.generateProofs(
'wise', // platform
intentHashHexToDecimalString(intentHash), // safe decimal string for extension
0, // originalIndex from extension metadata
{ requiredProofs: 1, pollIntervalMs: 3000, timeoutMs: 60000 },
(p) => console.log('progress', p)
);
// Option A: assemble bytes and submit manually
const bytes = assembleProofBytes(proofs, { paymentMethod: 1 });
// ... submit via your own viem client if desired
// Option B: submit via SDK using the raw proofs
await client.fulfillIntent({
intentHash,
paymentProofs: proofs.map((proof) => ({ proof })),
paymentMethod: 1,
});
} finally {
flow.dispose();
}
Surface transaction candidates first, let the user choose, then generate proofs. This gives integrators full control over selection logic.
// 1) Start metadata flow (receives metadata pushed from extension)
const meta = new ExtensionMetadataFlow({ versionPollMs: 5000 });
const unsubscribe = meta.subscribe((platform, record) => {
if (platform !== 'wise') return;
const visible = metadataUtils.filterVisible(record.metadata);
const sorted = metadataUtils.sortByDateDesc(visible);
// Render `sorted` to your UI and let the user pick one
});
// Optionally, request metadata via extension action (advanced; orchestrator handles this internally)
// meta.requestMetadata('<actionTypeFromPlatformConfig>', 'wise');
// 2) Once user picks a transaction, capture its `originalIndex`
const chosenOriginalIndex = 0; // from the user’s selection
// 3) Generate proof(s)
const flow = new ExtensionProofFlow({ debug: false });
const proofs = await flow.generateProofs('wise', intentHashHexToDecimalString(intentHash), chosenOriginalIndex, { requiredProofs: 1 });
// 4) Submit
await client.fulfillIntent({ intentHash, paymentProofs: proofs.map(p => ({ proof: p })), paymentMethod: 1 });
// Cleanup on unmount
unsubscribe();
meta.dispose();
flow.dispose();
Notes:
postMessage
; the SDK caches and emits the latest per-platform.expiresAt
indicates when metadata becomes stale. Use meta.isExpired(platform)
to decide re-fetch strategy.Enable debug logging to see postMessage traffic and progress:
import { ExtensionOrchestrator, ExtensionMetadataFlow, ExtensionProofFlow } from '@zkp2p/client-sdk/extension';
const orch = new ExtensionOrchestrator({ debug: true });
const meta = new ExtensionMetadataFlow({ debug: true });
const proofFlow = new ExtensionProofFlow({ debug: true });
encodeProofAsBytes(proof)
encodeTwoProofs(proof1, proof2)
encodeManyProofs([proofs])
encodeProofAndPaymentMethodAsBytes(bytes, method)
assembleProofBytes(proofs, { paymentMethod? })
parseExtensionProof(payload)
createDeposit(params): creates a deposit on-chain and stores deposit details via API
{ token, amount, intentAmountRange, conversionRates, processorNames, depositData, onSuccess, onMined, onError }
{ depositDetails, hash }
signalIntent(params): verifies intent via API and emits on-chain signalIntent
{ processorName, depositId, tokenAmount, payeeDetails, toAddress, currency, onSuccess, onMined, onError }
SignalIntentResponse & { txHash?: Hash }
fulfillIntent({ intentHash, paymentProofs, paymentMethod? }): submits proof bytes to fulfill an intent
withdrawDeposit({ depositId }): withdraws a deposit
cancelIntent({ intentHash }): cancels a pending intent
releaseFundsToPayer({ intentHash }): releases escrowed funds back to payer
getQuote(req): retrieves quotes from API (exact-fiat by default)
getPayeeDetails({ platform, hashedOnchainId })
validatePayeeDetails({ processorName, depositData }): validates payee details via API
getAccountDeposits(address): reads deposit views from chain
getAccountIntent(address): reads current intent view from chain
See TypeScript types exported from the package for full shapes.
Use payee validation to pre-check user-provided payee details before creating a deposit or signaling an intent. The API returns a boolean and optional error strings.
import { Zkp2pClient } from '@zkp2p/client-sdk';
const client = new Zkp2pClient({ walletClient, apiKey, chainId: 8453 });
// Example: Revolut username
const res = await client.validatePayeeDetails({
processorName: 'revolut',
depositData: { revolutUsername: 'alice' },
});
if (!res.responseObject.isValid) {
console.warn('Payee validation failed:', res.responseObject.errors || []);
// Show user-friendly error, do not proceed to createDeposit/signalIntent
}
@zkp2p/client-sdk/extension
is browser-only. In SSR environments (Next.js), use dynamic import or guards to avoid referencing window
during server rendering.Callbacks (optional and per-method):
onSuccess({ hash })
: emitted after the transaction is broadcastonMined({ hash })
: emitted after transaction is confirmedonError(error)
: emitted when any step failsError classes:
ZKP2PError
(base), NetworkError
, APIError
, ContractError
, ValidationError
, ProofGenerationError
window
.@zkp2p/client-sdk/extension
is browser-only. In SSR, import it dynamically or guard with typeof window !== 'undefined'
.await client.createDeposit({
token: client.getUsdcAddress(),
amount: 1000000n, // 1 USDC with 6 decimals
intentAmountRange: { min: 500000n, max: 2000000n },
processorNames: ['wise'],
conversionRates: [[{ currency: 'USD', conversionRate: '1000000' }]],
depositData: [{ /* payee details per processor */ }],
});
await client.signalIntent({
processorName: 'wise',
depositId: '1',
tokenAmount: '1000000',
payeeDetails: '{"email":"alice@example.com"}',
toAddress: '0xRecipient',
currency: 'USD',
});
// ext.generateProof(...)
// const proof = parseExtensionProof(...)
await client.fulfillIntent({ intentHash, paymentProofs: [{ proof }] });
See PUBLISHING.md in this package for up‑to‑date publishing instructions.
MIT
FAQs
Browser-first TypeScript SDK for ZKP2P with React hooks, unified authentication, and peerauth extension integration
The npm package @zkp2p/client-sdk receives a total of 11 weekly downloads. As such, @zkp2p/client-sdk popularity was classified as not popular.
We found that @zkp2p/client-sdk demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Maintainers back GitHub’s npm security overhaul but raise concerns about CI/CD workflows, enterprise support, and token management.
Product
Socket Firewall is a free tool that blocks malicious packages at install time, giving developers proactive protection against rising supply chain attacks.
Research
Socket uncovers malicious Rust crates impersonating fast_log to steal Solana and Ethereum wallet keys from source code.