jerrymusaga/Conduit-Protocol

Safety layer for agent-to-agent payments. Per-call delegation binding + x402 receipt enforcement on MetaMask Smart Accounts.

★ 0Forks 0TypeScriptGitHub ↗Compare

Project website ↗

README

Conduit

Conduit is infrastructure for paying AI agents you don't have to trust — an open x402 + ERC-7710 facilitator and a family of on-chain caveat enforcers on MetaMask Smart Accounts. It gives an autonomous agent a budget it physically cannot misuse: a fully compromised agent that redirects a payment, overspends a cap, or goes off-allowlist is reverted on-chain in the same transaction.

Its core strength is settlement through the 1Shot Permissionless Relayer — a coordinator hires N agents and the whole team is paid in one atomic redeemDelegations transaction: 1 signature, N agents, 1 fee, gas in USDC (all-or-nothing — an over-budget leg reverts the entire transaction and spends nothing), with status confirmed by 1Shot's Ed25519-signed webhooks. Agent intelligence is by Venice.

ConduitPay is the product built on Conduit — a dapp where you pay agent teams, subscribe for intel, and deposit into yield, every action bounded by a caveat you sign. Live on Base mainnet: https://conduit-protocol.vercel.app

The core idea

A delegation is signed once. From that single signature, narrow agents act repeatedly, but only within bounds the user pinned on-chain at signing time. Each enforcer's beforeHook runs inside the Delegation Manager's redeemDelegations call, before the guarded execution, and reverts the entire redemption if the attempted action steps outside the authorized envelope.

The guarantee is structural, not advisory. A hijacked agent cannot pay the wrong recipient, overspend a cap, swap into a token outside the signed set, deposit into a venue outside the signed set, accept a worse fill than the floor, or redirect any proceeds. Every such attempt reverts on-chain and moves no funds.

Architecture

User account (EOA → MetaMask Smart Account via EIP-7702)
  │  signs ONE bounded root delegation (or grants it via ERC-7715)
  ▼
Coordinator (ephemeral in-session EOA)
  │  redelegates — may NARROW caveats, never widen
  ▼
Task agents  ──►  Conduit Facilitator  ──►  1Shot Permissionless Relayer
  (Venice)        /supported /verify /settle    redeemDelegations, gas in USDC, 7702
                  Ed25519 webhook status         │
                                                 ▼
                          MetaMask DelegationManager (unmodified)
                          + Conduit CaveatEnforcer.beforeHook  → revert if off-policy
src/                    Foundry contracts — the CaveatEnforcer family
test/                   Unit + fork tests for every enforcer (solc 0.8.23, via-IR)
script/                 Deploy scripts (per-enforcer + a one-shot DeployMainnet)
conduit-facilitator/    Express + viem facilitator (oneshot-pl relay backend)
conduit-endpoint/       x402 resource server (services catalog, 402 envelopes)
conduit-dapp/           Next.js app — ConduitPay (Pay, Yield, Subscriptions, Portfolio)

Accounts and signers

Conduit needs the user's account to be a smart account so a delegation can be redeemed against it. It reaches that state with EIP-7702: the EOA is designated to MetaMask's EIP7702StatelessDeleGatorImpl (0x63c0c19a282a1B52b07dD5a65b58948A07DAE32B, same address on Base Sepolia and mainnet), which makes the EOA execute as a MetaMask Smart Account while keeping its address and ECDSA key. The 7702 authorization is either signed by the dapp and bundled into the first redeemDelegations (embedded/passkey signers) or performed natively by MetaMask during an ERC-7715 grant.

Three signer backends sit behind one interface — useActiveWallet() (conduit-dapp/lib/activeWallet.tsx) — exposing address, walletClient, signAuthorization (EIP-7702), and signOut, so every feature is signer-agnostic:

  • MetaMask extension — granted via ERC-7715 Advanced Permissions (wallet_requestExecutionPermissions, @metamask/smart-accounts-kit). MetaMask signs a bounded erc20-token-periodic permission and performs the 7702 upgrade in its own UI. MetaMask deliberately blocks dapp-initiated raw delegation signatures for its accounts (anti-phishing), so ERC-7715 is the sanctioned path; Conduit uses it for the budget root and adds the custom caveat on the coordinator's leaf.
  • Privy embedded wallet — email/social login mints an embedded EOA; the dapp signs the root delegation (signTypedData_v4 of the DelegationManager EIP-712 Delegation) and the 7702 authorization directly.
  • Passkey wallet (WebAuthn-PRF) — a non-custodial secp256k1 wallet whose key is derived from the passkey's PRF extension and held inside an isolated, origin-locked iframe (conduit-dapp/app/wallet-iframe, conduit-dapp/lib/passkey/*). The key never touches the app context or any server; the iframe signs signTypedData and signAuthorization (7702) on request. Verified on Chrome/Android with PRF.

