import { __assign, __awaiter, __generator, __rest, __spreadArray } from "tslib";
import { MessageTransmitterClient, SvmSpokeClient, TokenMessengerMinterClient } from "../../svm";
import { decodeMessageHeader } from "../../svm/web3-v1/cctpHelpers";
import { SYSTEM_PROGRAM_ADDRESS } from "@solana-program/system";
import { ASSOCIATED_TOKEN_PROGRAM_ADDRESS, TOKEN_PROGRAM_ADDRESS, fetchMint, getApproveCheckedInstruction, getCreateAssociatedTokenIdempotentInstruction, getCreateAssociatedTokenInstruction, } from "@solana-program/token";
import { AccountRole, appendTransactionMessageInstruction, fetchEncodedAccount, fetchEncodedAccounts, getAddressEncoder, getBase64EncodedWireTransaction, getProgramDerivedAddress, getSignatureFromTransaction, getU32Encoder, getU64Encoder, pipe, signTransactionMessageWithSigners, some, compileTransaction, } from "@solana/kit";
import assert from "assert";
import { arrayify } from "ethers/lib/utils";
import { CHAIN_IDs, TOKEN_SYMBOLS_MAP } from "../../constants";
import { FillStatus, } from "../../interfaces";
import { EvmAddress, SvmAddress, bs58, chainIsProd, chainIsSvm, chunk, getMessageHash, isDefined, isUnsafeDepositId, keccak256, mapAsync, unpackDepositEvent, unpackFillEvent, } from "../../utils";
import { createDefaultTransaction, getCCTPNoncePda, getEventAuthority, getFillStatusPda, getSelfAuthority, getStatePda, isDepositForBurnEvent, simulateAndDecode, toAddress, unwrapEventData, getRootBundlePda, getAcrossPlusMessageDecoder, getAccountMeta, getInstructionParamsPda, toSvmRelayData, } from "./";
import { SVM_LONG_TERM_STORAGE_SLOT_SKIPPED, SVM_SLOT_SKIPPED, isSolanaError } from "./provider";
import { SVMEventNames } from "./types";
import { getEmergencyDeleteRootBundleRootBundleId, getNearestSlotTime, isEmergencyDeleteRootBundleMessageBody, isRelayRootBundleMessageBody, } from "./utils";
/**
 * @note: Average Solana slot duration is about 400-500ms. We can be conservative
 *        and choose 400 to ensure that the most slots get included in our ranges
 */
export var SLOT_DURATION_MS = 400;
export var SOLANA_TX_SIZE_LIMIT = 1232;
export function getSlot(provider, commitment, logger) {
    return _callGetSlotWithRetry(provider, commitment, logger);
}
function _callGetSlotWithRetry(provider, commitment, logger) {
    return __awaiter(this, void 0, void 0, function () {
        var err_1, code;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    _a.trys.push([0, 2, , 3]);
                    return [4 /*yield*/, provider.getSlot({ commitment: commitment }).send()];
                case 1: return [2 /*return*/, _a.sent()];
                case 2:
                    err_1 = _a.sent();
                    if (isSolanaError(err_1)) {
                        code = err_1.context.__code;
                        logger === null || logger === void 0 ? void 0 : logger.debug({
                            at: "_getSlotWithRetry",
                            message: "Caught error from getSlot()",
                            code: code,
                            commitment: commitment,
                        });
                    }
                    // TODO: Implement retry logic once we better understand how these errors look:
                    throw err_1;
                case 3: return [2 /*return*/];
            }
        });
    });
}
/**
 * Retrieves the chain time at a particular slot.
 */
export function getTimestampForSlot(provider, slotNumber, maxRetries, logger) {
    if (maxRetries === void 0) { maxRetries = 2; }
    return _callGetTimestampForSlotWithRetry(provider, slotNumber, 0, maxRetries, logger);
}
function _callGetTimestampForSlotWithRetry(provider, slotNumber, retryAttempt, maxRetries, logger) {
    return __awaiter(this, void 0, void 0, function () {
        var slot, _timestamp, err_2, at, code, message, timestamp;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    slot = slotNumber.toString();
                    _a.label = 1;
                case 1:
                    _a.trys.push([1, 3, , 4]);
                    return [4 /*yield*/, provider.getBlockTime(slotNumber).send()];
                case 2:
                    // @note: getBlockTime receives a slot number, not a block number.
                    _timestamp = _a.sent();
                    return [3 /*break*/, 4];
                case 3:
                    err_2 = _a.sent();
                    if (!isSolanaError(err_2)) {
                        throw err_2;
                    }
                    at = "getTimestampForSlot";
                    code = err_2.context.__code;
                    switch (code) {
                        case SVM_SLOT_SKIPPED:
                        case SVM_LONG_TERM_STORAGE_SLOT_SKIPPED:
                            // No block available for this slot; caller must decide on how to handle this.
                            return [2 /*return*/, undefined];
                        default: {
                            message = "Caught error from getBlockTime()";
                            logger === null || logger === void 0 ? void 0 : logger.debug({ at: at, message: message, errorCode: code, slot: slot, retryAttempt: retryAttempt, maxRetries: maxRetries });
                            throw new Error("Unhandled SVM getBlockTime() error for slot ".concat(slot, ": ").concat(code), { cause: err_2 });
                        }
                    }
                    return [3 /*break*/, 4];
                case 4:
                    timestamp = Number(_timestamp);
                    assert(!isDefined(_timestamp) || BigInt(timestamp) === _timestamp, "Unexpected block timestamp for SVM slot ".concat(slot, ": ").concat(_timestamp));
                    return [2 /*return*/, timestamp];
            }
        });
    });
}
/**
 * Returns the current fill deadline buffer.
 * @param provider SVM Provider instance
 * @param statePda Spoke Pool's State PDA
 * @returns fill deadline buffer
 */
export function getFillDeadline(provider, statePda) {
    return __awaiter(this, void 0, void 0, function () {
        var state;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0: return [4 /*yield*/, SvmSpokeClient.fetchState(provider, statePda)];
                case 1:
                    state = _a.sent();
                    return [2 /*return*/, state.data.fillDeadlineBuffer];
            }
        });
    });
}
/**
 * Finds the deposit id at a specific block number.
 * @param blockTag The block number to search for the deposit ID at.
 * @returns The deposit ID.
 */
export function getDepositIdAtBlock(_contract, _blockTag) {
    throw new Error("getDepositIdAtBlock: not implemented");
}
/**
 * Finds deposit events within a 2-day window ending at the specified slot.
 *
 * @remarks
 * This implementation uses a slot-limited search approach because Solana PDA state has
 * limitations that prevent directly referencing old deposit IDs. Unlike EVM chains where
 * we might use binary search across the entire chain history, in Solana we must query within
 * a constrained slot range.
 *
 * The search window is calculated by:
 * 1. Using the provided slot (or current confirmed slot if none is provided)
 * 2. Looking back 2 days worth of slots from that point
 *
 * We use a 2-day window because:
 * 1. Most valid deposits that need to be processed will be recent
 * 2. This covers multiple bundle submission periods
 * 3. It balances performance with practical deposit age
 *
 * @important
 * This function may return `undefined` for valid deposit IDs that are older than the search
 * window (approximately 2 days before the specified slot). This is an acceptable limitation
 * as deposits this old are typically not relevant to current operations.
 *
 * @param eventClient - SvmCpiEventsClient instance
 * @param depositId - The deposit ID to search for
 * @param slot - The slot to search up to (defaults to current slot). The search will look
 *              for deposits between (slot - secondsLookback) and slot.
 * @param secondsLookback - The number of seconds to look back for deposits (defaults to 2 days).
 * @returns The deposit if found within the slot window, undefined otherwise
 */
export function findDeposit(eventClient_1, depositId_1, logger_1, slot_1) {
    return __awaiter(this, arguments, void 0, function (eventClient, depositId, logger, slot, secondsLookback // 2 days
    ) {
        var provider, opts, currentSlot, endSlot, slotsInElapsed, startSlot, depositEvent, txnIndex, logIndex, blockNumber, txnRef, rawData, _a, originChainId, deposit;
        var _b;
        if (secondsLookback === void 0) { secondsLookback = 2 * 24 * 60 * 60; }
        return __generator(this, function (_c) {
            switch (_c.label) {
                case 0:
                    // We can only perform this search when we have a safe deposit ID.
                    if (isUnsafeDepositId(depositId)) {
                        throw new Error("Cannot binary search for depositId ".concat(depositId));
                    }
                    provider = eventClient.getRpc();
                    opts = undefined;
                    return [4 /*yield*/, getNearestSlotTime(provider, opts, logger)];
                case 1:
                    currentSlot = (_c.sent()).slot;
                    endSlot = slot !== undefined ? BigInt(Math.min(Number(slot), Number(currentSlot))) : currentSlot;
                    slotsInElapsed = BigInt(Math.round((secondsLookback * 1000) / SLOT_DURATION_MS));
                    startSlot = endSlot - slotsInElapsed;
                    return [4 /*yield*/, eventClient.queryEvents("FundsDeposited", startSlot, endSlot)];
                case 2:
                    depositEvent = (_b = (_c.sent())) === null || _b === void 0 ? void 0 : _b.find(function (event) {
                        return depositId.eq(event.data.depositId);
                    });
                    // If no deposit event is found, return undefined
                    if (!depositEvent) {
                        return [2 /*return*/, undefined];
                    }
                    txnIndex = 0;
                    logIndex = 0;
                    blockNumber = Number(depositEvent.slot);
                    txnRef = depositEvent.signature.toString();
                    rawData = unwrapEventData(depositEvent.data, ["depositId", "outputAmount"]);
                    _a = unpackDepositEvent(__assign(__assign({}, rawData), { blockNumber: blockNumber, txnRef: txnRef, txnIndex: txnIndex, logIndex: logIndex }), CHAIN_IDs.SOLANA), originChainId = _a.originChainId, deposit = __rest(_a, ["originChainId"]);
                    return [2 /*return*/, __assign({}, deposit)];
            }
        });
    });
}
/**
 * Resolves the fill status of a deposit at a specific slot or at the current confirmed one.
 *
 * If no slot is provided, attempts to solve the fill status using the PDA. Otherwise, it is reconstructed from PDA events.
 *
 * @param programId - The spoke pool program ID.
 * @param relayData - Deposit information used to locate the fill status.
 * @param destinationChainId - Destination chain ID (must be an SVM chain).
 * @param provider - SVM provider instance.
 * @param svmEventsClient - SVM events client for querying events.
 * @param atHeight - (Optional) Specific slot number to query. Defaults to the latest confirmed slot.
 * @returns The fill status for the deposit at the specified or current slot.
 */
