trustless-generic-relayer/relayer_engine/src/plugin/src/syntheticBatch.ts

108 lines
3.4 KiB
TypeScript

import * as wh from "@certusone/wormhole-sdk"
import { PluginError } from "./utils"
import { IWormhole__factory, LogMessagePublishedEvent } from "../../../pkgs/sdk/src"
import * as ethers from "ethers"
import { Implementation__factory } from "@certusone/wormhole-sdk/lib/cjs/ethers-contracts"
import { retryAsyncUntilDefined } from "ts-retry/lib/cjs/retry"
import { ChainInfo } from "./plugin"
import { tryNativeToHexString } from "@certusone/wormhole-sdk"
import { Logger } from "winston"
// fetch the contract transaction receipt for the given sequence number emitted by the core relayer contract
export async function fetchReceipt(
sequence: BigInt,
chainId: wh.EVMChainId,
provider: ethers.providers.Provider,
chainConfig: ChainInfo,
logger: Logger
): Promise<ethers.ContractReceipt> {
const coreWHContract = IWormhole__factory.connect(chainConfig.coreContract!, provider)
const filter = coreWHContract.filters.LogMessagePublished(chainConfig.relayerAddress)
const blockNumber = await coreWHContract.provider.getBlockNumber()
for (let i = 0; i < 20; ++i) {
let paginatedLogs
if (i === 0) {
paginatedLogs = await coreWHContract.queryFilter(filter, -30)
} else {
paginatedLogs = await coreWHContract.queryFilter(
filter,
blockNumber - (i + 2) * 20,
blockNumber - i * 20
)
}
const log = paginatedLogs.find(
(log) => log.args.sequence.toString() === sequence.toString()
)
if (log) {
const rx = await log.getTransactionReceipt()
if (rx) {
return rx
} else {
logger.error("Returned transaction receipt was not defined", {
rx,
sequence,
chainId,
})
}
}
}
logger.info("Did not fetch receipt in initial scan, trying latest 500 blocks...")
try {
return await retryAsyncUntilDefined(
async () => {
const paginatedLogs = await coreWHContract.queryFilter(filter, -500)
const log = paginatedLogs.find(
(log) => log.args.sequence.toString() === sequence.toString()
)
if (log) {
return await log.getTransactionReceipt()
}
return undefined
},
{ maxTry: 10, delay: 500 }
)
} catch {
throw new PluginError("Could not find contract receipt", { sequence, chainId })
}
}
export function filterLogs(
rx: ethers.ContractReceipt,
nonce: number,
chainConfig: ChainInfo,
logger: Logger
): {
vaas: {
sequence: string
emitter: string
bytes: string
}[]
deliveryVaaIdx: number
} {
const onlyVAALogs = rx.logs.filter((log) => log.address === chainConfig.coreContract)
const vaas = onlyVAALogs.flatMap((bridgeLog: ethers.providers.Log) => {
const iface = Implementation__factory.createInterface()
const log = iface.parseLog(bridgeLog) as unknown as LogMessagePublishedEvent
// filter down to just synthetic batch
if (log.args.nonce !== nonce) {
return []
}
return [
{
sequence: log.args.sequence.toString(),
emitter: wh.tryNativeToHexString(log.args.sender, "ethereum"),
bytes: "",
},
]
})
logger.debug(vaas)
const emitterAddress = tryNativeToHexString(chainConfig.relayerAddress, "ethereum")
const deliveryVaaIdx = vaas.findIndex((vaa) => vaa.emitter === emitterAddress)
if (deliveryVaaIdx === -1) {
throw new PluginError("CoreRelayerVaa not found in fetched vaas", {
vaas,
})
}
return { vaas, deliveryVaaIdx }
}