"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.relayerRefundHashFn = exports.findProgramAddress = exports.calculateRelayHashUint8Array = exports.readProgramEvents = exports.readEvents = void 0;
exports.printLogs = printLogs;
exports.randomAddress = randomAddress;
exports.randomBigInt = randomBigInt;
exports.convertLeafIdToNumber = convertLeafIdToNumber;
exports.buildRelayerRefundMerkleTree = buildRelayerRefundMerkleTree;
exports.calculateRelayerRefundLeafHashUint8Array = calculateRelayerRefundLeafHashUint8Array;
exports.slowFillHashFn = slowFillHashFn;
exports.loadExecuteRelayerRefundLeafParams = loadExecuteRelayerRefundLeafParams;
const anchor_1 = require("@coral-xyz/anchor");
const web3_js_1 = require("@solana/web3.js");
const ethers_1 = require("ethers");
const crypto = __importStar(require("crypto"));
const SvmUtils_1 = require("../../src/SvmUtils");
Object.defineProperty(exports, "readEvents", { enumerable: true, get: function () { return SvmUtils_1.readEvents; } });
Object.defineProperty(exports, "readProgramEvents", { enumerable: true, get: function () { return SvmUtils_1.readProgramEvents; } });
Object.defineProperty(exports, "calculateRelayHashUint8Array", { enumerable: true, get: function () { return SvmUtils_1.calculateRelayHashUint8Array; } });
Object.defineProperty(exports, "findProgramAddress", { enumerable: true, get: function () { return SvmUtils_1.findProgramAddress; } });
const common_1 = require("@uma/common");
async function printLogs(connection, program, tx) {
    const latestBlockHash = await connection.getLatestBlockhash();
    await connection.confirmTransaction({
        blockhash: latestBlockHash.blockhash,
        lastValidBlockHeight: latestBlockHash.lastValidBlockHeight,
        signature: tx,
    }, "confirmed");
    const txDetails = await program.provider.connection.getTransaction(tx, {
        maxSupportedTransactionVersion: 0,
        commitment: "confirmed",
    });
    const logs = txDetails?.meta?.logMessages || null;
    if (!logs) {
        console.log("No logs found");
    }
}
function randomAddress() {
    const wallet = ethers_1.ethers.Wallet.createRandom();
    return wallet.address;
}
function randomBigInt(bytes = 8, signed = false) {
    const sign = signed && Math.random() < 0.5 ? "-" : "";
    const byteString = "0x" + Buffer.from(crypto.randomBytes(bytes)).toString("hex");
    return BigInt(sign + byteString);
}
function convertLeafIdToNumber(leaf) {
    return { ...leaf, leafId: leaf.leafId.toNumber() };
}
function buildRelayerRefundMerkleTree({ totalEvmDistributions, totalSolanaDistributions, mixLeaves, chainId, mint, svmRelayers, evmRelayers, evmTokenAddress, evmRefundAmounts, svmRefundAmounts, }) {
    const relayerRefundLeaves = [];
    const createSolanaLeaf = (index) => ({
        isSolana: true,
        leafId: new anchor_1.BN(index),
        chainId: new anchor_1.BN(chainId),
        amountToReturn: new anchor_1.BN(0),
        mintPublicKey: mint ?? web3_js_1.Keypair.generate().publicKey,
        refundAddresses: svmRelayers || [web3_js_1.Keypair.generate().publicKey, web3_js_1.Keypair.generate().publicKey],
        refundAmounts: svmRefundAmounts || [new anchor_1.BN(randomBigInt(2).toString()), new anchor_1.BN(randomBigInt(2).toString())],
    });
    const createEvmLeaf = (index) => ({
        isSolana: false,
        leafId: ethers_1.BigNumber.from(index),
        chainId: ethers_1.BigNumber.from(chainId),
        amountToReturn: ethers_1.BigNumber.from(0),
        l2TokenAddress: evmTokenAddress ?? randomAddress(),
        refundAddresses: evmRelayers || [randomAddress(), randomAddress()],
        refundAmounts: evmRefundAmounts || [ethers_1.BigNumber.from(randomBigInt()), ethers_1.BigNumber.from(randomBigInt())],
    });
    if (mixLeaves) {
        let solanaIndex = 0;
        let evmIndex = 0;
        const totalDistributions = totalSolanaDistributions + totalEvmDistributions;
        for (let i = 0; i < totalDistributions; i++) {
            if (solanaIndex < totalSolanaDistributions && (i % 2 === 0 || evmIndex >= totalEvmDistributions)) {
                relayerRefundLeaves.push(createSolanaLeaf(solanaIndex));
                solanaIndex++;
            }
            else if (evmIndex < totalEvmDistributions) {
                relayerRefundLeaves.push(createEvmLeaf(evmIndex));
                evmIndex++;
            }
        }
    }
    else {
        for (let i = 0; i < totalSolanaDistributions; i++) {
            relayerRefundLeaves.push(createSolanaLeaf(i));
        }
        for (let i = 0; i < totalEvmDistributions; i++) {
            relayerRefundLeaves.push(createEvmLeaf(i + totalSolanaDistributions));
        }
    }
    const merkleTree = new common_1.MerkleTree(relayerRefundLeaves, exports.relayerRefundHashFn);
    return { relayerRefundLeaves, merkleTree };
}
function calculateRelayerRefundLeafHashUint8Array(relayData) {
    const refundAmountsBuffer = Buffer.concat(relayData.refundAmounts.map((amount) => {
        const buf = Buffer.alloc(8);
        amount.toArrayLike(Buffer, "le", 8).copy(buf);
        return buf;
    }));
    const refundAddressesBuffer = Buffer.concat(relayData.refundAddresses.map((address) => address.toBuffer()));
    const contentToHash = Buffer.concat([
        relayData.amountToReturn.toArrayLike(Buffer, "le", 8),
        relayData.chainId.toArrayLike(Buffer, "le", 8),
        refundAmountsBuffer,
        relayData.leafId.toArrayLike(Buffer, "le", 4),
        relayData.mintPublicKey.toBuffer(),
        refundAddressesBuffer,
    ]);
    const relayHash = ethers_1.ethers.utils.keccak256(contentToHash);
    return relayHash;
}
const relayerRefundHashFn = (input) => {
    if (!input.isSolana) {
        const abiCoder = new ethers_1.ethers.utils.AbiCoder();
        const encodedData = abiCoder.encode([
            "tuple( uint256 amountToReturn, uint256 chainId, uint256[] refundAmounts, uint256 leafId, address l2TokenAddress, address[] refundAddresses)",
        ], [
            {
                leafId: input.leafId,
                chainId: input.chainId,
                amountToReturn: input.amountToReturn,
                l2TokenAddress: input.l2TokenAddress, // Type assertion
                refundAddresses: input.refundAddresses, // Type assertion
                refundAmounts: input.refundAmounts, // Type assertion
            },
        ]);
        return ethers_1.ethers.utils.keccak256(encodedData);
    }
    else {
        return calculateRelayerRefundLeafHashUint8Array(input);
    }
};
exports.relayerRefundHashFn = relayerRefundHashFn;
function slowFillHashFn(slowFillLeaf) {
    const contentToHash = Buffer.concat([
        slowFillLeaf.relayData.depositor.toBuffer(),
        slowFillLeaf.relayData.recipient.toBuffer(),
        slowFillLeaf.relayData.exclusiveRelayer.toBuffer(),
        slowFillLeaf.relayData.inputToken.toBuffer(),
        slowFillLeaf.relayData.outputToken.toBuffer(),
        slowFillLeaf.relayData.inputAmount.toArrayLike(Buffer, "le", 8),
        slowFillLeaf.relayData.outputAmount.toArrayLike(Buffer, "le", 8),
        slowFillLeaf.relayData.originChainId.toArrayLike(Buffer, "le", 8),
        slowFillLeaf.relayData.depositId.toArrayLike(Buffer, "le", 4),
        slowFillLeaf.relayData.fillDeadline.toArrayLike(Buffer, "le", 4),
        slowFillLeaf.relayData.exclusivityDeadline.toArrayLike(Buffer, "le", 4),
        slowFillLeaf.relayData.message,
        slowFillLeaf.chainId.toArrayLike(Buffer, "le", 8),
        slowFillLeaf.updatedOutputAmount.toArrayLike(Buffer, "le", 8),
    ]);
    const slowFillHash = ethers_1.ethers.utils.keccak256(contentToHash);
    return slowFillHash;
}
async function loadExecuteRelayerRefundLeafParams(program, caller, rootBundleId, relayerRefundLeaf, proof) {
    const maxInstructionParamsFragment = 900; // Should not exceed message size limit when writing to the data account.
    // Close the instruction params account if the caller has used it before.
    const [instructionParams] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("instruction_params"), caller.toBuffer()], program.programId);
    const accountInfo = await program.provider.connection.getAccountInfo(instructionParams);
    if (accountInfo !== null)
        await program.methods.closeInstructionParams().rpc();
    const accountCoder = new SvmUtils_1.LargeAccountsCoder(program.idl);
    const instructionParamsBytes = await accountCoder.encode("executeRelayerRefundLeafParams", {
        rootBundleId,
        relayerRefundLeaf,
        proof,
    });
    await program.methods.initializeInstructionParams(instructionParamsBytes.length).rpc();
    for (let i = 0; i < instructionParamsBytes.length; i += maxInstructionParamsFragment) {
        const fragment = instructionParamsBytes.slice(i, i + maxInstructionParamsFragment);
        await program.methods.writeInstructionParamsFragment(i, fragment).rpc();
    }
    return instructionParams;
}
