"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.fetchAdditionalVaas = exports.extractDeliveryArguments = exports.deliveryBudget = exports.deliver = void 0;
const ethers_contracts_1 = require("../../ethers-contracts");
const utils_1 = require("../../utils");
const vaa_1 = require("../../vaa");
const consts_1 = require("../consts");
const structs_1 = require("../structs");
const rpc_1 = require("../../rpc");
function deliver(deliveryVaa, signer, wormholeRPCs, environment = "MAINNET", overrides) {
    return __awaiter(this, void 0, void 0, function* () {
        const { budget, deliveryInstruction, deliveryHash } = extractDeliveryArguments(deliveryVaa, overrides);
        const additionalVaas = yield fetchAdditionalVaas(wormholeRPCs, deliveryInstruction.vaaKeys);
        const wormholeRelayerAddress = consts_1.getWormholeRelayerAddress(utils_1.toChainName(deliveryInstruction.targetChainId), environment);
        const wormholeRelayer = ethers_contracts_1.IWormholeRelayer__factory.connect(wormholeRelayerAddress, signer);
        const gasEstimate = yield wormholeRelayer.estimateGas.deliver(additionalVaas, deliveryVaa, signer.getAddress(), overrides ? structs_1.packOverrides(overrides) : new Uint8Array(), { value: budget });
        const tx = yield wormholeRelayer.deliver(additionalVaas, deliveryVaa, signer.getAddress(), overrides ? structs_1.packOverrides(overrides) : new Uint8Array(), { value: budget, gasLimit: gasEstimate.mul(2) });
        const rx = yield tx.wait();
        console.log(`Delivered ${deliveryHash} on ${rx.blockNumber}`);
        return rx;
    });
}
exports.deliver = deliver;
function deliveryBudget(delivery, overrides) {
    const receiverValue = (overrides === null || overrides === void 0 ? void 0 : overrides.newReceiverValue)
        ? overrides.newReceiverValue
        : delivery.requestedReceiverValue.add(delivery.extraReceiverValue);
    const getMaxRefund = (encodedDeliveryInfo) => {
        const [deliveryInfo] = structs_1.parseEVMExecutionInfoV1(encodedDeliveryInfo, 0);
        return deliveryInfo.targetChainRefundPerGasUnused.mul(deliveryInfo.gasLimit);
    };
    const maxRefund = getMaxRefund((overrides === null || overrides === void 0 ? void 0 : overrides.newExecutionInfo)
        ? overrides.newExecutionInfo
        : delivery.encodedExecutionInfo);
    return receiverValue.add(maxRefund);
}
exports.deliveryBudget = deliveryBudget;
function extractDeliveryArguments(vaa, overrides) {
    const parsedVaa = vaa_1.parseVaa(vaa);
    const payloadType = structs_1.parseWormholeRelayerPayloadType(parsedVaa.payload);
    if (payloadType !== structs_1.RelayerPayloadId.Delivery) {
        throw new Error(`Expected delivery payload type, got ${structs_1.RelayerPayloadId[payloadType]}`);
    }
    const deliveryInstruction = structs_1.parseWormholeRelayerSend(parsedVaa.payload);
    const budget = deliveryBudget(deliveryInstruction, overrides);
    return {
        budget,
        deliveryInstruction: deliveryInstruction,
        deliveryHash: parsedVaa.hash.toString("hex"),
    };
}
exports.extractDeliveryArguments = extractDeliveryArguments;
function fetchAdditionalVaas(wormholeRPCs, additionalVaaKeys) {
    return __awaiter(this, void 0, void 0, function* () {
        const rpcs = typeof wormholeRPCs === "string" ? [wormholeRPCs] : wormholeRPCs;
        const vaas = yield Promise.all(additionalVaaKeys.map((vaaKey) => __awaiter(this, void 0, void 0, function* () {
            return rpc_1.getSignedVAAWithRetry(rpcs, vaaKey.chainId, vaaKey.emitterAddress.toString("hex"), vaaKey.sequence.toBigInt().toString());
        })));
        return vaas.map((vaa) => vaa.vaaBytes);
    });
}
exports.fetchAdditionalVaas = fetchAdditionalVaas;
