What is Conduit

Conduit is an open x402 facilitator that implements the erc7710 settlement method on MetaMask Smart Accounts — and relays through the 1Shot Permissionless Relayer, so agents pay gas in stablecoins.

Point any x402-protected resource at a Conduit facilitator and your callers get: settlement via on-chain delegation redemption, a family of on-chain caveats that bind every action — X402ReceiptEnforcer for one exact request, X402SubscriptionEnforcer for fixed recurring charges, and SwapBounds/SwapAllowlist for bounded agent trades — gas paid in USDC with no relayer to run, and clean settlement webhooks. Conduit is the rail; your agent is the caller.

The safety property: a payment is fused to one recipient, one amount, one token, one request — fixed on-chain before the agent runs. A compromised agent can’t redirect or overspend it. A spending cap bounds the amount; Conduit binds the payment.

Quickstart

The x402 exchange is three steps. Your resource returns 402 with Conduit in extra; the caller pays via the X-PAYMENT header; Conduit verifies + settles.

# 1. Caller hits your resource → 402 Payment Required
GET /paid-data            → 402  { accepts: [{ ...,
  extra: { assetTransferMethod: "erc7710",
           facilitator, delegationManager, receiptEnforcer,
           redeemer, feeCollector, relayBackend } } ] }

# 2. Caller builds an intent-bound redelegation and re-requests
GET /paid-data
  X-PAYMENT: base64(x402 payment payload)

# 3. Your server forwards to the facilitator → settles on-chain
POST {facilitator}/verify   → { isValid }
POST {facilitator}/settle   → { jobId, status, transaction }

The seller never holds funds or keys — it just relays the payment payload to the facilitator. Conduit (and 1Shot) do the rest.

Facilitator API

The facilitator is a small HTTP surface (x402 V2 shape + Conduit extensions).

Conduit facilitator base URL

https://conduit-protocol-production.up.railway.app

Every {facilitator} below is this base URL. Probe it live: GET https://conduit-protocol-production.up.railway.app/supported.

GET/supported

Advertises the erc7710 capability + the conduit block: receiptEnforcer, delegationManager, relayBackend, redeemer (the address the work delegation must name), and feeCollector (oneshot-pl only — where the buyer pays gas).

POST/verify

Simulates the redemption on-chain. Returns { isValid, invalidReason }. A bound payment that violates its caveats fails here with the real revert reason.

POST/settle

Submits the redemption through the active relay backend. Returns a jobId + transaction. On oneshot-pl, this relays through 1Shot with gas paid in stablecoin.

GET/events

Server-Sent Events: the live payment lifecycle (request → permission → settle → settled) for building a live console.

POST/relayer-webhook

Inbound 1Shot status events (Ed25519-verified against the relayer JWKS) — the source of truth for settlement, forwarded to your webhook.

Build a payment

A payment is an intent-bound redelegation rooted in the user’s ERC-7715 grant. The leaf carries two caveats:

  • X402ReceiptEnforcer — binds token + recipient + amount + intent hash (89-byte packed terms).
  • IdEnforcer — one-shot: the intent can be redeemed once (replay-proof).
// the leaf delegation: signer → facilitator's redeemer
const caveats = [
  { enforcer: idEnforcer,
    terms: abiEncode(["uint256"], [BigInt(intentHash)]) },
  { enforcer: receiptEnforcer,          // X402ReceiptEnforcer
    terms: packed(["bytes32","address","address","uint128","uint8"],
                  [intentHash, token, payTo, maxAmount, 0]) },
];
// sign it, encode the chain [leaf, …, root], send as the X-PAYMENT payload.
On the 1Shot path, gas is paid in USDC — so a payment carries two bounded delegations merged into one batch: a fee delegation (pays the relayer’s feeCollector, capped at the live quote) and the work delegation above. Even the gas payment is intent-bound. The user’s account is upgraded to a MetaMask Smart Account via EIP-7702 in the same transaction.

