From 1d37c546362dede5b88f647d7accb3951bbde9bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Rojas?= Date: Fri, 4 Oct 2024 15:44:54 -0300 Subject: [PATCH 1/6] Allow adapters to be built dynamically --- src/adapters/index.ts | 2 +- src/adapters/test.ts | 2 +- src/utils/adapter.ts | 8 ++++---- src/utils/aggregate.ts | 4 ++-- src/utils/insertConfigRows.ts | 4 ++-- src/utils/insertRecordedBlocks.ts | 2 +- src/utils/testAdapterHistorical.ts | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/adapters/index.ts b/src/adapters/index.ts index 5c967eb1..1573ffab 100644 --- a/src/adapters/index.ts +++ b/src/adapters/index.ts @@ -138,5 +138,5 @@ export default { fastbtc, crowdswap, } as { - [bridge: string]: BridgeAdapter; + [bridge: string]: BridgeAdapter | Promise; }; diff --git a/src/adapters/test.ts b/src/adapters/test.ts index 6406599c..d2b5c119 100644 --- a/src/adapters/test.ts +++ b/src/adapters/test.ts @@ -24,7 +24,7 @@ const adapterName = process.argv[2]; const numberOfBlocks = process.argv[3]; const testAdapter = async () => { - const adapter = adapters[adapterName]; + const adapter = await adapters[adapterName]; if (!adapter) { throw new Error(`Adapter for ${adapterName} not found, check it is exported correctly.`); } diff --git a/src/utils/adapter.ts b/src/utils/adapter.ts index 849c84d0..ecb5c25c 100644 --- a/src/utils/adapter.ts +++ b/src/utils/adapter.ts @@ -117,7 +117,7 @@ export const runAdapterToCurrentBlock = async ( console.warn(`[WARN] No recorded blocks data for ${bridgeDbName}. Error: ${e.message}`); } - const adapter = adapters[bridgeDbName]; + const adapter = await adapters[bridgeDbName]; if (!adapter) { const errString = `Adapter for ${bridgeDbName} not found, check it is exported correctly.`; console.error(`[ERROR] ${errString}`); @@ -257,7 +257,7 @@ export const runAllAdaptersToCurrentBlock = async ( for (const bridgeNetwork of bridgeNetworks) { const { id, bridgeDbName } = bridgeNetwork; - const adapter = adapters[bridgeDbName]; + const adapter = await adapters[bridgeDbName]; if (!adapter) { const errString = `Adapter for ${bridgeDbName} not found, check it is exported correctly.`; await insertErrorRow({ @@ -311,7 +311,7 @@ export const runAllAdaptersTimestampRange = async ( ) => { for (const bridgeNetwork of bridgeNetworks) { const { id, bridgeDbName } = bridgeNetwork; - const adapter = adapters[bridgeDbName]; + const adapter = await adapters[bridgeDbName]; if (!adapter) { const errString = `Adapter for ${bridgeDbName} not found, check it is exported correctly.`; await insertErrorRow({ @@ -373,7 +373,7 @@ export const runAdapterHistorical = async ( const currentTimestamp = await getCurrentUnixTimestamp(); const bridgeNetwork = bridgeNetworks.filter((bridgeNetwork) => bridgeNetwork.id === bridgeNetworkId)[0]; const { bridgeDbName } = bridgeNetwork; - const adapter = adapters[bridgeDbName]; + const adapter = await adapters[bridgeDbName]; console.log(`[INFO] Running adapter for ${bridgeDbName} on ${chain} from ${startBlock} to ${endBlock}.`); diff --git a/src/utils/aggregate.ts b/src/utils/aggregate.ts index 74315ddb..2e45bf6c 100644 --- a/src/utils/aggregate.ts +++ b/src/utils/aggregate.ts @@ -66,7 +66,7 @@ export const runAggregateDataHistorical = async ( } const { bridgeDbName, largeTxThreshold } = bridgeNetwork!; - const adapter = adapters[bridgeDbName]; + const adapter = await adapters[bridgeDbName]; if (!adapter) { const errString = `Adapter for ${bridgeDbName} not found, check it is exported correctly.`; @@ -125,7 +125,7 @@ export const runAggregateDataAllAdapters = async (timestamp: number, hourly: boo .for(bridgeNetworks) .process(async (bridgeNetwork) => { const { bridgeDbName, largeTxThreshold } = bridgeNetwork; - const adapter = adapters[bridgeDbName]; + const adapter = await adapters[bridgeDbName]; const chains = Object.keys(adapter); const chainsPromises = Promise.all( chains.map(async (chain) => { diff --git a/src/utils/insertConfigRows.ts b/src/utils/insertConfigRows.ts index d9824f1c..8213d738 100644 --- a/src/utils/insertConfigRows.ts +++ b/src/utils/insertConfigRows.ts @@ -3,8 +3,8 @@ import { insertConfigEntriesForAdapter } from "./adapter"; import adapters from "../adapters"; async function insertConfigRows(bridgeDbName: string) { - const adapter = adapters[bridgeDbName]; + const adapter = await adapters[bridgeDbName]; await insertConfigEntriesForAdapter(adapter, bridgeDbName) } -insertConfigRows("allbridge") \ No newline at end of file +insertConfigRows("allbridge") diff --git a/src/utils/insertRecordedBlocks.ts b/src/utils/insertRecordedBlocks.ts index bc6dc552..bfd87220 100644 --- a/src/utils/insertRecordedBlocks.ts +++ b/src/utils/insertRecordedBlocks.ts @@ -8,7 +8,7 @@ const FileSystem = require("fs"); const insertRecordedBlocks = async (adapterName: string, startTimestamp: number, endTimestamp: number) => { let recordedBlocks = recordedBlocksRecord as { [adapterChain: string]: { startBlock: number; endBlock: number } }; - const adapter = adapters[adapterName]; + const adapter = await adapters[adapterName]; if (!adapter) { throw new Error(`Adapter for ${adapterName} not found, check it is exported correctly.`); } diff --git a/src/utils/testAdapterHistorical.ts b/src/utils/testAdapterHistorical.ts index 7e2b4fb6..853009c1 100644 --- a/src/utils/testAdapterHistorical.ts +++ b/src/utils/testAdapterHistorical.ts @@ -22,7 +22,7 @@ export const runAdapterHistorical = async ( const currentTimestamp = await getCurrentUnixTimestamp(); const bridgeNetwork = bridgeNetworkData.filter((bridgeNetwork) => bridgeNetwork.id === bridgeNetworkId)[0]; const { bridgeDbName } = bridgeNetwork; - const adapter = adapters[bridgeDbName]; + const adapter = await adapters[bridgeDbName]; if (!adapter) { const errString = `Adapter for ${bridgeDbName} not found, check it is exported correctly.`; From 08bfabfe1b2a9036231f7ea0ca6970203c8a51bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Rojas?= Date: Fri, 4 Oct 2024 15:48:40 -0300 Subject: [PATCH 2/6] Add custom filter for processing transactions --- src/helpers/bridgeAdapter.type.ts | 3 +++ src/helpers/processTransactions.ts | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/src/helpers/bridgeAdapter.type.ts b/src/helpers/bridgeAdapter.type.ts index 8490ead2..0f3209a7 100644 --- a/src/helpers/bridgeAdapter.type.ts +++ b/src/helpers/bridgeAdapter.type.ts @@ -1,4 +1,6 @@ import { Chain } from "@defillama/sdk/build/general"; +import { LlamaProvider } from "@defillama/sdk/build/util/LlamaProvider"; +import { ethers } from "ethers"; import { EventKeyMapping } from "../utils/types"; import { EventData } from "../utils/types"; @@ -16,6 +18,7 @@ export type EventLogFilter = { includeArg?: { [key: string]: string }[]; excludeArg?: { [key: string]: string }[]; includeTxData?: { [key: string]: string }[]; + custom?: (provider: LlamaProvider, iface: ethers.utils.Interface, transactionHash: string) => Promise; }; export type FunctionSignatureFilter = { diff --git a/src/helpers/processTransactions.ts b/src/helpers/processTransactions.ts index 946f3c0f..9f90fba8 100644 --- a/src/helpers/processTransactions.ts +++ b/src/helpers/processTransactions.ts @@ -271,6 +271,10 @@ export const getTxDataFromEVMEventLogs = async ( if (toFilter) dataKeysToFilter.push(i); } } + if (filter?.custom) { + let toFilter = await filter.custom(provider, iface, txLog.transactionHash); + if (toFilter) dataKeysToFilter.push(i); + } if (getTokenFromReceipt && getTokenFromReceipt.token) { const txReceipt = await provider.getTransactionReceipt(txLog.transactionHash); if (!txReceipt) { From 443612ce92c0a2ddf22f36384f0849e73b74b653 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Rojas?= Date: Fri, 4 Oct 2024 15:54:31 -0300 Subject: [PATCH 3/6] Update DefiLlama SDK --- package-lock.json | 9 +++++---- package.json | 3 ++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9784f4fc..bbc33012 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,10 +4,11 @@ "requires": true, "packages": { "": { + "name": "bridges-server", "dependencies": { "@aws-sdk/client-lambda": "^3.637.0", "@aws-sdk/client-s3": "^3.637.0", - "@defillama/sdk": "^5.0.80", + "@defillama/sdk": "^5.0.94", "@graphql-typed-document-node/core": "^3.2.0", "@solana/web3.js": "^1.87.3", "async-retry": "^1.3.1", @@ -982,9 +983,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.94", + "resolved": "https://registry.npmjs.org/@defillama/sdk/-/sdk-5.0.94.tgz", + "integrity": "sha512-XlR5Gawx/uQXvkWZN962DBGXz2+6W4L8NaDIJ3b04vkD2QRhBYAftTKZfHgsYqBh/dFCOGQxh5P0jM49sf8dFw==", "dependencies": { "@aws-sdk/client-s3": "^3.400.0", "@elastic/elasticsearch": "^8.13.1", diff --git a/package.json b/package.json index e83577aa..345a1d69 100644 --- a/package.json +++ b/package.json @@ -1,4 +1,5 @@ { + "name": "bridges-server", "scripts": { "deploy:prod": "sls deploy --stage prod", "serve": "sls offline start", @@ -26,7 +27,7 @@ "dependencies": { "@aws-sdk/client-lambda": "^3.637.0", "@aws-sdk/client-s3": "^3.637.0", - "@defillama/sdk": "^5.0.80", + "@defillama/sdk": "^5.0.94", "@graphql-typed-document-node/core": "^3.2.0", "@solana/web3.js": "^1.87.3", "async-retry": "^1.3.1", From 172fdb0cdfc41958c7c6433a669c786488ce9b3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Rojas?= Date: Fri, 4 Oct 2024 16:00:00 -0300 Subject: [PATCH 4/6] Add Hyperlane --- src/adapters/hyperlane/index.ts | 160 ++++++++++++++++++++++++++++++++ src/adapters/index.ts | 2 + src/data/bridgeNetworkData.ts | 80 ++++++++++++++++ 3 files changed, 242 insertions(+) create mode 100644 src/adapters/hyperlane/index.ts diff --git a/src/adapters/hyperlane/index.ts b/src/adapters/hyperlane/index.ts new file mode 100644 index 00000000..7145ca77 --- /dev/null +++ b/src/adapters/hyperlane/index.ts @@ -0,0 +1,160 @@ +import { ethers } from "ethers"; +import { getProvider, setProvider } from "@defillama/sdk"; +import providerList from "@defillama/sdk/build/providers.json"; +import { Chain } from "@defillama/sdk/build/general"; + +import { BridgeAdapter, PartialContractEventParams } from "../../helpers/bridgeAdapter.type"; +import { getTxDataFromEVMEventLogs } from "../../helpers/processTransactions"; + +import * as yaml from 'js-yaml'; + +const baseUri = "https://raw.githubusercontent.com/hyperlane-xyz/hyperlane-registry/main"; + +let metadata: Record; +let addresses: Record>; +const chainMapping: Record = {}; + +async function setUp(): Promise { + metadata = + await fetch(`${baseUri}/chains/metadata.yaml`) + .then((r) => r.text()) + .then((t) => yaml.load(t)); + addresses = + await fetch(`${baseUri}/chains/addresses.yaml`) + .then((r) => r.text()) + .then((t) => yaml.load(t)); + + for (const [, chain] of Object.entries(metadata)) { + if (chain.isTestnet) continue; + if (chain.protocol != "ethereum") continue; + + const provider = getProvider(chain.name); + if (provider === null) { + for (const p in providerList) { + const data = providerList[p]; + if (data.chainId == chain.chainId) { + chainMapping[chain.name] = p; + setProvider(chain.name, getProvider(p)); + break; + } + } + } + } + // const missing = []; + // for (const key in metadata) { + // const chain = metadata[key]; + // if (chain.isTestnet) continue; + // if (chain.protocol != "ethereum") continue; + + // const provider = getProvider(chain.name); + // if (provider === null) missing.push(chain); + // } + // console.log(missing.length); + // console.log(missing); +}; + +function bytes32ToAddress(bytes32: string) { + return ethers.utils.getAddress('0x' + bytes32.slice(26)); +} + +function constructParams(chain: string) { + let eventParams = [] as PartialContractEventParams[]; + const mailboxAddress = ethers.utils.getAddress(addresses[chain].mailbox); + + function buildFilter(eventName: string): () => Promise { + return async (provider, iface, txHash) => { + const txReceipt = await provider.getTransactionReceipt(txHash); + if (!txReceipt) return true; + + let toFilter = true; + txReceipt.logs.map((log) => { + let parsed; + try { + parsed = iface.parseLog(log); + } catch { return; } + // console.log(parsed); + // console.log(ethers.utils.getAddress(log.address)); + if (ethers.utils.getAddress(log.address) === mailboxAddress && parsed.name === eventName) { + toFilter = false; + } + }); + return toFilter; + } + } + + const commonParams = { + target: null, + logKeys: { + blockNumber: "blockNumber", + txHash: "transactionHash", + from: "address", + token: "address", + }, + argKeys: { + to: "recipient", + amount: "amount", + }, + argGetters: { + to: (log: any) => { bytes32ToAddress(log.recipient); } + }, + }; + const depositParams: PartialContractEventParams = { + ...commonParams, + topic: "SentTransferRemote(uint32,bytes32,uint256)", + abi: [ + "event SentTransferRemote(uint32 indexed destination, bytes32 indexed recipient, uint256 amount)", + "event Dispatch(address indexed sender, uint32 indexed destination, bytes32 indexed recipient, bytes message)", + ], + isDeposit: true, + filter: { + custom: buildFilter("Dispatch") + }, + }; + + const withdrawParams: PartialContractEventParams = { + ...commonParams, + topic: "ReceivedTransferRemote(uint32,bytes32,uint256)", + abi: [ + "event ReceivedTransferRemote(uint32 indexed origin, bytes32 indexed recipient, uint256 amount)", + "event Process(uint32 indexed origin, bytes32 indexed sender, address indexed recipient)", + ], + isDeposit: false, + filter: { + custom: buildFilter("Process") + }, + }; + + eventParams.push(depositParams, withdrawParams); + + return async (fromBlock: number, toBlock: number) => { + return await getTxDataFromEVMEventLogs("hyperlane", (chainMapping[chain] || chain) as Chain, fromBlock, toBlock, eventParams); + } +} + +const excludedChains = [ + "cheesechain", // TODO: not available in defillama sdk providerList, can be added manually + "lumia", // TODO: not available in defillama sdk providerList, can be added manually +]; + +async function buildAdapter(): Promise { + await setUp(); + + const adapter: BridgeAdapter = { + } + + for (const key in metadata) { + const chain = metadata[key]; + if (chain.isTestnet) continue; + if (chain.protocol != "ethereum") continue; + + if (!addresses.hasOwnProperty(key)) continue; + if (excludedChains.includes(key)) continue; + + const adapterKey = chainMapping[key] || key; + adapter[adapterKey] = constructParams(key); + } + + return adapter; +} + +export default buildAdapter(); diff --git a/src/adapters/index.ts b/src/adapters/index.ts index 1573ffab..9b73829c 100644 --- a/src/adapters/index.ts +++ b/src/adapters/index.ts @@ -67,6 +67,7 @@ import minibridge from "./minibridge"; import cometbridge from "./cometbridge"; import fastbtc from "./rootstock-fastbtc-bridge" import crowdswap from "./crowdswap" +import hyperlane from "./hyperlane"; export default { polygon, @@ -137,6 +138,7 @@ export default { cometbridge, fastbtc, crowdswap, + hyperlane, } as { [bridge: string]: BridgeAdapter | Promise; }; diff --git a/src/data/bridgeNetworkData.ts b/src/data/bridgeNetworkData.ts index cbe603d5..712db108 100644 --- a/src/data/bridgeNetworkData.ts +++ b/src/data/bridgeNetworkData.ts @@ -1484,4 +1484,84 @@ export default [ url: "https://scanner.crowdswap.org/", chains: ["Ethereum", "Arbitrum", "Optimism", "BSC", "Polygon"], }, + { + id: 72, + displayName: "Hyperlane", + bridgeDbName: "hyperlane", + iconLink: "icons:hyperlane", + largeTxThreshold: 10000, + url: "https://hyperlane.xyz/", + chains: [ + "Aleph Zero EVM", + "Ancient8", + "Arbitrum", + "Arthera", + "Astar", + "Astar zkEVM", + "Avalanche", + "Base", + "Bitlayer", + "Blast", + "BOB", + "BSC", + "Celo", + // "CheeseChain", + "Chiliz", + "Core", + "Cyber", + "Degen", + "Dogechain", + "Endurance", + "Ethereum", + "Everclear", + "Flare", + "Forma", + "Fraxtal", + "Fuse", + "Gnosis", + "Immutable zkEVM", + "Injective EVM", + "KalyChain", + "Kroma", + "Linea", + "Lisk", + "LUKSO", + // "Lumia Prism", + "Manta", + "Mantle", + "Merlin", + "Metis", + "Mint", + "Mode", + "Molten", + "Moonbeam", + "Oort", + "Optimism", + "Polygon", + "Polygon zkEVM", + "Proof of Play Apex", + "PulseChain", + "RARI Chain", + "re.al", + "Redstone", + "Rootstock", + "Sanko", + "Scroll", + "Sei", + "Shibarium", + "Superposition", + "Taiko", + "Tangle", + "Viction", + "World Chain", + "Xai", + "X Layer", + "ZetaChain", + "Zircuit", + "Zora", + ], + chainMapping: { + avalanche: "avax", + }, + }, ] as BridgeNetwork[]; From 202aa4db785c9ab7277fa5f3b5d271d11e5f8db9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Rojas?= Date: Mon, 7 Oct 2024 16:25:42 -0300 Subject: [PATCH 5/6] fix: keep the TypeScript linter happy --- package-lock.json | 8 ++++++++ package.json | 1 + src/adapters/hyperlane/index.ts | 17 +++++++++-------- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index bbc33012..9425cc0d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,6 +28,7 @@ "devDependencies": { "@types/async-retry": "^1.4.8", "@types/aws-lambda": "^8.10.101", + "@types/js-yaml": "^4.0.9", "@types/node": "^18.6.4", "@types/node-fetch": "^2.6.2", "@types/retry": "^0.12.5", @@ -3442,6 +3443,13 @@ "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", "dev": true }, + "node_modules/@types/js-yaml": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", + "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/json-buffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/json-buffer/-/json-buffer-3.0.0.tgz", diff --git a/package.json b/package.json index 345a1d69..466362c5 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "devDependencies": { "@types/async-retry": "^1.4.8", "@types/aws-lambda": "^8.10.101", + "@types/js-yaml": "^4.0.9", "@types/node": "^18.6.4", "@types/node-fetch": "^2.6.2", "@types/retry": "^0.12.5", diff --git a/src/adapters/hyperlane/index.ts b/src/adapters/hyperlane/index.ts index 7145ca77..2077f939 100644 --- a/src/adapters/hyperlane/index.ts +++ b/src/adapters/hyperlane/index.ts @@ -1,5 +1,6 @@ import { ethers } from "ethers"; import { getProvider, setProvider } from "@defillama/sdk"; +import { LlamaProvider } from "@defillama/sdk/build/util/LlamaProvider"; import providerList from "@defillama/sdk/build/providers.json"; import { Chain } from "@defillama/sdk/build/general"; @@ -11,18 +12,18 @@ import * as yaml from 'js-yaml'; const baseUri = "https://raw.githubusercontent.com/hyperlane-xyz/hyperlane-registry/main"; let metadata: Record; -let addresses: Record>; +let addresses: Record>; const chainMapping: Record = {}; async function setUp(): Promise { metadata = - await fetch(`${baseUri}/chains/metadata.yaml`) + (await fetch(`${baseUri}/chains/metadata.yaml`) .then((r) => r.text()) - .then((t) => yaml.load(t)); + .then((t) => yaml.load(t)) as Record); addresses = - await fetch(`${baseUri}/chains/addresses.yaml`) + (await fetch(`${baseUri}/chains/addresses.yaml`) .then((r) => r.text()) - .then((t) => yaml.load(t)); + .then((t) => yaml.load(t)) as Record>); for (const [, chain] of Object.entries(metadata)) { if (chain.isTestnet) continue; @@ -31,7 +32,7 @@ async function setUp(): Promise { const provider = getProvider(chain.name); if (provider === null) { for (const p in providerList) { - const data = providerList[p]; + const data = (providerList as any)[p]; if (data.chainId == chain.chainId) { chainMapping[chain.name] = p; setProvider(chain.name, getProvider(p)); @@ -61,13 +62,13 @@ function constructParams(chain: string) { let eventParams = [] as PartialContractEventParams[]; const mailboxAddress = ethers.utils.getAddress(addresses[chain].mailbox); - function buildFilter(eventName: string): () => Promise { + function buildFilter(eventName: string): (provider: LlamaProvider, iface: ethers.utils.Interface, txHash: string) => Promise { return async (provider, iface, txHash) => { const txReceipt = await provider.getTransactionReceipt(txHash); if (!txReceipt) return true; let toFilter = true; - txReceipt.logs.map((log) => { + txReceipt.logs.map((log: ethers.providers.Log) => { let parsed; try { parsed = iface.parseLog(log); From a9a073de21e95e32eb533a650940e3e955efc410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Rojas?= Date: Thu, 31 Oct 2024 14:17:30 -0300 Subject: [PATCH 6/6] fix(adapters): build async adapters only when necessary --- src/adapters/hyperlane/index.ts | 4 ++-- src/adapters/index.ts | 8 ++++---- src/adapters/test.ts | 4 +++- src/helpers/bridgeAdapter.type.ts | 5 +++++ src/utils/adapter.ts | 18 +++++++++++++----- src/utils/aggregate.ts | 7 +++++-- src/utils/insertConfigRows.ts | 4 +++- src/utils/insertRecordedBlocks.ts | 4 +++- src/utils/testAdapterHistorical.ts | 4 +++- 9 files changed, 41 insertions(+), 17 deletions(-) diff --git a/src/adapters/hyperlane/index.ts b/src/adapters/hyperlane/index.ts index 2077f939..99682717 100644 --- a/src/adapters/hyperlane/index.ts +++ b/src/adapters/hyperlane/index.ts @@ -137,7 +137,7 @@ const excludedChains = [ "lumia", // TODO: not available in defillama sdk providerList, can be added manually ]; -async function buildAdapter(): Promise { +async function build(): Promise { await setUp(); const adapter: BridgeAdapter = { @@ -158,4 +158,4 @@ async function buildAdapter(): Promise { return adapter; } -export default buildAdapter(); +export default { isAsync: true, build }; diff --git a/src/adapters/index.ts b/src/adapters/index.ts index d1c8cd64..f13b4028 100644 --- a/src/adapters/index.ts +++ b/src/adapters/index.ts @@ -1,4 +1,4 @@ -import { BridgeAdapter } from "../helpers/bridgeAdapter.type"; +import { BridgeAdapter, AsyncBridgeAdapter } from "../helpers/bridgeAdapter.type"; import polygon from "./polygon"; import synapse from "./synapse"; import hop from "./hop"; @@ -65,8 +65,8 @@ import memebridge from "./memebridge"; import bunnyfi from "./bunnyfi"; import minibridge from "./minibridge"; import cometbridge from "./cometbridge"; -import fastbtc from "./rootstock-fastbtc-bridge" -import crowdswap from "./crowdswap" +import fastbtc from "./rootstock-fastbtc-bridge"; +import crowdswap from "./crowdswap"; import mint from "./mint"; import suibridge from "./suibridge"; import retrobridge from "./retrobridge" @@ -148,5 +148,5 @@ export default { layerswap, hyperlane, } as { - [bridge: string]: BridgeAdapter | Promise; + [bridge: string]: BridgeAdapter | AsyncBridgeAdapter; }; diff --git a/src/adapters/test.ts b/src/adapters/test.ts index d2b5c119..d2239e7a 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 { isAsyncAdapter } from "../utils/adapter"; const logTypes = { txHash: "string", @@ -24,7 +25,8 @@ const adapterName = process.argv[2]; const numberOfBlocks = process.argv[3]; const testAdapter = async () => { - const adapter = await adapters[adapterName]; + let adapter = adapters[adapterName]; + adapter = isAsyncAdapter(adapter) ? await adapter.build() : adapter; if (!adapter) { throw new Error(`Adapter for ${adapterName} not found, check it is exported correctly.`); } diff --git a/src/helpers/bridgeAdapter.type.ts b/src/helpers/bridgeAdapter.type.ts index 0f3209a7..5e44d434 100644 --- a/src/helpers/bridgeAdapter.type.ts +++ b/src/helpers/bridgeAdapter.type.ts @@ -8,6 +8,11 @@ export type BridgeAdapter = { [chain: string]: (fromBlock: number, toBlock: number) => Promise; }; +export type AsyncBridgeAdapter = { + isAsync: boolean; + build: () => Promise; +}; + export type EventLogFilter = { includeToken?: string[]; includeFrom?: string[]; diff --git a/src/utils/adapter.ts b/src/utils/adapter.ts index 8a392690..6fb2caa5 100644 --- a/src/utils/adapter.ts +++ b/src/utils/adapter.ts @@ -7,7 +7,7 @@ import bridgeNetworks from "../data/bridgeNetworkData"; import adapters from "../adapters"; import { maxBlocksToQueryByChain, nonBlocksChains } from "./constants"; import { store } from "./s3"; -import { BridgeAdapter } from "../helpers/bridgeAdapter.type"; +import { BridgeAdapter, AsyncBridgeAdapter } from "../helpers/bridgeAdapter.type"; import { getCurrentUnixTimestamp } from "./date"; import type { RecordedBlocks } from "./types"; import { wait } from "../helpers/etherscan"; @@ -117,7 +117,8 @@ export const runAdapterToCurrentBlock = async ( console.warn(`[WARN] No recorded blocks data for ${bridgeDbName}. Error: ${e.message}`); } - const adapter = await adapters[bridgeDbName]; + let adapter = adapters[bridgeDbName]; + adapter = isAsyncAdapter(adapter) ? await adapter.build() : adapter; if (!adapter) { const errString = `Adapter for ${bridgeDbName} not found, check it is exported correctly.`; console.error(`[ERROR] ${errString}`); @@ -257,7 +258,8 @@ export const runAllAdaptersToCurrentBlock = async ( for (const bridgeNetwork of bridgeNetworks) { const { id, bridgeDbName } = bridgeNetwork; - const adapter = await adapters[bridgeDbName]; + let adapter = adapters[bridgeDbName]; + adapter = isAsyncAdapter(adapter) ? await adapter.build() : adapter; if (!adapter) { const errString = `Adapter for ${bridgeDbName} not found, check it is exported correctly.`; await insertErrorRow({ @@ -311,7 +313,8 @@ export const runAllAdaptersTimestampRange = async ( ) => { for (const bridgeNetwork of bridgeNetworks) { const { id, bridgeDbName } = bridgeNetwork; - const adapter = await adapters[bridgeDbName]; + let adapter = adapters[bridgeDbName]; + adapter = isAsyncAdapter(adapter) ? await adapter.build() : adapter; if (!adapter) { const errString = `Adapter for ${bridgeDbName} not found, check it is exported correctly.`; await insertErrorRow({ @@ -373,7 +376,8 @@ export const runAdapterHistorical = async ( const currentTimestamp = await getCurrentUnixTimestamp(); const bridgeNetwork = bridgeNetworks.filter((bridgeNetwork) => bridgeNetwork.id === bridgeNetworkId)[0]; const { bridgeDbName } = bridgeNetwork; - const adapter = await adapters[bridgeDbName]; + let adapter = adapters[bridgeDbName]; + adapter = isAsyncAdapter(adapter) ? await adapter.build() : adapter; console.log(`[INFO] Running adapter for ${bridgeDbName} on ${chain} from ${startBlock} to ${endBlock}.`); @@ -704,3 +708,7 @@ export const insertConfigEntriesForAdapter = async ( }) ); }; + +export function isAsyncAdapter(adapter: BridgeAdapter | AsyncBridgeAdapter): adapter is AsyncBridgeAdapter { + return (adapter as AsyncBridgeAdapter).isAsync; +} diff --git a/src/utils/aggregate.ts b/src/utils/aggregate.ts index 2e45bf6c..dac0c2ad 100644 --- a/src/utils/aggregate.ts +++ b/src/utils/aggregate.ts @@ -20,6 +20,7 @@ import { insertOrUpdateTokenWithoutPrice, } from "./wrappa/postgres/write"; import adapters from "../adapters"; +import { isAsyncAdapter } from "../utils/adapter"; import bridgeNetworks from "../data/bridgeNetworkData"; import { importBridgeNetwork } from "../data/importBridgeNetwork"; import { defaultConfidenceThreshold } from "./constants"; @@ -66,7 +67,8 @@ export const runAggregateDataHistorical = async ( } const { bridgeDbName, largeTxThreshold } = bridgeNetwork!; - const adapter = await adapters[bridgeDbName]; + let adapter = adapters[bridgeDbName]; + adapter = isAsyncAdapter(adapter) ? await adapter.build() : adapter; if (!adapter) { const errString = `Adapter for ${bridgeDbName} not found, check it is exported correctly.`; @@ -125,7 +127,8 @@ export const runAggregateDataAllAdapters = async (timestamp: number, hourly: boo .for(bridgeNetworks) .process(async (bridgeNetwork) => { const { bridgeDbName, largeTxThreshold } = bridgeNetwork; - const adapter = await adapters[bridgeDbName]; + let adapter = adapters[bridgeDbName]; + adapter = isAsyncAdapter(adapter) ? await adapter.build() : adapter; const chains = Object.keys(adapter); const chainsPromises = Promise.all( chains.map(async (chain) => { diff --git a/src/utils/insertConfigRows.ts b/src/utils/insertConfigRows.ts index 8213d738..6c15a6f9 100644 --- a/src/utils/insertConfigRows.ts +++ b/src/utils/insertConfigRows.ts @@ -1,9 +1,11 @@ import { insertConfigEntriesForAdapter } from "./adapter"; import adapters from "../adapters"; +import { isAsyncAdapter } from "../utils/adapter"; async function insertConfigRows(bridgeDbName: string) { - const adapter = await adapters[bridgeDbName]; + let adapter = adapters[bridgeDbName]; + adapter = isAsyncAdapter(adapter) ? await adapter.build() : adapter; await insertConfigEntriesForAdapter(adapter, bridgeDbName) } diff --git a/src/utils/insertRecordedBlocks.ts b/src/utils/insertRecordedBlocks.ts index bfd87220..d04eab0a 100644 --- a/src/utils/insertRecordedBlocks.ts +++ b/src/utils/insertRecordedBlocks.ts @@ -1,5 +1,6 @@ import recordedBlocksRecord from "./recordedBlocks.json"; import adapters from "../adapters"; +import { isAsyncAdapter } from "../utils/adapter"; import { lookupBlock } from "@defillama/sdk/build/util"; import { Chain } from "@defillama/sdk/build/general"; import bridgeNetworks from "../data/bridgeNetworkData"; @@ -8,7 +9,8 @@ const FileSystem = require("fs"); const insertRecordedBlocks = async (adapterName: string, startTimestamp: number, endTimestamp: number) => { let recordedBlocks = recordedBlocksRecord as { [adapterChain: string]: { startBlock: number; endBlock: number } }; - const adapter = await adapters[adapterName]; + let adapter = adapters[adapterName]; + adapter = isAsyncAdapter(adapter) ? await adapter.build() : adapter; if (!adapter) { throw new Error(`Adapter for ${adapterName} not found, check it is exported correctly.`); } diff --git a/src/utils/testAdapterHistorical.ts b/src/utils/testAdapterHistorical.ts index 853009c1..321fbd69 100644 --- a/src/utils/testAdapterHistorical.ts +++ b/src/utils/testAdapterHistorical.ts @@ -4,6 +4,7 @@ import bridgeNetworkData from "../data/bridgeNetworkData"; import { wait } from "../helpers/etherscan"; import { maxBlocksToQueryByChain, nonBlocksChains } from "./constants"; import adapters from "../adapters"; +import { isAsyncAdapter } from "../utils/adapter"; import { getCurrentUnixTimestamp } from "./date"; import { getBlockByTimestamp } from "./blocks"; const retry = require("async-retry"); @@ -22,7 +23,8 @@ export const runAdapterHistorical = async ( const currentTimestamp = await getCurrentUnixTimestamp(); const bridgeNetwork = bridgeNetworkData.filter((bridgeNetwork) => bridgeNetwork.id === bridgeNetworkId)[0]; const { bridgeDbName } = bridgeNetwork; - const adapter = await adapters[bridgeDbName]; + let adapter = adapters[bridgeDbName]; + adapter = isAsyncAdapter(adapter) ? await adapter.build() : adapter; if (!adapter) { const errString = `Adapter for ${bridgeDbName} not found, check it is exported correctly.`;