The delegation model

  • A root delegation is delegate=coordinator, delegator=user, authority=ROOT (0xff…ff), caveats=[enforcer], salt, signed under the DelegationManager EIP-712 domain (name="DelegationManager", version="1").
  • A leaf is delegate=relayer, delegator=coordinator, authority=hash(root), signed by the coordinator's in-memory key. The chain submitted to redeemDelegations is ordered [leaf, …, root]. v1.3.0 returns no data.
  • The DelegationManager walks the caveat chain on redemption; a child can only narrow the parent's caveats, never widen them — so the user's root bounds hold no matter what the agents do.

Caveat terms are byte-packed (matching the contracts and fork tests). Examples:

X402ReceiptEnforcer     intentId(32) ++ token(20) ++ payTo(20) ++ maxAmount(16) ++ flags(1)
ERC20PeriodTransfer     token(20) ++ periodAmount(32) ++ periodDuration(32) ++ startTime(32)
SwapAllowlistEnforcer   router(20) ++ tokenIn(20) ++ maxIn(16) ++ recipient(20) ++ N(1)
                          ++ N×[ tokenOut(20) ++ minOut(16) ]
YieldAllowlistEnforcer  asset(20) ++ maxIn(16) ++ recipient(20) ++ N(1)
                          ++ N×[ pool(20) ++ minAmount(16) ]
X402SubscriptionEnforcer subscriptionId(32) ++ token(20) ++ recipient(20)
                          ++ amountPerPeriod(16) ++ periodDuration(4) ++ reserved(2)

Settlement — 1 signature, N agents, 1 transaction (1Shot)

This is Conduit's core strength: a coordinator hires a whole agent team and the entire team is paid in a single transaction through 1Shot's Permissionless Relayer — atomically, with gas in USDC and status from signed webhooks. The facilitator's oneshot-pl backend uses 1Shot's JSON-RPC:

  • relayer_send7710Transaction submits the redemption. The payment carries works[] (the [approve, action] legs, e.g. [USDC.approve(router), router.exactInputSingle] or [USDC.approve(pool), pool.supply]) plus a feeChain — a bounded USDC fee leg that reimburses the relayer's gas in stablecoin. N agent payments plus the fee settle in one redeemDelegations batch (all-or-nothing: an over-budget leg reverts the whole transaction and spends nothing).
  • The EIP-7702 authorization is passed in authorizationList, so the account upgrade is bundled into the same transaction through the relayer.
  • relayer_estimate7710Transaction returns an exact, batch-aware fee quote used to size the fee leg.
  • Settlement status is driven by 1Shot's Ed25519-signed webhooks (verified against the relayer's JWKS by keyId), with relayer_getStatus polling as a fallback. The facilitator forwards a clean conduit.settlement event to the seller.

The relayer's per-chain capabilities (targetAddress = the redeemer the work delegation must name, feeCollector = where the fee leg pays) are fetched and warmed at startup; the dapp reads them from the 402 envelope.

Enforcer family

Every enforcer is a CaveatEnforcer (single-call, default-exec mode), independently deployed, verified, and unit-tested. Solidity source in src/ (each name links to its contract); verified addresses are under Deployed addresses.

Enforcer (source) Guards Pins on-chain
X402ReceiptEnforcer one x402 payment token, recipient, max amount, one-shot intent (paired with IdEnforcer)
X402SubscriptionEnforcer a recurring charge exact amount, merchant, one charge per period (on-chain period tracking)
SwapBoundsEnforcer one Uniswap v3 swap router, fixed pair, input cap, slippage floor, recipient
SwapAllowlistEnforcer a swap into a chosen set a signed set of output tokens, each with its own floor
ApproveBoundsEnforcer one ERC-20 approval token, single spender, capped amount (rides the same 1Shot batch)
YieldAllowlistEnforcer one Aave-V3 supply a signed set of venues, one asset, cap, onBehalfOf = user

