Skip to content

Facilitator

The Ultravioleta Facilitator (x402-rs) is a self-hosted Rust server that acts as the gas abstraction and settlement layer for all on-chain payment and identity operations on Execution Market.

Role in the System

Agent signs EIP-3009 auth → x402 SDK → Facilitator → On-chain TX (Facilitator pays gas)

The Facilitator:

  1. Receives signed EIP-3009 authorizations from the EM backend
  2. Validates them (amount, nonce, expiry, recipient, chain ID)
  3. Submits the transaction on-chain using its own EOA wallet
  4. Returns the transaction hash

Neither agents nor workers ever pay gas.

URL

https://facilitator.ultravioletadao.xyz

Current version: v1.40.0 · Swagger: https://facilitator.ultravioletadao.xyz/docs/

Facilitator EOA

0x103040545AC5031A11E8C03dd11324C7333a13C7

This wallet pays gas for all Execution Market transactions on all supported networks.

Endpoints

EndpointMethodPurpose
GET /healthGETHealth check
GET /versionGETCurrent version
GET /supportedGETList all supported networks and tokens
POST /verifyPOSTVerify a signed EIP-3009 authorization
POST /settlePOSTSubmit a settlement (direct, escrow authorize/release/refund)
POST /acceptsPOSTNegotiate payment requirements (Faremeter middleware)
POST /escrow/statePOSTQuery on-chain escrow state (read-only)
POST /registerPOSTRegister an ERC-8004 agent identity
POST /feedbackPOSTSubmit ERC-8004 reputation feedback
GET /feedbackGETList ERC-8004 supported networks
GET /reputation/:network/:agentIdGETQuery on-chain reputation score
GET /identity/:network/:agentIdGETQuery agent identity
GET /blacklistGETOFAC sanctioned addresses

Network Format

The facilitator accepts two network name formats:

FormatExampleNotes
v1 namebaseHuman-readable shorthand
CAIP-2eip155:8453Used in escrow payloads, canonical

Both formats are accepted by all endpoints. CAIP-2 is required in escrow paymentRequirements.network.

Supported Networks

Mainnets (19 EVM + Non-EVM)

NetworkChain IDCAIP-2EM Escrow
Base8453eip155:8453Yes (PaymentOperator)
Ethereum1eip155:1Yes
Polygon137eip155:137Yes
Arbitrum42161eip155:42161Yes
Avalanche43114eip155:43114Yes
Optimism10eip155:10Yes
Celo42220eip155:42220Yes
Monad10143eip155:10143Yes
HyperEVM999eip155:999
Unichain130eip155:130
BSC56eip155:56
SKALE Base1187947933eip155:1187947933
Scroll534352eip155:534352
Solanasolana:mainnetDirect SPL only
Sui
Fogo
NEAR
Stellar
Algorand

Execution Market uses 8 EVM mainnets with escrow + Solana (direct SPL transfers, no escrow).

Testnets (17)

Base Sepolia, Ethereum Sepolia, Arbitrum Sepolia, Optimism Sepolia, Polygon Amoy, Avalanche Fuji, Celo Alfajores, HyperEVM Testnet, Unichain Sepolia, SKALE Base Sepolia, Solana Devnet, NEAR Testnet, Stellar Testnet, Algorand Testnet, Sui Testnet, Fogo Testnet, Monad Testnet.

Supported Stablecoins

TokenNetworks
USDCAll 19 networks
AUSDEthereum, Polygon, Arbitrum, Avalanche, Monad, BSC, Solana, Sui
EURCEthereum, Base, Avalanche
USDTArbitrum, Celo, Optimism
PYUSDEthereum

Full Matrix:

NetworkUSDCAUSDEURCUSDTPYUSD
EthereumYYYY
BaseYY
ArbitrumYYY
OptimismYY
PolygonYY
AvalancheYYY
CeloYY
BSCYY
MonadYY
HyperEVMY
UnichainY
SolanaYY
SuiYY

Direct Payment (Fase 1)

Standard EIP-3009 settlement — used by Execution Market's default fase1 payment mode.

Agent wallet → (signs EIP-3009) → Facilitator → transferWithAuthorization() → Worker wallet
python
import hashlib

# Generate a unique nonce
nonce = "0x" + hashlib.sha256(f"{task_id}:worker:{timestamp}".encode()).hexdigest()

# POST /settle
{
  "x402Version": 1,
  "scheme": "exact",
  "payload": {
    "authorization": {
      "from": "0xAGENT",
      "to":   "0xWORKER",
      "value": "87000",        # 6-decimal USDC (e.g. $0.087)
      "validAfter": 0,
      "validBefore": deadline,
      "nonce": nonce
    },
    "signature": "0x...",
    "token": "0xUSDC_ADDRESS"
  },
  "paymentRequirements": {
    "scheme": "exact",
    "network": "base",
    "maxAmountRequired": "87000"
  }
}