export function relayFillStatus(programId, relayData, destinationChainId, svmEventsClient, logger, atHeight) {
    return __awaiter(this, void 0, void 0, function () {
        var provider, fillStatusPda, toSlot, commitment, _a, fillStatusAccount, _b, currentSlot, timestamp, decodedAccountData;
        return __generator(this, function (_c) {
            switch (_c.label) {
                case 0:
                    assert(chainIsSvm(destinationChainId), "Destination chain must be an SVM chain");
                    provider = svmEventsClient.getRpc();
                    return [4 /*yield*/, getFillStatusPda(programId, relayData, destinationChainId)];
                case 1:
                    fillStatusPda = _c.sent();
                    toSlot = BigInt(atHeight !== null && atHeight !== void 0 ? atHeight : 0);
                    if (!(atHeight === undefined)) return [3 /*break*/, 3];
                    commitment = "confirmed";
                    return [4 /*yield*/, Promise.all([
                            fetchEncodedAccount(provider, fillStatusPda, { commitment: commitment }),
                            getNearestSlotTime(provider, { commitment: commitment }, logger),
                        ])];
                case 2:
                    _a = _c.sent(), fillStatusAccount = _a[0], _b = _a[1], currentSlot = _b.slot, timestamp = _b.timestamp;
                    toSlot = currentSlot;
                    // If the PDA exists, return the stored fill status
                    if (fillStatusAccount.exists) {
                        decodedAccountData = SvmSpokeClient.decodeFillStatusAccount(fillStatusAccount);
                        return [2 /*return*/, decodedAccountData.data.status];
                    }
                    // If the PDA doesn't exist and the deadline hasn't passed yet, the deposit must be unfilled,
                    // since PDAs can't be closed before the fill deadline.
                    else if (timestamp < relayData.fillDeadline) {
                        return [2 /*return*/, FillStatus.Unfilled];
                    }
                    _c.label = 3;
                case 3: 
                // If status couldn't be determined from the PDA, or if a specific slot was requested, reconstruct from events.
                return [2 /*return*/, resolveFillStatusFromPdaEvents(fillStatusPda, toSlot, svmEventsClient)];
            }
        });
    });
}
/**
 * Resolves fill statuses for multiple deposits at a specific or latest confirmed slot,
 * using PDAs when possible and falling back to events if needed.
 *
 * @param programId The spoke pool program ID.
 * @param relayData An array of relay data to resolve fill statuses for.
 * @param destinationChainId The destination chain ID (must be an SVM chain).
 * @param provider SVM Provider instance.
 * @param svmEventsClient SVM events client instance for querying events.
 * @param atHeight (Optional) The slot number to query at. If omitted, queries the latest confirmed slot.
 * @returns An array of fill statuses for the specified deposits at the requested slot (or at the current confirmed slot).
 */
export function fillStatusArray(programId, relayData, destinationChainId, svmEventsClient, logger, atHeight) {
    return __awaiter(this, void 0, void 0, function () {
        var provider, chunkSize, chunkedRelayData, fillStatusPdas, fillStatuses, _a, missingStatuses, missingChunked, missingResults, opts, toSlot, _b, _i, missingChunked_1, chunk_1, chunkResults;
        var _this = this;
        return __generator(this, function (_c) {
            switch (_c.label) {
                case 0:
                    assert(chainIsSvm(destinationChainId), "Destination chain must be an SVM chain");
                    provider = svmEventsClient.getRpc();
                    chunkSize = 100;
                    chunkedRelayData = chunk(relayData, chunkSize);
                    return [4 /*yield*/, Promise.all(chunkedRelayData.map(function (relayDataChunk) {
                            return Promise.all(relayDataChunk.map(function (relayData) { return getFillStatusPda(programId, relayData, destinationChainId); }));
                        }))];
                case 1:
                    fillStatusPdas = (_c.sent()).flat();
                    if (atHeight !== undefined && logger) {
                        logger.warn({
                            at: "SvmSpokeUtils#fillStatusArray",
                            message: "Querying specific slots for large arrays is slow. For current status, omit 'atHeight' param to use latest confirmed slot instead.",
                        });
                    }
                    if (!(atHeight === undefined)) return [3 /*break*/, 3];
                    return [4 /*yield*/, fetchBatchFillStatusFromPdaAccounts(provider, fillStatusPdas, relayData, logger)];
                case 2:
                    _a = _c.sent();
                    return [3 /*break*/, 4];
                case 3:
                    _a = new Array(relayData.length).fill(undefined);
                    _c.label = 4;
                case 4:
                    fillStatuses = _a;
                    missingStatuses = fillStatuses.reduce(function (acc, status, index) {
                        if (status === undefined) {
                            acc.push(index);
                        }
                        return acc;
                    }, []);
                    missingChunked = chunk(missingStatuses, chunkSize);
                    missingResults = [];
                    opts = undefined;
                    if (!atHeight) return [3 /*break*/, 5];
                    _b = BigInt(atHeight);
                    return [3 /*break*/, 7];
                case 5: return [4 /*yield*/, getNearestSlotTime(provider, opts, logger)];
                case 6:
                    _b = (_c.sent()).slot;
                    _c.label = 7;
                case 7:
                    toSlot = _b;
                    _i = 0, missingChunked_1 = missingChunked;
                    _c.label = 8;
                case 8:
                    if (!(_i < missingChunked_1.length)) return [3 /*break*/, 11];
                    chunk_1 = missingChunked_1[_i];
                    return [4 /*yield*/, Promise.all(chunk_1.map(function (missingIndex) { return __awaiter(_this, void 0, void 0, function () {
                            var _a;
                            return __generator(this, function (_b) {
                                switch (_b.label) {
                                    case 0:
                                        _a = {
                                            index: missingIndex
                                        };
                                        return [4 /*yield*/, resolveFillStatusFromPdaEvents(fillStatusPdas[missingIndex], toSlot, svmEventsClient)];
                                    case 1: return [2 /*return*/, (_a.fillStatus = _b.sent(),
                                            _a)];
                                }
                            });
                        }); }))];
                case 9:
                    chunkResults = _c.sent();
                    missingResults.push.apply(missingResults, chunkResults);
                    _c.label = 10;
                case 10:
                    _i++;
                    return [3 /*break*/, 8];
                case 11:
                    // Fill in missing statuses back to the result array
                    missingResults.forEach(function (_a) {
                        var index = _a.index, fillStatus = _a.fillStatus;
                        fillStatuses[index] = fillStatus;
                    });
                    return [2 /*return*/, fillStatuses];
            }
        });
    });
}
/**
 * Finds the `FilledRelay` event for a given deposit within the provided slot range.
 *
 * @param relayData - Deposit information that is used to complete a fill.
 * @param destinationChainId - Destination chain ID (must be an SVM chain).
 * @param svmEventsClient - SVM events client instance for querying events.
 * @param fromSlot - Starting slot to search.
 * @param toSlot (Optional) Ending slot to search. If not provided, the current confirmed slot will be used.
 * @returns The fill event with block info, or `undefined` if not found.
 */
export function findFillEvent(relayData, destinationChainId, svmEventsClient, fromSlot, toSlot, logger) {
    return __awaiter(this, void 0, void 0, function () {
        var opts, _a, _b, programId, fillStatusPda, fillEvents, rawEvent, rawFill, fill;
        return __generator(this, function (_c) {
            switch (_c.label) {
                case 0:
                    assert(chainIsSvm(destinationChainId), "Destination chain must be an SVM chain");
                    opts = undefined;
                    if (!(toSlot !== null && toSlot !== void 0)) return [3 /*break*/, 1];
                    _a = toSlot;
                    return [3 /*break*/, 3];
                case 1:
                    _b = Number;
                    return [4 /*yield*/, getNearestSlotTime(svmEventsClient.getRpc(), opts, logger)];
                case 2:
                    _a = (toSlot = _b.apply(void 0, [(_c.sent()).slot]));
                    _c.label = 3;
                case 3:
                    _a;
                    programId = svmEventsClient.getProgramAddress();
                    return [4 /*yield*/, getFillStatusPda(programId, relayData, destinationChainId)];
                case 4:
                    fillStatusPda = _c.sent();
                    return [4 /*yield*/, svmEventsClient.queryDerivedAddressEvents(SVMEventNames.FilledRelay, fillStatusPda, BigInt(fromSlot), BigInt(toSlot), { limit: 10 })];
                case 5:
                    fillEvents = _c.sent();
                    assert(fillEvents.length <= 1, "Expected at most one fill event for ".concat(fillStatusPda, ", got ").concat(fillEvents.length));
                    rawEvent = fillEvents[0];
                    if (!isDefined(rawEvent)) {
                        return [2 /*return*/];
                    }
                    rawFill = unwrapEventData(rawEvent.data, ["depositId", "inputAmount"]);
                    fill = unpackFillEvent(rawFill, destinationChainId);
                    return [2 /*return*/, fill];
            }
        });
    });
}
/**
 * @param spokePool Address (program ID) of the SvmSpoke.
 * @param relayData RelayData instance, supplemented with destinationChainId
 * @param relayer Address of the relayer filling the deposit.
 * @param repaymentChainId Optional repaymentChainId (defaults to destinationChainId).
 * @returns An Ethers UnsignedTransaction instance.
 */
