Skip to content

Commit

Permalink
Add backfill testing
Browse files Browse the repository at this point in the history
  • Loading branch information
vrtnd committed Jul 9, 2023
1 parent dccde89 commit d480b27
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 4 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,16 @@ chains: ["Ethereum", "Polygon"], // Bridge chains. If your bridge is on 2 chains
destinationChain: "Polygon", // When bridge connects 2 chains, for example Ethereum<->Optimism and there is only one adapter on one chain which tracks deposits and withdrawals for both chains
```
### Testing
#### Adapter Testing:
```bash
npm run test [adapter name] [number of blocks to test on]
Example:
npm run test across 1000
```
#### Backfill testing:
```bash
npm run test-txs [start ts] [end ts] [adapter name]
Example:
npm run test-txs 1688476361 1688919317 synapse
```
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"deploy:prod": "export NODE_OPTIONS=--max-old-space-size=10240 && sls deploy --stage prod",
"serve": "sls offline start",
"build": "sls webpack",
"test": "ts-node ./src/adapters/test.ts"
"test": "ts-node ./src/adapters/test.ts",
"test-txs": "ts-node ./src/utils/testAdapterHistorical.ts"
},
"devDependencies": {
"@types/aws-lambda": "^8.10.101",
Expand Down
8 changes: 5 additions & 3 deletions src/helpers/processTransactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ export const getTxDataFromEVMEventLogs = async (
});
} catch (e) {
console.error(
`WARNING: Unable to parse log for ${adapterName}, SKIPPING TX with hash ${txLog.transactionHash}`
`WARNING: Unable to parse log for ${adapterName}, SKIPPING TX with hash ${txLog.transactionHash} ${chainContractsAreOn}`
);
dataKeysToFilter.push(i);
return;
Expand All @@ -183,7 +183,9 @@ export const getTxDataFromEVMEventLogs = async (
try {
const args = parsedLog?.args;
if (args === undefined || args.length === 0) {
throw new Error(`Unable to get log args for ${adapterName} with arg keys ${argKeys}.`);
throw new Error(
`Unable to get log args for ${adapterName} with arg keys ${argKeys} ${chainContractsAreOn}.`
);
}
Object.entries(argKeys).map(([eventKey, argKey]) => {
// @ts-ignore
Expand Down Expand Up @@ -223,7 +225,7 @@ export const getTxDataFromEVMEventLogs = async (
}
} catch (error) {
console.error(
`Unable to get log args for ${adapterName} with arg keys ${argKeys}. SKIPPING TX with hash ${txLog.transactionHash}`
`Unable to get log args for ${adapterName} with arg keys ${argKeys}. SKIPPING TX with hash ${txLog.transactionHash} ${chainContractsAreOn}`
);
return;
}
Expand Down
160 changes: 160 additions & 0 deletions src/utils/testAdapterHistorical.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import { Chain, getProvider } from "@defillama/sdk/build/general";
import { groupBy } from "lodash";
import { lookupBlock } from "@defillama/sdk/build/util";
import bridgeNetworkData from "../data/bridgeNetworkData";
import { wait } from "../helpers/etherscan";
import { maxBlocksToQueryByChain, nonBlocksChains } from "./constants";
import adapters from "../adapters";
import { getCurrentUnixTimestamp } from "./date";
const retry = require("async-retry");

const startTs = Number(process.argv[2]);
const endTs = Number(process.argv[3]);
const bridgeName = process.argv[4];

export const runAdapterHistorical = async (
startBlock: number,
endBlock: number,
bridgeNetworkId: number,
chain: string, // needed because different chains query over different block ranges
throwOnFailedInsert: boolean = true
) => {
const currentTimestamp = await getCurrentUnixTimestamp();
const bridgeNetwork = bridgeNetworkData.filter((bridgeNetwork) => bridgeNetwork.id === bridgeNetworkId)[0];
const { bridgeDbName } = bridgeNetwork;
const adapter = adapters[bridgeDbName];

if (!adapter) {
const errString = `Adapter for ${bridgeDbName} not found, check it is exported correctly.`;

throw new Error(errString);
}
const adapterChainEventsFn = adapter[chain];
if (!adapterChainEventsFn) {
const errString = `Chain ${chain} not found on adapter ${bridgeDbName}.`;

throw new Error(errString);
}
const chainContractsAreOn = bridgeNetwork.chainMapping?.[chain as Chain]
? bridgeNetwork.chainMapping?.[chain as Chain]
: chain;

const bridgeID = bridgeDbName;
if (!bridgeID) {
const errString = `${bridgeDbName} on chain ${chain} is missing in config table.`;

throw new Error(errString);
}
const maxBlocksToQuery = maxBlocksToQueryByChain[chainContractsAreOn]
? maxBlocksToQueryByChain[chainContractsAreOn]
: maxBlocksToQueryByChain.default;
const useChainBlocks = !nonBlocksChains.includes(chainContractsAreOn);
let block: number = startBlock;
console.log(`Searching for transactions for ${bridgeID} from ${startBlock} to ${block}.`);
while (block < endBlock) {
await wait(500);
const endBlockForQuery = block + maxBlocksToQuery > endBlock ? endBlock : block + maxBlocksToQuery;
try {
const eventLogs = await retry(() => adapterChainEventsFn(block, endBlockForQuery), { retries: 3, factor: 1 });

// console.log(eventLogs);
if (eventLogs.length === 0) {
console.log(`No transactions found for ${bridgeID} (${bridgeDbName}-${chain}) from ${block} to ${endBlock}.`);
block = block + maxBlocksToQuery;
continue;
}
console.log(
`${eventLogs.length} transactions were found for ${bridgeID} (${bridgeDbName}) on ${chain} from ${block} to ${endBlockForQuery}.`
);
let provider = undefined as any;
if (useChainBlocks) {
provider = getProvider(chainContractsAreOn as Chain);
if (!provider) {
const errString = `Could not get provider for chain ${chainContractsAreOn}.`;

throw new Error(errString);
}
}
let txBlocks = [] as number[];
eventLogs.map((log: any) => {
const { blockNumber } = log;
txBlocks.push(blockNumber);
});
const minBlock = Math.min(...txBlocks) ?? 0;
const maxBlock = Math.max(...txBlocks) ?? 0;
const blockRange = maxBlock - minBlock || 1;
// dividing blocks into 10 buckets and giving all blocks within a bucket the same timestamp,
// in order to reduce number of getBlock calls

// drop sus multi transfers
const groupedEvents = groupBy(eventLogs, (event: any) => event?.txHash);
const filteredEvents = Object.values(groupedEvents)
.filter((events) => events.length < 100)
.flat();
let storedBridgeIds = {} as { [chain: string]: string };
for (let i = 0; i < filteredEvents?.length; i++) {
let log = filteredEvents[i];

const { txHash, blockNumber, from, to, token, amount, isDeposit, chainOverride, isUSDVolume, txsCountedAs } =
log;
const bucket = Math.floor(((blockNumber - minBlock) * 9) / blockRange);

let amountString;
if (!amount) {
amountString = "0";
} else {
amountString = amount.toString();
}

if (
from.toLowerCase() === "0x0000000000000000000000000000000000000000" ||
to.toLowerCase() === "0x0000000000000000000000000000000000000000"
)
return;
}
console.log("finished inserting transactions");
} catch (e: any) {
const errString = `Adapter for ${bridgeDbName} failed to get and insert logs for chain ${chain} for blocks ${block}-${endBlockForQuery}. ${
e && e?.message
}`;

if (throwOnFailedInsert) {
throw new Error(errString + e);
}
console.error(errString, e);
}
block = block + maxBlocksToQuery;
}
console.log(`finished inserting all transactions for ${bridgeID}`);
};

async function fillAdapterHistorical(
startTimestamp: number,
endTimestamp: number,
bridgeDbName: string,
restrictChainTo?: string
) {
const adapter = bridgeNetworkData.find((x) => x.bridgeDbName === bridgeDbName);
if (!adapter) throw new Error("Invalid adapter");
console.log(`Found ${bridgeDbName}`);
const promises = Promise.all(
adapter.chains.map(async (chain, i) => {
let nChain;
if (adapter.chainMapping && adapter.chainMapping[chain.toLowerCase()]) {
nChain = adapter.chainMapping[chain.toLowerCase()];
} else {
nChain = chain.toLowerCase();
}
if (restrictChainTo && nChain !== restrictChainTo) return;
console.log(`Running adapter for ${chain} for ${bridgeDbName}`);
await wait(500 * i);
const startBlock = await lookupBlock(startTimestamp, { chain: nChain as Chain });
const endBlock = await lookupBlock(endTimestamp, { chain: nChain as Chain });
await runAdapterHistorical(startBlock.block, endBlock.block, adapter.id, chain.toLowerCase(), false);
})
);
await promises;
console.log(`Finished running adapter from ${startTimestamp} to ${endTimestamp} for ${bridgeDbName}`);
}

fillAdapterHistorical(startTs, endTs, bridgeName);

0 comments on commit d480b27

Please sign in to comment.