@zkp2p/zkp2p-react-native-sdk
v0.2.2
Published
React Native SDK for ZKP2P
Readme
zkp2p-react-native-sdk
React Native SDK for ZKP2P - A peer-to-peer fiat-to-crypto on/off-ramp powered by zero-knowledge proofs.
Installation
yarn add @zkp2p/zkp2p-react-native-sdk @react-native-async-storage/async-storage @react-native-cookies/cookies react-native-webview @zkp2p/webview-intercept viem react-native-svg react-native-device-infoiOS Setup
cd ios && pod installAdditional Dependencies
react-native-device-info: Used for dynamic memory management in proof generationreact-native-svg: Required for animated UI components
Quick Start
The SDK supports two modes:
- Full Mode: Access all features including blockchain operations (requires wallet & API key)
- Proof-Only Mode: Generate proofs without wallet or API key
Full Mode Setup
import { Zkp2pProvider, useZkp2p } from '@zkp2p/zkp2p-react-native-sdk';
import { createWalletClient, custom } from 'viem';
// 1. Setup wallet client
const walletClient = createWalletClient({
chain: base,
transport: custom(window.ethereum),
});
// 2. Wrap your app with Zkp2pProvider
function App() {
return (
<Zkp2pProvider
walletClient={walletClient}
apiKey="your-api-key"
chainId={8453} // Base
prover="reclaim_gnark" // or "reclaim_snarkjs"
rpcUrl="https://base-mainnet.g.alchemy.com/v2/your-key" // explicit RPC (recommended)
>
<YourApp />
</Zkp2pProvider>
);
}Proof-Only Mode Setup
// No wallet or API key required!
function App() {
return (
<Zkp2pProvider
chainId={8453} // Base
prover="reclaim_gnark"
// rpcUrl is optional here too if you want to pin to a specific endpoint
// rpcUrl="https://base-sepolia.g.alchemy.com/v2/your-key"
>
<YourApp />
</Zkp2pProvider>
);
}
// 3. Use the SDK in your components
function PaymentFlow() {
const {
flowState,
initiate,
authenticate,
generateProof,
zkp2pClient, // null in proof-only mode
proofData,
metadataList,
proofStatus,
} = useZkp2p();
// Check mode
const isProofOnlyMode = !zkp2pClient;
const isProofRunning = proofStatus.phase === 'running';
// Your component logic
}API Reference
Provider Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| walletClient | WalletClient | Optional | Viem wallet client for blockchain interactions (required for full mode) |
| apiKey | string | Optional | Your ZKP2P API key (required for full mode) |
| chainId | number | 8453 | Blockchain chain ID (8453 for Base, 31337 for Hardhat) |
| environment | 'production' \| 'staging' | 'production' | Environment (production or staging) |
| prover | 'reclaim_snarkjs' \| 'reclaim_gnark' | 'reclaim_gnark' | Proof generation method |
| witnessUrl | string | 'https://witness-proxy.zkp2p.xyz' | Witness server URL |
| baseApiUrl | string | 'https://api.zkp2p.xyz' | ZKP2P API base URL (versioned paths are appended by the SDK) |
| rpcUrl | string | Optional | HTTP RPC endpoint for the selected chain (e.g., Alchemy/Infura/Base). If omitted, viem uses the chain default. |
| rpcTimeout | number | 30000 | RPC timeout in milliseconds |
| configBaseUrl | string | 'https://raw.githubusercontent.com/zkp2p/providers/main/' | Provider configuration base URL |
| storage | Storage | Optional | App-provided secure storage instance (e.g., SecureStore). Required to persist credentials/consent. |
| hideDefaultProofUI | boolean | false | When true, the SDK does not render its proof progress sheet. Use this for headless/custom proof animations. |
| renderConsentSheet | (props) => ReactNode | Optional | If provided, SDK renders this after visible login when consent is unset. Your component must call onAccept / onSkip / onDeny. |
Core Flow Functions
1. signalIntent(args)
Signal your intent to buy/sell crypto. This must be called before initiating the payment flow.
const { zkp2pClient } = useZkp2p();
const signalArgs = {
depositId: '123', // The deposit ID you want to fulfill
amount: '100', // Amount in USD
// ... other contract parameters
};
const tx = await zkp2pClient.signalIntent(signalArgs);2. initiate(platform, actionType, options?)
Start the payment proof generation flow. Opens payment provider authentication.
const { initiate } = useZkp2p();
const provider = await initiate('venmo', 'transfer_venmo', {
// Optional: Auto-start with payment action
initialAction: {
enabled: true,
paymentDetails: {
RECIPIENT_ID: 'john-doe-123',
AMOUNT: '100'
}
}
});3. generateProof(provider, payload, intentHash, itemIndex?)
Generate zero-knowledge proof(s) for a specific transaction. Always returns an array of ProofData.
const { generateProof, provider, interceptedPayload } = useZkp2p();
try {
const proofs = await generateProof(
provider,
interceptedPayload,
'0x...', // Intent hash from signalIntent
0 // Transaction index to prove
);
console.log('Proofs generated:', proofs);
} catch (error) {
console.error('Proof generation failed:', error);
// Fallback to manual authentication
await authenticate('venmo', 'transfer_venmo');
}4. authenticate(platform, actionType, options?)
Start the authentication flow for a specific provider. Useful for manual flows or when you want to drive the UI yourself.
const { authenticate } = useZkp2p();
// Simple authentication without auto-proof
await authenticate('venmo', 'transfer_venmo');
// Or with auto-proof generation
await authenticate('venmo', 'transfer_venmo', {
autoGenerateProof: {
intentHash: '0x...',
itemIndex: 0,
onProofGenerated: (proofData) => {
// Handle generated proof
},
onProofError: (err) => console.error(err),
},
});
Note: Both `initiate(...)` and `authenticate(...)` start a new session and clear `proofData`, `metadataList`, and `interceptedPayload` to ensure no stale state carries into the new flow.
#### 4.1 `isSessionActive(platform, actionType, options?)`
Quickly check if a session is still valid before showing UI. This performs a read‑only network check using previously captured cookies. It does not mutate state or open any WebViews.
```ts
const { isSessionActive } = useZkp2p();
// Optionally pass a provider config you already have
const ok = await isSessionActive('venmo', 'transfer_venmo', {
existingProviderConfig: providerConfig, // optional
});
if (ok) {
// You can proceed directly to auto‑proof, or skip visible auth
} else {
// Ask the user to authenticate()
}Behavior:
- Replays a read‑only request (same‑origin) with stored cookies. Mirrors the same replay + extraction path as the restore flow, including the XPath HTML Accept fallback. Returns
truewhenever the replay succeeds (i.e., session cookies are valid), regardless of whether any metadata items are extracted.
Notes:
- Requires that a prior authentication captured an intercepted payload for the provider; if none is present, it returns
false. - No UI side effects, no state changes — safe to call at app startup or before rendering auth screens.
Tip:
- To check for the presence of metadata items, do not rely on
isSessionActive. Instead, run your normal metadata fetch/restore flow and assert the resulting list length is greater than zero (e.g.,metadataList.length > 0).
4.2 isInternalAction(platform, actionType, options?)
Returns a boolean indicating whether the initial payment action should route to the in‑app WebView (internal) as the primary path, mirroring the same decision logic as _handleInitialAction.
const { isInternalAction } = useZkp2p();
// With just platform/actionType (reads config)
const useInternal = await isInternalAction('venmo', 'transfer_venmo');
// Or pass an override to simulate runtime behavior
const useInternalWithOverride = await isInternalAction('venmo', 'transfer_venmo', {
initialAction: { useExternalActionOverride: false },
});Decision rules:
- If
useExternalActionis true in provider config (or override is set to true), prefer external when available; otherwise fall back to internal if present. - Otherwise prefer internal when available; else external if present.
- Returns
falsewhen neither action link is present.
Passing a custom RPC without the Provider
If you instantiate the client directly, pass rpcUrl on the options:
import { Zkp2pClient } from '@zkp2p/zkp2p-react-native-sdk';
import { base } from 'viem/chains';
const client = new Zkp2pClient({
prover: 'reclaim_gnark',
chainId: base.id,
apiKey: 'your-api-key',
rpcUrl: 'https://base-mainnet.g.alchemy.com/v2/your-key',
});Credential Storage & Consent (SDK-managed)
The SDK can store credentials (username/password) per provider/action when the user consents. You supply:
storage: aStorageimplementation (e.g., backed by SecureStore or AsyncStorage) viaZkp2pProvider.renderConsentSheet: an app-owned bottom sheet or modal. The SDK will call it after a successful visible login when consent is unset. You call back:onAccept→ SDK stores credentials and writes provider consent.onSkip→ SDK does not store; consent remains unset (prompt again next time).onDeny→ SDK writes provider consent = 'denied' (no further prompts). Keys used internally (no need to manage these directly):
- Credentials:
zkp2p_cred_{keccak256(platform:actionType:url)} - Consent:
zkp2p_consent_{keccak256(platform:actionType:url)}
Provider config must include login selectors. Optionally set a reveal timeout for invisible autofill flows:
{
"mobile": {
"login": {
"usernameSelector": "#email, input[name=\"email\"]",
"passwordSelector": "#password, input[name=\"password\"][type=\"password\"]",
"submitSelector": "button[type=\"submit\"]",
"revealTimeoutMs": 3000 // how long to keep the WebView minimized after submit before revealing
}
}
}Exposing helpers (optional):
import { useZkp2p } from '@zkp2p/zkp2p-react-native-sdk';
import type { ProviderSettings } from '@zkp2p/zkp2p-react-native-sdk';
const ExampleComponent = ({ providerCfg }: { providerCfg: ProviderSettings }) => {
const {
clearAllCredentials,
clearAllConsents,
clearProviderCredentials,
clearProviderConsent,
getProviderConsent,
} = useZkp2p();
const handleClear = async () => {
await clearAllCredentials();
await clearAllConsents();
await clearProviderCredentials(providerCfg);
await clearProviderConsent(providerCfg);
const consent = await getProviderConsent(providerCfg);
console.log('Current consent', consent);
};
// ...
};
#### 5. `fulfillIntent(params)`
Complete the transaction by first posting your proof to the attestation service and then submitting the attestation on-chain.
Required params:
- `platform: string`, `actionType: string`
- `intentHash: Hash`
- `zkTlsProof: string` (stringified proof JSON)
- `amount: string`, `timestampMs: string`
- `fiatCurrency: Hex`, `conversionRate: string`
- `payeeDetails: Hex`, `timestampBufferMs: string`
- `verifyingContract?: Address` (UnifiedPaymentVerifier)
```typescript
const { zkp2pClient } = useZkp2p();
await zkp2pClient.fulfillIntent({
platform: 'revolut',
actionType: 'transfer_revolut',
intentHash: '0x…',
zkTlsProof: proofJsonString,
amount: '1000000',
timestampMs: String(Date.now()),
fiatCurrency: currencyInfo.USD.currencyCodeHash as Hex,
conversionRate: '1000000000000000000',
payeeDetails: '0x…' as Hex,
timestampBufferMs: '10000000',
verifyingContract: '0x…',
});Notes:
- SDK encodes
paymentProoffrom the serviceresponseObjectand setsverificationData = abi.encode(['address'], [signer]). - No metadata is used from the service for on-chain verification.
Migration Reference: Updated Interfaces (0.1.0)
Use these as the “after” source of truth when upgrading.
// FulfillIntentParams
type FulfillIntentParams = {
intentHash: Hash;
zkTlsProof: string; // stringified proof JSON
platform: string;
actionType: string;
amount: string;
timestampMs: string;
fiatCurrency: Hex; // bytes32
conversionRate: string; // 1e18 scaled
payeeDetails: Hex; // bytes32
timestampBufferMs: string;
verifyingContract?: Address; // UnifiedPaymentVerifier
onSuccess?: ActionCallback;
onError?: (error: Error) => void;
onMined?: ActionCallback;
txOverrides?: SafeTxOverrides;
};
// SignalIntentParams
type SignalIntentParams = {
processorName: string;
depositId: string;
amount: string; // replaces tokenAmount
payeeDetails: string;
toAddress: Address; // was string
paymentMethodHash?: Hex; // optional override
currencyHash?: Hex; // replaces currency by code
conversionRate: string | bigint;
referrer?: Address;
referrerFee?: string | bigint;
onSuccess?: ActionCallback;
onError?: (error: Error) => void;
onMined?: ActionCallback;
txOverrides?: SafeTxOverrides;
};
// IntentSignalRequest (SDK → API)
type IntentSignalRequest = {
processorName: string;
payeeDetails: string;
depositId: string;
amount: string;
toAddress: Address;
paymentMethod: Hex;
fiatCurrency: Hex;
conversionRate: string;
chainId: string;
orchestratorAddress: Address;
escrowAddress: Address;
};
// SignalIntentResponse.responseObject.intentData
type IntentData = {
orchestratorAddress: Address;
escrowAddress: Address;
depositId: string;
amount: string;
recipientAddress: Address;
paymentMethod: Hex;
fiatCurrency: Hex;
conversionRate: string;
signatureExpiration: string;
chainId: string;
gatingServiceSignature: Hex;
};
// CreateDeposit
type CreateDepositConversionRate = {
currency: CurrencyType; // e.g., Currency.USD
conversionRate: string;
};
type CreateDepositParams = {
token: Address;
amount: bigint;
intentAmountRange: { min: bigint; max: bigint };
conversionRates: CreateDepositConversionRate[][]; // per payment method
processorNames: string[];
depositData: Record<string, string>[];
// new optionals
delegate?: Address;
intentGuardian?: Address;
referrer?: Address;
referrerFee?: string | bigint;
onSuccess?: ActionCallback;
onError?: (error: Error) => void;
onMined?: ActionCallback;
txOverrides?: SafeTxOverrides;
};
// Auto-generate proof callback
type AutoGenerateProofOptions = {
intentHash?: string;
itemIndex?: number;
onProofGenerated?: (proofData: ProofData | ProofData[]) => void;
onProofError?: (error: Error) => void;
};
// AttestationResponse (service output used by encoders)
type AttestationResponse = {
success: boolean;
message: string;
responseObject: {
signature: Hex;
signer: Address;
typedDataSpec: {
primaryType: string;
types: Record<string, { type: string; name: string }[]>;
};
typedDataValue: { intentHash: Hex; releaseAmount: string; dataHash: Hex };
encodedPaymentDetails: Hex;
metadata: Hex;
};
statusCode: number;
};Base API URL (versionless)
- Set
baseApiUrlto the root (e.g.,https://api.zkp2p.xyz). Do not append/v1or/v2; the SDK appends versioned paths internally. - Signal intent →
/v2/verify/intent; quotes →/v1/quote/(exact-fiat|exact-token); makers →/v1/makers/create.
Hook Return Values
const {
// State
flowState, // Current flow state: 'idle' | 'authenticating' | 'authenticated' | 'actionStarted' | 'proofGenerating' | 'proofGeneratedSuccess' | 'proofGeneratedFailure'
provider, // Current provider configuration
proofData, // Generated proof data (ProofData[])
metadataList, // List of transactions from authentication
authError, // Authentication error if any
proofError, // Proof generation error if any
interceptedPayload, // Network event data from authentication
authWebViewProps, // Props for the authentication WebView
// Methods
initiate, // Start the flow
authenticate, // Manual authentication
generateProof, // Generate proof(s) for transaction (returns ProofData[])
closeAuthWebView, // Close authentication modal
clearSession, // Clear cookies/storage for a fresh login
resetState, // Reset in-memory SDK state and cancel background work
// Client
zkp2pClient, // Direct access to contract methods (null in proof-only mode)
} = useZkp2p();Performance: Lazy Circuit Loading
- Circuits are now loaded lazily per algorithm (e.g.,
aes-256-ctr,aes-128-ctr,chacha20) instead of at SDK mount. This significantly reduces app startup time. - The SDK extracts the cipher from the witness and begins preloading that specific circuit just before proof generation. Only one circuit is initialized at a time, on demand.
- If you want to explicitly warm up a circuit even earlier, you may call the native preload via
GnarkBridge.preloadAlgorithm(algorithm)when you have enough context, though this is optional — the bridge also ensures lazy initialization on first use.
Forcing TLS Cipher Suites (mobile)
- Provider configs can now set
mobile.additionalClientOptions.cipherSuitesto restrict TLS negotiation to specific suites on mobile. When present, the SDK will:- Pass the list through to the attestor as
params.additionalClientOptions.cipherSuites. - Pre-warm only the corresponding gnark circuits, which reduces first-run latency and memory use.
- Pass the list through to the attestor as
- Example snippet inside a provider config JSON:
{
// ... existing fields ...
"mobile": {
"additionalClientOptions": {
"cipherSuites": [
"TLS_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256"
]
}
}
}Supported families map to gnark algorithms as follows:
- CHACHA20 →
chacha20 - AES_256_* →
aes-256-ctr - AES_128_* →
aes-128-ctr
Complete Flow Example
function BuyCrypto() {
const {
flowState,
initiate,
generateProof,
zkp2pClient,
proofData,
provider,
interceptedPayload
} = useZkp2p();
const handleBuy = async () => {
try {
const signalTx = await zkp2pClient.signalIntent({
processorName: 'venmo',
depositId: '123',
tokenAmount: '100',
payeeDetails: '0x1234567890123456789012345678901234567890',
toAddress: '0x0000000000000000000000000000000000000000',
currency: 'USD',
});
const intentHash = signalTx.responseObject.signedIntent;
await initiate('venmo', 'transfer_venmo', {
initialAction: {
enabled: true,
paymentDetails: {
venmoUsername: 'crypto-seller',
note: 'Cash',
amount: '100.00'
}
},
// Optional: After authenticate step, you can auto-generate proof
// (Recommended to pass autoGenerateProof in authenticate instead.)
});
// Example: Manually authenticate and auto-generate proof
await authenticate('venmo', 'transfer_venmo', {
autoGenerateProof: {
intentHash,
itemIndex: 0,
onProofGenerated: async (_singleProof) => {
// For multiple-proof configs, read the array from the hook state
const proofsToUse = proofData && proofData.length > 0 ? proofData : [];
if (proofsToUse.length === 0) return;
const fulfillTx = await zkp2pClient.fulfillIntent({
paymentProofs: proofsToUse,
intentHash,
onSuccess: (tx) => console.log('Transaction complete:', tx.hash),
onError: (error) => console.error('Fulfillment failed:', error),
});
console.log('Transaction complete:', fulfillTx.hash);
},
onProofError: async (error) => {
console.error('Auto-proof failed, trying manual:', error);
if (provider && interceptedPayload) {
const proofs = await generateProof(
provider,
interceptedPayload,
intentHash,
0
);
await zkp2pClient.fulfillIntent({ paymentProofs: proofs, intentHash });
}
},
},
});
} catch (error) {
console.error('Buy flow failed:', error);
}
};
return (
<View>
<Button onPress={handleBuy} title="Buy Crypto" />
<Text>Status: {flowState}</Text>
</View>
);
}Flow States
idle- No active operationactionStarted- Payment action initiated (Venmo/CashApp/etc opened)authenticating- User authenticating with payment providerauthenticated- Authentication complete, transactions availableproofGenerating- Generating zero-knowledge proofproofGeneratedSuccess- Proof successfully generatedproofGeneratedFailure- Proof generation failed
Custom Proof UI
The provider renders a default progress sheet. To supply your own UI, opt out and read the live status from context:
function App() {
return (
<Zkp2pProvider hideDefaultProofUI>
<HeadlessProofScreen />
</Zkp2pProvider>
);
}
function HeadlessProofScreen() {
const { proofStatus, cancelProof } = useZkp2p();
if (proofStatus.phase === 'idle') return null;
return (
<YourCustomIndicator
progress={proofStatus.progress}
message={proofStatus.meta}
status={proofStatus.phase}
error={proofStatus.error}
onCancel={cancelProof}
/>
);
}proofStatus updates as the proof lifecycle progresses:
phase:'idle' | 'running' | 'success' | 'failure'progress: normalized value from0to1meta: human-readable status messageerror: latest proof error (if any)
Call cancelProof() to stop the active proof and close the default sheet. Re-running generateProof(...) after a failure automatically reinitializes the RPC bridge. For a full reset, call resetState().
Supported Platforms and Actions
| Platform | Action Types | Description |
|----------|-------------|-------------|
| Venmo | transfer_venmo | Venmo P2P transfers |
| Cash App | transfer_cashapp | Cash App transfers |
| Revolut | transfer_revolut | Revolut transfers |
| Wise | transfer_wise | Wise transfers |
| MercadoPago | transfer_mercadopago | MercadoPago transfers |
| Zelle | transfer_zelle | Zelle transfers |
Error Handling
// Check authentication errors
const { authError } = useZkp2p();
if (authError) {
console.error('Auth failed:', authError.message);
}
// Handle proof generation errors
try {
await generateProof(provider, interceptedPayload, intentHash, 0);
} catch (error) {
console.error('Proof generation failed:', error);
}Advanced Configuration
Custom User Agent
The SDK allows configuring custom user agents per provider in the provider configuration:
provider.mobile?.userAgent = {
ios: 'Custom iOS User Agent',
android: 'Custom Android User Agent'
};Multiple Proof Generation
The SDK supports generating multiple proofs for different transaction data:
const proofs = await generateProof(
provider,
interceptedPayload,
intentHash,
0 // itemIndex
);
// Returns ProofData[] - array of proofs if multiple configuredDynamic Memory Management
The SDK automatically adjusts proof generation concurrency based on device memory:
- Devices with 8GB+ RAM: Up to 6 concurrent proofs
- Devices with 6GB+ RAM: Up to 4 concurrent proofs
- Devices with 4GB+ RAM: Up to 3 concurrent proofs
- Devices with less than 4GB: Up to 2 concurrent proofs
Resetting SDK State
resetState()— Resets internal SDK state and cancels background proof tasks:- Cancels all active native gnark proofs and cleans up memory
- Aborts pending RPC requests and remounts the RPC bridge
- Clears
proofData,metadataList,interceptedPayload - Closes/minimizes auth webview and resets flow to
idle
clearSession(options?)— Clears persisted cookies/storage used for web auth. Use this to force fresh logins. It does not cancel native tasks by itself.
Client Methods
Contract Interactions
const { zkp2pClient } = useZkp2p();
// Available methods:
zkp2pClient.signalIntent(params) // Signal buy/sell intent
zkp2pClient.fulfillIntent(params) // Complete transaction with proof
zkp2pClient.createDeposit(params) // Create new deposit
zkp2pClient.withdrawDeposit(params) // Withdraw deposit
zkp2pClient.cancelIntent(params) // Cancel pending intent
zkp2pClient.releaseFundsToPayer(params) // Release escrowed funds
// Query methods:
zkp2pClient.getQuote(params) // Get price quotes
zkp2pClient.getPayeeDetails(params) // Get payee information
zkp2pClient.getAccountDeposits(address) // Get user's deposits
zkp2pClient.getAccountIntents(address) // Get user's intents (array)
// Utility methods:
zkp2pClient.getUsdcAddress() // Get USDC contract address
zkp2pClient.getDeployedAddresses() // Get all contract addressesQuote Params
Required
paymentPlatforms: string[]fiatCurrency: stringuser: string— taker addressrecipient: string— on-chain recipientdestinationChainId: numberdestinationToken: stringamount: string— useisExactFiatto indicate fiat vs token units
Optional
isExactFiat?: boolean— defaults to truequotesToReturn?: number— API limits server-sidereferrer?: string,useMultihop?: booleanescrowAddresses?: string[]— override the escrow(s) used for quoting; defaults to[zkp2pClient.getDeployedAddresses().escrow]minDepositSuccessRateBps?: number— minimum acceptable historical completion rate; defaults to3000(30%)
Quote Responses
getQuote(params) returns { success, message, responseObject, statusCode } where responseObject contains:
fiat:{ currencyCode, currencyName, currencySymbol, countryCode }token:{ token, decimals, name, symbol, chainId }quotes: array of quotes with fields:fiatAmount,fiatAmountFormattedtokenAmount,tokenAmountFormattedpaymentMethod,payeeAddress,conversionRateintent:{ depositId, amount, processorName, toAddress, payeeDetails, fiatCurrencyCode, chainId, escrowAddress? }depositSuccessRateBps?: historical completion rate for the source deposit, in basis points (0–10000)depositIntentStats?:{ totalIntents, signaledIntents, fulfilledIntents, prunedIntents }
Example quote item
{
"fiatAmount": "1000000",
"fiatAmountFormatted": "1.00 USD",
"tokenAmount": "990099",
"tokenAmountFormatted": "0.99 USDC",
"paymentMethod": "venmo",
"payeeAddress": "0x...",
"conversionRate": "1010000000000000000",
"depositSuccessRateBps": 4166,
"depositIntentStats": {
"totalIntents": 12,
"signaledIntents": 12,
"fulfilledIntents": 5,
"prunedIntents": 7
},
"intent": {
"depositId": "1910",
"amount": "985221",
"processorName": "venmo",
"toAddress": "0x...",
"payeeDetails": "0x...",
"fiatCurrencyCode": "0x...",
"chainId": "8453",
"escrowAddress": "0x..."
}
}On-chain Views Enrichment
- When calling
getAccountDeposits(address)andgetAccountIntents(address), the SDK parses on-chain views. - Enrichment adds two fields:
paymentMethod: platform key derived from the verifier address (e.g.,venmo,cashapp,revolut,wise). Always set when the verifier is a supported platform.paymentData: opaque key-value details fetched from the API, available only when anapiKeyis provided.
Where the data appears
- Per-verifier:
EscrowDepositView.verifiers[i].verificationData.paymentMethodand...verificationData.paymentData. - Top-level intent:
EscrowIntent.paymentMethodandEscrowIntent.paymentData(copied from the verifier matchingpaymentVerifier).
Example
const intentViews = await zkp2pClient.getAccountIntents('0xYourAddress');
if (intentView) {
// Top-level enrichment
console.log(intentView.intent.paymentMethod); // e.g. 'venmo'
console.log(intentView.intent.paymentData); // e.g. { username: 'alice', contact: '...' }
// Per-verifier enrichment
for (const v of intentView.deposit.verifiers) {
console.log(v.verificationData.paymentMethod);
console.log(v.verificationData.paymentData);
}
}
const deposits = await zkp2pClient.getAccountDeposits('0xYourAddress');
for (const d of deposits) {
for (const v of d.verifiers) {
console.log(v.verificationData.paymentMethod);
console.log(v.verificationData.paymentData);
}
}Notes
- Enrichment is best-effort; failures are logged and do not throw.
paymentDatarequires a validapiKey.paymentMethoddoes not.
Platform Configuration
- Supported payment platforms are defined once in
ENABLED_PLATFORMS(src/utils/constants.ts). - Contract addresses use a typed
ContractSetthat maps those platforms to verifier addresses, keeping config and types in sync. - Helpers:
platformFromVerifierAddress(addresses, verifierAddress)resolves the platform key from a verifier contract address.getPlatformAddressMap(addresses)returns{ [platform]: address }restricted to enabled platforms.
Type Definitions
Core Types
// Proof data structure
interface ProofData {
proofType: 'reclaim';
proof: ReclaimProof;
}
// Flow states
type FlowState =
| 'idle'
| 'authenticating'
| 'authenticated'
| 'actionStarted'
| 'proofGenerating'
| 'proofGeneratedSuccess'
| 'proofGeneratedFailure';
// Initiate options
interface InitiateOptions {
authOverrides?: AuthWVOverrides;
existingProviderConfig?: ProviderSettings;
initialAction?: {
enabled?: boolean;
paymentDetails?: Record<string, string>; // For URL/JS injection
useExternalActionOverride?: boolean; // Override internal vs external action
};
}
// Authenticate options
interface AuthenticateOptions {
authOverrides?: AuthWVOverrides;
existingProviderConfig?: ProviderSettings;
autoGenerateProof?: {
intentHash?: string;
itemIndex?: number;
onProofGenerated?: (proofData: ProofData) => void;
onProofError?: (error: Error) => void;
};
}
// Fulfill intent params (subset)
interface FulfillIntentParams {
paymentProofs: ProofData[];
intentHash: string;
}Contributing
See the contributing guide to learn how to contribute to the repository and the development workflow.
License
MIT
Made with create-react-native-library