Roadmap — cross-chain: 1Shot’s relayer also supports multichain settlement (send7710TransactionMultichain) — one relayer call that fans out to several chains, each leg settled and gas paid in USDC on its own chain. So a scout can pick the best venue across chains and the deposit lands wherever wins, with the bounded allowlist enforced per chain. Conduit’s relay seam is built for it; shipping post-hackathon.

In practice this is one call — Conduit’s client picks the right shape from the facilitator’s advertised relayBackend. See lib/payment.ts in the demo for a reference implementation.

Subscriptions

For recurring charges, a service advertises a subscription instead of a one-shot price. Its 402 carries paymentKind: "subscription" and a subscription block:

GET /services/pulse-feed → 402  { accepts: [{ ...,
  extra: { paymentKind: "subscription",
           subscription: { enforcer, subscriptionId,
                           periodSeconds, amountPerPeriod } } }] }

The buyer signs a service-bound root delegation whose caveat is the X402SubscriptionEnforcer — the user’s own signature binds merchant + exact amount + cadence. 94-byte packed terms:

subscriptionId(bytes32) ++ token(address) ++ recipient(address)
  ++ amountPerPeriod(uint128) ++ periodDuration(uint32) ++ reserved(uint16)

Each period the agent can charge exactly once; a second charge in the same period reverts X402Sub:already-charged-this-period on-chain. Every charge emits X402SubscriptionCharged (subscriptionId, amount, period) for off-chain reconciliation.

vs. a rolling spend cap: a periodic allowance only limits “up to X per window” — it doesn’t fix the recipient, the exact price, or prevent multiple charges. The subscription enforcer is a fixed-price, merchant-bound, once-per-period charge with on-chain double-charge protection. That’s the actual subscription semantic.

On the 1Shot path the strict subscription root can’t fund gas (it only authorizes the exact subscription transfer), so a subscription grant is two bounded user roots: the subscription root + a small gas-fee budget root. Both bounded, both user-approved. See lib/subscription.ts.

Lifecycle. Subscribe once (one signature) → the agent charges once per period → each charge buys a real deliverable → cancel any time. The period is tracked on-chain by the enforcer, so double-charge protection and the “next charge in…” countdown are both derived from the same on-chain truth — not a server clock.

DELIVERABLEcharge → product

A subscription isn’t just a recurring transfer — each settled charge delivers. Conduit’s demo products (Market Pulse, AI Alpha Daily, DeFi Yield Weekly) each return a live Venice-generated report for the period the charge paid for, attached to the on-chain receipt. The payment and the product settle together: no charge, no deliverable.

REVOKEgasless cancel (no ETH)

Cancelling is disableDelegation(subscriptionRoot). Conduit runs it gaslessly — the relayer executes it from the user’s account, bounded by MetaMask’s AllowedTargets + AllowedMethods enforcers and reimbursed by a small USDC fee — so a fresh account with no ETH can still kill a subscription. Falls back to a direct tx if needed.

LOOPintel → action

Subscriptions are intel; Pay is action. A deliverable carries an “Act on this in Pay →” handoff that deep-links into a matching bounded action — a token-intel report into a SwapAllowlist swap, a yield report into a YieldAllowlist deposit. The report tells you what to do; the enforcer guarantees the agent can only do that.

Gas, in USDC. A subscription charges itself on a schedule, unattended — so each future charge has to pay its own gas without you there to approve it, and without you holding ETH. You sign one extra bounded root at subscribe time: a small rolling gas allowance whose period matches the billing period, that each charge’s 1Shot fee is reimbursed from in USDC. It’s an allowance, not a deposit — nothing is escrowed.Edge cases: too much — harmless; it’s a cap, unused allowance is never spent and resets each period. too little — the dapp auto-sizes the allowance to at least the live gas estimate, so a normal charge can’t under-fund; if gas later spikes past the cap, that period’s charge simply reverts on-chain (no funds move, no deliverable) and retries next period. exhausted mid-way — can’t deplete permanently: the allowance refreshes every billing period, so it self-replenishes. A charge either fully settles within budget or fully reverts — never a partial spend.

