Skip to main content

Overview

0xmeta uses pre-settlement fee collection to maintain x402’s core principle: trust minimization. Customer funds flow directly to merchants—never through the facilitator. Fees are collected before settlement via standard ERC-20 approval patterns.
Trust-Minimized with Pre-Settlement: Customer payments go DIRECTLY to merchants. The facilitator collects the $0.01 fee from the merchant’s pre-approved balance BEFORE executing the settlement. If fee collection fails, settlement is blocked.

Payment Architecture

Trust-Minimized Flow with Pre-Settlement Fee Collection

Key Principle: Fee collected FIRST (orange), then customer payment executes (blue). Customer funds go DIRECTLY to merchant. No fee = no settlement.

Why This Architecture?

x402 Trust-Minimization Requirement

From x402 specification:
“The facilitator must never have custody or control over customer payments. All funds must flow directly from payer to payee.”
Previous approach (REJECTED):
❌ Client → Treasury → Merchant
   Problem: Facilitator custody = trust required
Current approach (APPROVED):
✅ Fee: Merchant → Treasury (FIRST, via transferFrom)
✅ Payment: Client → Merchant (THEN, direct payment)
   Solution: No facilitator custody = trust-minimized
   Guarantee: No fee collection = no settlement

Pre-Settlement Enforcement

Why collect fee BEFORE settlement?
  1. Prevents free service: Merchants cannot exploit failed settlements
  2. Guarantees payment: Facilitator only executes if paid
  3. Atomic guarantee: Fee collection failure blocks settlement
  4. Economic sustainability: Every settlement attempt costs merchant
Trade-off: Merchant pays $0.01 even if settlement fails after fee collection. Rationale: This is payment for the verification and settlement attempt service. Without this, merchants could intentionally cause settlement failures for free verification.

ERC-20 Approval Pattern

Standard pattern used across DeFi:
// Merchant approves facilitator treasury (one-time)
USDC.approve(facilitatorTreasury, 100 * 10^6); // 100 USDC

// Before each settlement, facilitator collects fee FIRST
USDC.transferFrom(merchant, treasury, 0.01 * 10^6); // $0.01

// Then (and only then) executes customer payment
transferWithAuthorization(...);
Benefits:
  • ✅ Standard ERC-20 pattern (well understood)
  • ✅ Merchant controls via approval amount
  • ✅ Auditable on-chain
  • ✅ Can revoke anytime (settlements will fail)
  • ✅ Fee collection happens first (no free rides)

Payment Flow Details

Step 1: One-Time Merchant Approval

Merchant approves facilitator treasury for USDC spending:
# Using provided script
EVM_PRIVATE_KEY=0x... node approve-facilitator.mjs

# Or manually via contract interaction
usdc.approve(
  "0x5D791e3554D0e83f171126905Bda1640Bf6f9A8B", // Treasury
  ethers.parseUnits("100", 6) // 100 USDC = 10,000 settlements
)
Approval amount = settlements * $0.01 Example: 100 USDC approval = 10,000 settlements

Step 2: Client Requests Resource

Client makes GET request to protected endpoint:
GET /premium-content HTTP/1.1
Host: merchant.com

Step 3: Merchant Returns 402

Server responds with payment requirements:
HTTP/1.1 402 Payment Required
x402-accept: exact;price="$0.02";network="eip155:8453";payto="0xMERCHANT"

{
  "scheme": "exact",
  "price": "$0.02",
  "network": "eip155:8453",
  "payTo": "0xA821f428Ef8cC9f54A9915336A82220853059090" // ← Merchant address
}
Critical: payTo must be merchant address, NOT treasury.

Step 4: Client Creates Authorization

Client signs EIP-3009 authorization to merchant:
const authorization = {
  from: clientAddress,
  to: merchantAddress,    // ← MERCHANT, not treasury
  value: "20000",         // $0.02 USDC (6 decimals)
  validAfter: "0",
  validBefore: "1735689600",
  nonce: randomNonce(),
  token: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" // USDC
};

const signature = await signTypedData(authorization);

Step 5: Facilitator Verification

Facilitator validates authorization cryptographically:
# Verify signature
recovered_address = recover_signer(authorization, signature)
assert recovered_address == authorization['from'], "Invalid signature"

# Verify recipient
assert authorization['to'] == merchant_address, "Must pay to merchant"

# Verify amount
assert authorization['value'] == expected_amount, "Amount mismatch"

# Return verification_id

Step 6: Pre-Settlement Fee Collection

BEFORE executing customer payment, facilitator collects fee:
# Check merchant allowance
allowance = usdc.allowance(merchant, treasury)
required = 10000  # $0.01 in wei

if allowance < required:
    raise InsufficientAllowanceError(
        f"Merchant must approve {required} wei ($0.01). "
        f"Run: node approve-facilitator.mjs"
    )

# Collect fee FIRST (pre-settlement)
fee_tx = usdc.transferFrom(
    from_=merchant,
    to=treasury,
    value=required
)