export function fillRelayInstruction(spokePool, relayData, signer, recipientTokenAccount, repaymentAddress, repaymentChainId) {
    return __awaiter(this, void 0, void 0, function () {
        var program, messageHash, _relayDataHash, relayDataHash, relayer, _a, statePda, fillStatusPda, eventAuthority, delegatePda, relayerTokenAccount, svmRelayData;
        return __generator(this, function (_b) {
            switch (_b.label) {
                case 0:
                    program = toAddress(spokePool);
                    assert(repaymentAddress.isValidOn(repaymentChainId), "Invalid repayment address for chain ".concat(repaymentChainId, ": ").concat(repaymentAddress.toNative(), "."));
                    messageHash = getMessageHash(relayData.message);
                    _relayDataHash = getRelayDataHash(__assign(__assign({}, relayData), { messageHash: messageHash }), relayData.destinationChainId);
                    relayDataHash = new Uint8Array(Buffer.from(_relayDataHash.slice(2), "hex"));
                    relayer = SvmAddress.from(signer.address);
                    return [4 /*yield*/, Promise.all([
                            getStatePda(program),
                            getFillStatusPda(program, relayData, relayData.destinationChainId),
                            getEventAuthority(program),
                            getFillRelayDelegatePda(relayDataHash, BigInt(repaymentChainId), signer.address, program),
                            getAssociatedTokenAddress(relayer, relayData.outputToken),
                        ])];
                case 1:
                    _a = _b.sent(), statePda = _a[0], fillStatusPda = _a[1], eventAuthority = _a[2], delegatePda = _a[3], relayerTokenAccount = _a[4];
                    svmRelayData = toSvmRelayData(relayData);
                    return [2 /*return*/, SvmSpokeClient.getFillRelayInstruction({
                            signer: signer,
                            state: statePda,
                            delegate: delegatePda,
                            mint: svmRelayData.outputToken,
                            relayerTokenAccount: relayerTokenAccount,
                            recipientTokenAccount: recipientTokenAccount,
                            fillStatus: fillStatusPda,
                            eventAuthority: eventAuthority,
                            program: program,
                            relayHash: relayDataHash,
                            relayData: some(svmRelayData),
                            repaymentChainId: some(BigInt(repaymentChainId)),
                            repaymentAddress: some(toAddress(repaymentAddress)),
                        })];
            }
        });
    });
}
/**
 * @param mint Address of the token corresponding to the account being made.
 * @param relayer Address of the relayer filling the deposit.
 * @returns An instruction for creating a new token account.
 */
export function createTokenAccountsInstruction(mint, relayer) {
    return SvmSpokeClient.getCreateTokenAccountsInstruction({
        signer: relayer,
        mint: toAddress(mint),
    });
}
/**
 * @notice Return the fillRelay transaction for a given deposit
 * @param spokePoolAddr Address of the spoke pool we're trying to fill through
 * @param solanaClient RPC client to interact with Solana chain
 * @param relayData RelayData instance, supplemented with destinationChainId
 * @param signer signer associated with the relayer creating a Fill. Can be VoidSigner for gas estimation
 * @param repaymentChainId Chain id where relayer repayment is desired
 * @param repaymentAddress Address to which repayment will go to on repaymentChainId
 * @returns FillRelay transaction
 */
export function getFillRelayTx(spokePoolAddr, solanaClient, relayData, signer, repaymentChainId, repaymentAddress) {
    return __awaiter(this, void 0, void 0, function () {
        var svmRelayData, program, messageHash, _relayDataHash, relayDataHash, _a, state, delegate, mintInfo, fillStatus, eventAuthority, _b, recipientAta, relayerAta, recipientAtaEncodedAccount, remainingAccounts, acrossPlusMessage_1, fillInput;
        return __generator(this, function (_c) {
            switch (_c.label) {
                case 0:
                    svmRelayData = toSvmRelayData(relayData);
                    assert(repaymentAddress.isValidOn(repaymentChainId), "getFillRelayTx: repayment address ".concat(repaymentAddress, " not valid on chain ").concat(repaymentChainId, ")"));
                    program = toAddress(spokePoolAddr);
                    messageHash = getMessageHash(relayData.message);
                    _relayDataHash = getRelayDataHash(__assign(__assign({}, relayData), { messageHash: messageHash }), relayData.destinationChainId);
                    relayDataHash = new Uint8Array(Buffer.from(_relayDataHash.slice(2), "hex"));
                    return [4 /*yield*/, Promise.all([
                            getStatePda(program),
                            getFillRelayDelegatePda(relayDataHash, BigInt(repaymentChainId), toAddress(repaymentAddress), program),
                            getMintInfo(solanaClient, svmRelayData.outputToken),
                            getFillStatusPda(program, relayData, relayData.destinationChainId),
                            getEventAuthority(program),
                        ])];
                case 1:
                    _a = _c.sent(), state = _a[0], delegate = _a[1], mintInfo = _a[2], fillStatus = _a[3], eventAuthority = _a[4];
                    return [4 /*yield*/, Promise.all([
                            getAssociatedTokenAddress(relayData.recipient, relayData.outputToken, mintInfo.programAddress),
                            getAssociatedTokenAddress(SvmAddress.from(signer.address), relayData.outputToken, mintInfo.programAddress),
                        ])];
                case 2:
                    _b = _c.sent(), recipientAta = _b[0], relayerAta = _b[1];
                    return [4 /*yield*/, fetchEncodedAccount(solanaClient, recipientAta)];
                case 3:
                    recipientAtaEncodedAccount = _c.sent();
                    remainingAccounts = [];
                    if (relayData.message !== "0x") {
                        acrossPlusMessage_1 = deserializeMessage(relayData.message);
                        // The first `remainingAccount` _must_ be the handler address.
                        // https://github.com/across-protocol/contracts/blob/3310f8dc716407a5f97ef5fd2eae63df83251f2f/programs/svm-spoke/src/utils/message_utils.rs#L36.
                        remainingAccounts.push(getAccountMeta(acrossPlusMessage_1.handler, true));
                        remainingAccounts.push.apply(remainingAccounts, acrossPlusMessage_1.accounts.map(function (account, idx) {
                            return getAccountMeta(account, idx < acrossPlusMessage_1.accounts.length - acrossPlusMessage_1.read_only_len);
                        }));
                    }
                    fillInput = {
                        signer: signer,
                        state: state,
                        delegate: delegate,
                        mint: svmRelayData.outputToken,
                        relayerTokenAccount: relayerAta,
                        recipientTokenAccount: recipientAta,
                        fillStatus: fillStatus,
                        tokenProgram: mintInfo.programAddress,
                        associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ADDRESS,
                        systemProgram: SYSTEM_PROGRAM_ADDRESS,
                        eventAuthority: eventAuthority,
                        program: program,
                        relayHash: relayDataHash,
                        relayData: svmRelayData,
                        repaymentChainId: BigInt(repaymentChainId),
                        repaymentAddress: toAddress(repaymentAddress),
                    };
                    // Pass createRecipientAtaIfNeeded =true to the createFillInstruction function to create the recipient token account
                    // if it doesn't exist.
                    return [2 /*return*/, createFillInstruction(signer, solanaClient, fillInput, svmRelayData, mintInfo.data.decimals, !recipientAtaEncodedAccount.exists, remainingAccounts)];
            }
        });
    });
}
/**
 * Creates a fill instruction with an instruction params PDA as the relayData input.
 * @param spokePoolAddr Address of the spoke pool we're trying to fill through
 * @param solanaClient RPC client to interact with Solana chain
 * @param relayData RelayData instance, supplemented with destinationChainId
 * @param signer signer associated with the relayer creating a Fill. Can be VoidSigner for gas estimation
 * @param repaymentChainId Chain id where relayer repayment is desired
 * @param repaymentAddress Address to which repayment will go to on repaymentChainId
 * @returns FillRelay transaction
 */
