Version: 1.1.0 | Date: 2026-03-22 | Duration: 6 weeks Branch:
feature/v5.2-x402-facilitator(fromfeature/uups-migration)References:
- Master Plan
- V5 Design Doc §4 — x402 integration design
- V5 Roadmap §V5.2 — Feature specifications (F1-F6)
- Tempo/MPP Research §8.6 — Code-level borrowings
- Ecosystem Evaluation §5 — SDK requirements
Transform SuperPaymaster from a Gas-only paymaster into a full x402 Facilitator with Payment Channel support. Three parallel workstreams:
- On-chain: x402 verify/settle + Payment Channel contract
- Off-chain: Operator Node framework (Facilitator HTTP service)
- SDK:
@aastar/x402+@aastar/channelpackages
Dependency: Requires V5.1 _consumeCredit() to be merged first for the shared billing kernel.
Description: Read-only verification of x402 payment credential (EIP-3009 signature check).
function verifyX402Payment(
address from, // Payer (Agent wallet)
address to, // Payee (Resource Server)
address asset, // ERC-20 (USDC)
uint256 amount,
uint256 validAfter, // EIP-3009 time window
uint256 validBefore,
bytes32 nonce, // EIP-3009 random nonce
bytes calldata signature
) external view returns (bool valid, string memory reason);Sub-tasks:
| # | Task | Est. | File |
|---|---|---|---|
| T1.1 | Implement EIP-3009 signature reconstruction and verification | 3h | SuperPaymaster.sol |
| T1.2 | Check from balance and allowance for asset |
1h | SuperPaymaster.sol |
| T1.3 | Validate time window (validAfter <= now <= validBefore) |
0.5h | SuperPaymaster.sol |
| T1.4 | Return structured reason on failure | 1h | SuperPaymaster.sol |
| T1.5 | Unit tests: valid payment, expired, insufficient balance, bad sig | 3h | test/v3/X402Settle.t.sol |
Acceptance Criteria:
- Valid EIP-3009 signature →
(true, "") - Invalid signature →
(false, "InvalidSignature") - Expired time window →
(false, "Expired") - Insufficient payer balance →
(false, "InsufficientBalance") - Zero gas cost (view function)
Description: Execute x402 payment settlement — transfer tokens + charge facilitator fee.
function settleX402Payment(
address from,
address to,
address asset,
uint256 amount,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
bytes calldata signature
) external nonReentrant returns (bytes32 txHash);Settlement Flow:
1. Verify EIP-3009 signature (reuse verifyX402Payment logic)
2. Call asset.transferWithAuthorization(from, to, netAmount, validAfter, validBefore, nonce, signature)
3. Deduct facilitator fee from transfer amount
4. Fee split: operatorFee → operator earnings, protocolFee → protocolRevenue
5. Emit X402PaymentSettled event
Sub-tasks:
| # | Task | Est. | File |
|---|---|---|---|
| T2.1 | Implement settleX402Payment() with EIP-3009 transferWithAuthorization call |
4h | SuperPaymaster.sol |
| T2.2 | Implement fee calculation: amount * facilitatorFeeBPS / BPS_DENOMINATOR |
1h | SuperPaymaster.sol |
| T2.3 | Implement fee split: operator share + protocol share | 1h | SuperPaymaster.sol |
| T2.4 | Add X402PaymentSettled event with full details |
0.5h | SuperPaymaster.sol |
| T2.5 | Add x402SettlementNonces mapping to prevent replay |
1h | SuperPaymaster.sol |
| T2.6 | Unit tests: happy path, double-settle, fee calculation, zero fee | 4h | test/v3/X402Settle.t.sol |
Acceptance Criteria:
- USDC transferred from
fromtoto(minus fee) - Facilitator fee correctly split between operator and protocol
- Double-settle with same nonce → revert
-
X402PaymentSettledevent emitted - Works with USDC (6 decimals) and other EIP-3009 tokens
Description: For non-EIP-3009 tokens, use Uniswap Permit2.
function settleX402PaymentPermit2(
ISignatureTransfer.PermitTransferFrom calldata permit,
ISignatureTransfer.SignatureTransferDetails calldata transferDetails,
bytes calldata signature
) external nonReentrant returns (bytes32 txHash);Sub-tasks:
| # | Task | Est. | File |
|---|---|---|---|
| T3.1 | Add Permit2 interface import (Uniswap canonical address) | 0.5h | SuperPaymaster.sol |
| T3.2 | Implement settleX402PaymentPermit2() |
3h | SuperPaymaster.sol |
| T3.3 | Fee deduction from Permit2 transfer | 1h | SuperPaymaster.sol |
| T3.4 | Unit tests with mock Permit2 | 3h | test/v3/X402Permit2.t.sol |
Acceptance Criteria:
- Works with any ERC-20 token via Permit2
- Fee correctly calculated and split
- Correct Permit2 canonical address used per chain
Sub-tasks:
| # | Task | Est. | File |
|---|---|---|---|
| T4.1 | Add facilitatorFeeBPS state variable (default: 30 = 0.3%) |
0.5h | SuperPaymaster.sol |
| T4.2 | Add operatorFacilitatorFees mapping for per-operator override |
0.5h | SuperPaymaster.sol |
| T4.3 | setFacilitatorFeeBPS(uint256) owner-only |
0.5h | SuperPaymaster.sol |
| T4.4 | setOperatorFacilitatorFee(address, uint256) |
0.5h | SuperPaymaster.sol |
| T4.5 | Add MAX_FACILITATOR_FEE constant (500 = 5%) |
0.5h | SuperPaymaster.sol |
| T4.6 | Storage layout check: add after V5.1 variables | 0.5h | — |
| T4.7 | Unit tests for fee config | 2h | test/v3/X402Settle.t.sol |
Acceptance Criteria:
- Default fee: 30 BPS (0.3%)
- Owner can change global fee
- Per-operator override takes precedence
- Fee > MAX → revert
- Storage layout: no collision with V5.1 slots
function convertFacilitatorEarnings(
address operator,
address stablecoin,
uint256 amount
) external;Sub-tasks:
| # | Task | Est. | File |
|---|---|---|---|
| T5.1 | Add facilitatorEarnings mapping per operator |
0.5h | SuperPaymaster.sol |
| T5.2 | Accumulate earnings in settleX402Payment() |
0.5h | SuperPaymaster.sol |
| T5.3 | Implement convertFacilitatorEarnings() — stablecoin→aPNTs swap + deposit |
3h | SuperPaymaster.sol |
| T5.4 | Integration with DEX (placeholder — manual conversion in V5.2) | 1h | SuperPaymaster.sol |
| T5.5 | Unit tests | 2h | test/ |
Note: V5.2 implements manual conversion (operator calls convert). Automated DEX swap is V5.4+.
Acceptance Criteria:
- Facilitator earnings tracked per operator
- Conversion correctly updates
operators[].aPNTsBalance - Only operator or owner can convert
- 经济飞轮验证 (Roadmap §V5.2 经济模型): Agent 支付 $1.00 → $0.997 payee + $0.002 operator fee + $0.001 protocol fee → operator fee 可 convert 为 aPNTs → 补充 Gas 赞助余额 → 为更多 Agent 赞助 → 更多手续费 → 闭环
Description: Unidirectional payment channel for streaming micropayments. Deployed as a separate contract, not part of SuperPaymaster proxy.
Design References: V5 Roadmap §V5.2 F6, Tempo Research §8.6.2
Core Data Structure:
struct MicroPaymentChannel {
address payer; // Agent (depositor)
address payee; // Service provider
address token; // Payment token (USDC, aPNTs, etc.)
address authorizedSigner; // Delegated signer (AirAccount Session Key)
uint128 deposit; // Total deposited amount
uint128 settled; // Total settled amount
uint64 closeRequestedAt; // Dispute window start (0 = open)
bool finalized; // Channel closed
}
bytes32 constant VOUCHER_TYPEHASH =
keccak256("Voucher(bytes32 channelId,uint128 cumulativeAmount)");Sub-tasks:
| # | Task | Est. | File |
|---|---|---|---|
| T6.1 | Create MicroPaymentChannel.sol with struct + storage (继承 solady EIP712) |
2h | contracts/src/paymasters/superpaymaster/v3/ |
| T6.2 | openChannel(payee, token, deposit, salt, authorizedSigner) — 含 salt 参数 |
3h | MicroPaymentChannel.sol |
| T6.3 | settleChannel(channelId, cumulativeAmount, signature) |
4h | MicroPaymentChannel.sol |
| T6.4 | topUpChannel(channelId, amount) |
1h | MicroPaymentChannel.sol |
| T6.5 | requestCloseChannel(channelId) + dispute window |
2h | MicroPaymentChannel.sol |
| T6.6 | closeChannel(channelId) — finalize after dispute window |
1.5h | MicroPaymentChannel.sol |
| T6.7 | withdrawChannel(channelId) — payer 在 grace period 后提款(Design §7.7.3) |
1.5h | MicroPaymentChannel.sol |
| T6.8 | EIP-712 Voucher signature verification (使用 SignatureCheckerLib) | 2h | MicroPaymentChannel.sol |
| T6.9 | Events: ChannelOpened, ChannelSettled, ChannelClosed, ChannelTopUp, ChannelWithdrawn |
1h | MicroPaymentChannel.sol |
| T6.10 | getChannel(channelId) view function |
0.5h | MicroPaymentChannel.sol |
| T6.11 | Configurable dispute window (CLOSE_TIMEOUT, default 15 min — 借鉴 Tempo grace period) |
1h | MicroPaymentChannel.sol |
Core Logic (借鉴 Design Doc §7.7.3 Tempo TempoStreamChannel):
openChannel:
1. Transfer deposit from payer to contract (SafeERC20)
2. channelId = keccak256(payer, payee, token, salt, authorizedSigner, address(this), block.chainid)
→ 绑定合约地址 + chainId → 防跨链、跨合约重放
3. Store channel struct
4. Emit ChannelOpened
settleChannel (payee 中间结算):
1. Verify EIP-712 voucher: _hashTypedData(Voucher(channelId, cumulativeAmount))
→ SignatureCheckerLib.isValidSignatureNow(payer OR authorizedSigner)
2. Verify cumulativeAmount > channel.settled (累积式,非增量)
3. delta = cumulativeAmount - channel.settled
4. Transfer delta to payee
5. channel.settled = cumulativeAmount
6. Emit ChannelSettled
requestCloseChannel (payer 请求关闭):
1. payer calls → sets closeRequestedAt = block.timestamp
2. Emit CloseRequested
closeChannel (payee 带最终 voucher 关闭):
1. Verify final voucher + settle remaining
2. Refund (deposit - settled) to payer
3. channel.finalized = true
withdrawChannel (payer grace period 后提款):
1. Require closeRequestedAt > 0 && block.timestamp > closeRequestedAt + CLOSE_TIMEOUT
2. Refund (deposit - settled) to payer
3. channel.finalized = true
关键设计 (Design Doc §7.7.3):
- 累积式 Voucher (cumulative, 非 delta) → 天然防重放,无需 per-session nonce
- authorizedSigner 委托 → AirAccount Session Key 签 Voucher,冷钱包无需在线
- channelId 绑定 chainId + contract address → 防跨链重放
Acceptance Criteria:
- Open channel: tokens transferred from payer to contract
- Sign voucher off-chain (EIP-712), settle on-chain — tokens to payee
- Cumulative voucher: settle(0.50) after settle(0.30) transfers only 0.20
- Cumulative voucher: settle(0.20) after settle(0.30) → revert (non-decreasing)
-
authorizedSignercan sign vouchers instead ofpayer - Invalid voucher signature → revert
- Top up increases deposit
- Close: dispute window enforced, refund excess to payer
- Cannot settle after finalized
- Cannot double-close
- Reentrancy protection on all state-changing functions
| # | Task | Est. | File |
|---|---|---|---|
| T7.1 | Reentrancy guard on all external functions | 1h | MicroPaymentChannel.sol |
| T7.2 | Settlement cannot exceed deposit | 0.5h | MicroPaymentChannel.sol |
| T7.3 | Only payer can top up or request close | 0.5h | MicroPaymentChannel.sol |
| T7.4 | Only payee can settle | 0.5h | MicroPaymentChannel.sol |
| T7.5 | ERC-20 token safety (SafeERC20) | 0.5h | MicroPaymentChannel.sol |
| T7.6 | Fuzz testing: random voucher amounts, concurrent settle | 4h | test/ |
| T7.7 | Edge case: zero deposit, zero amount voucher, max uint128 | 2h | test/ |
Acceptance Criteria:
- No reentrancy attack vector
- Settlement capped at deposit
- SafeERC20 for all token transfers
- Fuzz tests pass (1000+ runs)
Description: Open-source Node.js framework for running an x402 Facilitator node.
Architecture (from Research §8.6.3):
Operator Node (Hono HTTP server)
├── GET /health → Node status
├── POST /verify → Call verifyX402Payment() on-chain
├── POST /settle → Call settleX402Payment() on-chain
├── GET /quote → Return fee rates
├── GET /.well-known/x-payment-info → x402 discovery
└── POST /channel/open → Call openChannel() on-chain
Key Design (Design Doc §7.7.2 + §7.7.4 战术级借鉴):
- HMAC-SHA256 无状态 Challenge (MPP
mppx/src/Challenge.ts):challenge_id = HMAC-SHA256(server_secret, realm|method|intent|request|expires|digest|opaque)— 无需 Redis/DB - Method 插件 + compose 模式 (MPP
mppx/src/server/Mppx.ts): 支付方法可插拔组合 - Transport 抽象 (MPP
mppx/src/Transport.ts): HTTP / MCP / SSE 三种传输协议共用同一支付逻辑 - Push/Pull 凭证模式: Push = Agent 广播交易发 txHash,Pull = Agent 签名由服务端广播
Sub-tasks:
| # | Task | Est. | File |
|---|---|---|---|
| T8.1 | Project scaffolding: Hono + viem + TypeScript (借鉴 mppx 项目结构) | 2h | packages/x402-facilitator-node/ |
| T8.2 | /health endpoint |
0.5h | server.ts |
| T8.3 | /verify endpoint → calls verifyX402Payment() view |
2h | verify.ts |
| T8.4 | /settle endpoint → calls settleX402Payment() tx |
3h | settle.ts |
| T8.5 | /quote endpoint → returns fee rates from contract |
1h | quote.ts |
| T8.6 | /.well-known/x-payment-info → x402 discovery metadata |
1h | discovery.ts |
| T8.7 | HMAC-SHA256 challenge: 7 positional slots (realm|method|intent|base64url(JCS(request))|expires|digest|opaque),完全无状态 (Design §7.7.2) | 2h | challenge.ts |
| T8.8 | Method 插件架构: superpaymaster.charge() + superpaymaster.session() 可 compose (Design §7.7.4) |
3h | methods/ |
| T8.9 | Transport 抽象层: HTTP 402 + MCP -32042 共用支付逻辑 (Design §7.7.4) | 2h | transport.ts |
| T8.10 | Configuration: env vars for chain, operator key, contract addresses | 1h | config.ts |
| T8.11 | Docker compose setup | 1h | Dockerfile |
| T8.12 | Integration test: full x402 flow (client → node → chain) | 4h | test/ |
Acceptance Criteria:
- Node starts with single
OPERATOR_PRIVATE_KEYenv var -
/healthreturns 200 with operator status -
/verifyreturns{ valid: true/false, reason }from on-chain call -
/settleexecutes on-chain settlement and returns tx hash -
/quotereturns current fee rates - HMAC challenge is stateless (no Redis/DB)
- Docker compose:
docker compose upstarts node - Full E2E: HTTP 402 → payment → 200 flow works
New Test Files:
| File | Tests | Focus |
|---|---|---|
test/v3/X402Settle.t.sol |
15+ | x402 verify/settle/Permit2 |
test/v3/MicroPaymentChannel.t.sol |
20+ | Channel lifecycle + voucher signing |
test/v3/FacilitatorFee.t.sol |
8+ | Fee config, split, conversion |
test/v3/X402Integration.t.sol |
5+ | End-to-end x402 + channel flow |
Sub-tasks:
| # | Task | Est. | File |
|---|---|---|---|
| T9.1 | Mock USDC with EIP-3009 support for tests | 2h | test/mocks/ |
| T9.2 | x402 settle tests (USDC path) | 4h | X402Settle.t.sol |
| T9.3 | Permit2 settle tests | 3h | X402Settle.t.sol |
| T9.4 | Channel lifecycle tests (open/settle/top-up/close) | 6h | MicroPaymentChannel.t.sol |
| T9.5 | Channel + AirAccount Session Key simulation (mock SessionKeyValidator algId 0x08) | 3h | MicroPaymentChannel.t.sol |
| T9.5b | AirAccount 跨合约集成测试 (evaluate-roadmap §6 P0 要求): 1) 创建 Session Key 2) 用 Session Key 签 EIP-712 Voucher (链下) 3) payee 提交 Voucher 结算 (链上) → 验证 authorizedSigner 路径完整 | 3h | test/v3/IntegrationAirAccountChannel.t.sol |
| T9.6 | Fee configuration and split tests | 3h | FacilitatorFee.t.sol |
| T9.7 | Integration: x402 settle → fee → operator balance update | 3h | X402Integration.t.sol |
| T9.8 | Regression: all V5.1 + existing tests | 1h | — |
Acceptance Criteria:
- 380+ total tests, 0 failures
- 100% branch coverage for x402 settlement functions
- 100% branch coverage for MicroPaymentChannel
- V5.1 regression: all tests still pass
- V4.1 regression: all existing tests still pass
| # | Task | Est. | File |
|---|---|---|---|
| T10.1 | Deploy MicroPaymentChannel.sol to Sepolia |
1h | — |
| T10.2 | Upgrade SuperPaymaster proxy with V5.2 impl | 1h | — |
| T10.3 | Configure facilitator fees on-chain | 0.5h | — |
| T10.4 | E2E: x402 payment flow with real USDC (testnet) | 3h | script/gasless-tests/ |
| T10.5 | E2E: Payment Channel open/settle/close with test tokens | 3h | script/gasless-tests/ |
| T10.6 | Start Operator Node on staging server | 1h | — |
| T10.7 | E2E: Full stack — HTTP 402 → Operator Node → on-chain settle | 2h | — |
| T10.8 | Update deployments/config.sepolia.json |
0.5h | deployments/ |
Acceptance Criteria:
- SuperPaymaster V5.2 upgraded successfully on Sepolia
- MicroPaymentChannel deployed on Sepolia
- x402 payment settles correctly with testnet USDC
- Payment Channel open/settle/close works E2E
- Operator Node running and responding on staging
- All existing E2E gasless tests still pass
| Package | Task | Effort | Repo |
|---|---|---|---|
@aastar/x402 |
New package: Facilitator client SDK (verify/settle/fetch) | 3-5 days | aastar-sdk |
@aastar/channel |
New package: Payment Channel client (open/sign/settle) | 3-5 days | aastar-sdk |
@aastar/operator-node |
New package: Facilitator node framework | 5-7 days | aastar-sdk |
AirAccount Coordination:
| Task | Effort | Repo |
|---|---|---|
| Session Key EIP-712 Voucher signing E2E test | 1 day | airaccount-contract |
| AgentSessionKey + MicroPaymentChannel callTarget config | 0.5 day | airaccount-contract |
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| EIP-3009 not widely supported | Medium | Medium | Permit2 fallback (T3) covers all ERC-20 tokens |
| Payment Channel dispute attack | Low | High | Configurable timeout + AirAccount spendCap |
| Operator Node key compromise | Medium | Medium | HMAC-SHA256 challenges are stateless; rotate key only |
| Fee calculation rounding | Low | Medium | Round in protocol's favor; fuzz test edge cases |
| MicroPaymentChannel reentrancy | Low | Critical | ReentrancyGuard + SafeERC20 + fuzz testing |
| Week | Focus | Key Deliverable |
|---|---|---|
| Week 1 | T1 + T2 | x402 verify + settle implemented |
| Week 2 | T3 + T4 + T6 start | Permit2 fallback + fee config + Channel contract start |
| Week 3 | T5 + T6 continue | Earnings conversion + Channel core functions |
| Week 4 | T6 complete + T7 + T8 start | Channel security + Operator Node start |
| Week 5 | T8 continue + T9 | Operator Node + comprehensive tests |
| Week 6 | T10 + T9 remaining | Sepolia deployment + E2E validation |
Total Estimated Effort: ~120-150 hours (contract + off-chain + tests + deployment)
-
verifyX402Payment()andsettleX402Payment()deployed and tested -
settleX402PaymentPermit2()deployed and tested -
MicroPaymentChannel.soldeployed independently - Facilitator fee system working (global + per-operator)
- Payment Channel: open/sign/settle/close lifecycle E2E verified
- Operator Node: HTTP service running, serving /verify, /settle, /quote
- AirAccount Session Key can sign Payment Channel vouchers (E2E)
- 380+ Foundry tests, 0 failures
- Sepolia deployment successful, all E2E tests pass
- Competitive score: 33/50 (within 2 points of Tempo)