Smart Contracts
PayNode’s contracts are minimal, immutable, and fully verified on Basescan. The protocol uses CREATE2 for deterministic addresses across all EVM chains.
📍 Contract Addresses
The Treasury Address receiving the 1% protocol fee is 0x598bF63F5449876efafa7b36b77Deb2070621C0E.
🔵 Base Mainnet (Production)
| Configuration | Value |
|---|---|
| Chain ID | 8453 |
| Public RPC | https://mainnet.base.org |
| Block Explorer | Basescan |
| PayNode Router | 0x4A73696ccF76E7381b044cB95127B3784369Ed63 |
| USDC | 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 |
🧪 Base Sepolia (Testnet / Sandbox)
💡 How to get Testnet Gas (ETH)?
- Optimism Faucet (Highly Recommended): console.optimism.io/faucet — 0.01 ETH daily.
Note: Faucets frequently go down or change rules. If this link fails, search for ‘Base Sepolia Faucet’ or bridge ETH from Sepolia (L1) to Base (L2) independently.
💰 How to get Testnet USDC?
Use the PayNode CLI:
bun run paynode-402 mint --network testnetor manually callminton the MockUSDC contract.
| Configuration | Value |
|---|---|
| Chain ID | 84532 |
| Public RPC | https://sepolia.base.org |
| Block Explorer | Basescan Sepolia |
| PayNode Router | 0x24cD8b68aaC209217ff5a6ef1Bf55a59f2c8Ca6F |
| Mock USDC | 0x65c088EfBDB0E03185Dbe8e258Ad0cf4Ab7946b0 |
🚰 How to get Testnet USDC?
- Open the MockUSDC Basescan Page.
- Click the “Contract” tab -> “Write Contract” button.
- Click “Connect to Web3” and link your wallet.
- Expand function
3. mint. - Enter your
addressand1000000000(for 1,000 USDC). Click Write.
📄 Core ABI
pay — Standard Payment
function pay(
address token, // ERC20 token (e.g. USDC)
address merchant, // Merchant receiving 99%
uint256 amount, // Total amount (smallest unit)
bytes32 orderId // External tracking ID
) external;payWithPermit — Single-Tx Payment (EIP-2612)
Allows a third-party relayer (e.g. AI Agent) to pay on behalf of a token holder using an offline signature. No prior approve() needed.
function payWithPermit(
address payer, // Token holder who signed the permit
address token, // ERC20 token with EIP-2612 support
address merchant, // Merchant receiving 99%
uint256 amount, // Total amount
bytes32 orderId, // External tracking ID
uint256 deadline, // Permit expiry timestamp
uint8 v, bytes32 r, bytes32 s // ECDSA signature
) external;PaymentReceived Event
Emitted on every successful payment. SDK Verifiers decode this event for on-chain verification.
event PaymentReceived(
bytes32 indexed orderId,
address indexed merchant,
address indexed payer,
address token,
uint256 amount,
uint256 fee,
uint256 chainId // Cross-chain replay protection
);🛡️ Security Features
| Feature | Description |
|---|---|
| SafeERC20 | Handles non-standard ERC20 return values safely |
| Ownable2Step | Two-step ownership transfer prevents accidental lockout |
| Pausable | Owner can emergency-pause all payments |
| Custom Errors | InvalidAddress(), AmountTooLow(), UnauthorizedCaller() — saves gas vs string requires |
| Deterministic Deploy | CREATE2 ensures same address across all EVM chains |
| ChainId in Events | Prevents cross-chain replay attacks |
🚰 Faucet & Test Tokens (Sandbox)
To test PayNode on Base Sepolia, you need mock USDC. Our test token includes a public mint function for easy onboarding.
Option 1: Manual Mint via Basescan
- Visit the MockUSDC Contract on Basescan.
- Connect your wallet (MetaMask/Coinbase Wallet).
- Go to Write Contract ->
mint. - Enter your address and an amount (e.g.,
1000000000for 1,000 USDC — remember it has 6 decimals). - Click Write and confirm the transaction.
Option 2: Programmatic Mint (Python SDK)
Agents can “self-fund” their testing wallets:
from paynode_sdk import PayNodeAgentClient
from web3 import Web3
# Initialize with private key
client = PayNodeAgentClient("YOUR_PRIVATE_KEY", ["https://sepolia.base.org"])
# Mock USDC ABI (minimal)
usdc_abi = [{"inputs": [{"name": "to", "type": "address"}, {"name": "amount", "type": "uint256"}], "name": "mint", "outputs": [], "stateMutability": "nonpayable", "type": "function"}]
usdc = client.w3.eth.contract(address="0x65c088EfBDB0E03185Dbe8e258Ad0cf4Ab7946b0", abi=usdc_abi)
# Mint 1,000 USDC
tx = usdc.functions.mint(client.account.address, 1000 * 10**6).build_transaction({
'from': client.account.address,
'nonce': client.w3.eth.get_transaction_count(client.account.address),
'gas': 100000,
'gasPrice': int(client.w3.eth.gas_price * 1.2)
})
signed_tx = client.account.sign_transaction(tx)
client.w3.eth.send_raw_transaction(signed_tx.raw_transaction)
print("💰 Test USDC Minted!")Payment Flow (Manual Integration)
If building without the SDK:
USDC.approve(RouterAddress, amount)Router.pay(USDC_Address, merchant, amount, orderId)- Router atomically splits: 99% → merchant, 1% → treasury
PaymentReceivedevent is emitted- Verify the event in your backend to confirm payment