export function getIPFillRelayTx(spokePoolAddr, solanaClient, relayData, signer, repaymentChainId, repaymentAddress) {
    return __awaiter(this, void 0, void 0, function () {
        var program, _relayDataHash, relayDataHash, _a, state, delegate, instructionParams, mint, mintInfo, _b, recipientAta, relayerAta, fillStatus, eventAuthority, recipientAtaEncodedAccount, remainingAccounts, acrossPlusMessage_2, fillInput;
        return __generator(this, function (_c) {
            switch (_c.label) {
                case 0:
                    program = toAddress(spokePoolAddr);
                    _relayDataHash = getRelayDataHash(__assign(__assign({}, relayData), { messageHash: getMessageHash(relayData.message) }), relayData.destinationChainId);
                    relayDataHash = new Uint8Array(Buffer.from(_relayDataHash.slice(2), "hex"));
                    return [4 /*yield*/, Promise.all([
                            getStatePda(program),
                            getFillRelayDelegatePda(relayDataHash, BigInt(repaymentChainId), toAddress(repaymentAddress), program),
                            getInstructionParamsPda(program, signer.address),
                        ])];
                case 1:
                    _a = _c.sent(), state = _a[0], delegate = _a[1], instructionParams = _a[2];
                    mint = toAddress(relayData.outputToken);
                    return [4 /*yield*/, getMintInfo(solanaClient, mint)];
                case 2:
                    mintInfo = _c.sent();
                    return [4 /*yield*/, Promise.all([
                            getAssociatedTokenAddress(relayData.recipient, relayData.outputToken, mintInfo.programAddress),
                            getAssociatedTokenAddress(SvmAddress.from(signer.address), relayData.outputToken, mintInfo.programAddress),
                            getFillStatusPda(program, relayData, relayData.destinationChainId),
                            getEventAuthority(program),
                        ])];
                case 3:
                    _b = _c.sent(), recipientAta = _b[0], relayerAta = _b[1], fillStatus = _b[2], eventAuthority = _b[3];
                    return [4 /*yield*/, fetchEncodedAccount(solanaClient, recipientAta)];
                case 4:
                    recipientAtaEncodedAccount = _c.sent();
                    remainingAccounts = [];
                    if (relayData.message !== "0x") {
                        acrossPlusMessage_2 = deserializeMessage(relayData.message);
                        // The first `remainingAccount` _must_ be the handler address.
                        // https://github.com/across-protocol/contracts/blob/3310f8dc716407a5f97ef5fd2eae63df83251f2f/programs/svm-spoke/src/utils/message_utils.rs#L36.
                        remainingAccounts.push(getAccountMeta(acrossPlusMessage_2.handler, true));
                        remainingAccounts.push.apply(remainingAccounts, acrossPlusMessage_2.accounts.map(function (account, idx) {
                            return getAccountMeta(account, idx < acrossPlusMessage_2.accounts.length - acrossPlusMessage_2.read_only_len);
                        }));
                    }
                    fillInput = {
                        signer: signer,
                        state: state,
                        delegate: delegate,
                        mint: mint,
                        relayerTokenAccount: relayerAta,
                        recipientTokenAccount: recipientAta,
                        fillStatus: fillStatus,
                        tokenProgram: mintInfo.programAddress,
                        associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ADDRESS,
                        systemProgram: SYSTEM_PROGRAM_ADDRESS,
                        eventAuthority: eventAuthority,
                        program: program,
                        instructionParams: instructionParams,
                        relayHash: relayDataHash,
                        relayData: null,
                        repaymentChainId: null,
                        repaymentAddress: null,
                    };
                    // Pass createRecipientAtaIfNeeded =true to the createFillInstruction function to create the recipient token account
                    // if it doesn't exist.
                    return [2 /*return*/, createFillInstruction(signer, solanaClient, fillInput, { outputAmount: relayData.outputAmount.toBigInt(), recipient: toAddress(relayData.recipient) }, mintInfo.data.decimals, !recipientAtaEncodedAccount.exists, remainingAccounts)];
            }
        });
    });
}
/**
 * Creates a fill instruction.
 * @param signer - The signer of the transaction.
 * @param solanaClient - The Solana client.
 * @param fillInput - The fill input.
 * @param tokenDecimals - The token decimals.
 * @param createRecipientAta - Whether to create a recipient token account.
 * @returns The fill instruction.
 */
export var createFillInstruction = function (signer_1, solanaClient_1, fillInput_1, relayData_1, tokenDecimals_1) {
    var args_1 = [];
    for (var _i = 5; _i < arguments.length; _i++) {
        args_1[_i - 5] = arguments[_i];
    }
    return __awaiter(void 0, __spreadArray([signer_1, solanaClient_1, fillInput_1, relayData_1, tokenDecimals_1], args_1, true), void 0, function (signer, solanaClient, fillInput, relayData, tokenDecimals, createRecipientAta, remainingAccounts) {
        var mintInfo, approveIx, getCreateAssociatedTokenIx, createFillIx, _a;
        var _b;
        if (createRecipientAta === void 0) { createRecipientAta = false; }
        if (remainingAccounts === void 0) { remainingAccounts = []; }
        return __generator(this, function (_c) {
            switch (_c.label) {
                case 0: return [4 /*yield*/, getMintInfo(solanaClient, fillInput.mint)];
                case 1:
                    mintInfo = _c.sent();
                    approveIx = getApproveCheckedInstruction({
                        source: fillInput.relayerTokenAccount,
                        mint: fillInput.mint,
                        delegate: fillInput.delegate,
                        owner: fillInput.signer,
                        amount: relayData.outputAmount,
                        decimals: tokenDecimals,
                    }, {
                        programAddress: mintInfo.programAddress,
                    });
                    getCreateAssociatedTokenIx = function () {
                        return getCreateAssociatedTokenInstruction({
                            payer: signer,
                            owner: relayData.recipient,
                            mint: fillInput.mint,
                            ata: fillInput.recipientTokenAccount,
                            systemProgram: SYSTEM_PROGRAM_ADDRESS,
                            tokenProgram: fillInput.tokenProgram,
                        });
                    };
                    createFillIx = SvmSpokeClient.getFillRelayInstruction(fillInput);
                    // Add remaining accounts.
                    (_b = createFillIx.accounts).push.apply(_b, remainingAccounts);
                    _a = pipe;
                    return [4 /*yield*/, createDefaultTransaction(solanaClient, signer)];
                case 2: return [2 /*return*/, _a.apply(void 0, [_c.sent(), function (tx) { return (createRecipientAta ? appendTransactionMessageInstruction(getCreateAssociatedTokenIx(), tx) : tx); },
                        function (tx) { return appendTransactionMessageInstruction(approveIx, tx); },
                        function (tx) { return appendTransactionMessageInstruction(createFillIx, tx); }])];
            }
        });
    });
};
export function deserializeMessage(_message) {
    var message = new Uint8Array(Buffer.from(_message.startsWith("0x") ? _message.slice(2) : _message, "hex"));
    // Add remaining accounts if the relayData has a non-empty message.
    // @dev ! since in the context of creating a `fillRelayTx`, `relayData` must be defined.
    var acrossPlusMessageDecoder = getAcrossPlusMessageDecoder();
    var deserialized = acrossPlusMessageDecoder.decode(message);
    var valueAmountMethod2 = extractValueAmount(message);
    assert(deserialized.value_amount === valueAmountMethod2, "svm | deserializeMessage: Deserialization mismatch for value_amount");
    return deserialized;
}
/**
 * Creates a deposit instruction.
 * @param signer - The signer of the transaction.
 * @param solanaClient - The Solana client.
 * @param depositInput - The deposit input.
 * @param tokenDecimals - The token decimals.
 * @param createVaultAtaIfNeeded - Whether to create a vault token account.
 * @returns The deposit instruction.
 */
export var createDepositInstruction = function (signer_1, solanaClient_1, depositInput_1, tokenDecimals_1) {
    var args_1 = [];
    for (var _i = 4; _i < arguments.length; _i++) {
        args_1[_i - 4] = arguments[_i];
    }
    return __awaiter(void 0, __spreadArray([signer_1, solanaClient_1, depositInput_1, tokenDecimals_1], args_1, true), void 0, function (signer, solanaClient, depositInput, tokenDecimals, createVaultAtaIfNeeded) {
        var getCreateAssociatedTokenIdempotentIx, mintInfo, approveIx, depositIx, _a;
        if (createVaultAtaIfNeeded === void 0) { createVaultAtaIfNeeded = true; }
        return __generator(this, function (_b) {
            switch (_b.label) {
                case 0:
                    getCreateAssociatedTokenIdempotentIx = function () {
                        return getCreateAssociatedTokenIdempotentInstruction({
                            payer: signer,
                            owner: depositInput.state,
                            mint: depositInput.mint,
                            ata: depositInput.vault,
                            systemProgram: depositInput.systemProgram,
                            tokenProgram: depositInput.tokenProgram,
                        });
                    };
                    return [4 /*yield*/, getMintInfo(solanaClient, depositInput.mint)];
                case 1:
                    mintInfo = _b.sent();
                    approveIx = getApproveCheckedInstruction({
                        source: depositInput.depositorTokenAccount,
                        mint: depositInput.mint,
                        delegate: depositInput.delegate,
                        owner: depositInput.depositor,
                        amount: depositInput.inputAmount,
                        decimals: tokenDecimals,
                    }, {
                        programAddress: mintInfo.programAddress,
                    });
                    depositIx = SvmSpokeClient.getDepositInstruction(depositInput);
                    _a = pipe;
                    return [4 /*yield*/, createDefaultTransaction(solanaClient, signer)];
                case 2: return [2 /*return*/, _a.apply(void 0, [_b.sent(), function (tx) {
                            return createVaultAtaIfNeeded ? appendTransactionMessageInstruction(getCreateAssociatedTokenIdempotentIx(), tx) : tx;
                        },
                        function (tx) { return appendTransactionMessageInstruction(approveIx, tx); },
                        function (tx) { return appendTransactionMessageInstruction(depositIx, tx); }])];
            }
        });
    });
};
/**
 * Creates a request slow fill instruction.
 * @param signer - The signer of the transaction.
 * @param solanaClient - The Solana client.
 * @param requestSlowFillInput - The input arguments for the `requestSlowFill` instruction.
 * @returns The request slow fill instruction.
 */
export var createRequestSlowFillInstruction = function (signer, solanaClient, requestSlowFillInput) { return __awaiter(void 0, void 0, void 0, function () {
    var requestSlowFillIx, _a;
    return __generator(this, function (_b) {
        switch (_b.label) {
            case 0:
                requestSlowFillIx = SvmSpokeClient.getRequestSlowFillInstruction(requestSlowFillInput);
                _a = pipe;
                return [4 /*yield*/, createDefaultTransaction(solanaClient, signer)];
            case 1: return [2 /*return*/, _a.apply(void 0, [_b.sent(), function (tx) {
                        return appendTransactionMessageInstruction(requestSlowFillIx, tx);
                    }])];
        }
    });
}); };
/**
 * @notice Return the requestSlowFill transaction for a given deposit
 * @param spokePoolAddr Address of the spoke pool we're trying to fill through
 * @param solanaClient RPC client to interact with Solana chain
 * @param relayData RelayData instance, supplemented with destinationChainId
 * @param signer signer associated with the relayer creating a Fill.
 * @returns requestSlowFill transaction
 */
