ChainsEVM Chains

EVM Chains

Detailed guide for EVM (Ethereum Virtual Machine) compatible chains.

Supported EVM Chains

T402 supports all major EVM chains through the @t402/evm package.

Mainnet Chains

ChainNetwork IDUSDT0 AddressGasless
Ethereumeip155:10x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee
Arbitrumeip155:421610xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9
Baseeip155:84530x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
Optimismeip155:100x94b008aA00579c1307B0EF2c499aD98a8ce58e58
Inkeip155:570730x0200C29006150606B650577BBE7B6248F58470c1
Berachaineip155:800940x779Ded0c9e1022225f8E0630b35a9b54bE713736
Unichaineip155:1300x9151434b16b9763660705744891fA906F660EcC5

Layer 2 Benefits

BenefitDescription
Lower gas~100x cheaper than Ethereum mainnet
Faster finalitySeconds vs minutes
Same securitySecured by Ethereum
Same addressesUse same wallet across L2s

Integration

Server Setup

import { t402ResourceServer, createFacilitatorClient } from '@t402/core/server';
import { registerExactEvmScheme } from '@t402/evm/exact/server';
 
const facilitator = createFacilitatorClient({
  url: 'https://facilitator.t402.io',
});
 
const server = new t402ResourceServer(facilitator);
registerExactEvmScheme(server, {});
 
// Configure routes
const routes = {
  '/api/premium/*': {
    accepts: [
      { scheme: 'exact', network: 'eip155:8453', payTo: '0x...', price: '$0.01' },
      { scheme: 'exact', network: 'eip155:42161', payTo: '0x...', price: '$0.01' },
    ],
  },
};

Client Setup

import { t402Client } from '@t402/core/client';
import { ExactEvmScheme } from '@t402/evm/exact/client';
import { createWalletClient, custom } from 'viem';
import { base } from 'viem/chains';
 
// Create viem wallet client
const walletClient = createWalletClient({
  chain: base,
  transport: custom(window.ethereum),
});
 
// Create T402 client
const client = new t402Client();
client.register('eip155:*', new ExactEvmScheme(walletClient));

EIP-3009 (TransferWithAuthorization)

T402 uses EIP-3009 for gasless token transfers.

How It Works

  1. User signs an off-chain authorization message
  2. Facilitator executes the transferWithAuthorization on-chain
  3. User pays no gas - facilitator covers gas costs

Authorization Structure

interface TransferWithAuthorization {
  from: string;        // Payer address
  to: string;          // Recipient address
  value: bigint;       // Amount in smallest unit
  validAfter: bigint;  // Unix timestamp (usually 0)
  validBefore: bigint; // Deadline timestamp
  nonce: bytes32;      // Random nonce
}

Signature Verification

The EIP-712 typed data structure:

const domain = {
  name: 'USD0',
  version: '1',
  chainId: 8453,
  verifyingContract: '0x...',
};
 
const types = {
  TransferWithAuthorization: [
    { name: 'from', type: 'address' },
    { name: 'to', type: 'address' },
    { name: 'value', type: 'uint256' },
    { name: 'validAfter', type: 'uint256' },
    { name: 'validBefore', type: 'uint256' },
    { name: 'nonce', type: 'bytes32' },
  ],
};

Gas Estimation

Average Costs by Chain

ChainTransfer CostSettlement Cost
Ethereum~$5-50~$10-100
Arbitrum~$0.01-0.10~$0.02-0.20
Base~$0.001-0.01~$0.002-0.02
Optimism~$0.01-0.10~$0.02-0.20

Gas costs vary with network congestion. L2s are generally 100x cheaper than Ethereum mainnet.

Programmatic Estimation

import { estimateGas } from '@t402/evm';
 
const estimate = await estimateGas({
  network: 'eip155:8453',
  operation: 'transferWithAuthorization',
  rpcUrl: 'https://mainnet.base.org',
});
 
console.log(`Estimated gas: ${estimate.gasLimit}`);
console.log(`Estimated cost: $${estimate.costUsd}`);

Multi-Chain Support

Same Address Across Chains

EVM addresses work across all EVM chains:

const payToAddress = '0x742d35Cc6634C0532925a3b844Bc9e7595f5b1A1';
 
const routes = {
  '/api/premium/*': {
    accepts: [
      { scheme: 'exact', network: 'eip155:1', payTo: payToAddress, price: '$0.01' },
      { scheme: 'exact', network: 'eip155:8453', payTo: payToAddress, price: '$0.01' },
      { scheme: 'exact', network: 'eip155:42161', payTo: payToAddress, price: '$0.01' },
    ],
  },
};

User Network Selection

Let users choose their preferred chain:

import { getNetworkDisplayName, getChainConfig } from '@t402/evm';
 
const supportedNetworks = [
  'eip155:8453',   // Base
  'eip155:42161',  // Arbitrum
  'eip155:10',     // Optimism
];
 
// Display network options to user
supportedNetworks.forEach(network => {
  const config = getChainConfig(network);
  console.log(`${getNetworkDisplayName(network)}: ~$${config.avgGasCost}`);
});

Wallet Integration

MetaMask

// Request account access
await window.ethereum.request({ method: 'eth_requestAccounts' });
 
// Get current chain
const chainId = await window.ethereum.request({ method: 'eth_chainId' });
 
// Switch to Base
await window.ethereum.request({
  method: 'wallet_switchEthereumChain',
  params: [{ chainId: '0x2105' }], // Base = 8453 = 0x2105
});

WalletConnect

import { createWalletClient } from 'viem';
import { WalletConnectConnector } from '@wagmi/connectors/walletConnect';
 
const connector = new WalletConnectConnector({
  options: {
    projectId: 'your-project-id',
    chains: [base, arbitrum, optimism],
  },
});
 
await connector.connect();
const walletClient = await connector.getWalletClient();

Safe Multi-Sig

import { createMultisigClient } from '@t402/wdk-multisig';
 
const multisigClient = await createMultisigClient({
  safeAddress: '0xYourSafeAddress',
  chainId: 8453,
  owners: ['0xOwner1', '0xOwner2', '0xOwner3'],
  threshold: 2,
});

RPC Endpoints

ChainPublic RPCPaid Options
Ethereumhttps://eth.llamarpc.comAlchemy, Infura
Arbitrumhttps://arb1.arbitrum.io/rpcAlchemy, QuickNode
Basehttps://mainnet.base.orgAlchemy, QuickNode
Optimismhttps://mainnet.optimism.ioAlchemy, QuickNode

Configuration

const rpcConfig = {
  'eip155:1': process.env.ETH_RPC_URL || 'https://eth.llamarpc.com',
  'eip155:8453': process.env.BASE_RPC_URL || 'https://mainnet.base.org',
  'eip155:42161': process.env.ARB_RPC_URL || 'https://arb1.arbitrum.io/rpc',
};

Troubleshooting

Common Issues

IssueCauseSolution
”Invalid signature”Wrong chain IDVerify network matches
”Insufficient funds”No USDT balanceFund wallet with USDT
”Nonce already used”Replay attemptGenerate new nonce
”Deadline passed”Authorization expiredSign new authorization

Debugging

import { verifySignature, decodePayment } from '@t402/evm';
 
// Decode payment header
const payment = decodePayment(xPaymentHeader);
console.log('Payment details:', payment);
 
// Verify signature locally
const isValid = await verifySignature({
  message: payment.message,
  signature: payment.signature,
  expectedSigner: payment.from,
});
console.log('Signature valid:', isValid);