Nonce rules:

  • Must be unique per settlement — never reuse, even on failure
  • Recommended: keccak256(taskId + ":" + type + ":" + timestamp)
  • If a settlement fails, generate a fresh nonce before retrying

Escrow Lifecycle (Fase 2 / Fase 5)

Used when EM_PAYMENT_MODE=fase2 or fase5. Funds are locked on-chain at task creation and released atomically at approval.

authorize — Lock funds at task creation

json
{
  "x402Version": 2,
  "scheme": "escrow",
  "action": "authorize",
  "payload": {
    "authorization": {
      "from": "0xAGENT_ADDRESS",
      "to": "0xTOKEN_COLLECTOR",
      "value": "1000000",
      "validAfter": "0",
      "validBefore": "1738500000",
      "nonce": "0x..."
    },
    "signature": "0x...",
    "paymentInfo": {
      "operator": "0xOPERATOR_ADDRESS",
      "receiver": "0xWORKER_ADDRESS",
      "token": "0xUSDC_ADDRESS",
      "maxAmount": "1000000",
      "preApprovalExpiry": 281474976710655,
      "authorizationExpiry": 281474976710655,
      "refundExpiry": 281474976710655,
      "minFeeBps": 0,
      "maxFeeBps": 1300,
      "feeReceiver": "0xOPERATOR_ADDRESS",
      "salt": "0x..."
    }
  },
  "paymentRequirements": {
    "scheme": "escrow",
    "network": "eip155:8453",
    "extra": {
      "escrowAddress": "0xESCROW_ADDRESS",
      "operatorAddress": "0xOPERATOR_ADDRESS",
      "tokenCollector": "0xTOKEN_COLLECTOR"
    }
  }
}

release — Send funds to worker on approval

No EIP-3009 signature required — the operator contract authorizes the release.

json
{
  "x402Version": 2,
  "scheme": "escrow",
  "action": "release",
  "payload": {
    "paymentInfo": {
      "operator": "0xOPERATOR_ADDRESS",
      "receiver": "0xWORKER_ADDRESS",
      "token": "0xUSDC_ADDRESS",
      "maxAmount": "1000000",
      "preApprovalExpiry": 281474976710655,
      "authorizationExpiry": 281474976710655,
      "refundExpiry": 281474976710655,
      "minFeeBps": 0,
      "maxFeeBps": 1300,
      "feeReceiver": "0xOPERATOR_ADDRESS",
      "salt": "0x..."
    },
    "payer": "0xAGENT_ADDRESS",
    "amount": "1000000"
  },
  "paymentRequirements": {
    "scheme": "escrow",
    "network": "eip155:8453",
    "extra": {
      "escrowAddress": "0xESCROW_ADDRESS",
      "operatorAddress": "0xOPERATOR_ADDRESS",
      "tokenCollector": "0xTOKEN_COLLECTOR"
    }
  }
}

refundInEscrow — Return funds to agent on cancel

Same structure as release but with "action": "refundInEscrow".

json
{
  "x402Version": 2,
  "scheme": "escrow",
  "action": "refundInEscrow",
  "payload": { "...same as release..." },
  "paymentRequirements": { "...same as release..." }
}

POST /escrow/state — Query on-chain state

Read-only, no gas. Use to verify escrow status before attempting release/refund.

Request:

json
{
  "paymentInfo": { "...paymentInfo object..." },
  "payer": "0xAGENT_ADDRESS",
  "network": "eip155:8453",
  "extra": {
    "escrowAddress": "0xESCROW_ADDRESS",
    "operatorAddress": "0xOPERATOR_ADDRESS",
    "tokenCollector": "0xTOKEN_COLLECTOR"
  }
}

Response:

json
{
  "hasCollectedPayment": false,
  "capturableAmount": "1000000",
  "refundableAmount": "0",
  "paymentInfoHash": "0xabcdef...",
  "network": "eip155:8453"
}

/accepts Endpoint (Faremeter Middleware)

POST /accepts — Added in v1.36.0 for compatibility with @faremeter/middleware.

The endpoint receives merchant payment requirements and returns the subset the facilitator can fulfill, enriched with facilitator data (fee payer, token details, escrow contracts). Both v1 ("base") and CAIP-2 ("eip155:8453") network formats are accepted.

bash
curl -X POST https://facilitator.ultravioletadao.xyz/accepts \
  -H "Content-Type: application/json" \
  -d '{
    "paymentRequirements": [{
      "scheme": "exact",
      "network": "base",
      "maxAmountRequired": "100000",
      "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
    }]
  }'

ERC-8004 Identity & Reputation

