@perkos/scheme-exact
v1.1.1
Published
EIP-3009 TransferWithAuthorization verification utilities for x402 exact scheme payments
Downloads
324
Maintainers
Readme
@perkos/scheme-exact
EIP-3009 TransferWithAuthorization verification utilities for x402 exact scheme payments. Provides signature verification, balance checking, and nonce validation for immediate on-chain token transfers.
Installation
npm install @perkos/scheme-exactOverview
The exact scheme enables immediate on-chain payments using EIP-3009 transferWithAuthorization:
- Client signs authorization (EIP-712) for token transfer
- Facilitator verifies signature, balance, and timing
- Settlement executes
transferWithAuthorizationon-chain - Tokens transfer directly from client to recipient
Usage
Basic Verification
import { ExactSchemeVerifier } from '@perkos/scheme-exact';
import type { ExactPayload, PaymentRequirements } from '@perkos/scheme-exact';
const verifier = new ExactSchemeVerifier({
network: 'base',
rpcUrl: 'https://mainnet.base.org' // optional
});
const payload: ExactPayload = {
authorization: {
from: '0x...payer',
to: '0x...recipient',
value: '1000000',
validAfter: '0',
validBefore: '1735689600',
nonce: '0x...'
},
signature: '0x...'
};
const requirements: PaymentRequirements = {
scheme: 'exact',
network: 'base',
maxAmountRequired: '1000000',
resource: '/api/service',
payTo: '0x...recipient',
maxTimeoutSeconds: 3600,
asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' // USDC
};
const result = await verifier.verify(payload, requirements);
if (result.isValid) {
console.log('Valid authorization from:', result.payer);
} else {
console.error('Invalid:', result.invalidReason);
}Create Authorization for Signing
import {
generateNonce,
createAuthorizationMessage,
createEIP712Domain,
TRANSFER_WITH_AUTHORIZATION_TYPES
} from '@perkos/scheme-exact';
import { signTypedData } from 'viem/accounts';
// Generate unique nonce
const nonce = generateNonce();
// Create authorization message
const message = createAuthorizationMessage(
'0x...from', // payer
'0x...to', // recipient
'1000000', // value (USDC 6 decimals)
'0', // validAfter (immediately valid)
Math.floor(Date.now() / 1000) + 3600, // validBefore (1 hour)
nonce
);
// Create EIP-712 domain for USDC
const domain = createEIP712Domain(
8453, // chainId (Base)
'0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' // USDC address
);
// Sign the authorization (client-side)
const signature = await signTypedData({
domain,
types: TRANSFER_WITH_AUTHORIZATION_TYPES,
primaryType: 'TransferWithAuthorization',
message,
privateKey: '0x...'
});Check Balance
const verifier = new ExactSchemeVerifier({
network: 'base'
});
const hasBalance = await verifier.checkBalance(
'0x...address',
'1000000',
'0x...tokenAddress'
);
if (!hasBalance) {
console.log('Insufficient balance');
}Check Nonce State
const isUsed = await verifier.checkNonceState(
'0x...authorizer',
'0x...nonce',
'0x...tokenAddress'
);
if (isUsed) {
console.log('Nonce already used');
}Parse Signature for Contract Call
import { parseSignature } from '@perkos/scheme-exact';
const { v, r, s } = parseSignature('0x...');
// Use with transferWithAuthorization contract call
await tokenContract.write.transferWithAuthorization([
from, to, value, validAfter, validBefore, nonce, v, r, s
]);API Reference
ExactSchemeVerifier
class ExactSchemeVerifier {
constructor(config: ExactSchemeConfig);
// Verification
verify(payload: ExactPayload, requirements: PaymentRequirements): Promise<VerifyResponse>;
validateAuthorization(auth: ExactPayload['authorization'], requirements: PaymentRequirements): boolean;
recoverSigner(authorization: ExactPayload['authorization'], signature: Hex, tokenAddress: Address): Promise<Address | null>;
// Balance and nonce checks
checkBalance(address: Address, amount: string, tokenAddress: Address): Promise<boolean>;
checkNonceState(authorizer: Address, nonce: Hex, tokenAddress: Address): Promise<boolean>;
// Getters
getNetwork(): SupportedNetwork;
getChainId(): number;
getEIP712Domain(tokenAddress: Address): EIP712Domain;
}ExactSchemeConfig
interface ExactSchemeConfig {
network: SupportedNetwork;
rpcUrl?: string;
tokenName?: string; // default: chain-specific USDC name
tokenVersion?: string; // default: "2"
}EIP712Domain
interface EIP712Domain {
name: string;
version: string;
chainId: number;
verifyingContract: Address;
}SignatureParts
interface SignatureParts {
v: number;
r: Hex;
s: Hex;
}Utility Functions
generateNonce
Generate a random bytes32 nonce for authorization.
import { generateNonce } from '@perkos/scheme-exact';
const nonce = generateNonce();
// => '0x1234...abcd' (32 bytes hex)createAuthorizationMessage
Create an authorization message object for EIP-712 signing.
import { createAuthorizationMessage } from '@perkos/scheme-exact';
const message = createAuthorizationMessage(
'0x...from', // payer address
'0x...to', // recipient address
'1000000', // value
'0', // validAfter
'1735689600', // validBefore
'0x...nonce' // nonce
);createEIP712Domain
Create an EIP-712 domain for token signing.
import { createEIP712Domain } from '@perkos/scheme-exact';
const domain = createEIP712Domain(
8453, // chainId
'0x...', // tokenAddress
'USD Coin', // optional token name
'2' // optional version
);parseSignature
Parse a signature into v, r, s components for contract calls.
import { parseSignature } from '@perkos/scheme-exact';
const { v, r, s } = parseSignature('0x...');isWithinValidityWindow
Check if current time is within the validity window.
import { isWithinValidityWindow } from '@perkos/scheme-exact';
const isValid = isWithinValidityWindow(
'0', // validAfter
'1735689600', // validBefore
Date.now() / 1000 // optional timestamp
);EIP-712 Type Definition
The TransferWithAuthorization type definition used for EIP-712 signing:
import {
TRANSFER_WITH_AUTHORIZATION_TYPES,
TRANSFER_WITH_AUTHORIZATION_TYPE_DEF
} from '@perkos/scheme-exact';
// TRANSFER_WITH_AUTHORIZATION_TYPE_DEF structure:
[
{ name: "from", type: "address" },
{ name: "to", type: "address" },
{ name: "value", type: "uint256" },
{ name: "validAfter", type: "uint256" },
{ name: "validBefore", type: "uint256" },
{ name: "nonce", type: "bytes32" }
]Contract ABIs
The package exports ABIs for EIP-3009 token interactions:
import {
ERC20_BALANCE_ABI,
EIP3009_AUTHORIZATION_STATE_ABI,
TRANSFER_WITH_AUTHORIZATION_ABI
} from '@perkos/scheme-exact';Available Functions
| ABI | Function | Description |
|-----|----------|-------------|
| ERC20_BALANCE_ABI | balanceOf(account) | Get token balance |
| EIP3009_AUTHORIZATION_STATE_ABI | authorizationState(authorizer, nonce) | Check nonce used |
| TRANSFER_WITH_AUTHORIZATION_ABI | transferWithAuthorization(...) | Execute transfer |
USDC Token Names
The package includes standard USDC token names by chain:
import { USDC_TOKEN_NAMES } from '@perkos/scheme-exact';
// Chain ID => Token Name
{
1: "USD Coin", // Ethereum Mainnet
8453: "USD Coin", // Base
84532: "USDC", // Base Sepolia
43114: "USD Coin", // Avalanche
43113: "USDC", // Avalanche Fuji
137: "USD Coin", // Polygon
80002: "USDC", // Polygon Amoy
42161: "USD Coin", // Arbitrum
421614: "USDC", // Arbitrum Sepolia
10: "USD Coin", // Optimism
11155420: "USDC", // Optimism Sepolia
42220: "USD Coin", // Celo
11155111: "USDC", // Sepolia
}Verification Flow
The verify() method performs these checks in order:
- Authorization Validation: Checks recipient and amount against requirements
- Signature Recovery: Recovers signer using EIP-712 typed data
- Signer Verification: Ensures signer matches authorization
fromaddress - Balance Check: Verifies sufficient token balance
- Timing Validation: Checks
validAfterandvalidBeforewindows - Nonce Check: Verifies nonce not already used on-chain
Re-exported Types
import type {
ExactPayload,
VerifyResponse,
PaymentRequirements,
Address,
Hex
} from '@perkos/scheme-exact';
// V2 helper
import { getPaymentAmount } from '@perkos/scheme-exact';Related Packages
- @perkos/types-x402 - Core x402 types
- @perkos/util-chains - Chain utilities
- @perkos/scheme-deferred - Deferred payment scheme
- @perkos/service-x402 - x402 service orchestrator
License
MIT
