How it works

The agent signs. Someone else pays the gas.

An agent hits a paid endpoint, gets a price in US dollars, and signs two offchain messages. It never sends a transaction and never holds a gas token.

The Scrip payment sequence The agent requests a resource and receives HTTP 402 with a price in USD. It signs an EIP-2612 permit and a PaymentIntent offchain and sends both to a relayer. The relayer calls settle() on Flare, which moves FXRP to the payee and emits PaymentSettled. The endpoint then returns the resource to the agent. Agent Endpoint Relayer Flare GET /resource 402 · price in USD signs permit + intent offchain · no gas · no transaction both signatures, over HTTP settle() relayer pays the gas PaymentSettled 200 · resource

Why two signatures

This is the part that is specific to Flare, and the part most x402 implementations get to skip.

An EIP-3009 authorisation - what USDC uses - names its recipient. The signature itself says move X to this address. An EIP-2612 permit, which is what FXRP implements, does not. It commits only to (owner, spender, value, nonce, deadline): it grants an allowance and says nothing about where the money then goes.

settle() is permissionless by design, because any relayer should be able to carry a payment. That turns the missing recipient from an omission into a hole: anyone who observed a permit signature - in the mempool, or on the x402 HTTP path it necessarily travels - could call settle() naming themselves as payee.

The permit

signed over FXRP's domain
  • owner
  • spender
  • value
  • nonce
  • deadline

Authorises an allowance. Says nothing about the destination.

The PaymentIntent

signed over Scrip's own domain
  • invoiceId
  • payer
  • payee
  • amount
  • deadline

Authorises the destination. Every field a relayer supplies as calldata appears here, so none can be substituted.

The permit authorises the allowance; the intent authorises the destination. Neither is sufficient alone - which is why a relayer can choose when a payment lands, but nothing about what it is.

Step by step

  1. The endpoint answers 402

    The price is quoted in US dollars, then converted to FXRP at Flare's FTSO rate. The invoice carries an ID that makes the payment single-use.

  2. The agent signs twice, offchain

    An EIP-2612 permit over FXRP's domain, and a PaymentIntent over the facilitator's. No transaction, no gas, no chain interaction at all - just two signatures.

  3. A relayer submits settle()

    The relayer pays the fee. It cannot redirect the payment, inflate the amount, or reuse the invoice, because all three are covered by the intent digest.

  4. The contract measures what arrived

    FAssets parameters are asset-manager controlled and may levy a transfer fee, so settle() reads the payee's balance before and after and reverts with Underdelivered if the payee got less than the invoice. A rail that silently underpays is a broken one.

What a front-runner can and cannot do

A permit signature is visible the moment it travels. Someone can lift it from the mempool and submit it straight to FXRP, which consumes the payer's nonce. A bare permit call inside settle() would then revert - griefing every payment on the rail for the price of gas.

So the permit call is wrapped in try/catch. Swallowing that revert is only safe because the intent already fixed the destination: a griefer who replays the permit merely moves the allowance into place and gains nothing. The allowance is then checked explicitly, so a genuinely missing one still reports itself rather than failing opaquely inside the token.

This is tested rather than argued - see what is verified.

What comes next

EIP-2612 nonces are sequential per payer, so an agent paying two endpoints concurrently has the second permit fail on a consumed nonce. EIP-3009's random nonces avoid this and FXRP does not implement EIP-3009, so payments are serialised per payer today.

Everything else on this page is settled. settled is keyed on (invoiceId, payer), so a stranger who lifts an invoice id off the HTTP path lands in their own slot and leaves the real one payable; and a zero-amount intent, which moved nothing while still occupying an invoice and costing a relayer its gas, is refused. Both are in the deployed contract and covered by the fork test.