The two allowlist enforcers make agent autonomy safe: the user signs a set (of tokens, or of yield venues, each with its own floor); a Venice scout reasons over live data and picks the best member; the agent executes into it without the user re-signing. The set the agent may choose from is exactly the set the user signed — resolving "the best token" or "the best APY" never grants reach beyond the allowlist.

Gasless revocation

Revocation is DelegationManager.disableDelegation(root), gated onlyDeleGator so only the user's account can send it; disabling a root cascades to every child redelegation at once. Conduit runs it gaslessly: the relayer executes it from the user's account, bounded by MetaMask's AllowedTargetsEnforcer (0x7F20f61b1f09b08D970938F6fa563634d65c4EeB) + AllowedMethodsEnforcer (0x2c21fD0Cb9DC8445CB3fb0DC5E7Bb0Aca01842B5) so the relayer may only call disableDelegation and nothing else, reimbursed by a small USDC fee leg. A direct on-chain transaction is the automatic fallback. So a fresh account with no ETH can still kill its permissions.

ConduitPay (the dapp)

A gated product surface over the primitives, with one signer abstraction:

  • Pay — hire and pay an agent team (coordinator discovers specialists on the ERC-8004 Identity Registry, pays each via erc7710 redelegation, all in one atomic 1Shot batch), or run a bounded swap into a user-selected token set.
  • Yield — deposit USDC into the best APY across a user-selected set of Aave-V3 venues (Aave, Seamless, ZeroLend on Base), via a gasless [approve, supply] batch.
  • Subscriptions — fixed-price, one-merchant, once-per-period charges; each charge delivers a live Venice report, and a deliverable hands off into a matching Pay or Yield action (intel → action).
  • Portfolio — every active permission with its decoded on-chain caveat and a gasless kill switch.

Signer support per flow: the MetaMask extension drives Pay (and Subscriptions) via ERC-7715 Advanced Permissions; Privy embedded and passkey wallets drive every flow (including swap/yield, whose allowlist enforcers are custom caveats on the user-signed root). All are MetaMask Smart Accounts via the 7702 DeleGator.

Smart Accounts Kit usage

Conduit is built on @metamask/smart-accounts-kit (+ /actions, /utils). The user's EOA becomes a MetaMask Smart Account via EIP-7702 (EIP7702StatelessDeleGatorImpl), and every agent payment is a DelegationManager.redeemDelegations call gated by Conduit's caveat enforcers.

Advanced Permissions (ERC-7715)

Delegations

Redelegation

  • Create — a coordinator redelegates a narrowed leaf to the relayer (authority = hashDelegation(parent), a child narrows-never-widens): conduit-dapp/lib/payment.ts#L284 (multi-hop leaf signing at L171-L208).

x402

1Shot API usage

Venice AI usage

Deployed addresses (Base mainnet)

The full enforcer family is deployed and verified on Base mainnet.

Conduit's custom caveat enforcers (the moat — each deployed + verified on Basescan):

Enforcer Address (Basescan)
X402ReceiptEnforcer 0xF3D95eD5949970F483b11867b3b6509422a617AA
X402SubscriptionEnforcer 0x177e5DC050Da4aCE6655B721E3a24B2A553B5F9F
SwapBoundsEnforcer 0x62DabA9aAD63B914Cba295B08a65263eEc401EE3
SwapAllowlistEnforcer 0x150933Eb33176B763c79609FF771d14D8Dc665c5
ApproveBoundsEnforcer 0x388084511a9a1891021ea6989b8A756D1561e0aA
YieldAllowlistEnforcer 0xcBc69E09A6dfeCd503881DcAd595166f81836029

Framework + tokens:

Contract Address (Basescan)
DelegationManager (MetaMask v1.3.0) 0xdb9B1e94B5b69Df7e401DDbedE43491141047dB3
EIP7702StatelessDeleGatorImpl 0x63c0c19a282a1B52b07dD5a65b58948A07DAE32B
USDC 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913

