From a3a336403514ed6b502d193724edfd0966e7effb Mon Sep 17 00:00:00 2001 From: kyoag Date: Thu, 26 Mar 2026 15:43:04 +0900 Subject: [PATCH 1/3] kyoag --- aggregators/kyoag/index.ts | 156 +++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 aggregators/kyoag/index.ts diff --git a/aggregators/kyoag/index.ts b/aggregators/kyoag/index.ts new file mode 100644 index 0000000000..18669e8207 --- /dev/null +++ b/aggregators/kyoag/index.ts @@ -0,0 +1,156 @@ +import { ethers } from "ethers"; +import { FetchOptions, SimpleAdapter } from "../../adapters/types"; +import { CHAIN } from "../../helpers/chains"; + +const LEGACY_ROUTERS: Record = { + [CHAIN.HYPERLIQUID]: [ + "0x5d78854510a2d008E2E21c2fAB4Cc78582B0F2Ce", + "0x91B81ACd2Ee2D8F26518139Af632c56Fad28Dcb4", + ], + [CHAIN.SONEIUM]: [ + "0xD2dFd922fd6bBAe0399F68F99CD444adbd80d255", + "0xf4087AFfE358c1f267Cca84293abB89C4BD10712", + ], +}; + +// Router delegates execution to logic contracts passed in calldata, so keep a small +// manual allowlist of known logic contracts per chain and index inflows to both. +const LOGICS: Record = { + [CHAIN.HYPERLIQUID]: [ + "0x775f533c082a466156bd0e771957853375c96265", + "0xE34E1A6b31D90ED63E1e8EB0640495978b4eB172", + "0xD3027e4869da32d3c295271E4c8d4c4f6C170464", + "0xcb7a9dF8074c2a2c06496b8Bee28372051f3abd7", + "0x69E896668A0dDe9450C8EdD8c1D2Cee2bF99A9a9", + "0xC049cA5Fa95CdE6Fd4ADCeaFaaf331A6bb33C435", + "0xF8A773940Dee10144D5204c44749220e8FFa7b49", + "0x7A34F2C757589825aa795aa98B72F301A8980be0", + "0x0cB398DE0616c8E7b2b737fF715ACcDc888e99ce", + "0x484408554626d420DE940f91786Ffb4913A78000", + "0x734b73B13594638a11f59B35257390738D08543d", + ], + [CHAIN.SONEIUM]: [ + "0xB3f8F67230CbAcD5Adf297BE3F1884A845c9c3C0", + "0xd118e1c57d347D13BF2b14Bd665B74b7B56AF563", + "0xC049cA5Fa95CdE6Fd4ADCeaFaaf331A6bb33C435", + "0x36af20bc2d7F1B3cBd9cADC61395a3c5c56A75D0", + "0x26Dd7F2672C96761280DC2f7Ad9D431e518002e9", + "0xf582CBB0788323FAaCB00a6677caF4890ed19aCF", + "0xaec97346c8d562EccfBa46325d00Af3fFB7246d0", + "0x23120352144E920dbAC60bcDa2d78dE4845A084f", + ], +}; + +const EVENT_ROUTERS: Record = { + [CHAIN.HYPERLIQUID]: ["0x463E176246c4fF727153a8b98381531df1B66b80"], + [CHAIN.SONEIUM]: ["0x206D7FBBD740780D7eFf488D40744276e8dAf077"], + [CHAIN.MONAD]: ["0x852a57ae203fec9c96c7ac9a774db048cbe4e34e"], +}; + +const TRANSFER_TOPIC = + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"; +const DEPOSIT_TOPIC = + "0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c"; +const NATIVE_TOKEN = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; +const SWAP_EXECUTED_EVENT = + "event SwapExecuted(address indexed sender, address indexed srcToken, address indexed dstToken, address logic, uint256 amountIn, uint256 amountOut)"; + +const addVolume = (dailyVolume: ReturnType, token: string, amount: string) => { + if (token.toLowerCase() === NATIVE_TOKEN.toLowerCase()) dailyVolume.addGasToken(amount); + else dailyVolume.add(token, amount); +}; + +const fetch = async ({ createBalances, getLogs, chain }: FetchOptions) => { + const dailyVolume = createBalances(); + const eventRouters = EVENT_ROUTERS[chain] ?? []; + const trackedTargets = [...eventRouters, ...(LEGACY_ROUTERS[chain] ?? []), ...(LOGICS[chain] ?? [])]; + const trackedSet = new Set(trackedTargets.map((r) => r.toLowerCase())); + const allLogs: any[] = []; + const eventTxs = new Set(); + + if (eventRouters.length) { + const eventLogs = await getLogs({ + targets: eventRouters, + eventAbi: SWAP_EXECUTED_EVENT, + }); + + for (const log of eventLogs) { + eventTxs.add(log.transactionHash.toLowerCase()); + addVolume(dailyVolume, log.srcToken, log.amountIn); + } + } + + for (const target of trackedTargets) { + const padded = ethers.zeroPadValue(target, 32); + + const [transferLogs, depositLogs] = await Promise.all([ + getLogs({ + topics: [TRANSFER_TOPIC, null as any, padded], + noTarget: true, + eventAbi: + "event Transfer(address indexed from, address indexed to, uint256 value)", + entireLog: true, + }), + getLogs({ + topics: [DEPOSIT_TOPIC, padded], + noTarget: true, + eventAbi: "event Deposit(address indexed dst, uint256 wad)", + entireLog: true, + }), + ]); + + for (const log of transferLogs) { + if (log.data === "0x") continue; + if (eventTxs.has(log.transactionHash.toLowerCase())) continue; + // Exclude transfers from other tracked contracts (router/logic internal routing) + const from = "0x" + log.topics[1].slice(26).toLowerCase(); + if (trackedSet.has(from)) continue; + allLogs.push(log); + } + + for (const log of depositLogs) { + if (log.data === "0x") continue; + if (eventTxs.has(log.transactionHash.toLowerCase())) continue; + allLogs.push(log); + } + } + + // Per transaction: keep only the first inbound transfer/deposit (lowest log index). + // This is the sell-token inflow before DEX routing, internal hops, or fee payouts. + const firstByTx: Record = {}; + for (const log of allLogs) { + const txHash = log.transactionHash.toLowerCase(); + const idx = log.logIndex ?? log.index ?? 0; + const prev = firstByTx[txHash]; + if (!prev || idx < (prev.logIndex ?? prev.index ?? 0)) { + firstByTx[txHash] = log; + } + } + + for (const log of Object.values(firstByTx)) { + addVolume(dailyVolume, log.address, log.data); + } + + return { dailyVolume }; +}; + +const methodology = { + Volume: + "New KYO AG routers are indexed from SwapExecuted events using the srcToken " + + "amountIn side when present. Routers or methods that do not emit a swap event " + + "(including legacy routers and swapMulti flows) are indexed via the earliest " + + "inbound ERC-20 Transfer or wrapped-native Deposit to the router or allowlisted " + + "logic contract; internal hops and fee payouts are excluded.", +}; + +const adapter: SimpleAdapter = { + version: 2, + adapter: { + [CHAIN.HYPERLIQUID]: { fetch, start: "2026-02-03" }, + [CHAIN.MONAD]: { fetch, start: "2026-03-23" }, + [CHAIN.SONEIUM]: { fetch, start: "2025-12-30" }, + }, + methodology, +}; + +export default adapter; From a3c38a3e9644fffeec95ca4d0e5cd98f5e25b4b0 Mon Sep 17 00:00:00 2001 From: kyoag Date: Fri, 27 Mar 2026 10:30:00 +0900 Subject: [PATCH 2/3] fix --- aggregators/kyoag/index.ts | 52 ++++++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/aggregators/kyoag/index.ts b/aggregators/kyoag/index.ts index 18669e8207..1a3e432995 100644 --- a/aggregators/kyoag/index.ts +++ b/aggregators/kyoag/index.ts @@ -1,6 +1,7 @@ import { ethers } from "ethers"; import { FetchOptions, SimpleAdapter } from "../../adapters/types"; import { CHAIN } from "../../helpers/chains"; +import ADDRESSES from "../../helpers/coreAssets.json"; const LEGACY_ROUTERS: Record = { [CHAIN.HYPERLIQUID]: [ @@ -55,7 +56,27 @@ const NATIVE_TOKEN = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; const SWAP_EXECUTED_EVENT = "event SwapExecuted(address indexed sender, address indexed srcToken, address indexed dstToken, address logic, uint256 amountIn, uint256 amountOut)"; -const addVolume = (dailyVolume: ReturnType, token: string, amount: string) => { +const WRAPPED_NATIVE_TOKENS: Record = { + [CHAIN.HYPERLIQUID]: [ADDRESSES.hyperliquid.WHYPE], + [CHAIN.SONEIUM]: [ADDRESSES.soneium.WETH], + [CHAIN.MONAD]: [ADDRESSES.monad.WMON, ADDRESSES.monad.WETH], +}; + +const isPositiveAmount = (amount?: string) => { + if (!amount || amount === "0x") return false; + try { + return BigInt(amount) > 0n; + } catch { + return false; + } +}; + +const addVolume = ( + dailyVolume: ReturnType, + token?: string, + amount?: string, +) => { + if (!token || !isPositiveAmount(amount)) return; if (token.toLowerCase() === NATIVE_TOKEN.toLowerCase()) dailyVolume.addGasToken(amount); else dailyVolume.add(token, amount); }; @@ -65,6 +86,7 @@ const fetch = async ({ createBalances, getLogs, chain }: FetchOptions) => { const eventRouters = EVENT_ROUTERS[chain] ?? []; const trackedTargets = [...eventRouters, ...(LEGACY_ROUTERS[chain] ?? []), ...(LOGICS[chain] ?? [])]; const trackedSet = new Set(trackedTargets.map((r) => r.toLowerCase())); + const wrappedNativeSet = new Set((WRAPPED_NATIVE_TOKENS[chain] ?? []).map((a) => a.toLowerCase())); const allLogs: any[] = []; const eventTxs = new Set(); @@ -72,11 +94,17 @@ const fetch = async ({ createBalances, getLogs, chain }: FetchOptions) => { const eventLogs = await getLogs({ targets: eventRouters, eventAbi: SWAP_EXECUTED_EVENT, + onlyArgs: false, }); for (const log of eventLogs) { - eventTxs.add(log.transactionHash.toLowerCase()); - addVolume(dailyVolume, log.srcToken, log.amountIn); + const txHash = (log.transactionHash as string | undefined)?.toLowerCase(); + if (txHash) eventTxs.add(txHash); + addVolume( + dailyVolume, + log.srcToken ?? log.args?.srcToken, + log.amountIn ?? log.args?.amountIn, + ); } } @@ -100,17 +128,24 @@ const fetch = async ({ createBalances, getLogs, chain }: FetchOptions) => { ]); for (const log of transferLogs) { - if (log.data === "0x") continue; - if (eventTxs.has(log.transactionHash.toLowerCase())) continue; + if (!isPositiveAmount(log.data)) continue; + const txHash = (log.transactionHash as string | undefined)?.toLowerCase(); + if (!txHash) continue; + if (eventTxs.has(txHash)) continue; // Exclude transfers from other tracked contracts (router/logic internal routing) + if (!log.topics?.[1]) continue; const from = "0x" + log.topics[1].slice(26).toLowerCase(); if (trackedSet.has(from)) continue; allLogs.push(log); } for (const log of depositLogs) { - if (log.data === "0x") continue; - if (eventTxs.has(log.transactionHash.toLowerCase())) continue; + if (!isPositiveAmount(log.data)) continue; + const txHash = (log.transactionHash as string | undefined)?.toLowerCase(); + if (!txHash) continue; + if (eventTxs.has(txHash)) continue; + const emitter = (log.address as string | undefined)?.toLowerCase(); + if (!emitter || !wrappedNativeSet.has(emitter)) continue; allLogs.push(log); } } @@ -119,7 +154,8 @@ const fetch = async ({ createBalances, getLogs, chain }: FetchOptions) => { // This is the sell-token inflow before DEX routing, internal hops, or fee payouts. const firstByTx: Record = {}; for (const log of allLogs) { - const txHash = log.transactionHash.toLowerCase(); + const txHash = (log.transactionHash as string | undefined)?.toLowerCase(); + if (!txHash) continue; const idx = log.logIndex ?? log.index ?? 0; const prev = firstByTx[txHash]; if (!prev || idx < (prev.logIndex ?? prev.index ?? 0)) { From 1890b8d4e4d47b9252325a05f56c1312c06942f5 Mon Sep 17 00:00:00 2001 From: bheluga Date: Sun, 29 Mar 2026 20:00:00 +0530 Subject: [PATCH 3/3] add pullhourly --- aggregators/kyoag/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/aggregators/kyoag/index.ts b/aggregators/kyoag/index.ts index 1a3e432995..1d079b1279 100644 --- a/aggregators/kyoag/index.ts +++ b/aggregators/kyoag/index.ts @@ -181,6 +181,7 @@ const methodology = { const adapter: SimpleAdapter = { version: 2, + pullHourly: true, adapter: { [CHAIN.HYPERLIQUID]: { fetch, start: "2026-02-03" }, [CHAIN.MONAD]: { fetch, start: "2026-03-23" },