Enforcers

The facilitator relays any ERC-7710 execution — Conduit ships the on-chain caveats that make payments safe. Each binds a delegated transfer so a compromised agent can’t misuse it. The pattern is pluggable: one facilitator, many enforcers.

ENFORCERX402ReceiptEnforcer

One-shot, intent-bound payment: binds token + recipient + amount + intent hash; pair with IdEnforcer for replay protection. The standard x402 pay-once flow.

ENFORCERX402SubscriptionEnforcer

Recurring payment: binds token + recipient + exact amount, charged at most once per period. An agent can renew each period without a new grant — but can’t change the recipient/amount/token or double-charge.

Beyond payments — trading. The same pattern binds DEX swaps, so an agent can trade within bounds it cannot exceed. The facilitator relays these unchanged (its oneshot path is execution-agnostic).

ENFORCERSwapBoundsEnforcer

One bounded Uniswap v3 swap: binds router + tokenIn/tokenOut + maxAmountIn + minAmountOut (slippage floor) + recipient. A hijacked agent can’t swap a different token, overspend, accept a worse fill, or redirect the proceeds. Emits SwapBounded.

ENFORCERSwapAllowlistEnforcer

The dynamic-token version: the user signs a set of output tokens, each with its own floor (router · tokenIn · maxIn · recipient · N · [tokenOut·floor]×N). A scout agent picks the best token from the signed set — choosing a token never gives reach beyond it.

ENFORCERApproveBoundsEnforcer

Bounds the ERC-20 approve a swap or deposit needs (token + spender + cap), so the allowance can ride the same 1Shot batch as the action — gas paid in USDC, the user never needs ETH, no standing approval.

Beyond trading — yield. The same pattern binds a lending-pool deposit, so an agent can move funds into yield within a venue set it cannot escape.

ENFORCERYieldAllowlistEnforcer

One bounded Aave-V3 supply: the user signs a set of yield venues (asset · maxIn · recipient · N · [pool·minAmount]×N). A scout agent picks the best APY from the signed set; a hijacked agent can’t supply into a venue you didn’t approve, overspend, supply a different asset, or redirect the position. Emits YieldAllowed.

All six are CaveatEnforcer — Conduit-custom caveats on the MetaMask Delegation Framework’s extension point (override beforeHook), enforced by the unmodified DelegationManager on every hop. The allowlist enforcers (swap + yield) are what make “pick the best token/APY” safe: the agent can only ever choose from the set you signed.

Bounds & revocation

Every grant — one-shot budget or subscription — is bounded and revocable by the user, not the agent.

CAVEATTimestampEnforcer — expiry

Adds a validity window: packed (uint128 after, uint128 before). Past before, redemption reverts TimestampEnforcer:expired-delegation. Without it, a rolling period cap never actually expires on-chain — so Conduit attaches it to make the budget/subscription genuinely die at its deadline.

REVOKEDelegationManager.disableDelegation(root)

The kill switch. Gated onlyDeleGator(delegator), so the user’s account sends it — not the agent, not the relayer. Disabling the root cascades: every child redelegation under it dies at once. A direct on-chain tx (needs gas).

The blast radius is bounded by design: a compromised agent can never widen a delegation, the spend is caveat-fixed, the grant expires, and the user can revoke the whole tree in one call.

Webhooks

Settlement is asynchronous. Register a WEBHOOK_URL and Conduit pushes you a clean event the moment a payment confirms — on any relay path.

POST {your WEBHOOK_URL}
{
  "type": "conduit.settlement",
  "jobId": "…",
  "status": "confirmed",      // | "failed"
  "success": true,
  "txHash": "0x…",
  "error": null
}

Conduit consumes 1Shot’s Ed25519-signed webhooks (verifying them against the relayer’s JWKS), then forwards this simple event to you. You never touch the relayer, the chain, or signature verification.

Addresses

Base mainnet (live). The full enforcer family is deployed and verified on both networks; these addresses reflect the chain this app is configured for.