diff --git a/package-lock.json b/package-lock.json index 9784f4fc..1a9bf2d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -982,9 +982,9 @@ } }, "node_modules/@defillama/sdk": { - "version": "5.0.80", - "resolved": "https://registry.npmjs.org/@defillama/sdk/-/sdk-5.0.80.tgz", - "integrity": "sha512-XG1tm90rWmTnv55Du6cUHvSI3XVCSbVVjAHcDh9f2hOHuoKezx1zTjBrDUirYQ4yCCe2zl2g4QmUgD6JR96ibA==", + "version": "5.0.92", + "resolved": "https://registry.npmjs.org/@defillama/sdk/-/sdk-5.0.92.tgz", + "integrity": "sha512-t6A4qSX9y1VPIaiVE7/O1UPrkDmljjiGv8DrtKZuzuCpwMsL2uMDNIn5a8WLnQizft8mUV3YY4aojx6WC7L7Sw==", "dependencies": { "@aws-sdk/client-s3": "^3.400.0", "@elastic/elasticsearch": "^8.13.1", diff --git a/src/adapters/allbridge-classic/index.ts b/src/adapters/allbridge-classic/index.ts index bdc42a03..24c26131 100644 --- a/src/adapters/allbridge-classic/index.ts +++ b/src/adapters/allbridge-classic/index.ts @@ -1,7 +1,5 @@ -import { Chain } from "@defillama/sdk/build/general"; -import { BridgeAdapter, PartialContractEventParams } from "../../helpers/bridgeAdapter.type"; -import { constructTransferParams } from "../../helpers/eventParams"; -import { getTxDataFromEVMEventLogs } from "../../helpers/processTransactions"; +import { ContractEventParamsV2, } from "../../helpers/bridgeAdapter.type"; +import { processEVMLogsExport } from "../../helpers/processTransactions"; const bridgeAddresses = { ethereum: "0xBBbD1BbB4f9b936C3604906D7592A644071dE884", @@ -11,46 +9,32 @@ const bridgeAddresses = { polygon: "0xBBbD1BbB4f9b936C3604906D7592A644071dE884", } as { [chain: string]: string }; -const constructParams = (chain: string) => { - let eventParams = [] as PartialContractEventParams[]; - const bridgeAddress = bridgeAddresses[chain]; - const depositParams = constructTransferParams(bridgeAddress, true, { - excludeFrom: [bridgeAddress], - }); - // const withdrawParams = constructTransferParams(bridgeAddress, false, { - // excludeTo: [bridgeAddress], - // }); - const withdrawParams = { +const adapter = {} as any + +Object.entries(bridgeAddresses).forEach(([chain, bridgeAddress]) => { + const withdrawParams: ContractEventParamsV2 = { target: bridgeAddress, - topic: "Received(address,address,uint256,uint128,bytes4)", - abi: [ - "event Received(address indexed recipient, address token, uint256 amount, uint128 indexed lockId, bytes4 source)", - ], - logKeys: { - blockNumber: "blockNumber", - txHash: "transactionHash", - }, - argKeys: { - amount: "amount", - token: "token", - to: "recipient", - }, + abi: "event Received(address indexed to, address token, uint256 amount, uint128 indexed lockId, bytes4 source)", fixedEventData: { from: bridgeAddress, }, isDeposit: false, }; - eventParams.push(depositParams, withdrawParams); - return async (fromBlock: number, toBlock: number) => - getTxDataFromEVMEventLogs("allbridge-classic", chain as Chain, fromBlock, toBlock, eventParams); -}; + const depositParams: ContractEventParamsV2 = { + target: bridgeAddress, + abi: "event Sent (bytes4 tokenSource, bytes32 tokenSourceAddress, address from, bytes32 indexed recipient, uint256 amount, uint128 indexed lockId, bytes4 destination)", + fixedEventData: { + to: bridgeAddress, + }, + isDeposit: true, + transformLog: (log: any) => { + log.token = '0x'+log.args.tokenSourceAddress.slice(0, 40) + return log; + } + }; + + adapter[chain] = processEVMLogsExport([withdrawParams, depositParams]); +}) -const adapter: BridgeAdapter = { - ethereum: constructParams("ethereum"), - polygon: constructParams("polygon"), - fantom: constructParams("fantom"), - avalanche: constructParams("avax"), - bsc: constructParams("bsc"), -}; export default adapter; diff --git a/src/adapters/allbridge-core/eventParsing.ts b/src/adapters/allbridge-core/eventParsing.ts deleted file mode 100644 index d29da3c4..00000000 --- a/src/adapters/allbridge-core/eventParsing.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { EventData } from '../../utils/types'; -import { getTronLogs, tronGetTimestampByBlockNumber } from '../../helpers/tron'; -import { get } from 'lodash'; -import { PromisePool } from '@supercharge/promise-pool'; - -const EventKeyTypes = { - blockNumber: "number", - txHash: "string", - from: "string", - to: "string", - token: "string", - amount: "object", -} as { - [key: string]: string; -}; - -export const getTxDataFromTronEventLogs = async ( - adapterName: string, - fromBlock: number, - toBlock: number, - paramsArray: any, -): Promise => { - const from = await tronGetTimestampByBlockNumber(fromBlock); - const to = await tronGetTimestampByBlockNumber(toBlock); - - let accEventData = [] as EventData[]; - const getLogsPromises = Promise.all( - paramsArray.map(async (params: any) => { - let { - target, - topic, - abi, - logKeys, - argKeys, - txKeys, - topics, - isDeposit, - chain, - isTransfer, - fixedEventData, - inputDataExtraction, - selectIndexesFromArrays, - functionSignatureFilter, - filter, - mapTokens, - getTokenFromReceipt, - argGetters, - eventName, - } = params; - - let data = {} as any; - let logs = [] as any[]; - for (let i = 0; i < 5; i++) { - try { - logs = await getTronLogs(target, eventName, from, to); - if (logs.length === 0) { - console.info(`No logs received for ${adapterName} from ${fromBlock} to ${toBlock} with event ${eventName} (${isDeposit ? 'Deposit' : 'Withdrawal'}) for ${target}.`); - } - break; - } catch (e) { - if (i >= 4) { - console.error(target, e); - } else { - continue; - } - } - } - - const { results, errors } = await PromisePool.withConcurrency(20) - .for(logs) - .process(async (txLog: any, i) => { - data[i] = data[i] || {}; - data[i]["isDeposit"] = isDeposit; - Object.entries(logKeys!).map(([eventKey, logKey]) => { - const value = txLog[logKey as string]; - if (typeof value !== EventKeyTypes[eventKey]) { - throw new Error( - `Type of ${eventKey} retrieved using ${logKey} is ${typeof value} when it must be ${ - EventKeyTypes[eventKey] - }.` - ); - } - data[i][eventKey] = value; - }); - if (argKeys) { - try { - const args = txLog?.result; - if (args === undefined || args.length === 0) { - throw new Error( - `Unable to get log args for ${adapterName} with arg keys ${argKeys} tron.` - ); - } - Object.entries(argKeys).map(([eventKey, argKey]) => { - // @ts-ignore - const value = argGetters?.[eventKey]?.(args) || get(args, argKey); - if (typeof value !== EventKeyTypes[eventKey] && !Array.isArray(value)) { - throw new Error( - `Type of ${eventKey} retrieved using ${argKey} is ${typeof value} when it must be ${ - EventKeyTypes[eventKey] - }.` - ); - } - data[i][eventKey] = value; - }); - } catch (error: any) { - console.error( - `Unable to get log args for ${adapterName} with arg keys ${argKeys}. SKIPPING TX with hash ${txLog.transaction_id} tron - Error: ${error?.message} - ` - ); - return; - } - } - - if (fixedEventData) { - Object.entries(fixedEventData).map(([eventKey, value]) => { - if (typeof value !== EventKeyTypes[eventKey]) { - throw new Error( - `Type of ${eventKey} in fixedEventData is ${typeof value} when it must be ${EventKeyTypes[eventKey]}.` - ); - } - data[i][eventKey] = value; - }); - } - }); - - if (errors.length > 0) { - console.error("Errors in getTxDataFromTronEventLogs", errors); - } - - const eventData = Object.values(data) as EventData[]; - accEventData.push(...eventData); - }) - ); - await getLogsPromises; - - return accEventData; -}; diff --git a/src/adapters/allbridge-core/index.ts b/src/adapters/allbridge-core/index.ts index d952f499..43928210 100644 --- a/src/adapters/allbridge-core/index.ts +++ b/src/adapters/allbridge-core/index.ts @@ -1,14 +1,10 @@ import axios, { AxiosResponse } from 'axios'; import { BigNumber } from 'ethers'; -import { BridgeAdapter, PartialContractEventParams } from "../../helpers/bridgeAdapter.type"; -import { Chain } from "@defillama/sdk/build/general"; +import { BridgeAdapter, ContractEventParamsV2, } from "../../helpers/bridgeAdapter.type"; import { EventData } from '../../utils/types'; -import { fromHex } from 'tron-format-address'; import { getConnection } from '../../helpers/solana'; -import { getTxDataFromEVMEventLogs } from "../../helpers/processTransactions"; -import { getTxDataFromTronEventLogs } from './eventParsing'; +import { processEVMLogsExport } from "../../helpers/processTransactions"; -const adapterName = "allbridge-core"; const lpAddresses = { bsc: [ @@ -50,55 +46,30 @@ const lpAddresses = { }; const constructParams = (chain: string) => { - let eventParams = [] as PartialContractEventParams[]; + let eventParams = [] as ContractEventParamsV2[]; const lps = lpAddresses[chain]; - for (const lpAddress of Object.values(lps)) { - const depositParams: PartialContractEventParams = { + for (const lpAddress of lps) { + const depositParams: ContractEventParamsV2 = { target: lpAddress, - topic: "SwappedToVUsd(address,address,uint256,uint256,uint256)", - abi: [ - "event SwappedToVUsd(address sender, address token, uint amount, uint vUsdAmount, uint fee)" - ], - logKeys: { - blockNumber: "blockNumber", - txHash: "transactionHash" - }, - argKeys: { - from: "sender", - token: "token", - amount: "amount" - }, + abi: "event SwappedToVUsd(address from, address token, uint amount, uint vUsdAmount, uint fee)", fixedEventData: { to: lpAddress, }, isDeposit: true }; - const withdrawParams = { + const withdrawParams: ContractEventParamsV2 = { target: lpAddress, - topic: "SwappedFromVUsd(address,address,uint256,uint256,uint256)", - abi: [ - "event SwappedFromVUsd(address recipient, address token, uint256 vUsdAmount, uint256 amount, uint256 fee)", - ], - logKeys: { - blockNumber: "blockNumber", - txHash: "transactionHash", - }, - argKeys: { - amount: "amount", - token: "token", - to: "recipient", - }, + abi: "event SwappedFromVUsd(address to, address token, uint256 vUsdAmount, uint256 amount, uint256 fee)", fixedEventData: { from: lpAddress, }, isDeposit: false, }; - eventParams.push(depositParams, withdrawParams); + eventParams.push(depositParams, withdrawParams) } - return async (fromBlock: number, toBlock: number) => - getTxDataFromEVMEventLogs(adapterName, chain as Chain, fromBlock, toBlock, eventParams); + return processEVMLogsExport(eventParams) }; interface SolanaEvent { @@ -148,61 +119,6 @@ const getSolanaEvents = async (fromSlot: number, toSlot: number): Promise BigNumber.from(log.amount), - from: (log: any) => fromHex(log.sender), - token: (log: any) => fromHex(log.token), - }, - fixedEventData: { - to: lpAddress, - }, - isDeposit: true, - } - const withdrawParams = { - target: lpAddress, - eventName: "SwappedFromVUsd", - logKeys: { - blockNumber: "block_number", - txHash: "transaction_id", - }, - argKeys: { - amount: "amount", - token: "token", - to: "recipient", - }, - argGetters: { - amount: (log: any) => BigNumber.from(log.amount), - to: (log: any) => fromHex(log.recipient), - token: (log: any) => fromHex(log.token), - }, - fixedEventData: { - from: lpAddress, - }, - isDeposit: false, - } - eventParams.push(depositParams, withdrawParams); - } - return async (fromBlock: number, toBlock: number) => - getTxDataFromTronEventLogs(adapterName, fromBlock, toBlock, eventParams); -} - const adapter: BridgeAdapter = { ethereum: constructParams("ethereum"), polygon: constructParams("polygon"), diff --git a/src/adapters/base/index.ts b/src/adapters/base/index.ts index 23ed4fb0..713e10af 100644 --- a/src/adapters/base/index.ts +++ b/src/adapters/base/index.ts @@ -1,93 +1,53 @@ -import { BridgeAdapter, PartialContractEventParams } from "../../helpers/bridgeAdapter.type"; -import { getTxDataFromEVMEventLogs } from "../../helpers/processTransactions"; -import { getTxsBlockRangeEtherscan } from "../../helpers/etherscan"; +import { ContractEventParamsV2, } from "../../helpers/bridgeAdapter.type"; +import { processEVMLogsExport, } from "../../helpers/processTransactions"; const WETH = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"; const BASE_PORTAL = "0x49048044D57e1C92A77f79988d21Fa8fAF74E97e"; const BASE_BRIDGE = "0x3154Cf16ccdb4C6d922629664174b904d80F2C35"; -const erc20DepositParams: PartialContractEventParams = { +const erc20DepositParams: ContractEventParamsV2 = { target: BASE_BRIDGE, - topic: "ERC20BridgeInitiated(address,address,address,address,uint256,bytes)", - abi: [ - "event ERC20BridgeInitiated(address indexed localToken, address indexed remoteToken, address indexed from, address to, uint256 amount, bytes extraData)", - ], - logKeys: { - blockNumber: "blockNumber", - txHash: "transactionHash", - }, + abi: "event ERC20BridgeInitiated(address indexed localToken, address indexed remoteToken, address indexed from, address to, uint256 amount, bytes extraData)", + isDeposit: true, argKeys: { - from: "from", - amount: "amount", token: "localToken", }, - fixedEventData: { - to: BASE_BRIDGE, - }, - isDeposit: true, -}; +} -const erc20WithdrawParams: PartialContractEventParams = { +const erc20WithdrawalParams: ContractEventParamsV2 = { target: BASE_BRIDGE, - topic: "ERC20WithdrawalFinalized(address,address,address,address,uint256,bytes)", - abi: [ - "event ERC20WithdrawalFinalized(address indexed l1Token, address indexed l2Token, address indexed from, address to, uint256 amount, bytes extraData)", - ], - logKeys: { - blockNumber: "blockNumber", - txHash: "transactionHash", - }, + abi: "event ERC20WithdrawalFinalized(address indexed l1Token, address indexed l2Token, address indexed from, address to, uint256 amount, bytes extraData)", + isDeposit: false, argKeys: { - to: "to", - amount: "amount", token: "l1Token", }, +} + +const ethWithdrawalParams: ContractEventParamsV2 = { + target: BASE_PORTAL, + abi: "event ETHWithdrawalFinalized(address indexed from, address indexed to, uint256 amount, bytes extraData)", + isDeposit: false, fixedEventData: { - from: BASE_BRIDGE, + from: BASE_PORTAL, + token: WETH, }, - isDeposit: false, -}; - -const constructParams = () => { - const eventParams = [erc20WithdrawParams, erc20DepositParams]; - return async (fromBlock: number, toBlock: number) => { - const eventLogsRes = await getTxDataFromEVMEventLogs("base", "ethereum", fromBlock, toBlock, eventParams); - const txs = await getTxsBlockRangeEtherscan("ethereum", BASE_PORTAL, fromBlock, toBlock); - const depositEvents = txs - .filter((tx: any) => tx?.methodId === "0xe9e05c42") - .map((tx: any) => { - const event = { - txHash: tx.hash, - blockNumber: +tx.blockNumber, - from: tx.from, - to: tx.to, - token: WETH, - amount: tx.value, - isDeposit: true, - }; - return event; - }); +} - const withdrawalEvents = txs - .filter((tx: any) => tx?.methodId === "0x8c3152e9") - .map((tx: any) => { - const event = { - txHash: tx.hash, - blockNumber: +tx.blockNumber, - from: BASE_PORTAL, - to: tx.to, - token: WETH, - amount: tx.value, - isDeposit: false, - }; - return event; - }); - return [...eventLogsRes, ...depositEvents, ...withdrawalEvents]; - }; -}; +const ethDepositParams: ContractEventParamsV2 = { + target: BASE_PORTAL, + abi: "event TransactionDeposited (address indexed from, address indexed to, uint256 indexed version, bytes opaqueData)", + isDeposit: true, + fixedEventData: { + token: WETH, + to: BASE_PORTAL, + }, + filter: (i: any) => i.args.opaqueData.length === 148, // only these transactions are for eth deposits + transformLog: (log: any) => { + log.amount = Number('0x'+log.args.opaqueData.slice(2,66)).toString() + return log; + }, +} -const adapter: BridgeAdapter = { - ethereum: constructParams(), +export default { + ethereum: processEVMLogsExport([erc20DepositParams, erc20WithdrawalParams, ethWithdrawalParams, ethDepositParams]), }; - -export default adapter; diff --git a/src/adapters/test.ts b/src/adapters/test.ts index 6406599c..715a2175 100644 --- a/src/adapters/test.ts +++ b/src/adapters/test.ts @@ -4,6 +4,7 @@ import adapters from "./"; import { importBridgeNetwork } from "../data/importBridgeNetwork"; import { getLlamaPrices } from "../utils/prices"; import { transformTokens } from "../helpers/tokenMappings"; +import { runAdapter } from "../utils/runAdapter"; const logTypes = { txHash: "string", @@ -54,17 +55,18 @@ const testAdapter = async () => { return; } - const { number, timestamp } = block; + let { number, timestamp } = block; if (!(number && timestamp)) { console.error(`Missing block number or timestamp for chain ${contractsChain}.`); return; } + number = number - 2000 // 2000 blocks behind to let indexer catch up const startBlock = number - parseInt(numberOfBlocks); console.log(`Getting event logs on chain ${contractsChain} from block ${startBlock} to ${number}.`); - const eventLogs = await adapterChainEventsFn(startBlock, number); + const eventLogs = await runAdapter({ fromBlock: startBlock, toBlock: number, adapterChainEventsFn, chain }); // console.log(eventLogs) console.log(`Found ${eventLogs.length} event logs on chain ${contractsChain}.`); - for (const log of eventLogs) { + for (const log of eventLogs.slice(0, 12)) { console.log(`[${contractsChain}] ${log.isDeposit ? "Deposit" : "Withdrawal"} ${log.txHash}`); console.log(`From: ${log.from} To: ${log.to} `); } @@ -73,7 +75,7 @@ const testAdapter = async () => { eventLogs.map(async (log: any) => { ["txHash", "blockNumber", "from", "to", "token", "isDeposit"].map((key) => { if (!(log[key] !== null && typeof log[key] === logTypes[key])) { - console.log("Yes it is missing", key); + console.log("Yes it is missing", key, log.txHash); throw new Error( `${key} is missing, null, or wrong type in log. It is of type ${typeof log[ key @@ -85,9 +87,9 @@ const testAdapter = async () => { const { amount, isUSDVolume } = log; if (!isUSDVolume) { - if (!amount._isBigNumber) { - throw new Error(`Amount type ${typeof amount} and should be of type BigNumber.`); - } + // if (!amount._isBigNumber) { + // throw new Error(`Amount type ${typeof amount} and should be of type BigNumber.`); + // } const token = log.token.toLowerCase(); const tokenKey = transformTokens[contractsChain]?.[token] ? transformTokens[contractsChain]?.[token] diff --git a/src/adapters/zksync/index.ts b/src/adapters/zksync/index.ts index 321fba4f..ac3f6664 100644 --- a/src/adapters/zksync/index.ts +++ b/src/adapters/zksync/index.ts @@ -1,6 +1,5 @@ -import { BridgeAdapter, PartialContractEventParams } from "../../helpers/bridgeAdapter.type"; -import { getTxDataFromEVMEventLogs } from "../../helpers/processTransactions"; -import { constructTransferParams } from "../../helpers/eventParams"; +import { BridgeAdapter, ContractEventParamsV2, } from "../../helpers/bridgeAdapter.type"; +import { processEVMLogs } from "../../helpers/processTransactions"; /* @@ -18,73 +17,62 @@ import { constructTransferParams } from "../../helpers/eventParams"; const WETH = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"; -const ethDepositEventParams: PartialContractEventParams = { +const ethWithdrawalParams: ContractEventParamsV2 = { target: "0x32400084C286CF3E17e7B677ea9583e60a000324", - // topic: "NewPriorityRequest(uint256,bytes32,uint64,tuple,bytes[])", // as shown on Etherscan - topic: - "NewPriorityRequest(uint256,bytes32,uint64,(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256[4],bytes,bytes,uint256[],bytes,bytes),bytes[])", // expand tuple data types - // topic: "NewPriorityRequest(uint256,bytes32,uint64,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256[4],bytes,bytes,uint256[],bytes,bytes,bytes[])", // break out data types from tuple - abi: [ - // "event NewPriorityRequest(uint256 txId, bytes32 txHash, uint64 expirationTimestamp, tuple transaction, bytes[] factoryDeps)", // as shown on Etherscan - "event NewPriorityRequest(uint256 txId, bytes32 txHash, uint64 expirationTimestamp, tuple(uint256 txType, uint256 from, uint256 to, uint256 gasLimit, uint256 gasPerPubdataByteLimit, uint256 maxFeePerGas, uint256 maxPriorityFeePerGas, uint256 paymaster, uint256 nonce, uint256 value, uint256[4] reserved, bytes data, bytes signature, uint256[] factoryDeps, bytes paymasterInput, bytes reservedDynamic) transaction, bytes[] factoryDeps)", - ], - argKeys: { - to: "transaction[2]", - amount: "transaction[9]", - }, - argGetters: { - to: (log: any) => log?.transaction?.[2]?.toHexString(), - amount: (log: any) => log?.transaction?.[9], - }, - logKeys: { - blockNumber: "blockNumber", - txHash: "transactionHash", - }, + abi: "event EthWithdrawalFinalized(address indexed to, uint256 amount)", + isDeposit: false, fixedEventData: { from: "0x32400084C286CF3E17e7B677ea9583e60a000324", token: WETH, }, - isDeposit: true, -}; - -const erc20DepositEventParams: PartialContractEventParams = constructTransferParams( - "0x57891966931Eb4Bb6FB81430E6cE0A03AAbDe063", - true -); +} +const erc20WithdrawalParams: ContractEventParamsV2 = { + target: "0x57891966931Eb4Bb6FB81430E6cE0A03AAbDe063", + abi: "event WithdrawalFinalized(address indexed to, address indexed l1Token, uint256 amount)", + isDeposit: false, + argKeys: { + token: "l1Token", + }, + fixedEventData: { + from: "0x57891966931Eb4Bb6FB81430E6cE0A03AAbDe063", + }, +} -const ethWithdrawalEventParams: PartialContractEventParams = { - target: "0x32400084C286CF3E17e7B677ea9583e60a000324", - topic: "EthWithdrawalFinalized(address,uint256)", - abi: ["event EthWithdrawalFinalized(address indexed to, uint256 amount)"], +const erc20DepositParams: ContractEventParamsV2 = { + target: "0x57891966931Eb4Bb6FB81430E6cE0A03AAbDe063", + abi: "event DepositInitiated (bytes32 indexed l2DepositTxHash, address indexed from, address indexed to, address l1Token, uint256 amount)", + isDeposit: true, argKeys: { - to: "to", - amount: "amount", + token: "l1Token", + }, +} +const ethDepositParams: ContractEventParamsV2 = { + target: "0x32400084C286CF3E17e7B677ea9583e60a000324", + topic: '0x4531cd5795773d7101c17bdeb9f5ab7f47d7056017506f937083be5d6e77a382', + abi: "event NewPriorityRequest(uint256 txId, bytes32 txHash, uint64 expirationTimestamp, tuple(uint256 txType, uint256 from, uint256 to, uint256 gasLimit, uint256 gasPerPubdataByteLimit, uint256 maxFeePerGas, uint256 maxPriorityFeePerGas, uint256 paymaster, uint256 nonce, uint256 value, uint256[4] reserved, bytes data, bytes signature, uint256[] factoryDeps, bytes paymasterInput, bytes reservedDynamic) transaction, bytes[] factoryDeps)", + isDeposit: true, + transformLog: (log: any) => { + log.amount = log.args.transaction.value + log.to = '0x'+log.args.transaction.to.toString(16) + log.from = '0x'+log.args.transaction.from.toString(16) + return log; }, fixedEventData: { - from: "0x32400084C286CF3E17e7B677ea9583e60a000324", token: WETH, }, - isDeposit: false, -}; - -const erc20WithdrawalEventParams: PartialContractEventParams = constructTransferParams( - "0x57891966931Eb4Bb6FB81430E6cE0A03AAbDe063", - false -); - -const constructParams = () => { - const eventParams = [ - ethDepositEventParams, - erc20DepositEventParams, - ethWithdrawalEventParams, - erc20WithdrawalEventParams, - ]; - return async (fromBlock: number, toBlock: number) => - getTxDataFromEVMEventLogs("zksync", "ethereum", fromBlock, toBlock, eventParams); -}; + filter: (i: any) => Number(i.amount) > 0, +} const adapter: BridgeAdapter = { - ethereum: constructParams(), + ethereum: async (_from, _to, options) => { + const logs = await processEVMLogs({ options: options!, contractEventParams:[erc20DepositParams, erc20WithdrawalParams, ethWithdrawalParams] }); + const ethDepositLogs = await processEVMLogs({ options: options!, contractEventParams:[ethDepositParams, ] }); + + // there was a bug where we assumed all NewPriorityRequest logs were for eth deposits, but we need to exclude all erc20 deposits + const txSet = new Set(logs.map((log) => log.txHash)); + const filteredEthDepositLogs = ethDepositLogs.filter((log) => !txSet.has(log.txHash)); + return logs.concat(filteredEthDepositLogs) + }, }; export default adapter; diff --git a/src/helpers/bridgeAdapter.type.ts b/src/helpers/bridgeAdapter.type.ts index 8490ead2..79771ba3 100644 --- a/src/helpers/bridgeAdapter.type.ts +++ b/src/helpers/bridgeAdapter.type.ts @@ -2,8 +2,11 @@ import { Chain } from "@defillama/sdk/build/general"; import { EventKeyMapping } from "../utils/types"; import { EventData } from "../utils/types"; +type AdapterChainFnV2ParamsOptional = (fromBlock: number, toBlock: number, v2Params?: AdapterV2Params) => Promise +type AdapterChainFnV2ParamsMandatory = (fromBlock: number, toBlock: number, v2Params: AdapterV2Params) => Promise + export type BridgeAdapter = { - [chain: string]: (fromBlock: number, toBlock: number) => Promise; + [chain: string]: AdapterChainFnV2ParamsMandatory | AdapterChainFnV2ParamsOptional; }; export type EventLogFilter = { @@ -79,3 +82,30 @@ export type PartialContractEventParams = { native?: string; }; }; + +export enum Erc20TransferType { + TRANSFER = "erc20Transfer", + TRANSFER_FROM = "erc20TransferFrom", + TRANSFER_TO = "erc20TransferTo", +} + +export type ContractEventParamsV2 = { + target?: string; + targets?: string[]; + topic?: string; + abi?: string[]|string; + logKeys?: EventKeyMapping; + argKeys?: EventKeyMapping; + isDeposit?: boolean; + fixedEventData?: EventKeyMapping; + transformLog?: Function; + filter?: Function; + eventLogType?: Erc20TransferType; +}; + +export type AdapterV2Params = { + fromBlock: number; + toBlock: number; + chain: Chain; + getLogs: Function; +} \ No newline at end of file diff --git a/src/helpers/processTransactions.ts b/src/helpers/processTransactions.ts index 946f3c0f..9aa9e5d9 100644 --- a/src/helpers/processTransactions.ts +++ b/src/helpers/processTransactions.ts @@ -2,10 +2,11 @@ import { getLogs } from "@defillama/sdk/build/util"; import { ethers } from "ethers"; import { Chain } from "@defillama/sdk/build/general"; import { get } from "lodash"; -import { ContractEventParams, PartialContractEventParams } from "../helpers/bridgeAdapter.type"; +import { AdapterV2Params, ContractEventParams, ContractEventParamsV2, Erc20TransferType, PartialContractEventParams } from "../helpers/bridgeAdapter.type"; import { EventData } from "../utils/types"; import { PromisePool } from "@supercharge/promise-pool"; import { getProvider } from "../utils/provider"; +import * as sdk from "@defillama/sdk"; const EventKeyTypes = { blockNumber: "number", @@ -18,13 +19,6 @@ const EventKeyTypes = { [key: string]: string; }; -interface EventLog { - blockNumber: number; - blockHash: string; - transactionIndex: number; - removed: boolean; - address: string; -} const setTransferEventParams = (isDeposit: boolean, target: string) => { const topic = "Transfer(address,address,uint256)"; @@ -103,15 +97,15 @@ export const getTxDataFromEVMEventLogs = async ( if (!logKeys) { logKeys = isDeposit ? { - blockNumber: "blockNumber", - txHash: "transactionHash", - to: "address", - } + blockNumber: "blockNumber", + txHash: "transactionHash", + to: "address", + } : { - blockNumber: "blockNumber", - txHash: "transactionHash", - from: "address", - }; + blockNumber: "blockNumber", + txHash: "transactionHash", + from: "address", + }; } if (!(topic && abi)) { throw new Error( @@ -137,7 +131,7 @@ export const getTxDataFromEVMEventLogs = async ( ).output; //console.log(logs) if (logs.length === 0) { - console.info(`No logs received for ${adapterName} from ${fromBlock} to ${toBlock} with topic ${topic} (${isDeposit ? 'Deposit': 'Withdrawal'}) for ${targetValue}.`); + console.info(`No logs received for ${adapterName} from ${fromBlock} to ${toBlock} with topic ${topic} (${isDeposit ? 'Deposit' : 'Withdrawal'}) for ${targetValue}.`); } break; @@ -161,8 +155,7 @@ export const getTxDataFromEVMEventLogs = async ( const value = txLog[logKey]; if (typeof value !== EventKeyTypes[eventKey]) { throw new Error( - `Type of ${eventKey} retrieved using ${logKey} is ${typeof value} when it must be ${ - EventKeyTypes[eventKey] + `Type of ${eventKey} retrieved using ${logKey} is ${typeof value} when it must be ${EventKeyTypes[eventKey] }.` ); } @@ -194,8 +187,7 @@ export const getTxDataFromEVMEventLogs = async ( const value = argGetters?.[eventKey]?.(args) || get(args, argKey); if (typeof value !== EventKeyTypes[eventKey] && !Array.isArray(value)) { throw new Error( - `Type of ${eventKey} retrieved using ${argKey} is ${typeof value} when it must be ${ - EventKeyTypes[eventKey] + `Type of ${eventKey} retrieved using ${argKey} is ${typeof value} when it must be ${EventKeyTypes[eventKey] }.` ); } @@ -244,8 +236,7 @@ export const getTxDataFromEVMEventLogs = async ( const value = tx[logKey]; if (typeof value !== EventKeyTypes[eventKey]) { throw new Error( - `Type of ${eventKey} retrieved using ${logKey} is ${typeof value} when it must be ${ - EventKeyTypes[eventKey] + `Type of ${eventKey} retrieved using ${logKey} is ${typeof value} when it must be ${EventKeyTypes[eventKey] }.` ); } @@ -375,8 +366,7 @@ export const getTxDataFromEVMEventLogs = async ( } if (typeof value !== EventKeyTypes[eventKey]) { throw new Error( - `Type of ${eventKey} retrieved using ${inputDataKey} with inputDataExtraction is ${typeof value} when it must be ${ - EventKeyTypes[eventKey] + `Type of ${eventKey} retrieved using ${inputDataKey} with inputDataExtraction is ${typeof value} when it must be ${EventKeyTypes[eventKey] }.` ); } @@ -542,3 +532,145 @@ export const makeTxHashesUnique = (eventData: EventData[]) => { return event; }); }; + +const dummyFunction = (i: any) => i + +export async function getEVMLogs({ chain = 'ethereum', entireLog = true, fromBlock, toBlock, topic, topics, eventAbi, target, targets, transformLog = dummyFunction }: { + chain?: Chain, + entireLog?: boolean, + fromBlock: number, + toBlock: number, + topic?: string, + topics?: (string | null)[], + eventAbi?: string | any, + target?: string, + targets?: string[], + transformLog?: Function +}) { + const logs = await sdk.getEventLogs({ + chain, entireLog: true, fromBlock, toBlock, topic, topics, eventAbi, target, targets, + }) + if (!entireLog && transformLog !== dummyFunction) + return logs.map((log: any) => log.args) + + return logs.map((log: any) => { + log.txHash = log.transactionHash + return transformLog(log) + }) +} + +const defaultArgKeys = ["from", "to", "token", "amount",] + +export async function processEVMLogs({ contractEventParams, options, }: { + options: AdapterV2Params, + contractEventParams: ContractEventParamsV2 | ContractEventParamsV2[] +}) { + + const allLogs = [] as any[] + if (!Array.isArray(contractEventParams)) contractEventParams = [contractEventParams] + + for (const contractEventParam of contractEventParams as ContractEventParamsV2[]) { + let { topic, target, targets, abi, logKeys = {}, argKeys = {}, isDeposit, fixedEventData = {}, transformLog, filter = (i: any) => i, eventLogType } = contractEventParam; + const isTransferType = eventLogType && [Erc20TransferType.TRANSFER, Erc20TransferType.TRANSFER_FROM, Erc20TransferType.TRANSFER_TO].includes(eventLogType) + + if (!isTransferType && typeof isDeposit !== 'boolean') throw new Error("isDeposit is required for processing logs") + if (typeof abi === 'string') abi = [abi] + if (abi === undefined) throw new Error("ABI is required for processing logs") + if (abi.length > 1) throw new Error("ABI must be a single string") + abi = abi[0] + + + let logs = [] as any[] + + if (isTransferType) + logs = await getERC20TransferLogs({ options, target, targets, eventLogType, }) + else + logs = await options.getLogs({ target, targets, topic, eventAbi: abi, }) + + + allLogs.push(logs.map((log: any) => { + defaultArgKeys.filter(i => log.args[i] !== undefined).map((argKey) => log[argKey] = log.args[argKey]) + Object.entries(logKeys).map(([eventKey, logKey]) => log[eventKey] = log[logKey]) + Object.entries(argKeys).map(([eventKey, argKey]) => log[eventKey] = log.args[argKey]) + Object.entries(fixedEventData).map(([eventKey, value]) => log[eventKey] = value) + if (!isTransferType) + log.isDeposit = isDeposit + return transformLog ? transformLog(log) : log + }).filter(filter as any)) + } + + return allLogs.flat().map(filterOutNonEventDataKeys) +} + +const eventDataKeySet = new Set(['blockNumber', 'txHash', 'from', 'to', 'token', 'amount', 'isDeposit', 'isUSDVolume', 'chainOverride', 'timestamp', 'txsCountedAs']) + +const filterOutNonEventDataKeys = (log: any) => { + const keys = Object.keys(log) + keys.filter(key => !eventDataKeySet.has(key)).map(key => delete log[key]) + return log +} + + +export function processEVMLogsExport(contractEventParams: ContractEventParamsV2 | ContractEventParamsV2[]) { + return async (_fromBlock: number, _toBlock: number, options: AdapterV2Params): Promise => { + return processEVMLogs({ options, contractEventParams, }) + } +} + +export async function getERC20TransferLogs({ options, target, targets, skipIndexer = false, eventLogType }: { + options: AdapterV2Params, + target?: string, + targets?: string[], + eventLogType: Erc20TransferType, + skipIndexer?: boolean +}): Promise { + if (eventLogType === Erc20TransferType.TRANSFER) { + const fromLogs = await getERC20TransferLogs({ options, target, targets, eventLogType: Erc20TransferType.TRANSFER_FROM, skipIndexer }) + const toLogs = await getERC20TransferLogs({ options, target, targets, eventLogType: Erc20TransferType.TRANSFER_TO, skipIndexer }) + return fromLogs.concat(toLogs) + } + + const isDeposit = eventLogType === Erc20TransferType.TRANSFER_TO + const { chain, fromBlock, toBlock } = options + + if (!skipIndexer) { + try { + const iTokenTransfers = await sdk.indexer.getTokenTransfers({ + chain, fromBlock, toBlock, target, targets, transferType: isDeposit ? 'in': 'out' + }) + return iTokenTransfers.map((log: any) => { + log.txHash = log.transaction_hash + log.blockNumber = log.block_number + log.from = log.from_address + log.to = log.to_address + log.amount = log.value + return log + }) + } catch (e) { + console.error(`Error getting token transfers from indexer: ${(e as any)?.message}`) + return getERC20TransferLogs({ options, target, targets, eventLogType, skipIndexer: true }) + } + } + + if (targets?.length) { + const allLogs = [] as any[] + for (const target of targets) { + allLogs.push(await getERC20TransferLogs({ options, target, eventLogType, skipIndexer: true })) + } + return allLogs.flat() + } + + if (!target) throw new Error("target is required for processing logs") + + let topics = []; + if (isDeposit) { + topics = [ethers.utils.id("Transfer(address,address,uint256)"), null, ethers.utils.hexZeroPad(target, 32)]; + } else { + topics = [ethers.utils.id("Transfer(address,address,uint256)"), ethers.utils.hexZeroPad(target, 32)]; + } + + return getEVMLogs({ + chain, fromBlock, toBlock, target, topics, + eventAbi: "event Transfer(address indexed from, address indexed to, uint256 amount)", + }) +} \ No newline at end of file diff --git a/src/utils/adapter.ts b/src/utils/adapter.ts index 849c84d0..02dcc4c4 100644 --- a/src/utils/adapter.ts +++ b/src/utils/adapter.ts @@ -17,6 +17,7 @@ import { groupBy } from "lodash"; import { getProvider } from "./provider"; import { sendDiscordText } from "./discord"; import { getConnection } from "../helpers/solana"; +import runAdapter from "./runAdapter"; const axios = require("axios"); const retry = require("async-retry"); @@ -451,7 +452,7 @@ export const runAdapterHistorical = async ( try { const eventLogs = await retry( () => - adapterChainEventsFn(block, endBlockForQuery).catch((e) => { + runAdapter({ fromBlock: block, toBlock: endBlockForQuery, chain, adapterChainEventsFn}).catch((e) => { console.error( `[ERROR] Failed to fetch event logs for ${bridgeDbName} on ${chain} from ${block} to ${endBlockForQuery}. Error: ${e.message}` ); diff --git a/src/utils/runAdapter.ts b/src/utils/runAdapter.ts new file mode 100644 index 00000000..9555d3fc --- /dev/null +++ b/src/utils/runAdapter.ts @@ -0,0 +1,19 @@ +import { getEVMLogs } from "../helpers/processTransactions"; +import { EventData } from "./types"; + +export async function runAdapter({ fromBlock, toBlock, adapterChainEventsFn, chain}: { + fromBlock: number; + toBlock: number; + adapterChainEventsFn: Function; + chain: string; +}): Promise { + const v2Params = { + fromBlock, + toBlock, + chain, + getLogs: (args: Parameters[0]) => getEVMLogs({ chain, ...args, fromBlock, toBlock }), + } + return adapterChainEventsFn(fromBlock, toBlock, v2Params); +} + +export default runAdapter; \ No newline at end of file diff --git a/src/utils/testAdapterHistorical.ts b/src/utils/testAdapterHistorical.ts index 7e2b4fb6..c202862d 100644 --- a/src/utils/testAdapterHistorical.ts +++ b/src/utils/testAdapterHistorical.ts @@ -6,6 +6,7 @@ import { maxBlocksToQueryByChain, nonBlocksChains } from "./constants"; import adapters from "../adapters"; import { getCurrentUnixTimestamp } from "./date"; import { getBlockByTimestamp } from "./blocks"; +import runAdapter from "./runAdapter"; const retry = require("async-retry"); const startTs = Number(process.argv[2]); @@ -61,7 +62,7 @@ export const runAdapterHistorical = async ( await wait(500); const endBlockForQuery = block + maxBlocksToQuery > endBlock ? endBlock : block + maxBlocksToQuery; try { - const eventLogs = await retry(() => adapterChainEventsFn(block, endBlockForQuery), { retries: 3, factor: 1 }); + const eventLogs = await retry(() => runAdapter({ fromBlock: block, toBlock: endBlockForQuery, chain, adapterChainEventsFn}), { retries: 3, factor: 1 }); // console.log(eventLogs); if (eventLogs.length === 0) {