export function getSlowFillRequestTx(spokePoolAddr, solanaClient, relayData, signer) {
    return __awaiter(this, void 0, void 0, function () {
        var program, messageHash, relayDataHash, _a, state, fillStatus, eventAuthority, svmRelayData, requestSlowFillInput;
        return __generator(this, function (_b) {
            switch (_b.label) {
                case 0:
                    program = toAddress(spokePoolAddr);
                    messageHash = getMessageHash(relayData.message);
                    relayDataHash = getRelayDataHash(__assign(__assign({}, relayData), { messageHash: messageHash }), relayData.destinationChainId);
                    return [4 /*yield*/, Promise.all([
                            getStatePda(program),
                            getFillStatusPda(program, relayData, relayData.destinationChainId),
                            getEventAuthority(program),
                        ])];
                case 1:
                    _a = _b.sent(), state = _a[0], fillStatus = _a[1], eventAuthority = _a[2];
                    svmRelayData = toSvmRelayData(relayData);
                    requestSlowFillInput = {
                        signer: signer,
                        state: state,
                        fillStatus: fillStatus,
                        eventAuthority: eventAuthority,
                        program: program,
                        relayHash: arrayify(relayDataHash),
                        relayData: svmRelayData,
                        systemProgram: SYSTEM_PROGRAM_ADDRESS,
                    };
                    return [2 /*return*/, createRequestSlowFillInstruction(signer, solanaClient, requestSlowFillInput)];
            }
        });
    });
}
/**
 * Creates a close fill PDA instruction.
 * @param signer - The signer of the transaction.
 * @param solanaClient - The Solana client.
 * @param fillStatusPda - The fill status PDA.
 * @returns The close fill PDA instruction.
 */
export var createCloseFillPdaInstruction = function (signer, solanaClient, fillStatusPda) { return __awaiter(void 0, void 0, void 0, function () {
    var closeFillPdaIx, _a, _b, _c;
    var _d;
    return __generator(this, function (_e) {
        switch (_e.label) {
            case 0:
                _b = (_a = SvmSpokeClient).getCloseFillPdaInstruction;
                _d = {
                    signer: signer
                };
                return [4 /*yield*/, getStatePda(SvmSpokeClient.SVM_SPOKE_PROGRAM_ADDRESS)];
            case 1:
                closeFillPdaIx = _b.apply(_a, [(_d.state = _e.sent(),
                        _d.fillStatus = fillStatusPda,
                        _d)]);
                _c = pipe;
                return [4 /*yield*/, createDefaultTransaction(solanaClient, signer)];
            case 2: return [2 /*return*/, _c.apply(void 0, [_e.sent(), function (tx) {
                        return appendTransactionMessageInstruction(closeFillPdaIx, tx);
                    }])];
        }
    });
}); };
export var createReceiveMessageInstruction = function (signer, solanaClient, input, remainingAccounts) { return __awaiter(void 0, void 0, void 0, function () {
    var receiveMessageIx, _a;
    var _b;
    return __generator(this, function (_c) {
        switch (_c.label) {
            case 0:
                receiveMessageIx = MessageTransmitterClient.getReceiveMessageInstruction(input);
                (_b = receiveMessageIx.accounts).push.apply(_b, remainingAccounts);
                _a = pipe;
                return [4 /*yield*/, createDefaultTransaction(solanaClient, signer)];
            case 1: return [2 /*return*/, _a.apply(void 0, [_c.sent(), function (tx) {
                        return appendTransactionMessageInstruction(receiveMessageIx, tx);
                    }])];
        }
    });
}); };
export function getAssociatedTokenAddress(owner_1, mint_1) {
    return __awaiter(this, arguments, void 0, function (owner, mint, tokenProgramId) {
        var encoder, associatedToken;
        if (tokenProgramId === void 0) { tokenProgramId = TOKEN_PROGRAM_ADDRESS; }
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    encoder = getAddressEncoder();
                    return [4 /*yield*/, getProgramDerivedAddress({
                            programAddress: ASSOCIATED_TOKEN_PROGRAM_ADDRESS,
                            seeds: [encoder.encode(toAddress(owner)), encoder.encode(tokenProgramId), encoder.encode(toAddress(mint))],
                        })];
                case 1:
                    associatedToken = (_a.sent())[0];
                    return [2 /*return*/, associatedToken];
            }
        });
    });
}
export function getRelayDataHash(relayData, destinationChainId) {
    assert(relayData.messageHash.startsWith("0x"), "Message hash must be a hex string");
    var uint64Encoder = getU64Encoder();
    var svmRelayData = toSvmRelayData(relayData);
    var relayDataEncoder = SvmSpokeClient.getRelayDataEncoder();
    var encodedRelayData = relayDataEncoder.encode(svmRelayData);
    var encodedMessage = Buffer.from(relayData.message.slice(2), "hex");
    var encodedMessageHash = Uint8Array.from(Buffer.from(relayData.messageHash.slice(2), "hex"));
    // Reformat the encoded relay data the same way it is done in the SvmSpoke:
    // https://github.com/across-protocol/contracts/blob/3310f8dc716407a5f97ef5fd2eae63df83251f2f/programs/svm-spoke/src/utils/merkle_proof_utils.rs#L5
    // We want to use messageHash always so we can construct the relayDataHash just from the Fill.
    // If we don't have a message, we can just pass an empty message here.
    var messageOffset = encodedRelayData.length - 4 - encodedMessage.length;
    var contentToHash = Buffer.concat([
        encodedRelayData.slice(0, messageOffset),
        encodedMessageHash,
        Uint8Array.from(uint64Encoder.encode(BigInt(destinationChainId))),
    ]);
    return keccak256(contentToHash);
}
function resolveFillStatusFromPdaEvents(fillStatusPda, toSlot, svmEventsClient) {
    return __awaiter(this, void 0, void 0, function () {
        var eventsToQuery, relevantEvents, fillStatusEvent;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    eventsToQuery = [SVMEventNames.FilledRelay, SVMEventNames.RequestedSlowFill];
                    return [4 /*yield*/, Promise.all(eventsToQuery.map(function (eventName) {
                            // PDAs should have only a few events, requesting up to 10 should be enough.
                            return svmEventsClient.queryDerivedAddressEvents(eventName, fillStatusPda, undefined, toSlot, { limit: 10 });
                        }))];
                case 1:
                    relevantEvents = (_a.sent()).flat();
                    if (relevantEvents.length === 0) {
                        // No fill or requested slow fill events found for this PDA
                        return [2 /*return*/, FillStatus.Unfilled];
                    }
                    // Sort events in ascending order of slot number
                    relevantEvents.sort(function (a, b) { return Number(a.slot - b.slot); });
                    fillStatusEvent = relevantEvents.pop();
                    switch (fillStatusEvent.name) {
                        case SVMEventNames.FilledRelay:
                            return [2 /*return*/, FillStatus.Filled];
                        case SVMEventNames.RequestedSlowFill:
                            return [2 /*return*/, FillStatus.RequestedSlowFill];
                        default:
                            throw new Error("Unexpected event name: ".concat(fillStatusEvent.name));
                    }
                    return [2 /*return*/];
            }
        });
    });
}
/**
 * Attempts to resolve the fill status for an array of deposits by reading their fillStatus PDAs.
 *
 * - If a PDA exists, the status is read directly from it.
 * - If the PDA does not exist but the deposit's fill deadline has not passed, the deposit is considered unfilled.
 * - If the PDA does not exist and the fill deadline has passed, the status cannot be determined and is set to undefined.
 *
 * Assumes PDAs can only be closed after the fill deadline expires.
 *
 * @param provider SVM provider instance
 * @param fillStatusPdas An array of fill status PDAs to retrieve the fill status for.
 * @param relayData An array of relay data from which the fill status PDAs were derived.
 */
function fetchBatchFillStatusFromPdaAccounts(provider, fillStatusPdas, relayDataArray, logger) {
    return __awaiter(this, void 0, void 0, function () {
        var chunkSize, commitment, _a, pdaAccounts, timestamp, fillStatuses;
        return __generator(this, function (_b) {
            switch (_b.label) {
                case 0:
                    chunkSize = 100;
                    commitment = "confirmed";
                    return [4 /*yield*/, Promise.all([
                            Promise.all(chunk(fillStatusPdas, chunkSize).map(function (chunk) { return fetchEncodedAccounts(provider, chunk, { commitment: commitment }); })),
                            getNearestSlotTime(provider, { commitment: commitment }, logger),
                        ])];
                case 1:
                    _a = _b.sent(), pdaAccounts = _a[0], timestamp = _a[1].timestamp;
                    fillStatuses = pdaAccounts.flat().map(function (account, index) {
                        // If the PDA exists, we can fetch the status directly.
                        if (account.exists) {
                            var decodedAccount = SvmSpokeClient.decodeFillStatusAccount(account);
                            return decodedAccount.data.status;
                        }
                        // If the PDA doesn't exist and the deadline hasn't passed yet, the deposit must be unfilled,
                        // since PDAs can't be closed before the fill deadline.
                        if (timestamp < relayDataArray[index].fillDeadline) {
                            return FillStatus.Unfilled;
                        }
                        // If the PDA doesn't exist and the fill deadline has passed, then the status can't be determined and is set to undefined.
                        return undefined;
                    });
                    return [2 /*return*/, fillStatuses];
            }
        });
    });
}
export function getIPForFillRelayTxs(spokePool, relayData, repaymentChainId, repaymentAddress, signer, provider) {
    return __awaiter(this, void 0, void 0, function () {
        var ixs, txns;
        var _this = this;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0: return [4 /*yield*/, getFillRelayViaInstructionParamsInstructions(toAddress(spokePool), relayData, repaymentChainId, repaymentAddress, signer, provider)];
                case 1:
                    ixs = _a.sent();
                    return [4 /*yield*/, mapAsync(ixs, function (ix) { return __awaiter(_this, void 0, void 0, function () {
                            var _a;
                            return __generator(this, function (_b) {
                                switch (_b.label) {
                                    case 0:
                                        _a = pipe;
                                        return [4 /*yield*/, createDefaultTransaction(provider, signer)];
                                    case 1: return [2 /*return*/, _a.apply(void 0, [_b.sent(), function (tx) { return appendTransactionMessageInstruction(ix, tx); }])];
                                }
                            });
                        }); })];
                case 2:
                    txns = _a.sent();
                    return [2 /*return*/, txns];
            }
        });
    });
}
/**
 * Returns a set of instructions to execute to fill a relay via instruction params.
 * @param spokePool The program ID of the Solana spoke pool.
 * @param relayData The relay data to write to the instruction params PDA.
 * @param repaymentChainId: The chain ID to take repayment.
 * @param repaymentAddress: The address to receive repayment on the specified repayment chain.
 * @param signer The transaction signer and authority of the instruction params PDA.
 * @param maxWriteSize The maximum fragment size to write to instruction params.
 */
