Escrow contract that extends the x402 payment protocol flow with on-chain escrow to enable usage-based, pay-per-usage billing for AI services where costs are unknown at request time.
fortytwo-x402Escrow is an early-stage Solidity project in the AI payments / x402 ecosystem. It currently has 11 GitHub stars and 3 forks.
x402Escrow is a UUPS-upgradeable escrow contract developed by Fortytwo that extends the x402 payment protocol flow with on-chain escrow to enable usage-based, pay-per-token billing for AI services where costs are unknown at request time.
This repository focuses solely on X402Escrow: settlement, release, timeout refund, tests, and deployment.
X402Escrow is designed for facilitator-driven MCP billing:
settle(...) to pull USDC into escrow.release(escrowId, facilitatorAmount) after the request is completed.refundAfterTimeout(escrowId) and funds return to the client.Standard x402 is excellent for fixed-price endpoints, but inference workloads are variable:
X402Escrow solves this with a deterministic settlement model:
receiveWithAuthorization (EIP-3009): the authorization targets the escrow contract, reducing mempool signature misuse risk.release pays msg.sender with FACILITATOR_ROLE, avoiding per-escrow facilitator address coupling.refundAt; changing the global timeout does not rewrite existing deadlines.refundAfterTimeout, funds always return to the original client.nonReentrant.This contract is an x402-compatible extension:
402 Payment Required.X402Escrow and ERC-8183 target different layers of the same agentic commerce stack:
X402Escrow is optimized for objective, consumption-based settlement (metered inference).They are complementary: a task-level commerce flow can use ERC-8183 while metered inference inside that task can settle through X402Escrow.
owner:
can upgrade proxy implementation (UUPSUpgradeable), uses 2-step ownership transfer.DEFAULT_ADMIN_ROLE:
can grant/revoke roles, change timeout, rescue tokens.FACILITATOR_ROLE:
can call settle and release.src/X402Escrow.sol – escrow contract.test/X402Escrow.t.sol – unit tests (including proxy upgrade tests)..gas-snapshot – committed gas baseline for regression checks.script/DeployX402Escrow.s.sol – implementation + proxy deploy script..env.example – deployment environment template.forge --version
# Install dependencies (if lib/ is empty)
forge install --no-git foundry-rs/forge-std
forge install --no-git OpenZeppelin/openzeppelin-contracts
forge install --no-git OpenZeppelin/openzeppelin-contracts-upgradeable
forge build
forge test --offline --match-path test/X402Escrow.t.sol -vv
forge snapshot --offline --match-path test/X402Escrow.t.sol --check
If gas numbers have changed intentionally, refresh snapshot:
forge snapshot --offline --match-path test/X402Escrow.t.sol
cp .env.example .env
# fill PRIVATE_KEY, USDC_ADDRESS, FACILITATOR_ADDRESS, ADMIN_ADDRESS, OWNER_ADDRESS
source .env
# Base Sepolia
forge script script/DeployX402Escrow.s.sol --rpc-url base_sepolia --broadcast
You can also use --rpc-url base, --rpc-url monad, or --rpc-url monad_testnet.
settle(...) – lock client USDC via EIP-3009 receiveWithAuthorization.release(escrowId, facilitatorAmount) – split escrow amount between facilitator and client.refundAfterTimeout(escrowId) – timeout safety refund to the client.setTimeout(timeoutSecs) – admin timeout control for new escrows.rescueTokens(token, to, amount) – admin rescue hook.getEscrow(escrowId) – returns enriched escrow view (EscrowView).nonReentrant on state-changing financial paths.settle validates exact token delta to block fee-on-transfer behavior._authorizeUpgrade).