-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Add x402 protocol volume adapter for Base #4582
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
fosc19
wants to merge
1
commit into
DefiLlama:master
Choose a base branch
from
fosc19:add-x402-adapter
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,210 @@ | ||
| import { FetchOptions, SimpleAdapter } from "../../adapters/types"; | ||
| import { CHAIN } from "../../helpers/chains"; | ||
|
|
||
| /** | ||
| * x402 Protocol Volume Adapter | ||
| * | ||
| * x402 is a payment protocol enabling USDC payments to AI agents using Account Abstraction. | ||
| * This adapter tracks payment volume across ALL x402 facilitators on Base. | ||
| * | ||
| * Methodology: | ||
| * - Detects transactions via AuthorizationUsed events (EIP-3009) on USDC(Base) | ||
| * - Filters to x402 facilitator addresses (tx.from in allowlist) | ||
| * - Respects dateOfFirstTransaction per sender | ||
| * - Sums USDC Transfer amounts from matching transactions | ||
| * - Returns raw token balances as dailyVolume | ||
| */ | ||
|
|
||
| const USDC_BASE = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" as const; | ||
| const AUTH_USED_EVENT = | ||
| "event AuthorizationUsed(address indexed authorizer, bytes32 indexed nonce)"; | ||
| const TRANSFER_TOPIC = | ||
| "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"; // keccak256("Transfer(address,address,uint256)") | ||
|
|
||
| // All x402 facilitators on Base (36 addresses across 11 providers) | ||
| const FACILITATOR_SENDERS_BASE: { address: string; since: number }[] = [ | ||
| // 402104 | ||
| { address: "0x73b2b8df52fbe7c40fe78db52e3dffdd5db5ad07", since: Date.UTC(2025, 9, 29) }, | ||
|
|
||
| // Aurracloud | ||
| { address: "0x222c4367a2950f3b53af260e111fc3060b0983ff", since: Date.UTC(2025, 9, 5) }, | ||
| { address: "0xb70c4fe126de09bd292fe3d1e40c6d264ca6a52a", since: Date.UTC(2025, 9, 27) }, | ||
| { address: "0xd348e724e0ef36291a28dfeccf692399b0e179f8", since: Date.UTC(2025, 9, 29) }, | ||
|
|
||
| // Coinbase | ||
| { address: "0xdbdf3d8ed80f84c35d01c6c9f9271761bad90ba6", since: Date.UTC(2025, 4, 5) }, | ||
|
|
||
| // Codenut | ||
| { address: "0x8d8Fa42584a727488eeb0E29405AD794a105bb9b", since: Date.UTC(2025, 9, 13) }, | ||
| { address: "0x87aF99356d774312B73018b3B6562e1aE0e018C9", since: Date.UTC(2025, 9, 31) }, | ||
| { address: "0x65058CF664D0D07f68B663B0D4b4f12A5E331a38", since: Date.UTC(2025, 9, 31) }, | ||
| { address: "0x88E13D4c764a6c840Ce722A0a3765f55A85b327E", since: Date.UTC(2025, 9, 31) }, | ||
|
|
||
| // Daydreams | ||
| { address: "0x279e08f711182c79Ba6d09669127a426228a4653", since: Date.UTC(2025, 9, 16) }, | ||
|
|
||
| // Mogami | ||
| { address: "0xfe0920a0a7f0f8a1ec689146c30c3bbef439bf8a", since: Date.UTC(2025, 9, 24) }, | ||
|
|
||
| // OpenX402 | ||
| { address: "0x97316fa4730bc7d3b295234f8e4d04a0a4c093e8", since: Date.UTC(2025, 9, 16) }, | ||
| { address: "0x97db9b5291a218fc77198c285cefdc943ef74917", since: Date.UTC(2025, 9, 16) }, | ||
|
|
||
| // PayAI | ||
| { address: "0xc6699d2aada6c36dfea5c248dd70f9cb0235cb63", since: Date.UTC(2025, 4, 18) }, | ||
| { address: "0xb2bd29925cbbcea7628279c91945ca5b98bf371b", since: Date.UTC(2025, 9, 29) }, | ||
| { address: "0x25659315106580ce2a787ceec5efb2d347b539c9", since: Date.UTC(2025, 9, 29) }, | ||
| { address: "0xb8f41cb13b1f213da1e94e1b742ec1323235c48f", since: Date.UTC(2025, 9, 29) }, | ||
| { address: "0xe575fa51af90957d66fab6d63355f1ed021b887b", since: Date.UTC(2025, 9, 29) }, | ||
|
|
||
| // Questflow | ||
| { address: "0x724efafb051f17ae824afcdf3c0368ae312da264", since: Date.UTC(2025, 9, 29) }, | ||
| { address: "0xa9a54ef09fc8b86bc747cec6ef8d6e81c38c6180", since: Date.UTC(2025, 9, 29) }, | ||
| { address: "0x4638bc811c93bf5e60deed32325e93505f681576", since: Date.UTC(2025, 9, 29) }, | ||
| { address: "0xd7d91a42dfadd906c5b9ccde7226d28251e4cd0f", since: Date.UTC(2025, 9, 29) }, | ||
| { address: "0x4544b535938b67d2a410a98a7e3b0f8f68921ca7", since: Date.UTC(2025, 9, 29) }, | ||
| { address: "0x59e8014a3b884392fbb679fe461da07b18c1ff81", since: Date.UTC(2025, 9, 29) }, | ||
| { address: "0xe6123e6b389751c5f7e9349f3d626b105c1fe618", since: Date.UTC(2025, 9, 29) }, | ||
| { address: "0xf70e7cb30b132fab2a0a5e80d41861aa133ea21b", since: Date.UTC(2025, 9, 29) }, | ||
| { address: "0x90da501fdbec74bb0549100967eb221fed79c99b", since: Date.UTC(2025, 9, 29) }, | ||
| { address: "0xce7819f0b0b871733c933d1f486533bab95ec47b", since: Date.UTC(2025, 9, 29) }, | ||
|
|
||
| // Thirdweb | ||
| { address: "0x80c08de1a05df2bd633cf520754e40fde3c794d3", since: Date.UTC(2025, 9, 7) }, | ||
|
|
||
| // x402rs | ||
| { address: "0xd8dfc729cbd05381647eb5540d756f4f8ad63eec", since: Date.UTC(2024, 11, 5) }, | ||
| { address: "0x76eee8f0acabd6b49f1cc4e9656a0c8892f3332e", since: Date.UTC(2025, 9, 26) }, | ||
| { address: "0x97d38aa5de015245dcca76305b53abe6da25f6a5", since: Date.UTC(2025, 9, 24) }, | ||
| { address: "0x0168f80e035ea68b191faf9bfc12778c87d92008", since: Date.UTC(2025, 9, 24) }, | ||
| { address: "0x5e437bee4321db862ac57085ea5eb97199c0ccc5", since: Date.UTC(2025, 9, 24) }, | ||
| { address: "0xc19829b32324f116ee7f80d193f99e445968499a", since: Date.UTC(2025, 9, 26) }, | ||
|
|
||
| // XEcho | ||
| { address: "0x3be45f576696a2fd5a93c1330cd19f1607ab311d", since: Date.UTC(2025, 9, 30) }, | ||
| ]; | ||
|
|
||
| const SENDER_SET = new Set(FACILITATOR_SENDERS_BASE.map((s) => s.address.toLowerCase())); | ||
| const SINCE_BY_SENDER = new Map( | ||
| FACILITATOR_SENDERS_BASE.map((s) => [s.address.toLowerCase(), s.since || 0]), | ||
| ); | ||
|
|
||
| const START = "2024-12-05"; // First x402rs facilitator | ||
|
|
||
| const fetch = async (options: FetchOptions) => { | ||
| const dailyVolume = options.createBalances(); | ||
|
|
||
| // 1) Get AuthorizationUsed events from USDC(Base) | ||
| const authLogs: any[] = await options.getLogs({ | ||
| target: USDC_BASE, | ||
| eventAbi: AUTH_USED_EVENT | ||
| }); | ||
|
|
||
| if (!authLogs?.length) { | ||
| return { | ||
| dailyVolume, | ||
| dailyFees: 0, | ||
| dailyRevenue: 0, | ||
| }; | ||
| } | ||
|
|
||
| const txHashes = [...new Set(authLogs.map((l) => l.transactionHash))]; | ||
|
|
||
| // 2) Preferred path: receipts (filter sender+since; sum USDC Transfers) | ||
| const receipts = (options as any).getTxReceipts | ||
| ? await (options as any).getTxReceipts(txHashes) | ||
| : []; | ||
|
|
||
| if (receipts.length) { | ||
| for (const r of receipts) { | ||
| if (!r) continue; | ||
| const sender = (r.from || "").toLowerCase(); | ||
| if (!SENDER_SET.has(sender)) continue; | ||
|
|
||
| const blockTimeMs = (r.blockTimestamp ?? 0) * 1000; | ||
| const sinceMs = SINCE_BY_SENDER.get(sender) ?? 0; | ||
| if (sinceMs && blockTimeMs && blockTimeMs < sinceMs) continue; | ||
|
|
||
| // Note: We don't filter by method selector - AuthorizationUsed + sender is sufficient. | ||
| // This avoids missing x402 transactions that go through wrappers/variants. | ||
|
|
||
| for (const log of r.logs || []) { | ||
| const addr = (log.address || "").toLowerCase(); | ||
| if (addr !== USDC_BASE.toLowerCase()) continue; | ||
| if (!log.topics || log.topics[0]?.toLowerCase() !== TRANSFER_TOPIC) continue; | ||
| dailyVolume.add(`base:${USDC_BASE}`, log.data); | ||
| } | ||
| } | ||
| return { | ||
| dailyVolume, | ||
| dailyFees: 0, | ||
| dailyRevenue: 0, | ||
| }; | ||
| } | ||
|
|
||
| // 3) Fallback: if no receipts, use getTransactions to filter sender | ||
| if ((options as any).getTransactions) { | ||
| const txs: any[] = await (options as any).getTransactions(txHashes); | ||
| const allowedHashes = new Set<string>(); | ||
| for (const tx of txs) { | ||
| const sender = (tx.from || "").toLowerCase(); | ||
| if (!SENDER_SET.has(sender)) continue; | ||
| // Can't check since without timestamps; if runner exposes receipts "per hash", try 1 call | ||
| if ((options as any).getTxReceipts) { | ||
| const [r] = await (options as any).getTxReceipts([tx.hash]); | ||
| const blockTimeMs = (r?.blockTimestamp ?? 0) * 1000; | ||
| const sinceMs = SINCE_BY_SENDER.get(sender) ?? 0; | ||
| if (sinceMs && blockTimeMs && blockTimeMs < sinceMs) continue; | ||
| } | ||
| allowedHashes.add(tx.hash); | ||
| } | ||
|
|
||
| // Intersect with USDC Transfers to get amounts | ||
| if (allowedHashes.size) { | ||
| const TRANSFER_EVENT = "event Transfer(address indexed from, address indexed to, uint256 value)"; | ||
| const transferLogs: any[] = await options.getLogs({ | ||
| target: USDC_BASE, | ||
| eventAbi: TRANSFER_EVENT | ||
| }); | ||
| for (const log of transferLogs) { | ||
| if (allowedHashes.has(log.transactionHash)) { | ||
| dailyVolume.add(`base:${USDC_BASE}`, log.value); | ||
| } | ||
| } | ||
| } | ||
| return { | ||
| dailyVolume, | ||
| dailyFees: 0, | ||
| dailyRevenue: 0, | ||
| }; | ||
| } | ||
|
|
||
| // 4) Last resort: return empty to avoid false positives | ||
| return { | ||
| dailyVolume, | ||
| dailyFees: 0, | ||
| dailyRevenue: 0, | ||
| }; | ||
| }; | ||
|
|
||
| const methodology = { | ||
| Volume: | ||
| "USDC payment volume facilitated through x402 protocol on Base. Tracks transfers from authorized facilitator addresses (36 addresses across 11 providers: Coinbase, PayAI, Questflow, x402rs, Codenut, Aurracloud, OpenX402, Daydreams, Mogami, Thirdweb, 402104, XEcho) using EIP-3009 (transferWithAuthorization). Respects dateOfFirstTransaction for each facilitator address.", | ||
| Fees: | ||
| "x402 protocol does not charge user fees. All payment amounts go directly to recipients.", | ||
| Revenue: | ||
| "Protocol does not collect direct revenue from transactions. Revenue model is not public.", | ||
| }; | ||
|
|
||
| const adapter: SimpleAdapter = { | ||
| version: 2, | ||
| adapter: { | ||
| [CHAIN.BASE]: { | ||
| fetch, | ||
| start: START, | ||
| }, | ||
| }, | ||
| methodology, | ||
| }; | ||
|
|
||
| export default adapter; | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pulling all transfers of usdc on base is gonna be extremely slow and resource intensive
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, we won't allow it. I'm looking for other solution
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right now the adapter is already kind of hybrid in practice:
In practice, trying to brute-force all USDC traffic from inside a single adapter doesn’t seem to work well in this context. Getting a fully protocol-pure view might need a separate indexer for USDC/EIP-3009 flows on Base, but even with an indexer there would still need to be some source of truth for which addresses are x402 facilitators (either a curated list like now, or a future on-chain registry), and that part sits outside what an adapter is usually meant to handle.
A more realistic approach within this stack might be:
FACILITATORS_SET),AuthorizationUsedon USDC(Base),Transferamounts,Looking forward, if agent payments on Base keep growing around x402 and things like Google’s AP2, it probably won’t be realistic long term to maintain facilitator lists by hand. On top of that, more decentralised/permissionless facilitators are likely to show up, and individual agents will tend to use multiple addresses and multiple facilitators over time. Without some shared way to categorise and publish that mapping, it becomes very hard to keep any view of the system coherent. Standards in the ERC-8004 direction explicitly aim at on-chain agent/service registries, so if that kind of registry ends up being the decentralised way to publish which addresses are agents/services/facilitators, the adapter could eventually be updated to derive its facilitator set from there (or from an indexer built on top of it) instead of hard-coding addresses. Until that layer is mature and there is a clean on-chain way to discover all relevant facilitators, the hybrid approach (curated facilitators + pure accounting over that subset) might be the practical option.