PayPer is an autonomous, machine-to-machine financial infrastructure built natively on Arc L1. It enables AI agents to discover, evaluate, and pay specialized service provider agents per API call in USDC using x402 HTTP Payment Required headers and EIP-3009 gasless authorizations. Zero subscriptions, zero hardcoded API keys — Payment IS the Credential.
| Deployment Parameter | Live Contract Specification |
|---|---|
| Target Blockchain | Arc Testnet (Circle Stablecoin-Native Layer-1) |
| Chain ID | 5042002 (0x4cef02) |
| RPC Endpoint | https://rpc.testnet.arc.network |
| Block Explorer | https://testnet.arcscan.app |
| Native Gas & Settlement Token | USDC System Contract (0x3600000000000000000000000000000000000000) |
| PayPerRegistry Smart Contract | 0xdAea9d883f8d7F87F0D62378555e6660EC51AB77 |
| Deployer & Authority Wallet | 0x926b00bcAB0D17f059B884B14554efec4573F97c |
| Pitch Deck Document | PRESENTATION.md |
| Live Hosted Web Application | payper-three.vercel.app |
graph TD
subgraph Agentic Buyer Stack
A[Goal Prompt] --> B[Goal Decomposition Engine]
B --> C[Signal-Based Seller Selection]
C --> D[Circle Agent Policy Guardrails]
end
subgraph On-Chain Signal Layer
R[(PayPerRegistry Contract<br/>0xdAea...AB77)] -->|Rating / Speed ms / USDC Price| C
end
subgraph Seller x402 Execution Layer
D -->|1. HTTP Request| S[Seller x402 Server]
S -->|2. HTTP 402 Challenge| D
D -->|3. Signed EIP-3009 Payload| S
S -->|4. Upstream Execution FIRST| U[Upstream API Capability]
U -->|5. HTTP 200 OK| S
end
subgraph Arc L1 Settlement Layer
S -->|6. Settle EIP-3009 & Record Call| Arc[Arc L1 Blockchain<br/>USDC System Contract 0x3600...0000]
Arc -->|7. Verified On-Chain Receipt| A
end
sequenceDiagram
autonumber
actor BuyerAgent as Autonomous Buyer Agent (Circle W3S)
participant SellerServer as Seller Server (x402 Express)
participant UpstreamAPI as Upstream API Capability
participant ArcL1 as Arc L1 Blockchain (PayPerRegistry & USDC)
BuyerAgent->>SellerServer: 1. HTTP Request (No Credentials / API Key)
SellerServer-->>BuyerAgent: 2. HTTP 402 Payment Required (Price, Nonce, ServiceID)
Note over BuyerAgent: Evaluates Circle Guardrail Policy (Max/Call & Session Cap)
BuyerAgent->>BuyerAgent: 3. Signs EIP-3009 transferWithAuthorization Off-Chain
SellerServer->>UpstreamAPI: 4. Executes Upstream Service Call FIRST
alt Upstream Execution Succeeds (HTTP 200 OK)
UpstreamAPI-->>SellerServer: Upstream Response Data
SellerServer->>ArcL1: 5. Submits EIP-3009 Authorization to Arc
ArcL1-->>SellerServer: Settles USDC & Records Call Metrics in PayPerRegistry
SellerServer-->>BuyerAgent: 6. Returns API Data & Arc Receipt Tx Hash
else Upstream Execution Fails (HTTP 500 Error)
UpstreamAPI-->>SellerServer: Service Error
Note over SellerServer: Signature DISCARDED. Zero USDC deducted!
SellerServer-->>BuyerAgent: 500 Error (0 USDC charged)
end
As AI agents transition from conversational chatbots to autonomous execution systems, existing Web2 financial rails and traditional Web3 patterns create critical bottlenecks:
Traditional Web2 API Models Standard Web3 On-Chain Payments
-------------------------------- ---------------------------------
- Monthly SaaS Subscriptions - Volatile third-party gas tokens (ETH/MATIC)
- Manual Credit Card Checkout - Complex two-step Token Approve transactions
- Hardcoded Provider API Keys - Manual MetaMask popup approvals
- Vulnerable to Key Theft & Collisions - High latency (12s to 15m finality)
PayPer Machine-Native Financial Commerce
-------------------------------------------
1. Zero Subscriptions: Agents pay per execution (e.g. 0.01 USDC).
2. Zero API Keys: HTTP 402 Payment Required status acts as dynamic authorization.
3. Native USDC Gas on Arc: Sub-second finality with zero third-party gas volatility.
4. EIP-3009 Gasless Off-Chain Signing: One-step signature without prior approval transactions.
5. Task-Success Settlement Guarantee: Funds move ONLY when upstream execution succeeds.
When an autonomous buyer agent invokes a seller endpoint without credentials, the server responds with a standard HTTP 402 Payment Required header payload containing the precise payment challenge:
{
"status": 402,
"error": "Payment Required",
"challenge": {
"pricePerCall": 10000,
"priceUsdc": "0.01",
"payTo": "0x926b00bcAB0D17f059B884B14554efec4573F97c",
"validBefore": 1784793600,
"nonce": "0xa4f82d19e...",
"network": "arc-testnet",
"chainId": 5042002,
"serviceName": "Web Scraper Pro"
}
}The buyer agent signs an EIP-3009 typed data payload off-chain:
This eliminates the need for separate ERC20.approve() transactions, allowing instant, one-step sub-second settlement on Arc L1.
PayPer enforces a strict ordering rule in the seller payment middleware:
If a seller service throws an error or returns corrupted data, the signature is safely discarded. The buyer agent is NEVER charged for broken API calls.
Autonomous agents cannot rely on subjective marketing copy. The PayPerRegistry.sol smart contract acts as an immutable on-chain registry that tracks verifiable seller execution signals:
struct Listing {
uint256 id;
address seller;
string name;
string endpoint; // Public HTTP URL
uint256 pricePerCall; // USDC, 6 decimals
string category; // e.g. "scraping", "summarization", "image-gen"
string description;
bool active;
uint256 totalCalls;
uint256 successCount;
uint256 avgResponseMs; // Rolling average response speed
uint256 ratingScore; // Verified success percentage (1-100)
}The buyer agent calculates a deterministic composite score
This mathematical selection process eliminates hallucinated choices and guarantees that agents optimize for reliability, latency, and cost.
To guarantee the integrity of the reputation marketplace, PayPer shifts the responsibility of recording execution metrics from the seller to the buyer agent client.
- Traditional (Seller Logs Metrics): Sellers have a conflict of interest. They are incentivized to report artificially high success ratios and 1ms execution speeds to rank higher in the registry search algorithm. Additionally, sellers must configure and manage private keys and local databases to perform transactions on-chain.
- Buyer-Driven (Buyer Logs Metrics): The buyer agent client measures network latency and execution success. Since the buyer has no incentive to lie, they write authentic metrics to
recordCallMetrics()on the smart contract directly.
- Zero-Setup Seller Onboarding: Developers can publish wrapped API endpoints to the PayPer Registry with absolute ease. Their hosted endpoints do not require gas tokens, private keys, database setups, or manual environment variables mapping.
- Sybil Resistance: The smart contract filters metrics updates, validating that only callers interacting with the services can write updates.
Integrated with the official Circle Developer Stack, the buyer agent includes strict policy guardrails:
// Circle Agent Guardrail Policy Engine
const policy = {
maxSingleCallBudgetUSDC: 0.05, // Reject any call costing > 0.05 USDC
maxSessionBudgetUSDC: 0.15, // Hard spending cap per execution session
permittedCategories: ['scraping', 'summarization', 'image-gen']
};If an endpoint attempts to overcharge or request unauthorized spending, the Circle Agent Stack immediately rejects the authorization before signing.
The project includes a reference seller capability wrapper server in the seller/ directory. This server demonstrates how any service provider can wrap their upstream API (such as LLMs, data scrapers, or hosting endpoints) with PayPer's challenge-response middleware.
- Express-Based Handshake: Dynamically intercepts unauthenticated requests, returning the HTTP 402 Payment Required challenge containing the payment params and a single-use transaction nonce.
- EIP-3009 Verification & Settlement: Receives and validates signed EIP-3009 USDC transfer authorizations, executing the transaction on Arc L1 to collect payments from the buyer.
Developers and autonomous agentic systems can query any PayPer capability listed on-chain without static API credentials by implementing the x402 HTTP challenge-response handshake in their clients.
Here is the complete implementation of a buyer client that automatically handles EIP-3009 signature verification and settlement:
import { ethers } from 'ethers';
async function queryPayPerService(endpointUrl, privateKey, payload = {}) {
const wallet = new ethers.Wallet(privateKey);
// 1. Initial Request (Triggers HTTP 402 challenge)
let response = await fetch(endpointUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
// 2. Challenge Handshake
if (response.status === 402) {
const { x402 } = await response.json();
const domain = {
name: 'USD Coin',
version: '2',
chainId: x402.chainId,
verifyingContract: '0x3600000000000000000000000000000000000000'
};
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' }
]
};
const message = {
from: wallet.address,
to: x402.payTo,
value: x402.amount,
validAfter: 0,
validBefore: x402.validBefore,
nonce: x402.nonce
};
const signature = await wallet.signTypedData(domain, types, message);
const paymentAuth = {
from: wallet.address,
to: x402.payTo,
amount: x402.amount,
validBefore: x402.validBefore,
nonce: x402.nonce,
signature
};
// 3. Resubmit with signed payment authorization
response = await fetch(endpointUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-PAYMENT-AUTH': JSON.stringify(paymentAuth)
},
body: JSON.stringify(payload)
});
}
const result = await response.json();
if (!response.ok || !result.success) {
throw new Error(result.error || 'Execution failed');
}
return result;
}Service providers can monetize any API endpoint (such as AI models, databases, or computing resources) by wrapping it with PayPer's HTTP 402 middleware.
Sellers run an Express server that intercepts unauthenticated requests, returns the EIP-3009 payment challenge parameters, and settles the signed transfer payload on the Arc L1 blockchain.
Here is the complete template of a seller server middleware:
import express from 'express';
import { ethers } from 'ethers';
const app = express();
app.use(express.json());
const SELLER_WALLET = process.env.SELLER_WALLET; // Seller receiver address
const SELLER_PRIVATE_KEY = process.env.SELLER_PRIVATE_KEY; // Used to settle txs on-chain
const PRICE_PER_CALL = 10000; // 0.01 USDC (6 decimals)
const SERVICE_ID = 4; // Your registered service ID in registry contract
app.post('/api/service/gemini-flash', async (req, res) => {
const paymentHeader = req.headers['x-payment-auth'];
// STEP 1: If no payment is attached, return HTTP 402 Challenge
if (!paymentHeader) {
const nonce = ethers.hexlify(ethers.randomBytes(32));
const validBefore = Math.floor(Date.now() / 1000) + 120; // 2 minutes validity
return res.status(402).json({
error: 'Payment Required',
code: 402,
x402: {
payTo: SELLER_WALLET,
amount: PRICE_PER_CALL,
currency: 'USDC',
decimals: 6,
validBefore: validBefore,
nonce: nonce,
chainId: 5042002, // Arc Testnet
serviceId: SERVICE_ID
}
});
}
// STEP 2: Verify and Settle Payment On-Chain
try {
const paymentAuth = JSON.parse(paymentHeader);
const provider = new ethers.JsonRpcProvider('https://rpc.testnet.arc.network');
const signer = new ethers.Wallet(SELLER_PRIVATE_KEY, provider);
// Initialize USDC Native token contract
const USDC_CONTRACT = '0x3600000000000000000000000000000000000000';
const USDC_ABI = [
"function transferWithAuthorization(address from, address to, uint256 value, uint256 validAfter, uint256 validBefore, bytes32 nonce, uint8 v, bytes32 r, bytes32 s) external"
];
const usdcContract = new ethers.Contract(USDC_CONTRACT, USDC_ABI, signer);
// Split the buyer's signature into v, r, s parameters
const sig = ethers.Signature.from(paymentAuth.signature);
// Submit EIP-3009 transfer signature on-chain to move USDC
const transferTx = await usdcContract.transferWithAuthorization(
paymentAuth.from,
paymentAuth.to,
paymentAuth.amount,
0, // validAfter
paymentAuth.validBefore,
paymentAuth.nonce,
sig.v,
sig.r,
sig.s
);
await transferTx.wait();
// STEP 3: Execute Upstream API (e.g. Gemini, Scraper)
const resultText = "Your API execution output based on req.body";
// STEP 4: Submit metrics to the PayPerRegistry contract
const REGISTRY_CONTRACT = '0xdAea9d883f8d7F87F0D62378555e6660EC51AB77';
const REGISTRY_ABI = [
"function recordCallMetrics(uint256 id, bool success, uint256 responseTimeMs) external"
];
const registryContract = new ethers.Contract(REGISTRY_CONTRACT, REGISTRY_ABI, signer);
await registryContract.recordCallMetrics(SERVICE_ID, true, 150).catch(console.error);
// Return the response to the buyer with transaction receipt details
return res.status(200).json({
success: true,
data: { text: resultText },
paymentReceipt: {
settled: true,
amountUSDC: PRICE_PER_CALL / 1e6,
txHash: transferTx.hash,
settledAt: new Date().toISOString()
}
});
} catch (error) {
return res.status(500).json({ success: false, error: error.message });
}
});The frontend application features several Web3 optimizations to provide an excellent user experience:
- Typography: Uses Bricolage Grotesque for bold display headlines, IBM Plex Mono for technical and blockchain parameters, and Manrope for body text.
- State Persistence: The current view state (landing page vs. main app dashboard) is cached in LocalStorage, preventing frustrating page resets back to the cover page on browser refreshes.
- Navbar Wallet Dropdown: Connected MetaMask sessions show the native USDC balance (properly scaled using the 18-decimal gas token format) and a clean button to disconnect the session.
- Integration Detail Modals: Service cards are clickable, opening a panel that offers copyable endpoint paths, seller addresses, cURL test commands, and agent startup CLI commands.
- Public RPC Isolation: Reads decouple from MetaMask provider instances and route directly to the public Arc Testnet RPC node. Metrics fetch queries are caught individually, protecting the UI from freezing when specific contract parameters return call exceptions.
PAYPER/
├── buyer-agent/ # Autonomous Buyer Agent Engine & Guardrails
│ ├── agentEngine.js # Goal decomposition & signal ranking algorithm
│ ├── circleAgentStack.js # Circle W3S Agent Wallet & spending guardrails
│ └── runAgent.js # CLI entry point for autonomous pipeline
├── config/
│ └── arcConfig.js # Arc L1 Testnet & Arc App Kit parameters
├── contracts/
│ └── PayPerRegistry.sol # On-chain capability registry & metric tracker
├── frontend/
│ ├── src/
│ │ ├── App.jsx # Full-stack React app (Landing, Marketplace, Deck)
│ │ ├── index.css # Cyber-financial design system CSS
│ │ └── main.jsx # React DOM entry point
├── scripts/
│ └── deploy.js # Hardhat deployment script targeting Arc Testnet
├── seller/
│ ├── circleSellerWallet.js # Seller authorization verifier & wallet helper
│ └── x402Server.js # Express ESM server implementing HTTP 402 middleware
├── test/
│ ├── CircleAgentStack.test.js # Unit test suite for spending guardrails (4 tests)
│ └── PayPerRegistry.test.js # Unit test suite for PayPerRegistry contract (4 tests)
├── .env.example # Environment configuration template
├── deployments.json # Live deployed contract address artifact
├── hardhat.config.js # Hardhat configuration (Chain ID 5042002)
├── logo.png # PayPer Marketplace Logo
├── PRESENTATION.md # Hackathon presentation deck document
└── README.md # Primary project documentation
PayPer includes a complete unit testing suite for smart contracts and spending policy engines:
npm run test Circle Agent Stack Spending Guardrails
✔ Should approve call when price is within single-call and session limits
✔ Should reject call when price exceeds single-call budget limit
✔ Should reject call when category is not permitted
✔ Should reject call when cumulative session spending cap is exceeded
PayPer Marketplace Contracts
PayPerRegistry On-Chain Directory
✔ Should register a new seller service listing
✔ Should allow seller to toggle active status
✔ Should update metrics and network volume on call recording
✔ Should filter services by category
8 passing (390ms)
git clone https://github.com/ODbeke/payper.git
cd PAYPER
npm installCopy .env.example to .env:
cp .env.example .env(Optionally add your PRIVATE_KEY for Arc Testnet deployments or Circle API credentials).
npm run devOpen http://localhost:3001 in your browser to view the live marketplace, landing page, and slide deck.
npm run sellerStarts the seller backend server on http://localhost:4020.
npm run agentBuilt for the Encode Club Programmable Money Hackathon (Arc Track).
Repository: github.com/ODbeke/payper