export function getFillRelayViaInstructionParamsInstructions(spokePool_1, relayData_1, repaymentChainId_1, repaymentAddress_1, signer_1, provider_1) {
    return __awaiter(this, arguments, void 0, function (spokePool, relayData, repaymentChainId, repaymentAddress, signer, provider, maxWriteSize) {
        var instructionParams, encodedInstructionParams, relayDataEncoder, svmRelayData, encodedRelayData, initInstructionParamsIx, instructions, i, offset, offsetEnd, fragment, writeInstructionParamsIx;
        if (maxWriteSize === void 0) { maxWriteSize = 450; }
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0: return [4 /*yield*/, getInstructionParamsPda(spokePool, signer.address)];
                case 1:
                    instructionParams = _a.sent();
                    return [4 /*yield*/, fetchEncodedAccount(provider, instructionParams)];
                case 2:
                    encodedInstructionParams = _a.sent();
                    relayDataEncoder = SvmSpokeClient.getFillRelayParamsEncoder();
                    svmRelayData = toSvmRelayData(relayData);
                    encodedRelayData = relayDataEncoder.encode({
                        relayData: svmRelayData,
                        repaymentChainId: repaymentChainId,
                        repaymentAddress: toAddress(repaymentAddress),
                    });
                    initInstructionParamsIx = SvmSpokeClient.getInitializeInstructionParamsInstruction({
                        signer: signer,
                        instructionParams: instructionParams,
                        totalSize: encodedRelayData.length,
                    });
                    instructions = [initInstructionParamsIx];
                    for (i = 0; i <= encodedRelayData.length / maxWriteSize; ++i) {
                        offset = i * maxWriteSize;
                        offsetEnd = Math.min(offset + maxWriteSize, encodedRelayData.length);
                        fragment = encodedRelayData.slice(offset, offsetEnd);
                        writeInstructionParamsIx = SvmSpokeClient.getWriteInstructionParamsFragmentInstruction({
                            signer: signer,
                            instructionParams: instructionParams,
                            offset: offset,
                            fragment: fragment,
                        });
                        instructions.push(writeInstructionParamsIx);
                    }
                    return [2 /*return*/, encodedInstructionParams.exists
                            ? __spreadArray([SvmSpokeClient.getCloseInstructionParamsInstruction({ signer: signer, instructionParams: instructionParams })], instructions, true) : instructions];
            }
        });
    });
}
/**
 * Returns the delegate PDA for deposit.
 */
export function getDepositDelegatePda(depositData, programId) {
    return __awaiter(this, void 0, void 0, function () {
        var addrEnc, u64, u32, parts, seedHash, pda;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    addrEnc = getAddressEncoder();
                    u64 = getU64Encoder();
                    u32 = getU32Encoder();
                    parts = [
                        Uint8Array.from(addrEnc.encode(depositData.depositor)),
                        Uint8Array.from(addrEnc.encode(depositData.recipient)),
                        Uint8Array.from(addrEnc.encode(depositData.inputToken)),
                        Uint8Array.from(addrEnc.encode(depositData.outputToken)),
                        Uint8Array.from(u64.encode(depositData.inputAmount)),
                        Uint8Array.from(depositData.outputAmount),
                        Uint8Array.from(u64.encode(depositData.destinationChainId)),
                        Uint8Array.from(addrEnc.encode(depositData.exclusiveRelayer)),
                        Uint8Array.from(u32.encode(depositData.quoteTimestamp)),
                        Uint8Array.from(u32.encode(depositData.fillDeadline)),
                        Uint8Array.from(u32.encode(depositData.exclusivityParameter)),
                        Uint8Array.from(u32.encode(BigInt(depositData.message.length))),
                        depositData.message,
                    ];
                    seedHash = Buffer.from(keccak256(Buffer.concat(parts)).slice(2), "hex");
                    return [4 /*yield*/, getProgramDerivedAddress({
                            programAddress: programId,
                            seeds: [Buffer.from("delegate"), seedHash],
                        })];
                case 1:
                    pda = (_a.sent())[0];
                    return [2 /*return*/, pda];
            }
        });
    });
}
/**
 * Returns the delegate PDA for depositNow.
 */
export function getDepositNowDelegatePda(depositData, programId) {
    return __awaiter(this, void 0, void 0, function () {
        var addrEnc, u64, u32, parts, seedHash, pda;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    addrEnc = getAddressEncoder();
                    u64 = getU64Encoder();
                    u32 = getU32Encoder();
                    parts = [
                        Uint8Array.from(addrEnc.encode(depositData.depositor)),
                        Uint8Array.from(addrEnc.encode(depositData.recipient)),
                        Uint8Array.from(addrEnc.encode(depositData.inputToken)),
                        Uint8Array.from(addrEnc.encode(depositData.outputToken)),
                        Uint8Array.from(u64.encode(depositData.inputAmount)),
                        Uint8Array.from(depositData.outputAmount),
                        Uint8Array.from(u64.encode(depositData.destinationChainId)),
                        Uint8Array.from(addrEnc.encode(depositData.exclusiveRelayer)),
                        Uint8Array.from(u32.encode(depositData.fillDeadlineOffset)),
                        Uint8Array.from(u32.encode(depositData.exclusivityPeriod)),
                        Uint8Array.from(u32.encode(BigInt(depositData.message.length))),
                        depositData.message,
                    ];
                    seedHash = Buffer.from(keccak256(Buffer.concat(parts)).slice(2), "hex");
                    return [4 /*yield*/, getProgramDerivedAddress({
                            programAddress: programId,
                            seeds: [Buffer.from("delegate"), seedHash],
                        })];
                case 1:
                    pda = (_a.sent())[0];
                    return [2 /*return*/, pda];
            }
        });
    });
}
/**
 * Returns the fill-delegate PDA for fillRelay.
 */
export function getFillRelayDelegatePda(relayHash, repaymentChainId, repaymentAddress, programId) {
    return __awaiter(this, void 0, void 0, function () {
        var addrEnc, u64, parts, seedHash, pda;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    addrEnc = getAddressEncoder();
                    u64 = getU64Encoder();
                    parts = [
                        relayHash,
                        Uint8Array.from(u64.encode(repaymentChainId)),
                        Uint8Array.from(addrEnc.encode(repaymentAddress)),
                    ];
                    seedHash = Buffer.from(keccak256(Buffer.concat(parts)).slice(2), "hex");
                    return [4 /*yield*/, getProgramDerivedAddress({
                            programAddress: programId,
                            seeds: [Buffer.from("delegate"), seedHash],
                        })];
                case 1:
                    pda = (_a.sent())[0];
                    return [2 /*return*/, pda];
            }
        });
    });
}
/**
 * Checks if a CCTP message has been processed.
 * @param solanaClient The Solana client.
 * @param signer The signer of the transaction.
 * @param nonce The nonce to check.
 * @param sourceDomain The source domain.
 * @returns True if the message has been processed, false otherwise.
 * @dev This function intentionally does not have error handling for `getCCTPNoncePda` nor `simulateAndDecode` since
 * the error handling would have to account for the asynchronous opening/closing of PDAs, which is better handled downstream,
 * where the caller of this function has more context.
 */
export var hasCCTPV1MessageBeenProcessed = function (solanaClient, signer, nonce, sourceDomain, latestBlockhash) { return __awaiter(void 0, void 0, void 0, function () {
    var noncePda, isNonceUsedIx, parserFunction;
    return __generator(this, function (_a) {
        switch (_a.label) {
            case 0: return [4 /*yield*/, getCCTPNoncePda(solanaClient, signer, nonce, sourceDomain)];
            case 1:
                noncePda = _a.sent();
                isNonceUsedIx = MessageTransmitterClient.getIsNonceUsedInstruction({
                    nonce: nonce,
                    usedNonces: noncePda,
                });
                parserFunction = function (buf) {
                    if (buf.length != 1) {
                        throw new Error("Invalid buffer length for isNonceUsedIx");
                    }
                    return Boolean(buf[0]);
                };
                return [4 /*yield*/, simulateAndDecode(solanaClient, isNonceUsedIx, signer, parserFunction, latestBlockhash)];
            case 2: return [2 /*return*/, _a.sent()];
        }
    });
}); };
/**
 * Returns the account metas for a tokenless message.
 * @returns The account metas for a tokenless message.
 */