Agents (ERC-8004 Identity Registry). The marketplace agents are registered on the Base mainnet ERC-8004 Identity Registry (0x8004A169FB4a3325136EB29fA0ceB6D2e539a432) by registrant (0x131EC028Bb8Bd936A3416635777D905497F3D21f) — 13 agents, IDs 55321–55336 (including the Roaster, agentId 55324). Each agent's on-chain agentURI points at its AgentCard, e.g. /api/agent-card/roaster.

Aave-V3 yield venues: Aave · Seamless · ZeroLend.

Feedback

Detailed, balanced feedback for MetaMask, 1Shot, Venice, and the x402 / ERC-7710 spec — what we hit → why it cost time → a concrete suggestion for each — is in FEEDBACK.md.

Social Media

Conduit's build was documented in public on X throughout the hackathon.

Build log (chronological):

  1. https://x.com/ConduitProtocol/status/2055280609132445820
  2. https://x.com/ConduitProtocol/status/2056397273504903175
  3. https://x.com/ConduitProtocol/status/2056748237873164679
  4. https://x.com/ConduitProtocol/status/2057066557335810132
  5. https://x.com/ConduitProtocol/status/2057848263169003740
  6. https://x.com/JerryMusaga/status/2057888316096196688
  7. https://x.com/ConduitProtocol/status/2058873242933146025
  8. https://x.com/ConduitProtocol/status/2059006116215083088
  9. https://x.com/ConduitProtocol/status/2060052391656050960
  10. https://x.com/ConduitProtocol/status/2060079706469261761
  11. https://x.com/ConduitProtocol/status/2061426516890657270
  12. https://x.com/ConduitProtocol/status/2062951973687595017
  13. https://x.com/ConduitProtocol/status/2064934228261744697
  14. https://x.com/ConduitProtocol/status/2065825625621811477
  15. https://x.com/ConduitProtocol/status/2066377699308847112
  16. https://x.com/ConduitProtocol/status/2066472187884650814

Build and test

Contracts (Foundry, solc 0.8.23, via-IR):

forge build
forge test          # unit suites run offline; *.fork.t.sol need an RPC fork URL

Dapp (Next.js):

cd conduit-dapp
npm install
npm run build
npm run dev

Deploying enforcers

Per-enforcer scripts under script/, or the whole family in one broadcast with script/DeployMainnet.s.sol:

source .env
forge script script/DeployMainnet.s.sol:DeployMainnet \
  --rpc-url base --broadcast --slow --verify -vvv

Use --slow — a 7702-delegated deployer hits the relayer's in-flight transaction limit otherwise. After deploying, set the corresponding NEXT_PUBLIC_* addresses in the dapp (see conduit-dapp/lib/config.ts for the per-chain list). Required env: DEPLOYER_PRIVATE_KEY, BASE_RPC_URL / BASE_SEPOLIA_RPC_URL, BASESCAN_API_KEY.

Conventions

  • Pinned: delegation-framework v1.3.0, erc7579-implementation v0.0.2, account-abstraction v0.7.0, solc 0.8.23.
  • redeemDelegations returns no data in v1.3.0; chain order [leaf, …, root]; ROOT_AUTHORITY = bytes32(uint256.max).
  • Length-check calldata before field checks in every enforcer (post-audit ordering).
  • The facilitator's relay backend is oneshot-pl (1Shot Permissionless Relayer); the relayer URL is derived per chain (.dev testnet, .com mainnet) unless overridden.

Security model

Blast radius is bounded by which key leaks and by the caveats on the delegation that key can redeem. A leaked task-agent key can only ever perform the one bounded action its leaf permits; a leaked coordinator key can only narrow, never widen, the user's root; the root is revocable at any time — gaslessly — cascading to every child. The custom-enforcer flows (swap, yield, subscription) keep the safety-critical bound on the user-signed root, so even the coordinator cannot widen them.

Honest scope: EIP-3009 (transferWithAuthorization) is not ERC-7710 — Conduit settles via redeemDelegations precisely so the caveat family is enforceable on chain. MetaMask's ERC-7715 catalog covers token-spending permissions, so the extension drives the budget/subscription flows; custom-enforcer execution (swap, yield) is signed by an embedded or passkey wallet over the same MetaMask Smart Account.

License

MIT.

Contributors

jerrymusaga

Issues