EIP-712 Voucher-based deferred payment verification utilities for x402 deferred scheme
npm install @perkos/scheme-deferredEIP-712 voucher-based deferred payment verification utilities for x402 deferred scheme. Provides signature verification, escrow balance checking, and voucher validation for off-chain payment aggregation.
``bash`
npm install @perkos/scheme-deferred
The deferred scheme enables off-chain voucher signing with on-chain batch settlement:
1. Client deposits funds into escrow contract
2. Client signs vouchers (EIP-712) for each payment
3. Facilitator verifies signatures and escrow balance
4. Seller claims vouchers via escrow contract
`typescript
import { DeferredSchemeVerifier } from '@perkos/scheme-deferred';
import type { DeferredPayload, PaymentRequirements } from '@perkos/scheme-deferred';
const verifier = new DeferredSchemeVerifier({
network: 'base',
escrowAddress: '0x...',
rpcUrl: 'https://mainnet.base.org' // optional
});
const payload: DeferredPayload = {
voucher: {
id: '0x...',
buyer: '0x...',
seller: '0x...',
valueAggregate: '5000000',
asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
timestamp: '1735689600',
nonce: '1',
escrow: '0x...',
chainId: '8453'
},
signature: '0x...'
};
const requirements: PaymentRequirements = {
scheme: 'deferred',
network: 'base',
maxAmountRequired: '1000000',
resource: '/api/service',
payTo: '0x...',
maxTimeoutSeconds: 3600,
asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'
};
const result = await verifier.verify(payload, requirements);
if (result.isValid) {
console.log('Valid voucher from:', result.payer);
} else {
console.error('Invalid:', result.invalidReason);
}
`
`typescript
import {
generateVoucherId,
createVoucherMessage,
createEIP712Domain,
VOUCHER_TYPES
} from '@perkos/scheme-deferred';
import { signTypedData } from 'viem/accounts';
// Generate unique voucher ID
const voucherId = generateVoucherId();
// Create voucher message
const message = createVoucherMessage(
voucherId,
'0x...buyer', // buyer
'0x...seller', // seller
'5000000', // valueAggregate
'0x...usdc', // asset
Math.floor(Date.now() / 1000), // timestamp
'1', // nonce
'0x...escrow', // escrow
8453 // chainId
);
// Create EIP-712 domain
const domain = createEIP712Domain(
8453, // chainId
'0x...escrow' // escrow contract address
);
// Sign the voucher (client-side)
const signature = await signTypedData({
domain,
types: VOUCHER_TYPES,
primaryType: 'Voucher',
message,
privateKey: '0x...'
});
`
`typescript
const verifier = new DeferredSchemeVerifier({
network: 'base',
escrowAddress: '0x...'
});
const balance = await verifier.getEscrowBalance(
'0x...buyer',
'0x...seller',
'0x...asset'
);
console.log('Available balance:', balance.toString());
`
`typescript
const claimed = await verifier.isVoucherClaimed(
'0x...voucherId',
1n // nonce
);
if (claimed) {
console.log('Voucher already claimed');
}
`
`typescript
const signer = await verifier.recoverSigner(voucher, signature);
if (signer && signer.toLowerCase() === voucher.buyer.toLowerCase()) {
console.log('Valid signature from buyer');
}
`
`typescript
class DeferredSchemeVerifier {
constructor(config: DeferredSchemeConfig);
// Verification
verify(payload: DeferredPayload, requirements: PaymentRequirements): Promise
validateVoucher(voucher: Voucher, requirements: PaymentRequirements): boolean;
recoverSigner(voucher: Voucher, signature: Hex): Promise;
// Escrow operations
getEscrowBalance(buyer: Address, seller: Address, asset: Address): Promise
isVoucherClaimed(voucherId: Hex, nonce: bigint): Promise
// Getters
getNetwork(): SupportedNetwork;
getChainId(): number;
getEscrowAddress(): Address;
getEIP712Domain(): EIP712Domain;
}
`
`typescript`
interface DeferredSchemeConfig {
network: SupportedNetwork;
escrowAddress: Address;
rpcUrl?: string;
domainName?: string; // default: "X402DeferredEscrow"
domainVersion?: string; // default: "1"
}
`typescript`
interface EIP712Domain {
name: string;
version: string;
chainId: number;
verifyingContract: Address;
}
`typescript`
interface SignatureParts {
v: number;
r: Hex;
s: Hex;
}
Generate a random bytes32 voucher ID.
`typescript
import { generateVoucherId } from '@perkos/scheme-deferred';
const voucherId = generateVoucherId();
// => '0x1234...abcd' (32 bytes hex)
`
Create a voucher message object for EIP-712 signing.
`typescript
import { createVoucherMessage } from '@perkos/scheme-deferred';
const message = createVoucherMessage(
'0x...', // id
'0x...', // buyer
'0x...', // seller
'5000000', // valueAggregate
'0x...', // asset
1735689600, // timestamp
'1', // nonce
'0x...', // escrow
8453 // chainId
);
`
Convert a Voucher object to a tuple format for contract calls.
`typescript
import { createVoucherTuple } from '@perkos/scheme-deferred';
const tuple = createVoucherTuple(voucher);
// Use with escrow contract claimVoucher function
`
Create an EIP-712 domain for voucher signing.
`typescript
import { createEIP712Domain } from '@perkos/scheme-deferred';
const domain = createEIP712Domain(
8453, // chainId
'0x...', // escrowAddress
'CustomName', // optional domain name
'2' // optional version
);
`
Parse a signature into v, r, s components.
`typescript
import { parseSignature } from '@perkos/scheme-deferred';
const { v, r, s } = parseSignature('0x...');
`
The voucher type definition used for EIP-712 signing:
`typescript
import { VOUCHER_TYPES, VOUCHER_TYPE_DEF } from '@perkos/scheme-deferred';
// VOUCHER_TYPE_DEF structure:
[
{ name: "id", type: "bytes32" },
{ name: "buyer", type: "address" },
{ name: "seller", type: "address" },
{ name: "valueAggregate", type: "uint256" },
{ name: "asset", type: "address" },
{ name: "timestamp", type: "uint64" },
{ name: "nonce", type: "uint256" },
{ name: "escrow", type: "address" },
{ name: "chainId", type: "uint256" }
]
`
The package exports ABIs for interacting with the deferred escrow contract:
`typescript`
import {
DEFERRED_ESCROW_ABI,
DEFERRED_ESCROW_GET_BALANCE_ABI,
DEFERRED_ESCROW_VOUCHER_CLAIMED_ABI,
DEFERRED_ESCROW_CLAIM_VOUCHER_ABI,
ERC20_BALANCE_ABI
} from '@perkos/scheme-deferred';
| ABI | Function | Description |
|-----|----------|-------------|
| DEFERRED_ESCROW_GET_BALANCE_ABI | getAvailableBalance(buyer, seller, asset) | Get escrow balance |DEFERRED_ESCROW_VOUCHER_CLAIMED_ABI
| | voucherClaimed(voucherId, nonce) | Check if claimed |DEFERRED_ESCROW_CLAIM_VOUCHER_ABI
| | claimVoucher(voucher, signature) | Claim voucher |
The verify() method performs these checks in order:
1. Voucher Validation: Checks escrow address, chainId, seller, amount, and asset
2. Signature Recovery: Recovers signer using EIP-712 typed data
3. Signer Verification: Ensures signer matches voucher buyer
4. Claim Status: Checks if voucher already claimed on-chain
5. Balance Check: Verifies sufficient escrow balance
`typescript
import type {
DeferredPayload,
Voucher,
VerifyResponse,
PaymentRequirements,
Address,
Hex
} from '@perkos/scheme-deferred';
// V2 helper
import { getPaymentAmount } from '@perkos/scheme-deferred';
``
- @perkos/types-x402 - Core x402 types
- @perkos/util-chains - Chain utilities
- @perkos/scheme-exact - Exact payment scheme
- @perkos/contracts-escrow - Escrow contract ABI
- @perkos/service-x402 - x402 service orchestrator
MIT