# Wait for confirmation
receipt = wait_for_transaction(fee_tx)
assert receipt.status == 1, "Fee collection failed"

# Fee collection successful - proceed to settlement
Critical: If fee collection fails at this step, settlement is blocked. Customer is NOT charged. This guarantees the facilitator never provides free service.

Step 7: Settlement Execution (After Fee)

After fee successfully collected, execute customer → merchant payment:
# Execute via 1Shot API (only if fee was collected)
settlement_response = oneshot_api.execute_settlement(
    authorization=authorization,
    signature=signature
)

# Customer → Merchant payment executed on-chain
# Merchant receives 100% of customer payment

Trust Model

What Merchants Trust

Customer payments arrive directly
  • EIP-3009 authorization cryptographically bound to merchant address
  • Facilitator cannot redirect customer funds
  • 100% of customer payment received
Fee collection is explicit and controllable
  • Standard ERC-20 approval (revocable anytime)
  • Exact fee amount known upfront ($0.01)
  • On-chain auditable
Pre-settlement enforcement
  • Fee collected BEFORE settlement executes
  • No fee = no settlement (guaranteed)
  • Prevents free service exploitation
No custody risk
  • Facilitator never holds customer funds
  • Settlement executes customer → merchant directly

What Merchants Don’t Need to Trust

Facilitator custody
  • Never happens (funds go direct to merchant)
Fee calculation
  • Flat $0.01, no dynamic pricing or hidden fees
Arbitrary fund movement
  • Approval limits what facilitator can collect
Settlement execution without payment
  • Pre-settlement fee collection guarantees payment

Security Considerations

Approval Safety

Merchant controls:
  1. Approval amount - Decide how many settlements to fund
  2. Revocation - Can revoke approval anytime via:
    usdc.approve(treasury, 0) // Revoke all access
    
  3. Monitoring - Check remaining allowance:
    node check-allowance.mjs
    
Attack scenarios:
ScenarioProtection
Facilitator drains entire USDC balance❌ Impossible - only approved amount accessible
Facilitator collects more than $0.01❌ Blocked by smart contract (exact amount hardcoded)
Facilitator redirects customer funds❌ Impossible - EIP-3009 signature bound to merchant
Merchant has insufficient approval✅ Settlement blocked, customer not charged
Settlement fails after fee collection⚠️ Merchant paid for attempt (prevents free service)

Settlement Atomicity

Pre-settlement fee collection ensures:
  1. Fee collected FIRST
  2. If fee fails → settlement blocked, customer not charged
  3. If settlement fails → fee already collected (payment for service)
Critical difference from post-settlement:
  • Pre-settlement: Fee collection failure = no settlement = customer safe
  • Post-settlement (rejected): Settlement could succeed but fee fail = free service
Trade-off: Merchant pays fee even if settlement fails after fee collection. Economic justification: Merchant received verification and settlement attempt services worth $0.01. This prevents exploitation via intentional settlement failures.

Comparison: Pre-Settlement vs Post-Settlement

AspectPre-Settlement (Current)Post-Settlement (Rejected)
Fee collection timingBEFORE settlement ✅AFTER settlement
Free service riskZero (fee first) ✅High (could fail fee collection)
Customer protectionHigh (no fee = no charge) ✅Lower (could be charged with failed fee)
Merchant riskPays for attemptLower fee risk
Economic sustainabilityGuaranteed ✅Exploitable
Trust minimizationMaintained ✅Maintained
Why pre-settlement wins:
  1. Prevents free service exploitation
  2. Guarantees facilitator payment
  3. Customer never charged if fee collection fails
  4. Economically sustainable business model

Comparison: Old vs Current Architecture

AspectTreasury-First (Old)Pre-Settlement (Current)
Customer payment→ Treasury → Merchant→ Merchant (direct) ✅
Fee collectionAtomic splitPre-settlement via transferFrom ✅
Trust requiredHigh (custody)Minimal (approval only) ✅
x402 compliant❌ No✅ Yes
Merchant setupNoneOne-time approval
RevocabilityNot applicableStandard ERC-20 revoke ✅
Free service riskN/AEliminated ✅

Implementation Details

On-Chain Transactions

Setup (One-Time):
Merchant → USDC.approve(treasury, 100 USDC)
Gas cost: ~50,000 gas (~$0.50 on Base)
Per Settlement:
1. Facilitator → USDC.transferFrom(merchant, treasury, $0.01)
   Gas: Paid by facilitator
   Timing: BEFORE settlement (pre-settlement)
   
2. Facilitator → 1Shot.executeSettlement(authorization, signature)
   Result: Client → Merchant ($0.02)
   Gas: Paid by 1Shot
   Timing: AFTER fee collection
Total merchant gas cost: $0 (after initial approval)

Error Handling