export function getAccountMetasForTokenlessMessage(solanaClient, signer, messageBytes) {
    return __awaiter(this, void 0, void 0, function () {
        var messageHex, messageHeader, programAddress, statePda, selfAuthority, eventAuthority, base, rootBundleId, rootBundle, rootBundleId, rootBundle;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    messageHex = messageBytes.slice(2);
                    messageHeader = decodeMessageHeader(Buffer.from(messageHex, "hex"));
                    programAddress = SvmSpokeClient.SVM_SPOKE_PROGRAM_ADDRESS;
                    return [4 /*yield*/, getStatePda(programAddress)];
                case 1:
                    statePda = _a.sent();
                    return [4 /*yield*/, getSelfAuthority()];
                case 2:
                    selfAuthority = _a.sent();
                    return [4 /*yield*/, getEventAuthority(programAddress)];
                case 3:
                    eventAuthority = _a.sent();
                    base = [
                        { address: statePda, role: AccountRole.READONLY },
                        { address: selfAuthority, role: AccountRole.READONLY },
                        { address: programAddress, role: AccountRole.READONLY },
                    ];
                    if (!isRelayRootBundleMessageBody(messageHeader.messageBody)) return [3 /*break*/, 6];
                    return [4 /*yield*/, SvmSpokeClient.fetchState(solanaClient, statePda)];
                case 4:
                    rootBundleId = (_a.sent()).data.rootBundleId;
                    return [4 /*yield*/, getRootBundlePda(programAddress, rootBundleId)];
                case 5:
                    rootBundle = _a.sent();
                    return [2 /*return*/, __spreadArray(__spreadArray([], base, true), [
                            { address: signer.address, role: AccountRole.WRITABLE },
                            { address: statePda, role: AccountRole.WRITABLE },
                            { address: rootBundle, role: AccountRole.WRITABLE },
                            { address: SYSTEM_PROGRAM_ADDRESS, role: AccountRole.READONLY },
                            { address: eventAuthority, role: AccountRole.READONLY },
                            { address: programAddress, role: AccountRole.READONLY },
                        ], false)];
                case 6:
                    if (!isEmergencyDeleteRootBundleMessageBody(messageHeader.messageBody)) return [3 /*break*/, 8];
                    rootBundleId = getEmergencyDeleteRootBundleRootBundleId(messageHeader.messageBody);
                    return [4 /*yield*/, getRootBundlePda(programAddress, rootBundleId)];
                case 7:
                    rootBundle = _a.sent();
                    return [2 /*return*/, __spreadArray(__spreadArray([], base, true), [
                            { address: signer.address, role: AccountRole.READONLY },
                            { address: statePda, role: AccountRole.READONLY },
                            { address: rootBundle, role: AccountRole.WRITABLE },
                            { address: eventAuthority, role: AccountRole.READONLY },
                            { address: programAddress, role: AccountRole.READONLY },
                        ], false)];
                case 8: return [2 /*return*/, __spreadArray(__spreadArray([], base, true), [
                        { address: statePda, role: AccountRole.WRITABLE },
                        { address: eventAuthority, role: AccountRole.READONLY },
                        { address: programAddress, role: AccountRole.READONLY },
                    ], false)];
            }
        });
    });
}
/**
 * Returns the required PDAs for a deposit message.
 * @param hubChainId The chain ID of the corresponding Across hub.
 * @param cctpSourceDomain The source chain (Solana) domain ID.
 * @param tokenMessengerMinter The token messenger minter address.
 * @param messageTransmitterAddress The message transmitter address.
 */
export function getCCTPDepositAccounts(hubChainId, cctpDestinationDomainId, tokenMessengerMinterAddress, messageTransmitterAddress) {
    return __awaiter(this, void 0, void 0, function () {
        var l2Usdc, _a, tokenMessenger, tokenMinter, localToken, cctpEventAuthority, remoteTokenMessenger, tokenMessengerMinterSenderAuthority, messageTransmitter;
        return __generator(this, function (_b) {
            switch (_b.label) {
                case 0:
                    l2Usdc = SvmAddress.from(TOKEN_SYMBOLS_MAP.USDC.addresses[chainIsProd(hubChainId) ? CHAIN_IDs.SOLANA : CHAIN_IDs.SOLANA_DEVNET]);
                    return [4 /*yield*/, Promise.all([
                            getProgramDerivedAddress({
                                programAddress: tokenMessengerMinterAddress,
                                seeds: ["token_messenger"],
                            }),
                            getProgramDerivedAddress({
                                programAddress: tokenMessengerMinterAddress,
                                seeds: ["token_minter"],
                            }),
                            getProgramDerivedAddress({
                                programAddress: tokenMessengerMinterAddress,
                                seeds: ["local_token", bs58.decode(l2Usdc.toBase58())],
                            }),
                            getProgramDerivedAddress({
                                programAddress: tokenMessengerMinterAddress,
                                seeds: ["__event_authority"],
                            }),
                            getProgramDerivedAddress({
                                programAddress: tokenMessengerMinterAddress,
                                seeds: ["remote_token_messenger", String(cctpDestinationDomainId)],
                            }),
                            getProgramDerivedAddress({
                                programAddress: tokenMessengerMinterAddress,
                                seeds: ["sender_authority"],
                            }),
                            getProgramDerivedAddress({
                                programAddress: messageTransmitterAddress,
                                seeds: ["message_transmitter"],
                            }),
                        ])];
                case 1:
                    _a = _b.sent(), tokenMessenger = _a[0][0], tokenMinter = _a[1][0], localToken = _a[2][0], cctpEventAuthority = _a[3][0], remoteTokenMessenger = _a[4][0], tokenMessengerMinterSenderAuthority = _a[5][0], messageTransmitter = _a[6][0];
                    return [2 /*return*/, {
                            tokenMessenger: tokenMessenger,
                            tokenMinter: tokenMinter,
                            localToken: localToken,
                            cctpEventAuthority: cctpEventAuthority,
                            remoteTokenMessenger: remoteTokenMessenger,
                            tokenMessengerMinterSenderAuthority: tokenMessengerMinterSenderAuthority,
                            messageTransmitter: messageTransmitter,
                        }];
            }
        });
    });
}
/**
 * Returns true if the input deposit's corresponding relay data would result in a transaction size
 * that is larger than the Solana transaction size limit.
 * @param fillRelayTx The compilable fill relay transaction to check.
 * @returns Object containing a boolean if the input deposit requires a multipart fill, false otherwise and
 * the number of bytes in the serialized transaction.
 */
export function isSVMFillTooLarge(fillRelayTx) {
    var sizeBytes = calculateFillSizeBytes(fillRelayTx);
    return {
        tooLarge: sizeBytes > SOLANA_TX_SIZE_LIMIT,
        sizeBytes: sizeBytes,
    };
}
/**
 * Returns the byte size of a base64 transaction.
 * @param base64TxString base64 serialized Solana transaction.
 * @returns The number of bytes in the transaction.
 */
export function base64StrToByteSize(base64TxString) {
    // base64 string has 6 bits per character, so every 4 symbols represent 3 bytes
    // However, we also need to account for padding: https://en.wikipedia.org/wiki/Base64#Padding
    var paddingLen = base64TxString.endsWith("==") ? 2 : base64TxString.endsWith("=") ? 1 : 0;
    return (base64TxString.length * 3) / 4 - paddingLen;
}
/**
 * Returns the size of the fill relay transaction using the input relayData.
 * @param fillTx The compilable fill relay transaction.
 * @returns The number of bytes in the serialized fillRelay transaction.
 */
export function calculateFillSizeBytes(fillTx) {
    var signedTransaction = compileTransaction(fillTx);
    var serializedTx = getBase64EncodedWireTransaction(signedTransaction);
    return base64StrToByteSize(serializedTx);
}
/**
 * Returns the account metas for a deposit message.
 * @param message The CCTP message.
 * @param hubChainId The chain ID of the hub.
 * @param tokenMessengerMinter The token messenger minter address.
 * @param recipientAta The ATA of the recipient address.
 * @returns The account metas for a deposit message.
 */
