"use strict";
// This script deposits USDC on the SVM Sponsored CCTP bridge. The script requires the quote signer keys expected to be
// used only on devnet.
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;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const anchor = __importStar(require("@coral-xyz/anchor"));
const anchor_1 = require("@coral-xyz/anchor");
const spl_token_1 = require("@solana/spl-token");
const web3_js_1 = require("@solana/web3.js");
const crypto = __importStar(require("crypto"));
const ethers_1 = require("ethers");
const yargs_1 = __importDefault(require("yargs"));
const helpers_1 = require("yargs/helpers");
const web3_v1_1 = require("../../../src/svm/web3-v1");
const helpers_2 = require("../utils/helpers");
// Set up the provider and programs
const provider = anchor_1.AnchorProvider.env();
anchor.setProvider(provider);
const program = (0, web3_v1_1.getSponsoredCctpSrcPeripheryProgram)(provider);
const programId = program.programId;
const tokenMessengerMinterV2Program = (0, web3_v1_1.getTokenMessengerMinterV2Program)(provider);
const messageTransmitterV2Program = (0, web3_v1_1.getMessageTransmitterV2Program)(provider);
const burnToken = new web3_js_1.PublicKey((0, web3_v1_1.isSolanaDevnet)(provider) ? web3_v1_1.SOLANA_USDC_DEVNET : web3_v1_1.SOLANA_USDC_MAINNET);
const tokenProgram = spl_token_1.TOKEN_PROGRAM_ID;
const sourceDomain = 5; // CCTP domain for Solana
const quoteSigner = ethers_1.ethers.Wallet.fromMnemonic((0, helpers_2.requireEnv)("MNEMONIC"));
var ExecutionMode;
(function (ExecutionMode) {
    ExecutionMode[ExecutionMode["DirectToCore"] = 0] = "DirectToCore";
    ExecutionMode[ExecutionMode["ArbitraryActionsToCore"] = 1] = "ArbitraryActionsToCore";
    ExecutionMode[ExecutionMode["ArbitraryActionsToEVM"] = 2] = "ArbitraryActionsToEVM";
})(ExecutionMode || (ExecutionMode = {}));
// Parse arguments
const argv = (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
    .option("amount", {
    type: "number",
    demandOption: true,
    describe: "Amount to deposit in raw decimals (1 USDC = 1e6 raw decimals)",
})
    .option("remoteDomain", {
    type: "number",
    demandOption: false,
    default: 19,
    describe: "Remote CCTP domain for the burn token, defaults to HyperEVM (19)",
})
    .option("mintRecipient", {
    type: "string",
    demandOption: true,
    describe: "Mint recipient address, EVM format",
})
    .option("destinationCaller", {
    type: "string",
    demandOption: true,
    describe: "Destination caller address, EVM format",
})
    .option("finalRecipient", {
    type: "string",
    demandOption: true,
    describe: " Final recipient address, EVM format",
})
    .option("finalToken", {
    type: "string",
    demandOption: true,
    describe: " Final token address, EVM format",
})
    .option("maxFee", {
    type: "number",
    demandOption: false,
    describe: "Max CCTP fee in raw decimals, defaults to 1 bps from amount",
})
    .option("minFinalityThreshold", {
    type: "number",
    demandOption: false,
    default: 1000,
    describe: "Minimum CCTP finality threshold, defaults to confirmed (1000)",
})
    .option("maxBpsToSponsor", {
    type: "number",
    demandOption: false,
    default: 500, // 5 bps
    describe: "Maximum bps to sponsor, defaults to 500 (5 bps)",
})
    .option("maxUserSlippageBps", {
    type: "number",
    demandOption: false,
    default: 1000, // 10 bps
    describe: "Maximum user slippage in bps, defaults to 1000 (10 bps)",
})
    .option("executionMode", {
    type: "number",
    demandOption: false,
    default: ExecutionMode.DirectToCore,
    choices: Object.values(ExecutionMode),
    describe: "Execution mode for the sponsored CCTP flow",
})
    .option("actionData", {
    type: "string",
    demandOption: false,
    default: "0x",
    describe: "Action data for the sponsored CCTP flow, defaults to empty bytes",
})
    .option("deadline", {
    type: "number",
    demandOption: false,
    default: Math.floor(Date.now() / 1000) + 3600, // 1 hour from now
    describe: "Quote validity deadline, defaults to 1 hour from now",
})
    .option("useRentClaim", { type: "boolean", default: false, describe: "Pass optional rent_claim account" })
    .option("lookupTable", {
    type: "string",
    demandOption: false,
    describe: "Address of the address lookup table to use for the transaction in case of size limitations",
}).argv;
async function depositForBurn() {
    const resolvedArgv = await argv;
    const amount = resolvedArgv.amount;
    const remoteDomain = resolvedArgv.remoteDomain;
    const mintRecipient = ethers_1.ethers.utils.getAddress(resolvedArgv.mintRecipient);
    const destinationCaller = ethers_1.ethers.utils.getAddress(resolvedArgv.destinationCaller);
    const finalRecipient = ethers_1.ethers.utils.getAddress(resolvedArgv.finalRecipient);
    const finalToken = ethers_1.ethers.utils.getAddress(resolvedArgv.finalToken);
    const maxFee = resolvedArgv.maxFee || Math.ceil(amount * 0.0001); // Default to 1 bps of the amount
    const minFinalityThreshold = resolvedArgv.minFinalityThreshold;
    const maxBpsToSponsor = resolvedArgv.maxBpsToSponsor;
    const maxUserSlippageBps = resolvedArgv.maxUserSlippageBps;
    const executionMode = Number(resolvedArgv.executionMode);
    const actionData = ethers_1.ethers.utils.hexlify(resolvedArgv.actionData);
    const deadline = resolvedArgv.deadline;
    const useRentClaim = resolvedArgv.useRentClaim;
    const lookupTable = resolvedArgv.lookupTable;
    const depositor = provider.wallet.payer;
    if (!depositor) {
        throw new Error("Provider wallet does not have a keypair");
    }
    const depositorTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(burnToken, depositor.publicKey);
    const state = (0, web3_v1_1.findProgramAddress)("state", programId).publicKey;
    const rentFund = (0, web3_v1_1.findProgramAddress)("rent_fund", programId).publicKey;
    const [minimumDeposit] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("minimum_deposit"), burnToken.toBuffer()], programId);
    const [denylistAccount] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("denylist_account"), depositor.publicKey.toBuffer()], tokenMessengerMinterV2Program.programId);
    const tokenMessengerMinterSenderAuthority = (0, web3_v1_1.findProgramAddress)("sender_authority", tokenMessengerMinterV2Program.programId).publicKey;
    const messageTransmitter = (0, web3_v1_1.findProgramAddress)("message_transmitter", messageTransmitterV2Program.programId).publicKey;
    const tokenMessenger = (0, web3_v1_1.findProgramAddress)("token_messenger", tokenMessengerMinterV2Program.programId).publicKey;
    const remoteTokenMessenger = (0, web3_v1_1.findProgramAddress)("remote_token_messenger", tokenMessengerMinterV2Program.programId, [
        remoteDomain.toString(),
    ]).publicKey;
    const tokenMinter = (0, web3_v1_1.findProgramAddress)("token_minter", tokenMessengerMinterV2Program.programId).publicKey;
    const cctpEventAuthority = (0, web3_v1_1.findProgramAddress)("__event_authority", tokenMessengerMinterV2Program.programId).publicKey;
    const [localToken] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("local_token"), burnToken.toBuffer()], tokenMessengerMinterV2Program.programId);
    const messageSentEventData = web3_js_1.Keypair.generate();
    const nonce = crypto.randomBytes(32);
    const [usedNonce] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("used_nonce"), nonce], programId);
    const rentClaim = useRentClaim
        ? web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("rent_claim"), depositor.publicKey.toBuffer()], programId)[0]
        : programId;
    const quoteDataEvm = {
        sourceDomain,
        destinationDomain: remoteDomain,
        mintRecipient: ethers_1.ethers.utils.hexZeroPad(mintRecipient, 32),
        amount,
        burnToken: ethers_1.ethers.utils.hexlify(burnToken.toBuffer()),
        destinationCaller: ethers_1.ethers.utils.hexZeroPad(destinationCaller, 32),
        maxFee,
        minFinalityThreshold,
        nonce: ethers_1.ethers.utils.hexlify(nonce),
        deadline,
        maxBpsToSponsor,
        maxUserSlippageBps,
        finalRecipient: ethers_1.ethers.utils.hexZeroPad(finalRecipient, 32),
        finalToken: ethers_1.ethers.utils.hexZeroPad(finalToken, 32),
        executionMode,
        actionData,
    };
    // Hash the quote data for EVM signing
    const hash1 = ethers_1.ethers.utils.keccak256(ethers_1.ethers.utils.defaultAbiCoder.encode(["uint32", "uint32", "bytes32", "uint256", "bytes32", "bytes32", "uint256", "uint32"], [
        quoteDataEvm.sourceDomain,
        quoteDataEvm.destinationDomain,
        quoteDataEvm.mintRecipient,
        quoteDataEvm.amount,
        quoteDataEvm.burnToken,
        quoteDataEvm.destinationCaller,
        quoteDataEvm.maxFee,
        quoteDataEvm.minFinalityThreshold,
    ]));
    const hash2 = ethers_1.ethers.utils.keccak256(ethers_1.ethers.utils.defaultAbiCoder.encode(["bytes32", "uint256", "uint256", "uint256", "bytes32", "bytes32", "uint8", "bytes32"], [
        quoteDataEvm.nonce,
        quoteDataEvm.deadline,
        quoteDataEvm.maxBpsToSponsor,
        quoteDataEvm.maxUserSlippageBps,
        quoteDataEvm.finalRecipient,
        quoteDataEvm.finalToken,
        quoteDataEvm.executionMode,
        ethers_1.ethers.utils.keccak256(quoteDataEvm.actionData),
    ]));
    const typedDataHash = ethers_1.ethers.utils.keccak256(ethers_1.ethers.utils.defaultAbiCoder.encode(["bytes32", "bytes32"], [hash1, hash2]));
    const signature = Array.from(Buffer.from(ethers_1.ethers.utils.arrayify(ethers_1.ethers.utils.joinSignature(quoteSigner._signingKey().signDigest(typedDataHash)))));
    // Encode quote for Solana
    const quote = {
        sourceDomain: quoteDataEvm.sourceDomain,
        destinationDomain: quoteDataEvm.destinationDomain,
        mintRecipient: new web3_js_1.PublicKey(ethers_1.ethers.utils.arrayify(quoteDataEvm.mintRecipient)),
        amount: new anchor_1.BN(quoteDataEvm.amount.toString()),
        burnToken: new web3_js_1.PublicKey(ethers_1.ethers.utils.arrayify(quoteDataEvm.burnToken)),
        destinationCaller: new web3_js_1.PublicKey(ethers_1.ethers.utils.arrayify(quoteDataEvm.destinationCaller)),
        maxFee: new anchor_1.BN(quoteDataEvm.maxFee.toString()),
        minFinalityThreshold: quoteDataEvm.minFinalityThreshold,
        nonce: Array.from(ethers_1.ethers.utils.arrayify(quoteDataEvm.nonce)),
        deadline: new anchor_1.BN(quoteDataEvm.deadline.toString()),
        maxBpsToSponsor: new anchor_1.BN(quoteDataEvm.maxBpsToSponsor.toString()),
        maxUserSlippageBps: new anchor_1.BN(quoteDataEvm.maxUserSlippageBps.toString()),
        finalRecipient: new web3_js_1.PublicKey(ethers_1.ethers.utils.arrayify(quoteDataEvm.finalRecipient)),
        finalToken: new web3_js_1.PublicKey(ethers_1.ethers.utils.arrayify(quoteDataEvm.finalToken)),
        executionMode: quoteDataEvm.executionMode,
        actionData: Buffer.from(ethers_1.ethers.utils.arrayify(quoteDataEvm.actionData)),
    };
    const depositAccounts = {
        signer: depositor.publicKey,
        payer: depositor.publicKey,
        state,
        rentFund,
        minimumDeposit,
        usedNonce,
        rentClaim,
        depositorTokenAccount,
        burnToken,
        denylistAccount,
        tokenMessengerMinterSenderAuthority,
        messageTransmitter,
        tokenMessenger,
        remoteTokenMessenger,
        tokenMinter,
        localToken,
        cctpEventAuthority,
        tokenProgram,
        messageSentEventData: messageSentEventData.publicKey,
        program: programId,
    };
    const ix = await program.methods.depositForBurn({ quote, signature }).accounts(depositAccounts).instruction();
    console.log("Depositing on sponsored CCTP bridge...");
    console.table([
        { Property: "programId", Value: programId.toString() },
        { Property: "amount", Value: amount.toString() },
        { Property: "remoteDomain", Value: remoteDomain.toString() },
        { Property: "mintRecipient", Value: mintRecipient },
        { Property: "destinationCaller", Value: destinationCaller },
        { Property: "finalRecipient", Value: finalRecipient },
        { Property: "finalToken", Value: finalToken },
        { Property: "maxFee", Value: maxFee.toString() },
        { Property: "minFinalityThreshold", Value: minFinalityThreshold.toString() },
        { Property: "maxBpsToSponsor", Value: maxBpsToSponsor.toString() },
        { Property: "maxUserSlippageBps", Value: maxUserSlippageBps.toString() },
        { Property: "executionMode", Value: executionMode.toString() },
        { Property: "actionData", Value: actionData },
        { Property: "depositorTokenAccount", Value: depositorTokenAccount.toString() },
        { Property: "depositor", Value: depositor.publicKey.toString() },
        { Property: "state", Value: state.toString() },
        { Property: "rentFund", Value: rentFund.toString() },
        { Property: "minimumDeposit", Value: minimumDeposit.toString() },
        { Property: "usedNonce", Value: usedNonce.toString() },
        { Property: "rentClaim", Value: rentClaim.toString() },
        { Property: "burnToken", Value: burnToken.toString() },
        { Property: "depositorTokenAccount", Value: depositorTokenAccount.toString() },
        { Property: "denylistAccount", Value: denylistAccount.toString() },
        { Property: "tokenMessengerMinterSenderAuthority", Value: tokenMessengerMinterSenderAuthority.toString() },
        { Property: "messageTransmitter", Value: messageTransmitter.toString() },
        { Property: "tokenMessenger", Value: tokenMessenger.toString() },
        { Property: "remoteTokenMessenger", Value: remoteTokenMessenger.toString() },
        { Property: "tokenMinter", Value: tokenMinter.toString() },
        { Property: "localToken", Value: localToken.toString() },
        { Property: "cctpEventAuthority", Value: cctpEventAuthority.toString() },
        { Property: "messageSentEventData", Value: messageSentEventData.publicKey.toString() },
    ]);
    const tx = new web3_js_1.Transaction().add(ix);
    try {
        const txSignature = await provider.sendAndConfirm(tx, [depositor, messageSentEventData], {
            commitment: "confirmed",
        });
        console.log("Deposited to the sponsored CCTP bridge successfully, transaction signature:", txSignature);
    }
    catch (err) {
        const msg = err instanceof Error ? err.message : String(err);
        if (/Transaction too large/i.test(msg)) {
            if (!lookupTable) {
                throw new Error("Transaction too large, please provide an address lookup table using the --lookupTable option.");
            }
            console.log("Transaction too large, retrying with address lookup table...");
            const lookupTableAccount = (await provider.connection.getAddressLookupTable(new web3_js_1.PublicKey(lookupTable))).value;
            if (lookupTableAccount === null)
                throw new Error("AddressLookupTableAccount not fetched");
            const txSignature = await (0, web3_v1_1.sendTransactionWithExistingLookupTable)(provider.connection, [ix], lookupTableAccount, depositor, [messageSentEventData]);
            console.log("Deposited to the sponsored CCTP bridge successfully, transaction signature:", txSignature);
        }
        else {
            throw err;
        }
    }
}
depositForBurn();
