relayer: improve delivery process debugging, add utility functions (#3028)
* starting calcs for relayer inbound / outbound * monitoring additions & adding price monitor relay process * adding basic infra for pricing & monitoring process * added a large amount of debugging info to the relayer delivery process * redelivery relayer processing fixes & debug improvements * fixing delivery process breaks from renaming * fixing naming errors inside the pricing process * removed pricing process, rename delivery folders * removed test error & refactored util directory * moving out utilities from relayer delivery process * moved lambda logic inline * cleaning up comments * Restore Tiltfile change * Restore sdk package.json change --------- Co-authored-by: derpy-duck <115193320+derpy-duck@users.noreply.github.com>
This commit is contained in:
parent
12b18d4ccc
commit
515ccceedf
|
@ -0,0 +1,80 @@
|
|||
export type DeliveryExecutionRecord = {
|
||||
didError?: boolean; // if true, the error will be logged in fatalStackTrace
|
||||
errorName?: string; // If a detectable error occurred, this is the name of that failure.
|
||||
didSubmitTransaction?: boolean; // if true, the process submitted at least one transaction, which will be logged in transactionHashes
|
||||
|
||||
executionStartTime?: number; // unix timestamp in milliseconds
|
||||
executionEndTime?: number; // unix timestamp in milliseconds
|
||||
|
||||
rawVaaHex?: string; // hex string of the raw VAA
|
||||
rawVaaPayloadHex?: string; // hex string of the raw VAA payload
|
||||
payloadType?: string; // the payload type of the VAA
|
||||
didParse?: boolean; // if true, the VAA was successfully parsed
|
||||
specifiedDeliveryProvider?: string; // the relay provider specified in the VAA
|
||||
didMatchDeliveryProvider?: boolean; // if true, the relay provider specified in the VAA matched the relay provider for the chain
|
||||
|
||||
redeliveryRecord?: RedeliveryRecord; // if the VAA is a redelivery, the redeliveryRecord
|
||||
deliveryRecord?: DeliveryRecord; // information about the delivery process of the VAA
|
||||
|
||||
fatalStackTrace?: string; // if the top level unexpected exception try-catch caught, this was the stack trace
|
||||
};
|
||||
|
||||
export type RedeliveryRecord = {
|
||||
validVaaKeyFormat?: boolean; // if true, the VAA key format interpretable
|
||||
vaaKeyPrintable?: string; // the VAA key in printable format of the original VAA
|
||||
originalVaaFetchTimeStart?: number; // unix timestamp in milliseconds
|
||||
originalVaaFetchTimeEnd?: number; // unix timestamp in milliseconds
|
||||
originalVaaDidFetch?: boolean; // if true, the original VAA was successfully fetched
|
||||
originalVaaHex?: string; // hex string of the original VAA
|
||||
originalVaaDidParse?: boolean; // if true, the original VAA was successfully parsed
|
||||
isValidRedelivery?: boolean; // if true, the redelivery VAA is valid
|
||||
invalidRedeliveryReason?: string; // if the redelivery VAA is invalid, the reason why
|
||||
};
|
||||
|
||||
export type DeliveryRecord = {
|
||||
deliveryInstructionsPrintable?: string; // the delivery instructions in printable format
|
||||
hasAdditionalVaas?: boolean; // if true, the delivery instructions contain additional VAAs
|
||||
additionalVaaKeysFormatValid?: boolean; // if true, the additional VAA key format interpretable
|
||||
additionalVaaKeysPrintable?: string; // the additional VAA key in printable format
|
||||
fetchAdditionalVaasTimeStart?: number; // unix timestamp in milliseconds
|
||||
fetchAdditionalVaasTimeEnd?: number; // unix timestamp in milliseconds
|
||||
additionalVaasDidFetch?: boolean; // if true, the additional VAAs were successfully fetched
|
||||
additionalVaasHex?: string[]; // hex string of the additional VAAs
|
||||
chainId?: number; // the chain ID of the chain the VAA is being sent to
|
||||
receiverValue?: string; // the receiver value of the VAA;
|
||||
maxRefund?: string; // the max refund of the VAA;
|
||||
budget?: string; // the budget of the VAA;
|
||||
walletAcquisitionStartTime?: number; // unix timestamp in milliseconds
|
||||
walletAcquisitionEndTime?: number; // unix timestamp in milliseconds
|
||||
walletAcquisitionDidSucceed?: boolean; // if true, the wallet acquisition was successful
|
||||
walletAddress?: string; // the wallet address of the wallet used to send the VAA
|
||||
walletBalance?: string; // the balance of the wallet used to send the VAA
|
||||
walletNonce?: number; // the nonce of the wallet used to send the VAA
|
||||
gasUnitsEstimate?: number; // the gas units estimate for the transaction being submitted
|
||||
gasPriceEstimate?: string; // the gas price estimate for the transaction being submitted
|
||||
estimatedTransactionFee?: string; // the estimated transaction fee for the transaction being submitted
|
||||
estimatedTransactionFeeEther?: string; // the estimated transaction fee for the transaction being submitted in the base units of the chain
|
||||
transactionSubmitTimeStart?: number; // unix timestamp in milliseconds
|
||||
transactionSubmitTimeEnd?: number; // unix timestamp in milliseconds
|
||||
transactionDidSubmit?: boolean; // if true, the transaction was successfully submitted
|
||||
transactionHashes?: string[]; // the transaction hashes of the transactions submitted
|
||||
resultLogDidParse?: boolean; // if true, the result log was successfully parsed
|
||||
resultLog?: string; // the result log of the transaction
|
||||
};
|
||||
|
||||
export function deliveryExecutionRecordPrintable(
|
||||
executionRecord: DeliveryExecutionRecord
|
||||
): string {
|
||||
return JSON.stringify(executionRecord, null, 2); //TODO deal with line breaks and such better
|
||||
}
|
||||
|
||||
export function addFatalError(
|
||||
executionRecord: DeliveryExecutionRecord,
|
||||
e: any
|
||||
) {
|
||||
executionRecord.didError = true;
|
||||
executionRecord.errorName = e.name;
|
||||
executionRecord.fatalStackTrace = e.stack
|
||||
? e.stack.replace(/\n/g, "\\n")
|
||||
: "";
|
||||
}
|
|
@ -26,3 +26,5 @@ const jsonFormat = winston.format.combine(
|
|||
winston.format.json(),
|
||||
winston.format.errors({ stack: true })
|
||||
);
|
||||
|
||||
type ExecutionContext = {};
|
||||
|
|
|
@ -12,36 +12,67 @@ import {
|
|||
DeliveryInstruction,
|
||||
packOverrides,
|
||||
DeliveryOverrideArgs,
|
||||
parseEVMExecutionInfoV1
|
||||
parseEVMExecutionInfoV1,
|
||||
} from "@certusone/wormhole-sdk/lib/cjs/relayer";
|
||||
import { EVMChainId } from "@certusone/wormhole-sdk";
|
||||
import { GRContext } from "./app";
|
||||
import { BigNumber, ethers } from "ethers";
|
||||
import { IWormholeRelayerDelivery__factory } from "@certusone/wormhole-sdk/lib/cjs/ethers-contracts";
|
||||
|
||||
import { WormholeRelayer__factory } from "@certusone/wormhole-sdk/lib/cjs/ethers-contracts";
|
||||
import {
|
||||
DeliveryExecutionRecord,
|
||||
addFatalError,
|
||||
deliveryExecutionRecordPrintable,
|
||||
} from "./executionRecord";
|
||||
|
||||
export async function processGenericRelayerVaa(ctx: GRContext, next: Next) {
|
||||
ctx.logger.info(`Processing generic relayer vaa`);
|
||||
const payloadId = parseWormholeRelayerPayloadType(ctx.vaa!.payload);
|
||||
// route payload types
|
||||
if (payloadId == RelayerPayloadId.Delivery) {
|
||||
ctx.logger.info(`Detected delivery VAA, processing delivery payload...`);
|
||||
await processDelivery(ctx);
|
||||
} else if (payloadId == RelayerPayloadId.Redelivery) {
|
||||
ctx.logger.info(
|
||||
`Detected redelivery VAA, processing redelivery payload...`
|
||||
);
|
||||
await processRedelivery(ctx);
|
||||
} else {
|
||||
ctx.logger.error(`Expected GR Delivery payload type, found ${payloadId}`);
|
||||
throw new Error("Expected GR Delivery payload type");
|
||||
const executionRecord: DeliveryExecutionRecord = {};
|
||||
executionRecord.executionStartTime = Date.now();
|
||||
|
||||
try {
|
||||
ctx.logger.info(`Processing generic relayer vaa`);
|
||||
|
||||
executionRecord.rawVaaHex = ctx.vaaBytes!.toString("hex");
|
||||
executionRecord.rawVaaPayloadHex = ctx.vaa!.payload.toString("hex");
|
||||
|
||||
const payloadId = parseWormholeRelayerPayloadType(ctx.vaa!.payload);
|
||||
|
||||
executionRecord.payloadType = RelayerPayloadId[payloadId];
|
||||
|
||||
// route payload types
|
||||
if (payloadId == RelayerPayloadId.Delivery) {
|
||||
ctx.logger.info(`Detected delivery VAA, processing delivery payload...`);
|
||||
await processDelivery(ctx, executionRecord);
|
||||
} else if (payloadId == RelayerPayloadId.Redelivery) {
|
||||
ctx.logger.info(
|
||||
`Detected redelivery VAA, processing redelivery payload...`
|
||||
);
|
||||
await processRedelivery(ctx, executionRecord);
|
||||
} else {
|
||||
ctx.logger.error(`Expected GR Delivery payload type, found ${payloadId}`);
|
||||
throw new Error("Expected GR Delivery payload type");
|
||||
}
|
||||
|
||||
executionRecord.didError = false;
|
||||
} catch (e: any) {
|
||||
ctx.logger.error(`Fatal error in processGenericRelayerVaa: ${e}`);
|
||||
addFatalError(executionRecord, e);
|
||||
ctx.logger.error("Dumping execution context for fatal error");
|
||||
ctx.logger.error(deliveryExecutionRecordPrintable(executionRecord));
|
||||
}
|
||||
|
||||
executionRecord.executionEndTime = Date.now();
|
||||
await next();
|
||||
}
|
||||
|
||||
async function processDelivery(ctx: GRContext) {
|
||||
async function processDelivery(
|
||||
ctx: GRContext,
|
||||
executionRecord: DeliveryExecutionRecord
|
||||
) {
|
||||
const deliveryVaa = parseWormholeRelayerSend(ctx.vaa!.payload);
|
||||
const sourceDeliveryProvider = ethers.utils.getAddress(wh.tryUint8ArrayToNative(deliveryVaa.sourceDeliveryProvider, "ethereum"));
|
||||
executionRecord.didParse = true;
|
||||
const sourceDeliveryProvider = ethers.utils.getAddress(
|
||||
wh.tryUint8ArrayToNative(deliveryVaa.sourceDeliveryProvider, "ethereum")
|
||||
);
|
||||
if (
|
||||
sourceDeliveryProvider !==
|
||||
ctx.deliveryProviders[ctx.vaa!.emitterChain as EVMChainId]
|
||||
|
@ -49,14 +80,25 @@ async function processDelivery(ctx: GRContext) {
|
|||
ctx.logger.info("Delivery vaa specifies different relay provider", {
|
||||
sourceDeliveryProvider: deliveryVaa.sourceDeliveryProvider,
|
||||
});
|
||||
executionRecord.didMatchDeliveryProvider = false;
|
||||
executionRecord.specifiedDeliveryProvider = sourceDeliveryProvider;
|
||||
return;
|
||||
}
|
||||
processDeliveryInstruction(ctx, deliveryVaa, ctx.vaaBytes!);
|
||||
processDeliveryInstruction(ctx, deliveryVaa, ctx.vaaBytes!, executionRecord);
|
||||
}
|
||||
|
||||
async function processRedelivery(ctx: GRContext) {
|
||||
async function processRedelivery(
|
||||
ctx: GRContext,
|
||||
executionRecord: DeliveryExecutionRecord
|
||||
) {
|
||||
executionRecord.redeliveryRecord = {};
|
||||
const redeliveryVaa = parseWormholeRelayerResend(ctx.vaa!.payload);
|
||||
const sourceDeliveryProvider = ethers.utils.getAddress(wh.tryUint8ArrayToNative(redeliveryVaa.newSourceDeliveryProvider, "ethereum"));
|
||||
const sourceDeliveryProvider = ethers.utils.getAddress(
|
||||
wh.tryUint8ArrayToNative(
|
||||
redeliveryVaa.newSourceDeliveryProvider,
|
||||
"ethereum"
|
||||
)
|
||||
);
|
||||
if (
|
||||
sourceDeliveryProvider !==
|
||||
ctx.deliveryProviders[ctx.vaa!.emitterChain as EVMChainId]
|
||||
|
@ -64,32 +106,76 @@ async function processRedelivery(ctx: GRContext) {
|
|||
ctx.logger.info("Delivery vaa specifies different relay provider", {
|
||||
sourceDeliveryProvider: redeliveryVaa.newSourceDeliveryProvider,
|
||||
});
|
||||
executionRecord.didMatchDeliveryProvider = false;
|
||||
executionRecord.specifiedDeliveryProvider = sourceDeliveryProvider;
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!redeliveryVaa.deliveryVaaKey.emitterAddress ||
|
||||
!redeliveryVaa.deliveryVaaKey.sequence ||
|
||||
!redeliveryVaa.deliveryVaaKey.chainId
|
||||
) {
|
||||
executionRecord.redeliveryRecord.validVaaKeyFormat = false;
|
||||
throw new Error(`Received an invalid redelivery VAA key`);
|
||||
}
|
||||
|
||||
ctx.logger.info(
|
||||
`Redelivery requested for the following VAA: `,
|
||||
vaaKeyPrintable(redeliveryVaa.deliveryVaaKey)
|
||||
);
|
||||
executionRecord.redeliveryRecord.vaaKeyPrintable = vaaKeyPrintable(
|
||||
redeliveryVaa.deliveryVaaKey
|
||||
).toString();
|
||||
|
||||
let originalVaa = await ctx.fetchVaa(
|
||||
redeliveryVaa.deliveryVaaKey.chainId as wh.ChainId,
|
||||
Buffer.from(redeliveryVaa.deliveryVaaKey.emitterAddress!),
|
||||
redeliveryVaa.deliveryVaaKey.sequence!.toBigInt()
|
||||
);
|
||||
executionRecord.redeliveryRecord.originalVaaFetchTimeStart = Date.now();
|
||||
|
||||
let originalVaa: ParsedVaaWithBytes;
|
||||
try {
|
||||
originalVaa = await ctx.fetchVaa(
|
||||
redeliveryVaa.deliveryVaaKey.chainId as wh.ChainId,
|
||||
Buffer.from(redeliveryVaa.deliveryVaaKey.emitterAddress!),
|
||||
redeliveryVaa.deliveryVaaKey.sequence!.toBigInt()
|
||||
);
|
||||
executionRecord.redeliveryRecord.originalVaaDidFetch = true;
|
||||
executionRecord.redeliveryRecord.originalVaaHex =
|
||||
originalVaa.bytes.toString("hex");
|
||||
} catch (e: any) {
|
||||
//TODO this failure mode is encountered both if the VAA does not exist, I.E, the redelivery is invalid,
|
||||
// but also if there's just a network or RPC error in fetching the VAA. We should distinguish between these
|
||||
// two cases, because the first case does not need to be retried, but the second case does.
|
||||
ctx.logger.error(
|
||||
`Failed while attempting to pull original delivery VAA: ${e}`
|
||||
);
|
||||
addFatalError(executionRecord, e);
|
||||
return;
|
||||
}
|
||||
|
||||
executionRecord.redeliveryRecord.originalVaaFetchTimeEnd = Date.now();
|
||||
|
||||
ctx.logger.info("Retrieved original VAA!");
|
||||
const delivery = parseWormholeRelayerSend(originalVaa.payload);
|
||||
if (!isValidRedelivery(ctx, delivery, redeliveryVaa)) {
|
||||
const validityCheck = isValidRedelivery(ctx, delivery, redeliveryVaa); //TODO better name?
|
||||
if (!validityCheck.isValid) {
|
||||
ctx.logger.info("Exiting redelivery process");
|
||||
executionRecord.redeliveryRecord.isValidRedelivery = false;
|
||||
executionRecord.redeliveryRecord.invalidRedeliveryReason =
|
||||
validityCheck.reason;
|
||||
return;
|
||||
} else {
|
||||
executionRecord.redeliveryRecord.isValidRedelivery = true;
|
||||
ctx.logger.info("Redelivery is valid, proceeding with redelivery");
|
||||
processDeliveryInstruction(ctx, delivery, originalVaa.bytes, {
|
||||
newReceiverValue: redeliveryVaa.newRequestedReceiverValue,
|
||||
newExecutionInfo: redeliveryVaa.newEncodedExecutionInfo,
|
||||
redeliveryHash: ctx.vaa!.hash,
|
||||
});
|
||||
processDeliveryInstruction(
|
||||
ctx,
|
||||
delivery,
|
||||
originalVaa.bytes,
|
||||
executionRecord,
|
||||
{
|
||||
newReceiverValue: redeliveryVaa.newRequestedReceiverValue,
|
||||
newExecutionInfo: redeliveryVaa.newEncodedExecutionInfo,
|
||||
redeliveryHash: ctx.vaa!.hash,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,79 +183,68 @@ function isValidRedelivery(
|
|||
ctx: GRContext,
|
||||
delivery: DeliveryInstruction,
|
||||
redelivery: RedeliveryInstruction
|
||||
): boolean {
|
||||
//TODO check that the delivery & redelivery chains agree!
|
||||
): { isValid: boolean; reason?: string } {
|
||||
const output: any = { isValid: true };
|
||||
|
||||
if (delivery.targetChainId != redelivery.targetChainId) {
|
||||
ctx.logger.info(
|
||||
"Redelivery targetChain does not match original delivery targetChain"
|
||||
);
|
||||
ctx.logger.info(
|
||||
output.isValid = false;
|
||||
output.reason =
|
||||
"Redelivery targetChain does not match original delivery targetChain, " +
|
||||
"Original targetChain: " +
|
||||
delivery.targetChainId +
|
||||
" Redelivery targetChain: " +
|
||||
redelivery.targetChainId
|
||||
);
|
||||
return false;
|
||||
delivery.targetChainId +
|
||||
" Redelivery targetChain: " +
|
||||
redelivery.targetChainId;
|
||||
ctx.logger.info(output.reason);
|
||||
return output;
|
||||
}
|
||||
|
||||
//TODO check that the sourceRelayerAddress is one of this relayer's addresses
|
||||
if (!redelivery.newSourceDeliveryProvider) {
|
||||
}
|
||||
|
||||
const [deliveryExecutionInfo,] = parseEVMExecutionInfoV1(delivery.encodedExecutionInfo, 0);
|
||||
const [redeliveryExecutionInfo,] = parseEVMExecutionInfoV1(redelivery.newEncodedExecutionInfo, 0);
|
||||
|
||||
if (deliveryExecutionInfo.targetChainRefundPerGasUnused.gt(redeliveryExecutionInfo.targetChainRefundPerGasUnused)) {
|
||||
ctx.logger.info(
|
||||
"Redelivery target chain refund per gas unused is less than original delivery target chain refund per gas unused"
|
||||
);
|
||||
ctx.logger.info(
|
||||
"Original refund: " +
|
||||
deliveryExecutionInfo.targetChainRefundPerGasUnused.toBigInt().toLocaleString() +
|
||||
" Redelivery: " +
|
||||
redeliveryExecutionInfo.targetChainRefundPerGasUnused.toBigInt().toLocaleString()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (delivery.requestedReceiverValue.gt(redelivery.newRequestedReceiverValue)) {
|
||||
ctx.logger.info(
|
||||
"Redelivery requested receiverValue is less than original delivery requested receiverValue"
|
||||
);
|
||||
ctx.logger.info(
|
||||
"Original refund: " +
|
||||
delivery.requestedReceiverValue.toBigInt().toLocaleString(),
|
||||
+" Redelivery: " +
|
||||
redelivery.newRequestedReceiverValue.toBigInt().toLocaleString()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
deliveryExecutionInfo.gasLimit >
|
||||
redeliveryExecutionInfo.gasLimit
|
||||
delivery.requestedReceiverValue.gt(redelivery.newRequestedReceiverValue)
|
||||
) {
|
||||
ctx.logger.info(
|
||||
"Redelivery gasLimit is less than original delivery gasLimit"
|
||||
);
|
||||
ctx.logger.info(
|
||||
"Original refund: " + deliveryExecutionInfo.gasLimit,
|
||||
" Redelivery: " + redeliveryExecutionInfo.gasLimit
|
||||
);
|
||||
return false;
|
||||
output.isValid = false;
|
||||
(output.reason =
|
||||
"Redelivery receiverValueTarget is less than original delivery receiverValueTarget, " +
|
||||
"Original receiverValue: " +
|
||||
delivery.requestedReceiverValue.toBigInt().toLocaleString()),
|
||||
+" Redelivery: " +
|
||||
redelivery.newRequestedReceiverValue.toBigInt().toLocaleString();
|
||||
ctx.logger.info(output.reason);
|
||||
return output;
|
||||
}
|
||||
|
||||
return true;
|
||||
//TODO check that information inside the execution params is valid
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
async function processDeliveryInstruction(
|
||||
ctx: GRContext,
|
||||
delivery: DeliveryInstruction,
|
||||
deliveryVaa: Buffer | Uint8Array,
|
||||
executionRecord: DeliveryExecutionRecord,
|
||||
overrides?: DeliveryOverrideArgs
|
||||
) {
|
||||
executionRecord.deliveryRecord = {};
|
||||
executionRecord.deliveryRecord.deliveryInstructionsPrintable =
|
||||
deliveryInstructionsPrintable(delivery).toString();
|
||||
executionRecord.deliveryRecord.hasAdditionalVaas =
|
||||
delivery.vaaKeys.length > 0;
|
||||
|
||||
//TODO this check is not quite correct
|
||||
if (
|
||||
delivery.vaaKeys.findIndex(
|
||||
(m) => !m.emitterAddress || !m.sequence || !m.chainId
|
||||
) != -1
|
||||
) {
|
||||
executionRecord.deliveryRecord.additionalVaaKeysFormatValid = false;
|
||||
throw new Error(`Received an invalid additional VAA key`);
|
||||
}
|
||||
const vaaKeysString = delivery.vaaKeys.map((m) => vaaKeyPrintable(m));
|
||||
ctx.logger.info(`Fetching vaas from parsed delivery vaa manifest...`, {
|
||||
vaaKeys: delivery.vaaKeys.map(vaaKeyPrintable),
|
||||
vaaKeys: vaaKeysString,
|
||||
});
|
||||
executionRecord.deliveryRecord.additionalVaaKeysPrintable =
|
||||
vaaKeysString.toString();
|
||||
|
||||
const vaaIds = delivery.vaaKeys.map((m) => ({
|
||||
emitterAddress: m.emitterAddress!,
|
||||
|
@ -177,10 +252,25 @@ async function processDeliveryInstruction(
|
|||
sequence: m.sequence!.toBigInt(),
|
||||
}));
|
||||
|
||||
let results = await ctx.fetchVaas({
|
||||
ids: vaaIds,
|
||||
// txHash: ctx.sourceTxHash,
|
||||
});
|
||||
let results: ParsedVaaWithBytes[];
|
||||
|
||||
executionRecord.deliveryRecord.fetchAdditionalVaasTimeStart = Date.now();
|
||||
try {
|
||||
results = await ctx.fetchVaas({
|
||||
ids: vaaIds,
|
||||
// txHash: ctx.sourceTxHash,
|
||||
});
|
||||
executionRecord.deliveryRecord.additionalVaasDidFetch = true;
|
||||
} catch (e: any) {
|
||||
ctx.logger.error(`Failed while attempting to pull additional VAAs: ${e}`);
|
||||
executionRecord.deliveryRecord.additionalVaasDidFetch = false;
|
||||
addFatalError(executionRecord, e);
|
||||
return;
|
||||
}
|
||||
executionRecord.deliveryRecord.fetchAdditionalVaasTimeEnd = Date.now();
|
||||
executionRecord.deliveryRecord.additionalVaasHex = results.map((v) =>
|
||||
v.bytes.toString("hex")
|
||||
);
|
||||
|
||||
ctx.logger.debug(`Processing delivery`, {
|
||||
deliveryVaa: deliveryInstructionsPrintable(delivery),
|
||||
|
@ -189,64 +279,130 @@ async function processDeliveryInstruction(
|
|||
const chainId = delivery.targetChainId as EVMChainId;
|
||||
const receiverValue = overrides?.newReceiverValue
|
||||
? overrides.newReceiverValue
|
||||
: (delivery.requestedReceiverValue.add(delivery.extraReceiverValue));
|
||||
: delivery.requestedReceiverValue.add(delivery.extraReceiverValue);
|
||||
const getMaxRefund = (encodedDeliveryInfo: Buffer) => {
|
||||
const [deliveryInfo,] = parseEVMExecutionInfoV1(encodedDeliveryInfo, 0);
|
||||
return deliveryInfo.targetChainRefundPerGasUnused.mul(deliveryInfo.gasLimit);
|
||||
}
|
||||
const maxRefund = getMaxRefund(overrides?.newExecutionInfo
|
||||
? overrides.newExecutionInfo
|
||||
: delivery.encodedExecutionInfo);
|
||||
const [deliveryInfo] = parseEVMExecutionInfoV1(encodedDeliveryInfo, 0);
|
||||
return deliveryInfo.targetChainRefundPerGasUnused.mul(
|
||||
deliveryInfo.gasLimit
|
||||
);
|
||||
};
|
||||
const maxRefund = getMaxRefund(
|
||||
overrides?.newExecutionInfo
|
||||
? overrides.newExecutionInfo
|
||||
: delivery.encodedExecutionInfo
|
||||
);
|
||||
const budget = receiverValue.add(maxRefund);
|
||||
|
||||
await ctx.wallets.onEVM(chainId, async ({ wallet }) => {
|
||||
const wormholeRelayer = IWormholeRelayerDelivery__factory.connect(
|
||||
ctx.wormholeRelayers[chainId],
|
||||
wallet
|
||||
);
|
||||
executionRecord.deliveryRecord.chainId = chainId;
|
||||
executionRecord.deliveryRecord.receiverValue = receiverValue.toString();
|
||||
executionRecord.deliveryRecord.maxRefund = maxRefund.toString();
|
||||
executionRecord.deliveryRecord.budget = budget.toString();
|
||||
|
||||
const encodedVMs = results.map((v) => v.bytes);
|
||||
const packedOverrides = overrides ? packOverrides(overrides) : [];
|
||||
const gasUnitsEstimate = await wormholeRelayer.estimateGas.deliver(encodedVMs, deliveryVaa, wallet.address, packedOverrides, {
|
||||
value: budget,
|
||||
gasLimit: 3000000,
|
||||
executionRecord.deliveryRecord.walletAcquisitionStartTime = Date.now();
|
||||
|
||||
try {
|
||||
await ctx.wallets.onEVM(chainId, async ({ wallet }) => {
|
||||
executionRecord.deliveryRecord!.walletAcquisitionEndTime = Date.now();
|
||||
executionRecord.deliveryRecord!.walletAcquisitionDidSucceed = true;
|
||||
executionRecord.deliveryRecord!.walletAddress = wallet.address;
|
||||
executionRecord.deliveryRecord!.walletBalance = (
|
||||
await wallet.getBalance()
|
||||
).toString();
|
||||
executionRecord.deliveryRecord!.walletNonce =
|
||||
await wallet.getTransactionCount();
|
||||
|
||||
const wormholeRelayer = WormholeRelayer__factory.connect(
|
||||
ctx.wormholeRelayers[chainId],
|
||||
wallet
|
||||
);
|
||||
|
||||
//TODO properly import this type from the SDK for safety
|
||||
const input: any = {
|
||||
encodedVMs: results.map((v) => v.bytes),
|
||||
encodedDeliveryVAA: deliveryVaa,
|
||||
relayerRefundAddress: wallet.address,
|
||||
overrides: overrides ? packOverrides(overrides) : [],
|
||||
};
|
||||
|
||||
const gasUnitsEstimate = await wormholeRelayer.estimateGas.deliver(
|
||||
results.map((v) => v.bytes),
|
||||
deliveryVaa,
|
||||
wallet.address,
|
||||
overrides ? packOverrides(overrides) : [],
|
||||
{
|
||||
value: budget,
|
||||
gasLimit: 3000000,
|
||||
}
|
||||
);
|
||||
|
||||
const gasPrice = await wormholeRelayer.provider.getGasPrice();
|
||||
const estimatedTransactionFee = gasPrice.mul(gasUnitsEstimate);
|
||||
const estimatedTransactionFeeEther = ethers.utils.formatEther(
|
||||
estimatedTransactionFee
|
||||
);
|
||||
|
||||
executionRecord.deliveryRecord!.gasUnitsEstimate =
|
||||
gasUnitsEstimate.toNumber();
|
||||
executionRecord.deliveryRecord!.gasPriceEstimate = gasPrice.toString();
|
||||
executionRecord.deliveryRecord!.estimatedTransactionFee =
|
||||
estimatedTransactionFee.toString();
|
||||
executionRecord.deliveryRecord!.estimatedTransactionFeeEther =
|
||||
estimatedTransactionFeeEther;
|
||||
|
||||
ctx.logger.info(
|
||||
`Estimated transaction cost (ether): ${estimatedTransactionFeeEther}`,
|
||||
{
|
||||
gasUnitsEstimate: gasUnitsEstimate.toString(),
|
||||
gasPrice: gasPrice.toString(),
|
||||
estimatedTransactionFee: estimatedTransactionFee.toString(),
|
||||
estimatedTransactionFeeEther,
|
||||
valueEther: ethers.utils.formatEther(budget),
|
||||
}
|
||||
);
|
||||
process.stdout.write("");
|
||||
await sleep(200);
|
||||
ctx.logger.debug("Sending 'deliver' tx...");
|
||||
|
||||
executionRecord.deliveryRecord!.transactionSubmitTimeStart = Date.now();
|
||||
const receipt = await wormholeRelayer
|
||||
.deliver(
|
||||
results.map((v) => v.bytes),
|
||||
deliveryVaa,
|
||||
wallet.address,
|
||||
overrides ? packOverrides(overrides) : [],
|
||||
{
|
||||
value: budget,
|
||||
gasLimit: 3000000,
|
||||
}
|
||||
) //TODO more intelligent gas limit
|
||||
.then((x: any) => x.wait());
|
||||
executionRecord.deliveryRecord!.transactionSubmitTimeEnd = Date.now();
|
||||
executionRecord.deliveryRecord!.transactionDidSubmit = true;
|
||||
executionRecord.deliveryRecord!.transactionHashes = [
|
||||
receipt.transactionHash,
|
||||
];
|
||||
|
||||
logResults(ctx, receipt, chainId, executionRecord);
|
||||
});
|
||||
const gasPrice = await wormholeRelayer.provider.getGasPrice();
|
||||
const estimatedTransactionFee = gasPrice.mul(gasUnitsEstimate);
|
||||
const estimatedTransactionFeeEther = ethers.utils.formatEther(
|
||||
estimatedTransactionFee
|
||||
);
|
||||
ctx.logger.info(
|
||||
`Estimated transaction cost (ether): ${estimatedTransactionFeeEther}`,
|
||||
{
|
||||
gasUnitsEstimate: gasUnitsEstimate.toString(),
|
||||
gasPrice: gasPrice.toString(),
|
||||
estimatedTransactionFee: estimatedTransactionFee.toString(),
|
||||
estimatedTransactionFeeEther,
|
||||
valueEther: ethers.utils.formatEther(budget),
|
||||
}
|
||||
);
|
||||
process.stdout.write("");
|
||||
await sleep(200);
|
||||
ctx.logger.debug("Sending 'deliver' tx...");
|
||||
const receipt = await wormholeRelayer
|
||||
.deliver(encodedVMs, deliveryVaa, wallet.address, packedOverrides, { value: budget, gasLimit: 3000000 })
|
||||
.then((x: any) => x.wait());
|
||||
|
||||
logResults(ctx, receipt, chainId);
|
||||
});
|
||||
} catch (e: any) {
|
||||
ctx.logger.error(`Fatal error in processGenericRelayerVaa: ${e}`);
|
||||
addFatalError(executionRecord, e);
|
||||
ctx.logger.error("Dumping execution context for fatal error");
|
||||
ctx.logger.error(deliveryExecutionRecordPrintable(executionRecord));
|
||||
}
|
||||
}
|
||||
|
||||
function logResults(
|
||||
ctx: GRContext,
|
||||
receipt: ethers.ContractReceipt,
|
||||
chainId: EVMChainId
|
||||
chainId: EVMChainId,
|
||||
executionRecord: DeliveryExecutionRecord
|
||||
) {
|
||||
const relayerContractLog = receipt.logs?.find((x: any) => {
|
||||
return x.address === ctx.wormholeRelayers[chainId];
|
||||
});
|
||||
if (relayerContractLog) {
|
||||
const parsedLog = IWormholeRelayerDelivery__factory.createInterface().parseLog(
|
||||
const parsedLog = WormholeRelayer__factory.createInterface().parseLog(
|
||||
relayerContractLog!
|
||||
);
|
||||
const logArgs = {
|
||||
|
@ -257,15 +413,19 @@ function logResults(
|
|||
status: parsedLog.args[4],
|
||||
};
|
||||
ctx.logger.info("Parsed Delivery event", logArgs);
|
||||
executionRecord.deliveryRecord!.resultLogDidParse = true;
|
||||
switch (logArgs.status) {
|
||||
case 0:
|
||||
ctx.logger.info("Delivery Success");
|
||||
executionRecord.deliveryRecord!.resultLog = "Delivery Success";
|
||||
break;
|
||||
case 1:
|
||||
ctx.logger.info("Receiver Failure");
|
||||
executionRecord.deliveryRecord!.resultLog = "Receiver Failure";
|
||||
break;
|
||||
case 2:
|
||||
ctx.logger.info("Forwarding Failure");
|
||||
executionRecord.deliveryRecord!.resultLog = "Forwarding Failure";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue