"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.relayerRefundHashFn = exports.readUInt256BE = void 0;
exports.calculateRelayHashUint8Array = calculateRelayHashUint8Array;
exports.calculateRelayEventHashUint8Array = calculateRelayEventHashUint8Array;
exports.hashNonEmptyMessage = hashNonEmptyMessage;
exports.calculateRelayerRefundLeafHashUint8Array = calculateRelayerRefundLeafHashUint8Array;
exports.slowFillHashFn = slowFillHashFn;
const anchor_1 = require("@coral-xyz/anchor");
const ethers_1 = require("ethers");
const borsh_1 = require("borsh");
/**
 * Calculates the relay hash from relay data and chain ID.
 */
function calculateRelayHashUint8Array(relayData, chainId) {
    const contentToHash = Buffer.concat([
        relayData.depositor.toBuffer(),
        relayData.recipient.toBuffer(),
        relayData.exclusiveRelayer.toBuffer(),
        relayData.inputToken.toBuffer(),
        relayData.outputToken.toBuffer(),
        relayData.inputAmount.toArrayLike(Buffer, "le", 8),
        relayData.outputAmount.toArrayLike(Buffer, "le", 8),
        relayData.originChainId.toArrayLike(Buffer, "le", 8),
        Buffer.from(relayData.depositId),
        new anchor_1.BN(relayData.fillDeadline).toArrayLike(Buffer, "le", 4),
        new anchor_1.BN(relayData.exclusivityDeadline).toArrayLike(Buffer, "le", 4),
        hashNonEmptyMessage(relayData.message), // Replace with hash of message, so that relay hash can be recovered from event.
        chainId.toArrayLike(Buffer, "le", 8),
    ]);
    const relayHash = ethers_1.ethers.utils.keccak256(contentToHash);
    const relayHashBuffer = Buffer.from(relayHash.slice(2), "hex");
    return new Uint8Array(relayHashBuffer);
}
/**
 * Calculates the relay event hash from relay event data and chain ID.
 */
function calculateRelayEventHashUint8Array(relayEventData, chainId) {
    const contentToHash = Buffer.concat([
        relayEventData.depositor.toBuffer(),
        relayEventData.recipient.toBuffer(),
        relayEventData.exclusiveRelayer.toBuffer(),
        relayEventData.inputToken.toBuffer(),
        relayEventData.outputToken.toBuffer(),
        relayEventData.inputAmount.toArrayLike(Buffer, "le", 8),
        relayEventData.outputAmount.toArrayLike(Buffer, "le", 8),
        relayEventData.originChainId.toArrayLike(Buffer, "le", 8),
        Buffer.from(relayEventData.depositId),
        new anchor_1.BN(relayEventData.fillDeadline).toArrayLike(Buffer, "le", 4),
        new anchor_1.BN(relayEventData.exclusivityDeadline).toArrayLike(Buffer, "le", 4),
        Buffer.from(relayEventData.messageHash), // Renamed to messageHash in the event data.
        chainId.toArrayLike(Buffer, "le", 8),
    ]);
    const relayHash = ethers_1.ethers.utils.keccak256(contentToHash);
    const relayHashBuffer = Buffer.from(relayHash.slice(2), "hex");
    return new Uint8Array(relayHashBuffer);
}
/**
 * Reads a 256-bit unsigned integer from a buffer.
 */
const readUInt256BE = (buffer) => {
    let result = BigInt(0);
    for (let i = 0; i < buffer.length; i++) {
        result = (result << BigInt(8)) + BigInt(buffer[i]);
    }
    return result;
};
exports.readUInt256BE = readUInt256BE;
/**
 * Hashes a non-empty message using Keccak256.
 */
function hashNonEmptyMessage(message) {
    if (message.length > 0) {
        const hash = ethers_1.ethers.utils.keccak256(message);
        return Uint8Array.from(Buffer.from(hash.slice(2), "hex"));
    }
    // else return zeroed bytes32
    return new Uint8Array(32);
}
/**
 * Class for relay data.
 */