Insufficient Allowance:
{
  "error": {
    "code": "insufficient_allowance",
    "message": "Merchant must approve facilitator for USDC spending",
    "details": {
      "required": "10000",
      "available": "0",
      "approval_command": "node approve-facilitator.mjs"
    }
  }
}
Fee Collection Failed (Pre-Settlement):
{
  "error": {
    "code": "fee_collection_failed",
    "message": "Could not collect $0.01 fee from merchant before settlement",
    "details": {
      "reason": "Insufficient USDC balance",
      "merchant_balance": "0",
      "note": "Settlement blocked - customer not charged"
    }
  }
}

Alternatives Considered

1. Post-Settlement Fee Collection

Collect fee AFTER executing customer → merchant payment. Why rejected:
  • ❌ Merchants can exploit failed fee collection for free service
  • ❌ Race condition between settlement and fee collection
  • ❌ Customer could be charged even if fee collection fails
  • ❌ Economically unsustainable (free service exploitation)

2. Smart Contract Atomic Split

contract AtomicSplit {
    function splitPayment(
        address merchant,
        address treasury,
        uint256 totalAmount
    ) external {
        usdc.transferFrom(msg.sender, merchant, totalAmount - fee);
        usdc.transferFrom(msg.sender, treasury, fee);
    }
}
Why rejected:
  • ❌ Requires custom smart contract deployment
  • ❌ Higher gas costs (contract interaction)
  • ❌ Still requires customer to authorize contract (not direct to merchant)
  • ❌ Violates trust-minimization (contract has custody)

3. Merchant Voluntary Fee Payment

Merchant manually pays fees after receiving customer payment. Why rejected:
  • ❌ Merchants can skip payment (free service)
  • ❌ No enforcement mechanism
  • ❌ Unsustainable business model

4. Treasury-First (Original)

See comparison above. Why rejected:
  • ❌ Violates x402 trust-minimization
  • ❌ Facilitator custody of customer funds
  • ❌ Rejected by Coinbase x402 ecosystem

Monitoring & Operations

Check Allowance

Merchants can monitor remaining allowance:
node check-allowance.mjs

Output:
📍 Base Mainnet
   USDC Balance: 500.00 USDC
   Fee Allowance: 95.50 USDC
   Settlements: 9,550
 Good

Top Up Allowance

When allowance runs low:
# Approve additional 100 USDC
node approve-facilitator.mjs

Revoke Access

To stop allowing settlements:
# Set allowance to 0
APPROVAL_AMOUNT=0 node approve-facilitator.mjs
Effect: All future settlements will fail with insufficient_allowance error at the pre-settlement fee collection step.

Best Practices

Recommended: 100-1000 USDC (10,000-100,000 settlements)Avoid: Infinite approval (max uint256)Why: Limits exposure if facilitator compromised. With pre-settlement, you only risk the approved amount.
Set up alerts when allowance drops below threshold:
const allowance = await checkAllowance();
if (allowance.settlements < 100) {
  alert("Low allowance - top up soon");
}
Always test approval and settlements on Base Sepolia before mainnet:
NETWORK=sepolia node approve-facilitator.mjs
Ensure merchant address has USDC for fee payment:
  • Minimum: 1 USDC (100 settlements)
  • Recommended: Match approval amount
Pre-settlement means fee is collected BEFORE settlement, so you need sufficient balance.

FAQ

Economic security: Pre-settlement prevents free service exploitation.Without pre-settlement: Merchants could:
  1. Receive customer payment
  2. Intentionally cause fee collection to fail
  3. Get free verification/settlement service
With pre-settlement: Fee collected first = no free rides.Trade-off: Merchant pays $0.01 even if settlement fails after fee. This is payment for the service attempt.
You paid $0.01 for the settlement attempt.Why this is fair:
  • Facilitator provided verification service
  • Facilitator attempted settlement
  • Resources were consumed
  • Service was rendered
Prevents exploitation: Without charging for attempts, merchants could intentionally fail settlements for free verification.Mitigation: Test thoroughly on Sepolia testnet before production.
No. Only the approved amount is accessible. Pre-settlement doesn’t change this.
  • Approve 100 USDC → max risk is 100 USDC
  • Your remaining USDC: Safe
Settlement is blocked at pre-settlement fee collection step.Flow:
  1. Customer submits payment authorization
  2. Facilitator attempts fee collection
  3. Fee collection fails (insufficient balance)
  4. Settlement NEVER executes
  5. Customer NOT charged
Error: fee_collection_failed - Settlement blocked
Timing and guarantees:Pre-settlement (current):
1. Collect $0.01 from merchant
2. IF successful → Execute customer → merchant
3. IF failed → Block settlement, customer safe
Post-settlement (rejected):
1. Execute customer → merchant
2. TRY to collect $0.01 from merchant
3. IF failed → Merchant got free service
Pre-settlement eliminates free service risk.

Next Steps

Trust-Minimized with Pre-Settlement: Customer funds go DIRECTLY to merchants. Facilitator collects $0.01 fee BEFORE settlement. If fee collection fails, settlement is blocked and customer is never charged. No free rides, fully auditable.