158 lines
4.6 KiB
TypeScript
158 lines
4.6 KiB
TypeScript
import {
|
|
ChainId,
|
|
CHAIN_ID_TO_NAME,
|
|
Network,
|
|
tryNativeToHexString,
|
|
} from "@certusone/wormhole-sdk"
|
|
import { Implementation__factory } from "@certusone/wormhole-sdk/lib/cjs/ethers-contracts"
|
|
import { BigNumber, ContractReceipt, ethers } from "ethers"
|
|
import { getWormholeRelayer, RPCS_BY_CHAIN } from "../consts"
|
|
import {
|
|
parseWormholeRelayerPayloadType,
|
|
RelayerPayloadId,
|
|
parseWormholeRelayerSend,
|
|
DeliveryInstructionsContainer,
|
|
DeliveryStatus,
|
|
} from "../structs"
|
|
import { DeliveryEvent } from "../ethers-contracts/CoreRelayer"
|
|
type DeliveryTargetInfo = {
|
|
status: DeliveryStatus | string
|
|
deliveryTxHash: string | null
|
|
vaaHash: string | null
|
|
sourceChain: number | null
|
|
sourceVaaSequence: BigNumber | null
|
|
}
|
|
|
|
export function parseWormholeLog(log: ethers.providers.Log): {
|
|
type: RelayerPayloadId
|
|
parsed: DeliveryInstructionsContainer | string
|
|
} {
|
|
const abi = [
|
|
"event LogMessagePublished(address indexed sender, uint64 sequence, uint32 nonce, bytes payload, uint8 consistencyLevel);",
|
|
]
|
|
const iface = new ethers.utils.Interface(abi)
|
|
const parsed = iface.parseLog(log)
|
|
const payload = Buffer.from(parsed.args.payload.substring(2), "hex")
|
|
const type = parseWormholeRelayerPayloadType(payload)
|
|
if (type == RelayerPayloadId.Delivery) {
|
|
return { type, parsed: parseWormholeRelayerSend(payload) }
|
|
} else {
|
|
throw Error("Invalid wormhole log")
|
|
}
|
|
}
|
|
|
|
export function printChain(chainId: number) {
|
|
return `${CHAIN_ID_TO_NAME[chainId as ChainId]} (Chain ${chainId})`
|
|
}
|
|
|
|
export function getDefaultProvider(network: Network, chainId: ChainId) {
|
|
return new ethers.providers.StaticJsonRpcProvider(
|
|
RPCS_BY_CHAIN[network][CHAIN_ID_TO_NAME[chainId]]
|
|
)
|
|
}
|
|
|
|
export function getBlockRange(
|
|
provider: ethers.providers.Provider,
|
|
timestamp?: number
|
|
): [ethers.providers.BlockTag, ethers.providers.BlockTag] {
|
|
return [-2040, "latest"]
|
|
}
|
|
|
|
export async function getWormholeRelayerDeliveryEventsBySourceSequence(
|
|
environment: Network,
|
|
targetChain: ChainId,
|
|
targetChainProvider: ethers.providers.Provider,
|
|
sourceChain: number,
|
|
sourceVaaSequence: BigNumber,
|
|
blockStartNumber: ethers.providers.BlockTag,
|
|
blockEndNumber: ethers.providers.BlockTag
|
|
): Promise<DeliveryTargetInfo[]> {
|
|
const coreRelayer = getWormholeRelayer(targetChain, environment, targetChainProvider)
|
|
|
|
const deliveryEvents = coreRelayer.filters.Delivery(
|
|
null,
|
|
sourceChain,
|
|
sourceVaaSequence
|
|
)
|
|
|
|
// There is a max limit on RPCs sometimes for how many blocks to query
|
|
return await transformDeliveryEvents(
|
|
await coreRelayer.queryFilter(deliveryEvents, blockStartNumber, blockEndNumber),
|
|
targetChainProvider
|
|
)
|
|
}
|
|
|
|
export function deliveryStatus(status: number) {
|
|
switch (status) {
|
|
case 0:
|
|
return DeliveryStatus.DeliverySuccess
|
|
case 1:
|
|
return DeliveryStatus.ReceiverFailure
|
|
case 2:
|
|
return DeliveryStatus.ForwardRequestFailure
|
|
case 3:
|
|
return DeliveryStatus.ForwardRequestSuccess
|
|
case 4:
|
|
return DeliveryStatus.InvalidRedelivery
|
|
default:
|
|
return DeliveryStatus.ThisShouldNeverHappen
|
|
}
|
|
}
|
|
|
|
async function transformDeliveryEvents(
|
|
events: DeliveryEvent[],
|
|
targetProvider: ethers.providers.Provider
|
|
): Promise<DeliveryTargetInfo[]> {
|
|
return Promise.all(
|
|
events.map(async (x) => {
|
|
return {
|
|
status: deliveryStatus(x.args[4]),
|
|
deliveryTxHash: x.transactionHash,
|
|
vaaHash: x.args[3],
|
|
sourceVaaSequence: x.args[2],
|
|
sourceChain: x.args[1],
|
|
}
|
|
})
|
|
)
|
|
}
|
|
|
|
export function getWormholeRelayerLog(
|
|
receipt: ContractReceipt,
|
|
bridgeAddress: string,
|
|
emitterAddress: string,
|
|
index: number
|
|
): { log: ethers.providers.Log; sequence: string } {
|
|
const bridgeLogs = receipt.logs.filter((l) => {
|
|
return l.address === bridgeAddress
|
|
})
|
|
|
|
if (bridgeLogs.length == 0) {
|
|
throw Error("No core contract interactions found for this transaction.")
|
|
}
|
|
|
|
const parsed = bridgeLogs.map((bridgeLog) => {
|
|
const log = Implementation__factory.createInterface().parseLog(bridgeLog)
|
|
return {
|
|
sequence: log.args[1].toString(),
|
|
nonce: log.args[2].toString(),
|
|
emitterAddress: tryNativeToHexString(log.args[0].toString(), "ethereum"),
|
|
log: bridgeLog,
|
|
}
|
|
})
|
|
|
|
const filtered = parsed.filter((x) => x.emitterAddress == emitterAddress.toLowerCase())
|
|
|
|
if (filtered.length == 0) {
|
|
throw Error("No CoreRelayer contract interactions found for this transaction.")
|
|
}
|
|
|
|
if (index >= filtered.length) {
|
|
throw Error("Specified delivery index is out of range.")
|
|
} else {
|
|
return {
|
|
log: filtered[index].log,
|
|
sequence: filtered[index].sequence,
|
|
}
|
|
}
|
|
}
|