class RelayData {
    constructor(properties) {
        Object.assign(this, properties);
    }
}
/**
 * Schema for relay data.
 */
const relayDataSchema = new Map([
    [
        RelayData,
        {
            kind: "struct",
            fields: [
                ["amountToReturn", "u64"],
                ["chainId", "u64"],
                ["refundAmounts", ["u64"]],
                ["leafId", "u32"],
                ["mintPublicKey", [32]],
                ["refundAddresses", [[32]]],
            ],
        },
    ],
]);
/**
 * Calculates the relayer refund leaf hash for Solana.
 */
function calculateRelayerRefundLeafHashUint8Array(relayData) {
    const refundAddresses = relayData.refundAddresses.map((address) => address.toBuffer());
    const data = new RelayData({
        amountToReturn: relayData.amountToReturn,
        chainId: relayData.chainId,
        refundAmounts: relayData.refundAmounts,
        leafId: relayData.leafId,
        mintPublicKey: relayData.mintPublicKey.toBuffer(),
        refundAddresses: refundAddresses,
    });
    const serializedData = (0, borsh_1.serialize)(relayDataSchema, data);
    // SVM leaves require the first 64 bytes to be 0 to ensure EVM leaves can never be played on SVM and vice versa.
    const contentToHash = Buffer.concat([Buffer.alloc(64, 0), serializedData]);
    return ethers_1.ethers.utils.keccak256(contentToHash);
}
/**
 * Hash function for relayer refund leaves.
 */
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;
/**
 * Class for slow fill data.
 */
class SlowFillData {
    constructor(properties) {
        Object.assign(this, properties);
    }
}
/**
 * Schema for slow fill data.
 */
const slowFillDataSchema = new Map([
    [
        SlowFillData,
        {
            kind: "struct",
            fields: [
                ["depositor", [32]],
                ["recipient", [32]],
                ["exclusiveRelayer", [32]],
                ["inputToken", [32]],
                ["outputToken", [32]],
                ["inputAmount", "u64"],
                ["outputAmount", "u64"],
                ["originChainId", "u64"],
                ["depositId", [32]],
                ["fillDeadline", "u32"],
                ["exclusivityDeadline", "u32"],
                ["message", ["u8"]],
                ["chainId", "u64"],
                ["updatedOutputAmount", "u64"],
            ],
        },
    ],
]);
/**
 * Hash function for slow fill leaves.
 */
function slowFillHashFn(slowFillLeaf) {
    const data = new SlowFillData({
        depositor: Uint8Array.from(slowFillLeaf.relayData.depositor.toBuffer()),
        recipient: Uint8Array.from(slowFillLeaf.relayData.recipient.toBuffer()),
        exclusiveRelayer: Uint8Array.from(slowFillLeaf.relayData.exclusiveRelayer.toBuffer()),
        inputToken: Uint8Array.from(slowFillLeaf.relayData.inputToken.toBuffer()),
        outputToken: Uint8Array.from(slowFillLeaf.relayData.outputToken.toBuffer()),
        inputAmount: slowFillLeaf.relayData.inputAmount,
        outputAmount: slowFillLeaf.relayData.outputAmount,
        originChainId: slowFillLeaf.relayData.originChainId,
        depositId: Uint8Array.from(Buffer.from(slowFillLeaf.relayData.depositId)),
        fillDeadline: slowFillLeaf.relayData.fillDeadline,
        exclusivityDeadline: slowFillLeaf.relayData.exclusivityDeadline,
        message: Uint8Array.from(slowFillLeaf.relayData.message),
        chainId: slowFillLeaf.chainId,
        updatedOutputAmount: slowFillLeaf.updatedOutputAmount,
    });
    const serializedData = (0, borsh_1.serialize)(slowFillDataSchema, data);
    // SVM leaves require the first 64 bytes to be 0 to ensure EVM leaves cannot be played on SVM and vice versa
    const contentToHash = Buffer.concat([Buffer.alloc(64, 0), serializedData]);
    return ethers_1.ethers.utils.keccak256(contentToHash);
}