The facilitator provides gasless [[erc-8004|ERC-8004]] operations across 18 networks (16 EVM + Solana mainnet + Solana devnet).

ERC-8004 Supported Networks (16 EVM)

NetworkTypeIdentity RegistryReputation Registry
EthereumMainnet0x8004A169...9a4320x8004BAa1...dE9b63
BaseMainnetSame (CREATE2)Same (CREATE2)
PolygonMainnetSameSame
ArbitrumMainnetSameSame
CeloMainnetSameSame
BSCMainnetSameSame
MonadMainnetSameSame
AvalancheMainnetSameSame
Ethereum SepoliaTestnet0x8004A818...4BD9e0x8004B663...8713
Base SepoliaTestnetSameSame
Polygon AmoyTestnetSameSame
Arbitrum SepoliaTestnetSameSame
Celo AlfajoresTestnetSameSame
Avalanche FujiTestnetSameSame

All mainnet contracts are CREATE2 deterministic — same address on every EVM chain.

Solana ERC-8004 (v1.37.0+)

Solana uses the QuantuLabs 8004-solana Anchor program for identity + reputation:

  • Agent Registry: 8oo4dC4JvBLwy5tGgiH3WwK4B9PWxL9Z4XjA2jzkQMbQ
  • ATOM Engine: AToMw53aiPQ8j7iHVb4fGt6nzUNxUhcPc3tbPBZuzVVb

Features: ATOM Engine trust scoring (tiers 0-4: Unknown → Trusted), feedback submission via give_feedback, revoke via POST /feedback/revoke, registration mints a Metaplex Core NFT. Facilitator pays SOL gas.

ERC-8004 Endpoints

bash
# Register agent
POST /register
{ "network": "base", "agentId": "2106", "metadata": { "name": "...", "uri": "..." } }

# Submit feedback (agent → worker or worker → agent)
POST /feedback
{ "network": "base", "agentId": "2106", "rating": 5, "tags": ["quality"], "proofOfPayment": "0x..." }

# Query reputation
GET /reputation/base/2106

# Query identity
GET /identity/base/2106

PaymentOperator Registration

Execution Market's PaymentOperators are registered in the Facilitator's allowlist on all 8 EVM chains:

NetworkPaymentOperator
Base0x271f9fa7f8907aCf178CCFB470076D9129D8F0Eb
Ethereum0x69B67962ffb7c5C7078ff348a87DF604dfA8001b
Polygon0xB87F1ECC85f074e50df3DD16A1F40e4e1EC4102e
Arbitrum0xC2377a9Db1de2520BD6b2756eD012f4E82F7938e
Avalanche0xC2377a9Db1de2520BD6b2756eD012f4E82F7938e
Monad0x9620Dbe2BB549E1d080Dc8e7982623A9e1Df8cC3
Celo0xC2377a9Db1de2520BD6b2756eD012f4E82F7938e
Optimism0xC2377a9Db1de2520BD6b2756eD012f4E82F7938e

The StaticFeeCalculator (1300 bps = 13%) is deployed at 0xd643DB63028Cd1852AAFe62A0E3d2A5238d7465A on Base. It splits each release atomically: 87% to worker, 13% to operator (flushed to treasury via distributeFees()).

Security Model

The Facilitator cannot steal funds:

  • It can only submit exactly what was authorized by the EIP-3009 signature
  • Each signature is cryptographically bound to: amount, recipient, deadline, nonce, chain ID
  • Even with the Facilitator EOA key compromised, an attacker cannot move more than what was signed
  • Escrow: all client-provided addresses are validated against hardcoded contract addresses before any TX is sent

Error Reference

ErrorMeaningAction
insufficient_balanceAgent USDC balance too lowFund the wallet
invalid_signatureEIP-3009 sig invalidCheck signing code
expired_authorizationAuth deadline passedRe-sign with new deadline
nonce_already_usedNonce already consumedGenerate a new nonce
operator_not_registeredPaymentOperator not allowlistedContact Ultravioleta DAO
FeatureDisabledENABLE_PAYMENT_OPERATOR=true not setEnable operator feature
UnsupportedNetworkNo escrow contracts or operator on this chainCheck /supported
UnknownActionInvalid action stringValid: authorize, release, refundInEscrow
PaymentInfoInvalidAddress mismatch vs hardcoded deploymentsCheck operator/escrow addresses
ContractCallOn-chain transaction revertedCheck chain state + balances

Chain-Specific Payment Protocols

Chain typeProtocolNotes
EVMEIP-3009 transferWithAuthorizationStandard for all EVM chains
SolanaSPL Token + Token2022USDC (SPL), AUSD (Token2022)
NEARNEP-366 meta-transactionsDelegate actions
StellarSoroban smart contractsAuthorization on Soroban VM
AlgorandAtomic transaction groupsFee pooling; Facilitator signs tx0