function getAccountMetasForDepositMessage(message, hubChainId, tokenMessengerMinter, recipientAta) {
    return __awaiter(this, void 0, void 0, function () {
        var l1Usdc, l2Usdc, tokenMessengerPda, tokenMinterPda, localTokenPda, tokenMessengerEventAuthorityPda, custodyTokenAccountPda, tokenPairPda, remoteTokenMessengerPda;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    l1Usdc = EvmAddress.from(TOKEN_SYMBOLS_MAP.USDC.addresses[hubChainId]);
                    l2Usdc = SvmAddress.from(TOKEN_SYMBOLS_MAP.USDC.addresses[chainIsProd(hubChainId) ? CHAIN_IDs.SOLANA : CHAIN_IDs.SOLANA_DEVNET]);
                    return [4 /*yield*/, getProgramDerivedAddress({
                            programAddress: tokenMessengerMinter,
                            seeds: ["token_messenger"],
                        })];
                case 1:
                    tokenMessengerPda = (_a.sent())[0];
                    return [4 /*yield*/, getProgramDerivedAddress({
                            programAddress: tokenMessengerMinter,
                            seeds: ["token_minter"],
                        })];
                case 2:
                    tokenMinterPda = (_a.sent())[0];
                    return [4 /*yield*/, getProgramDerivedAddress({
                            programAddress: tokenMessengerMinter,
                            seeds: ["local_token", bs58.decode(l2Usdc.toBase58())],
                        })];
                case 3:
                    localTokenPda = (_a.sent())[0];
                    return [4 /*yield*/, getProgramDerivedAddress({
                            programAddress: tokenMessengerMinter,
                            seeds: ["__event_authority"],
                        })];
                case 4:
                    tokenMessengerEventAuthorityPda = (_a.sent())[0];
                    return [4 /*yield*/, getProgramDerivedAddress({
                            programAddress: tokenMessengerMinter,
                            seeds: ["custody", bs58.decode(l2Usdc.toBase58())],
                        })];
                case 5:
                    custodyTokenAccountPda = (_a.sent())[0];
                    return [4 /*yield*/, getProgramDerivedAddress({
                            programAddress: tokenMessengerMinter,
                            seeds: [
                                new Uint8Array(Buffer.from("token_pair")),
                                new Uint8Array(Buffer.from(String(message.sourceDomain))),
                                new Uint8Array(Buffer.from(l1Usdc.toBytes32().slice(2), "hex")),
                            ],
                        })];
                case 6:
                    tokenPairPda = (_a.sent())[0];
                    return [4 /*yield*/, getProgramDerivedAddress({
                            programAddress: tokenMessengerMinter,
                            seeds: ["remote_token_messenger", String(message.sourceDomain)],
                        })];
                case 7:
                    remoteTokenMessengerPda = (_a.sent())[0];
                    return [2 /*return*/, [
                            { address: tokenMessengerPda, role: AccountRole.READONLY },
                            { address: remoteTokenMessengerPda, role: AccountRole.READONLY },
                            { address: tokenMinterPda, role: AccountRole.WRITABLE },
                            { address: localTokenPda, role: AccountRole.WRITABLE },
                            { address: tokenPairPda, role: AccountRole.READONLY },
                            { address: toAddress(recipientAta), role: AccountRole.WRITABLE },
                            { address: custodyTokenAccountPda, role: AccountRole.WRITABLE },
                            { address: TOKEN_PROGRAM_ADDRESS, role: AccountRole.READONLY },
                            { address: tokenMessengerEventAuthorityPda, role: AccountRole.READONLY },
                            { address: tokenMessengerMinter, role: AccountRole.READONLY },
                        ]];
            }
        });
    });
}
/**
 * Returns the CCTP v1 receive message transaction.
 * @param solanaClient The Solana client.
 * @param signer The signer of the transaction.
 * @param message The CCTP message.
 * @param hubChainId The chain ID of the hub.
 * @param recipientAta The ATA of the recipient address (used for token finalizations only).
 * @returns The CCTP v1 receive message transaction.
 */
export function getCCTPV1ReceiveMessageTx(solanaClient, signer, message, hubChainId, recipientAta) {
    return __awaiter(this, void 0, void 0, function () {
        var messageTransmitterPda, eventAuthorityPda, cctpMessageReceiver, authorityPda, usedNonces, accountMetas, _a, messageBytes, input;
        return __generator(this, function (_b) {
            switch (_b.label) {
                case 0: return [4 /*yield*/, getProgramDerivedAddress({
                        programAddress: MessageTransmitterClient.MESSAGE_TRANSMITTER_PROGRAM_ADDRESS,
                        seeds: ["message_transmitter"],
                    })];
                case 1:
                    messageTransmitterPda = (_b.sent())[0];
                    return [4 /*yield*/, getProgramDerivedAddress({
                            programAddress: MessageTransmitterClient.MESSAGE_TRANSMITTER_PROGRAM_ADDRESS,
                            seeds: ["__event_authority"],
                        })];
                case 2:
                    eventAuthorityPda = (_b.sent())[0];
                    cctpMessageReceiver = isDepositForBurnEvent(message)
                        ? TokenMessengerMinterClient.TOKEN_MESSENGER_MINTER_PROGRAM_ADDRESS
                        : SvmSpokeClient.SVM_SPOKE_PROGRAM_ADDRESS;
                    return [4 /*yield*/, getProgramDerivedAddress({
                            programAddress: MessageTransmitterClient.MESSAGE_TRANSMITTER_PROGRAM_ADDRESS,
                            seeds: ["message_transmitter_authority", bs58.decode(cctpMessageReceiver)],
                        })];
                case 3:
                    authorityPda = (_b.sent())[0];
                    return [4 /*yield*/, getCCTPNoncePda(solanaClient, signer, message.nonce, message.sourceDomain)];
                case 4:
                    usedNonces = _b.sent();
                    if (!isDepositForBurnEvent(message)) return [3 /*break*/, 6];
                    return [4 /*yield*/, getAccountMetasForDepositMessage(message, hubChainId, TokenMessengerMinterClient.TOKEN_MESSENGER_MINTER_PROGRAM_ADDRESS, recipientAta)];
                case 5:
                    _a = _b.sent();
                    return [3 /*break*/, 8];
                case 6: return [4 /*yield*/, getAccountMetasForTokenlessMessage(solanaClient, signer, message.messageBytes)];
                case 7:
                    _a = _b.sent();
                    _b.label = 8;
                case 8:
                    accountMetas = _a;
                    messageBytes = message.messageBytes.startsWith("0x")
                        ? Buffer.from(message.messageBytes.slice(2), "hex")
                        : Buffer.from(message.messageBytes, "hex");
                    input = {
                        program: MessageTransmitterClient.MESSAGE_TRANSMITTER_PROGRAM_ADDRESS,
                        payer: signer,
                        caller: signer,
                        authorityPda: authorityPda,
                        messageTransmitter: messageTransmitterPda,
                        eventAuthority: eventAuthorityPda,
                        usedNonces: usedNonces,
                        receiver: cctpMessageReceiver,
                        systemProgram: SYSTEM_PROGRAM_ADDRESS,
                        message: messageBytes,
                        attestation: Buffer.from(message.attestation.slice(2), "hex"),
                    };
                    return [2 /*return*/, createReceiveMessageInstruction(signer, solanaClient, input, accountMetas)];
            }
        });
    });
}
/**
 * Finalizes CCTP deposits and messages on Solana.
 *
 * @param solanaClient The Solana client.
 * @param attestedMessages The CCTP messages to Solana.
 * @param signer A base signer to be converted into a Solana signer.
 * @param recipientAta The ATA of the recipient address (used for token finalizations only).
 * @param simulate Whether to simulate the transaction.
 * @param hubChainId The chain ID of the hub.
 * @returns A list of executed transaction signatures.
 */
export function finalizeCCTPV1Messages(solanaClient, attestedMessages, signer, recipientAta, simulate, hubChainId) {
    var _this = this;
    if (simulate === void 0) { simulate = false; }
    if (hubChainId === void 0) { hubChainId = 1; }
    return mapAsync(attestedMessages, function (message) { return __awaiter(_this, void 0, void 0, function () {
        var receiveMessageIx, result, _a, _b, _c, signedTransaction, signature, encodedTransaction;
        return __generator(this, function (_d) {
            switch (_d.label) {
                case 0: return [4 /*yield*/, getCCTPV1ReceiveMessageTx(solanaClient, signer, message, hubChainId, recipientAta)];
                case 1:
                    receiveMessageIx = _d.sent();
                    if (!simulate) return [3 /*break*/, 4];
                    _b = (_a = solanaClient)
                        .simulateTransaction;
                    _c = getBase64EncodedWireTransaction;
                    return [4 /*yield*/, signTransactionMessageWithSigners(receiveMessageIx)];
                case 2: return [4 /*yield*/, _b.apply(_a, [_c.apply(void 0, [_d.sent()]),
                        {
                            encoding: "base64",
                        }])
                        .send()];
                case 3:
                    result = _d.sent();
                    if (result.value.err) {
                        throw new Error(result.value.err.toString());
                    }
                    return [2 /*return*/, ""];
                case 4: return [4 /*yield*/, signTransactionMessageWithSigners(receiveMessageIx)];
                case 5:
                    signedTransaction = _d.sent();
                    signature = getSignatureFromTransaction(signedTransaction);
                    encodedTransaction = getBase64EncodedWireTransaction(signedTransaction);
                    return [4 /*yield*/, solanaClient
                            .sendTransaction(encodedTransaction, { preflightCommitment: "confirmed", encoding: "base64" })
                            .send()];
                case 6:
                    _d.sent();
                    return [2 /*return*/, signature];
            }
        });
    }); });
}
export function getMintInfo(solanaClient, mint, config) {
    return __awaiter(this, void 0, void 0, function () {
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0: return [4 /*yield*/, fetchMint(solanaClient, mint, config)];
                case 1: return [2 /*return*/, _a.sent()];
            }
        });
    });
}
// Extracts value_amount from the AcrossPlusMessage bytes. This serves as a 2nd method of deserializing the value, as
// a way to protect us against potential bugs in the deserialization logic.
function extractValueAmount(acrossPlusMessageBytes) {
    // Layout of the AcrossPlusMessage struct
    // #[derive(AnchorDeserialize)]
    // pub struct AcrossPlusMessage {
    //   pub handler: Pubkey,
    //   pub read_only_len: u8,
    //   pub value_amount: u64,
    //   pub accounts: Vec<Pubkey>,
    //   pub handler_message: Vec<u8>,
    // }
    var VALUE_OFFSET = 32 + 1; // 33
    var VALUE_END = VALUE_OFFSET + 8; // 41
    if (acrossPlusMessageBytes.length < VALUE_END) {
        throw new Error("svm | extractValueAmount: Message too short, need at least ".concat(VALUE_END, " bytes, got ").concat(acrossPlusMessageBytes.length));
    }
    return readU64LEExact(acrossPlusMessageBytes.subarray(VALUE_OFFSET, VALUE_END));
}
// Reads exactly 8 bytes as a little-endian u64 and returns bigint
function readU64LEExact(bytes) {
    if (bytes.length !== 8) {
        throw new Error("readU64LEExact expected 8 bytes, received ".concat(bytes.length));
    }
    var view = new DataView(bytes.buffer, bytes.byteOffset, 8);
    return view.getBigUint64(0, true); // little-endian
}
//# sourceMappingURL=SpokeUtils.js.map