diff --git a/contracts/script/test/7683/analyselogs/arb-to-base.ts b/contracts/script/test/7683/analyselogs/arb-to-base.ts index 5621a81a..16923d09 100644 --- a/contracts/script/test/7683/analyselogs/arb-to-base.ts +++ b/contracts/script/test/7683/analyselogs/arb-to-base.ts @@ -1,89 +1,29 @@ import { ethers } from "ethers"; -import T1ERC7683 from "../../../../artifacts/src/T1ERC7683.sol/T1ERC7683.json"; +import { analyzeERC7683Logs } from "./src/logAnalyzer7683.ts"; -// -------------------- Config -------------------- -const arbProvider = new ethers.JsonRpcProvider("https://arb1.arbitrum.io/rpc"); -const arbErc7683Address = "0x996f3583bd967bba19694733aa7a7623e6d780eb"; +const arbitrumProvider = new ethers.JsonRpcProvider("https://arb1.arbitrum.io/rpc"); +const arbitrumErc7683Address = "0x996f3583bd967bba19694733aa7a7623e6d780eb"; const baseProvider = new ethers.JsonRpcProvider("https://base-rpc.publicnode.com"); -const baseErc7683Address = "0xdbA711a6c1b187479e9a5b33020E5217D0BD5A1f"; - -const arbFromBlock = 418241010; -const arbLatest = 418842847; -// const arbLatest = await arbProvider.getBlockNumber(); - -const baseFromBlock = 40423900; -const baseLatest = 40499026; -// const baseLatest = await baseProvider.getBlockNumber(); - -const step = 10_000; - -const iface = new ethers.Interface(T1ERC7683.abi); - -// Topics -const openTopic0 = iface.getEvent("Open")!.topicHash; -const filledTopic0 = iface.getEvent("Filled")!.topicHash; - -const norm = (x: string) => x.toLowerCase(); - -// -------------------- 1) Collect Opens on Arbitrum -------------------- -const openedByOrderId = new Map(); // orderId -> { orderId, blockNumber, txHash, logIndex, args? } - -for (let start = arbFromBlock; start <= arbLatest; start += step + 1) { - const end = Math.min(arbLatest, start + step); - - const logs = await arbProvider.getLogs({ - address: arbErc7683Address, - fromBlock: start, - toBlock: end, - topics: [openTopic0], - }); - - for (const log of logs) { - const orderId = norm(log.topics[1]!); // indexed orderId - - // Dedup by orderId (keep first seen) - if (!openedByOrderId.has(orderId)) { - openedByOrderId.set(orderId, { - orderId, - blockNumber: log.blockNumber, - txHash: log.transactionHash, - logIndex: log.index, - }); - } - } -} - -console.log(`Searched in Arbitrum blocks from ${arbFromBlock} to ${arbLatest} and found...`); -console.log("Opened unique orderIds on Arbitrum:", openedByOrderId.size); - -// -------------------- 2) Collect Fills on Base -------------------- -const filledOrderIds = new Set(); - -for (let start = baseFromBlock; start <= baseLatest; start += step + 1) { - const end = Math.min(baseLatest, start + step); - - const logs = await baseProvider.getLogs({ - address: baseErc7683Address, - fromBlock: start, - toBlock: end, - topics: [filledTopic0], - }); - - for (const log of logs) { - const orderId = norm(log.topics[1]!); // indexed orderId - filledOrderIds.add(orderId); - } -} - -console.log(`Searched in Base blocks from ${baseFromBlock} to ${baseLatest} and found...`); -console.log("Filled unique orderIds on Base:", filledOrderIds.size); - -// -------------------- 3) Diff: Opened on Arb but NOT Filled on Base -------------------- -const unfilledOpenEvents = []; -for (const [orderId, openEvent] of openedByOrderId.entries()) { - if (!filledOrderIds.has(orderId)) unfilledOpenEvents.push(openEvent); -} - -console.log("Unfilled count:", unfilledOpenEvents.length); -console.log("Unfilled orders:", JSON.stringify(unfilledOpenEvents, null, 2)); +const baseErc7683Address = "0xdbA711a6c1b187479e9a5b33020E5217D0BD5A1f"; + +const arbitrumFromBlock = 417132015; +const arbitrumToBlock = 419554979; +// const sourceChainLatest = await sourceChainProvider.getBlockNumber(); + +const baseFromBlock = 40285370; +const baseToBlock = 40587883; +// const destinationChainLatest = await destinationChainProvider.getBlockNumber(); + +await analyzeERC7683Logs({ + sourceChainName: "Arbitrum", + sourceChainFromBlock: arbitrumFromBlock, + sourceChainLatest: arbitrumToBlock, + sourceChainProvider: arbitrumProvider, + sourceChainErc7683Address: arbitrumErc7683Address, + destinationChainName: "Base", + destinationChainFromBlock: baseFromBlock, + destinationChainLatest: baseToBlock, + destinationChainProvider: baseProvider, + destinationChainErc7683Address: baseErc7683Address, +}) diff --git a/contracts/script/test/7683/analyselogs/base-to-arb.ts b/contracts/script/test/7683/analyselogs/base-to-arb.ts index 0effc291..ea63b68d 100644 --- a/contracts/script/test/7683/analyselogs/base-to-arb.ts +++ b/contracts/script/test/7683/analyselogs/base-to-arb.ts @@ -1,88 +1,29 @@ import { ethers } from "ethers"; -import T1ERC7683 from "../../../../artifacts/src/T1ERC7683.sol/T1ERC7683.json"; +import { analyzeERC7683Logs } from "./src/logAnalyzer7683.ts"; -// -------------------- Config -------------------- const baseProvider = new ethers.JsonRpcProvider("https://base-rpc.publicnode.com"); -const baseErc7683Address = "0xdbA711a6c1b187479e9a5b33020E5217D0BD5A1f"; - -const arbProvider = new ethers.JsonRpcProvider("https://arb1.arbitrum.io/rpc"); -const arbErc7683Address = "0x996f3583bd967bba19694733aa7a7623e6d780eb"; - -// Your ranges (edit as needed) -const baseFromBlock = 40423900; -const baseLatest = 40499026; -// const baseLatest = await baseProvider.getBlockNumber(); - -const arbFromBlock = 418241010; -const arbLatest = 418842847; -// const arbLatest = await arbProvider.getBlockNumber(); - -const step = 10_000; - -const iface = new ethers.Interface(T1ERC7683.abi); - -const openTopic0 = iface.getEvent("Open")!.topicHash; -const filledTopic0 = iface.getEvent("Filled")!.topicHash; - -const norm = (x: string) => x.toLowerCase(); - -// -------------------- 1) Collect Opens on Base -------------------- -const openedByOrderId = new Map(); // orderId -> { orderId, blockNumber, txHash, logIndex } - -for (let start = baseFromBlock; start <= baseLatest; start += step + 1) { - const end = Math.min(baseLatest, start + step); - - const logs = await baseProvider.getLogs({ - address: baseErc7683Address, - fromBlock: start, - toBlock: end, - topics: [openTopic0], - }); - - for (const log of logs) { - const orderId = norm(log.topics[1]!); // indexed orderId - - if (!openedByOrderId.has(orderId)) { - openedByOrderId.set(orderId, { - orderId, - blockNumber: log.blockNumber, - txHash: log.transactionHash, - logIndex: log.index, - }); - } - } -} - -console.log(`Searched in Base blocks from ${baseFromBlock} to ${baseLatest} and found...`); -console.log("Opened unique orderIds on Base:", openedByOrderId.size); - -// -------------------- 2) Collect Fills on Arbitrum -------------------- -const filledOrderIds = new Set(); - -for (let start = arbFromBlock; start <= arbLatest; start += step + 1) { - const end = Math.min(arbLatest, start + step); - - const logs = await arbProvider.getLogs({ - address: arbErc7683Address, - fromBlock: start, - toBlock: end, - topics: [filledTopic0], - }); - - for (const log of logs) { - const orderId = norm(log.topics[1]!); // indexed orderId - filledOrderIds.add(orderId); - } -} - -console.log(`Searched in Arbitrum blocks from ${arbFromBlock} to ${arbLatest} and found...`); -console.log("Filled unique orderIds on Arbitrum:", filledOrderIds.size); - -// -------------------- 3) Diff: Opened on Base but NOT Filled on Arbitrum -------------------- -const unfilledOpenEvents = []; -for (const [orderId, openEvent] of openedByOrderId.entries()) { - if (!filledOrderIds.has(orderId)) unfilledOpenEvents.push(openEvent); -} - -console.log("Unfilled count:", unfilledOpenEvents.length); -console.log("Unfilled orders:", JSON.stringify(unfilledOpenEvents, null, 2)); +const baseErc7683Address = "0xdbA711a6c1b187479e9a5b33020E5217D0BD5A1f"; + +const destinationChainProvider = new ethers.JsonRpcProvider("https://arb1.arbitrum.io/rpc"); +const destinationChainErc7683Address = "0x996f3583bd967bba19694733aa7a7623e6d780eb"; + +const baseFromBlock = 40285370; +const baseToBlock = 40587883; +// const baseToBlock = await baseProvider.getBlockNumber(); + +const arbitrumFromBlock = 417132015; +const arbitrumToBlock = 419554979; +// const arbitrumToBlock = await destinationChainProvider.getBlockNumber(); + +await analyzeERC7683Logs({ + sourceChainName: "Base", + sourceChainFromBlock: baseFromBlock, + sourceChainLatest: baseToBlock, + sourceChainProvider: baseProvider, + sourceChainErc7683Address: baseErc7683Address, + destinationChainName: "Arbitrum", + destinationChainFromBlock: arbitrumFromBlock, + destinationChainLatest: arbitrumToBlock, + destinationChainProvider, + destinationChainErc7683Address, +}) diff --git a/contracts/script/test/7683/analyselogs/src/logAnalyzer7683.ts b/contracts/script/test/7683/analyselogs/src/logAnalyzer7683.ts new file mode 100644 index 00000000..75959df1 --- /dev/null +++ b/contracts/script/test/7683/analyselogs/src/logAnalyzer7683.ts @@ -0,0 +1,300 @@ +import { ethers, JsonRpcProvider } from "ethers"; +import T1ERC7683 from "../../../../../artifacts/src/T1ERC7683.sol/T1ERC7683.json"; + +const step = 10_000; + +const iface = new ethers.Interface(T1ERC7683.abi); + +// Topics +const openTopic0 = iface.getEvent("Open")!.topicHash; +const filledTopic0 = iface.getEvent("Filled")!.topicHash; +const svrTopic0 = iface.getEvent("SettlementVerificationRequested")!.topicHash; +const settledTopic0 = iface.getEvent("Settled")!.topicHash; + +const norm = (x: string) => x.toLowerCase(); + +type OpenEntry = { + orderId: string; + blockNumber: number; + blockTimestamp: number; + txHash: string; + logIndex: number; +}; + +type FilledEntry = { + orderId: string; + blockNumber: number; + blockTimestamp: number; + txHash: string; + logIndex: number; +}; + +type RepaymentStartEntry = { + orderId: string; + blockNumber: number; + blockTimestamp: number; + txHash: string; + logIndex: number; +}; + +type RepaymentEndEntry = { + orderId: string; + blockNumber: number; + blockTimestamp: number; + txHash: string; + logIndex: number; +}; + +type AnalyzeBlockParams = { + sourceChainName: string; + sourceChainFromBlock: number; + sourceChainLatest: number; + sourceChainProvider: JsonRpcProvider; + sourceChainErc7683Address: `0x${string}`; + destinationChainName: string; + destinationChainFromBlock: number; + destinationChainLatest: number; + destinationChainProvider: JsonRpcProvider; + destinationChainErc7683Address: `0x${string}`; +}; + +export const analyzeERC7683Logs = async (params: AnalyzeBlockParams) => { + // -------------------- 1) Collect Opens on Source Chain -------------------- + const openedByOrderId = new Map(); // orderId -> OpenEntry + + + console.log(`Searching in ${params.sourceChainName} blocks from [${params.sourceChainFromBlock}] to [${params.sourceChainLatest}] ...`); + + for (let start = params.sourceChainFromBlock; start <= params.sourceChainLatest; start += step + 1) { + const end = Math.min(params.sourceChainLatest, start + step); + + const logs = await params.sourceChainProvider.getLogs({ + address: params.sourceChainErc7683Address, + fromBlock: start, + toBlock: end, + topics: [openTopic0], + }); + + for (const log of logs) { + const orderId = norm(log.topics[1]!); // indexed orderId + + // Dedup by orderId (keep first seen) + if (!openedByOrderId.has(orderId)) { + const blk = await params.sourceChainProvider.getBlock(log.blockNumber); + const blockTimestamp = Number(blk?.timestamp ?? 0); + + openedByOrderId.set(orderId, { + orderId, + blockNumber: log.blockNumber, + blockTimestamp, + txHash: log.transactionHash, + logIndex: log.index, + }); + } + } + } + + console.log(`... found [${openedByOrderId.size}] Opened unique orderIds on ${params.sourceChainName}`, ); + +// -------------------- 2) Collect Fills on Destination Chain -------------------- + const filledByOrderId = new Map(); // orderId -> FilledEntry + + + console.log(`Searching in ${params.destinationChainName} blocks from [${params.destinationChainFromBlock}] to [${params.destinationChainLatest}] ...`); + + for (let start = params.destinationChainFromBlock; start <= params.destinationChainLatest; start += step + 1) { + const end = Math.min(params.destinationChainLatest, start + step); + + const logs = await params.destinationChainProvider.getLogs({ + address: params.destinationChainErc7683Address, + fromBlock: start, + toBlock: end, + topics: [filledTopic0], + }); + + for (const log of logs) { + const orderId = norm(log.topics[1]!); // indexed orderId + + // Dedup by orderId (keep first seen) + if (!filledByOrderId.has(orderId)) { + const blk = await params.destinationChainProvider.getBlock(log.blockNumber); + const blockTimestamp = Number(blk?.timestamp ?? 0); + + filledByOrderId.set(orderId, { + orderId, + blockNumber: log.blockNumber, + blockTimestamp, + txHash: log.transactionHash, + logIndex: log.index, + }); + } + } + } + + console.log(`... found [${filledByOrderId.size}] Filled unique orderIds on ${params.destinationChainName}`); + +// -------------------- 3) Diff: Opened on Arb but NOT Filled on Destination Chain -------------------- + const unfilledOpenEvents: OpenEntry[] = []; + for (const [orderId, openEvent] of openedByOrderId.entries()) { + if (!filledByOrderId.has(orderId)) unfilledOpenEvents.push(openEvent); + } + + console.log("Unfilled count:", unfilledOpenEvents.length); + console.log("Unfilled orders:", JSON.stringify(unfilledOpenEvents, null, 2)); + +// -------------------- 4) Fill time stats for matched orders -------------------- + const fillDurationsSec: number[] = []; + const matchedCount = (() => { + let cnt = 0; + for (const [orderId, openEvent] of openedByOrderId.entries()) { + const filled = filledByOrderId.get(orderId); + if (filled) { + const delta = filled.blockTimestamp - openEvent.blockTimestamp; + // Only include non-negative durations + if (Number.isFinite(delta) && delta >= 0) { + fillDurationsSec.push(delta); + cnt++; + } + } + } + return cnt; + })(); + + if (matchedCount === 0) { + console.log("No matched open->fill pairs within the specified ranges. Skipping stats."); + } else { + let fastest = Infinity; + let slowest = -Infinity; + let sum = 0; + + for (const d of fillDurationsSec) { + if (d < fastest) fastest = d; + if (d > slowest) slowest = d; + sum += d; + } + const avg = sum / fillDurationsSec.length; + + console.log("Matched Open->Filled pairs:", matchedCount); + console.log("Fastest fill time (s):", fastest); + console.log("Slowest fill time (s):", slowest); + console.log("Average fill time (s):", avg); + } + +// -------------------- 5) Solver repayment time on Source Chain -------------------- +// Start: SettlementVerificationRequested(orderId, requestId) +// End: Settled(orderId, settlementReceiver) + const repayStartByOrderId = new Map(); + const repayEndByOrderId = new Map(); + +// 5.a) Collect SettlementVerificationRequested (start) on Source Chain + + console.log(`Searching in ${params.sourceChainName} blocks from [${params.sourceChainFromBlock}] to [${params.sourceChainLatest}] ...`); + + for (let start = params.sourceChainFromBlock; start <= params.sourceChainLatest; start += step + 1) { + const end = Math.min(params.sourceChainLatest, start + step); + + const logs = await params.sourceChainProvider.getLogs({ + address: params.sourceChainErc7683Address, + fromBlock: start, + toBlock: end, + topics: [svrTopic0], + }); + + for (const log of logs) { + const orderId = norm(log.topics[1]!); // indexed orderId + if (!repayStartByOrderId.has(orderId)) { + const blk = await params.sourceChainProvider.getBlock(log.blockNumber); + const blockTimestamp = Number(blk?.timestamp ?? 0); + + repayStartByOrderId.set(orderId, { + orderId, + blockNumber: log.blockNumber, + blockTimestamp, + txHash: log.transactionHash, + logIndex: log.index, + }); + } + } + } + + console.log(`... found [${repayStartByOrderId.size}] SettlementVerificationRequested unique orderIds on ${params.sourceChainName}`); + +// 5.b) Collect Settled (end) on Source Chain + + console.log(`Searching in ${params.sourceChainName} blocks from [${params.sourceChainFromBlock}] to [${params.sourceChainLatest}] ...`); + + for (let start = params.sourceChainFromBlock; start <= params.sourceChainLatest; start += step + 1) { + const end = Math.min(params.sourceChainLatest, start + step); + + const logs = await params.sourceChainProvider.getLogs({ + address: params.sourceChainErc7683Address, + fromBlock: start, + toBlock: end, + topics: [settledTopic0], + }); + + for (const log of logs) { + const orderId = norm(log.topics[1]!); // indexed orderId + if (!repayEndByOrderId.has(orderId)) { + const blk = await params.sourceChainProvider.getBlock(log.blockNumber); + const blockTimestamp = Number(blk?.timestamp ?? 0); + + repayEndByOrderId.set(orderId, { + orderId, + blockNumber: log.blockNumber, + blockTimestamp, + txHash: log.transactionHash, + logIndex: log.index, + }); + } + } + } + + console.log(`... found [${repayEndByOrderId.size}] Settled unique orderIds on ${params.sourceChainName}`); + +// 5.c) Repayment time stats (start->end) per orderId + const repaymentDurationsSec: number[] = []; + let repayMatchedCount = 0; + + for (const [orderId, startEntry] of repayStartByOrderId.entries()) { + const endEntry = repayEndByOrderId.get(orderId); + if (endEntry) { + const delta = endEntry.blockTimestamp - startEntry.blockTimestamp; + if (Number.isFinite(delta) && delta >= 0) { + repaymentDurationsSec.push(delta); + repayMatchedCount++; + } + } + } + + if (repayMatchedCount === 0) { + console.log(`No matched SettlementVerificationRequested -> Settled pairs within the specified ${params.sourceChainName} range. Skipping repayment stats.`); + } else { + let fastest = Infinity; + let slowest = -Infinity; + let sum = 0; + + for (const d of repaymentDurationsSec) { + if (d < fastest) fastest = d; + if (d > slowest) slowest = d; + sum += d; + } + const avg = sum / repaymentDurationsSec.length; + + console.log("Matched repayment pairs (verification->settled):", repayMatchedCount); + console.log("Fastest solver repayment time (s):", fastest); + console.log("Slowest solver repayment time (s):", slowest); + console.log("Average solver repayment time (s):", avg); + } + +// -------------------- 5.d) Unsettled SettlementVerificationRequested (no Settled) -------------------- + const unsettledRepayStarts: { orderId: string; txHash: string }[] = []; + for (const [orderId, startEntry] of repayStartByOrderId.entries()) { + if (!repayEndByOrderId.has(orderId)) { + unsettledRepayStarts.push({ orderId, txHash: startEntry.txHash }); + } + } + + console.log("Unsettled SettlementVerificationRequested count:", unsettledRepayStarts.length); + console.log("Unsettled SettlementVerificationRequested:", JSON.stringify(unsettledRepayStarts, null, 2)); +}