"use strict";
// This script bridges remote call to pause deposits on Solana Spoke Pool. Required environment:
// - NODE_URL_${CHAIN_ID}: Ethereum RPC URL (must point to the Mainnet or Sepolia depending on Solana cluster).
// - MNEMONIC: Mnemonic of the wallet that will sign the sending transaction on Ethereum
// - HUB_POOL_ADDRESS: Hub Pool address
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 common_1 = require("@uma/common");
require("dotenv/config");
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 typechain_1 = require("../../typechain");
const constants_1 = require("../../utils/constants");
const helpers_2 = require("./utils/helpers");
// Set up Solana provider.
const provider = anchor_1.AnchorProvider.env();
anchor.setProvider(provider);
// Parse arguments
const argv = (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
    .option("originChainId", { type: "string", demandOption: true, describe: "Origin chain ID" })
    .option("destinationChainId", { type: "string", demandOption: true, describe: "Destination chain ID" })
    .option("depositsEnabled", { type: "boolean", demandOption: true, describe: "Deposits enabled" })
    .option("resumeRemoteTx", { type: "string", demandOption: false, describe: "Resume receiving remote tx" }).argv;
async function remoteHubPoolSetDepositRoute() {
    const resolvedArgv = await argv;
    const originChainId = resolvedArgv.originChainId;
    const destinationChainId = resolvedArgv.destinationChainId;
    const depositsEnabled = resolvedArgv.depositsEnabled;
    const seed = new anchor_1.BN(0);
    const resumeRemoteTx = resolvedArgv.resumeRemoteTx;
    // Set up Ethereum provider and signer.
    const isDevnet = (0, web3_v1_1.isSolanaDevnet)(provider);
    const nodeURL = isDevnet ? (0, common_1.getNodeUrl)("sepolia", true) : (0, common_1.getNodeUrl)("mainnet", true);
    const ethersProvider = new ethers_1.ethers.providers.JsonRpcProvider(nodeURL);
    const ethersSigner = ethers_1.ethers.Wallet.fromMnemonic((0, helpers_2.requireEnv)("MNEMONIC")).connect(ethersProvider);
    const hubPoolAddress = (0, helpers_2.requireEnv)("HUB_POOL_ADDRESS");
    const usdcProgramId = isDevnet ? web3_v1_1.SOLANA_USDC_DEVNET : web3_v1_1.SOLANA_USDC_MAINNET;
    const originToken = new web3_js_1.PublicKey(usdcProgramId);
    const originTokenAddress = (0, web3_v1_1.fromBytes32ToAddress)((0, web3_v1_1.fromBase58ToBytes32)(originToken.toBase58()));
    // CCTP domains.
    const remoteDomain = 0; // Ethereum
    // Get Solana programs and accounts.
    const svmSpokeProgram = (0, web3_v1_1.getSpokePoolProgram)(provider);
    const [statePda, _] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("state"), seed.toArrayLike(Buffer, "le", 8)], svmSpokeProgram.programId);
    const [routePda] = web3_js_1.PublicKey.findProgramAddressSync([
        Buffer.from("route"),
        originToken.toBytes(),
        seed.toArrayLike(Buffer, "le", 8),
        new anchor_1.BN(destinationChainId).toArrayLike(Buffer, "le", 8),
    ], svmSpokeProgram.programId);
    const vault = (0, spl_token_1.getAssociatedTokenAddressSync)(originToken, statePda, true, spl_token_1.TOKEN_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
    const messageTransmitterProgram = (0, web3_v1_1.getMessageTransmitterProgram)(provider);
    const [messageTransmitterState] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("message_transmitter")], messageTransmitterProgram.programId);
    const [authorityPda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("message_transmitter_authority"), svmSpokeProgram.programId.toBuffer()], messageTransmitterProgram.programId);
    const [selfAuthority] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("self_authority")], svmSpokeProgram.programId);
    const [eventAuthority] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("__event_authority")], svmSpokeProgram.programId);
    const irisApiUrl = isDevnet ? web3_v1_1.CIRCLE_IRIS_API_URL_DEVNET : web3_v1_1.CIRCLE_IRIS_API_URL_MAINNET;
    const solanaCluster = isDevnet ? "devnet" : "mainnet";
    const supportedEvmChainId = isDevnet ? constants_1.CHAIN_IDs.SEPOLIA : constants_1.CHAIN_IDs.MAINNET; // Sepolia is bridged to devnet, Ethereum to mainnet in CCTP.
    const evmChainId = (await ethersProvider.getNetwork()).chainId;
    if (evmChainId !== supportedEvmChainId) {
        throw new Error(`Chain ID ${evmChainId} does not match expected Solana cluster ${solanaCluster}`);
    }
    const hubPool = typechain_1.HubPool__factory.connect(hubPoolAddress, ethersProvider);
    console.log("Remotely configuring deposit route...");
    console.table([
        { Property: "seed", Value: seed.toString() },
        { Property: "evmChainId", Value: evmChainId.toString() },
        { Property: "originChainId", Value: originChainId },
        { Property: "destinationChainId", Value: destinationChainId },
        { Property: "depositsEnabled", Value: depositsEnabled },
        { Property: "svmSpokeProgramProgramId", Value: svmSpokeProgram.programId.toString() },
        { Property: "providerPublicKey", Value: provider.wallet.publicKey.toString() },
        { Property: "statePda", Value: statePda.toString() },
        { Property: "messageTransmitterProgramId", Value: messageTransmitterProgram.programId.toString() },
        { Property: "messageTransmitterState", Value: messageTransmitterState.toString() },
        { Property: "authorityPda", Value: authorityPda.toString() },
        { Property: "selfAuthority", Value: selfAuthority.toString() },
        { Property: "eventAuthority", Value: eventAuthority.toString() },
        { Property: "remoteSender", Value: ethersSigner.address },
    ]);
    // Send setDepositRoute call from Ethereum, unless resuming a remote transaction.
    let remoteTxHash;
    if (!resumeRemoteTx) {
        console.log("Sending setDepositRoute message from HubPool...");
        const tx = await hubPool
            .connect(ethersSigner)
            .setDepositRoute(originChainId, destinationChainId, originTokenAddress, depositsEnabled);
        await tx.wait();
        remoteTxHash = tx.hash;
        console.log("Message sent on remote chain, tx", remoteTxHash);
    }
    else
        remoteTxHash = resumeRemoteTx;
    // Fetch attestation from CCTP attestation service.
    const attestationResponse = await (0, web3_v1_1.getMessages)(remoteTxHash, remoteDomain, irisApiUrl);
    const { attestation, message } = attestationResponse.messages[0];
    console.log("CCTP attestation response:", attestationResponse.messages[0]);
    // Accounts in CCTP message_transmitter receive_message instruction.
    const nonce = (0, web3_v1_1.decodeMessageHeader)(Buffer.from(message.replace("0x", ""), "hex")).nonce;
    const usedNonces = (await messageTransmitterProgram.methods
        .getNoncePda({
        nonce: new anchor_1.BN(nonce.toString()),
        sourceDomain: remoteDomain,
    })
        .accounts({
        messageTransmitter: messageTransmitterState,
    })
        .view());
    const receiveMessageAccounts = {
        payer: provider.wallet.publicKey,
        caller: provider.wallet.publicKey,
        authorityPda,
        messageTransmitter: messageTransmitterState,
        usedNonces,
        receiver: svmSpokeProgram.programId,
        systemProgram: anchor_1.web3.SystemProgram.programId,
    };
    // accountMetas list to pass to remaining accounts when receiving message via CCTP.
    const remainingAccounts = [];
    // state in HandleReceiveMessage accounts (used for remote domain and sender authentication).
    remainingAccounts.push({
        isSigner: false,
        isWritable: false,
        pubkey: statePda,
    });
    // self_authority in HandleReceiveMessage accounts, also signer in self-invoked CPIs.
    remainingAccounts.push({
        isSigner: false,
        isWritable: false,
        pubkey: selfAuthority,
    });
    // program in HandleReceiveMessage accounts.
    remainingAccounts.push({
        isSigner: false,
        isWritable: false,
        pubkey: svmSpokeProgram.programId,
    });
    // payer
    remainingAccounts.push({
        isSigner: true,
        isWritable: true,
        pubkey: provider.wallet.publicKey,
    });
    // state in self-invoked CPIs (state can change as a result of remote call).
    remainingAccounts.push({
        isSigner: false,
        isWritable: true,
        pubkey: statePda,
    });
    // route
    remainingAccounts.push({
        isSigner: false,
        isWritable: true,
        pubkey: routePda,
    });
    // vault
    remainingAccounts.push({
        isSigner: false,
        isWritable: true,
        pubkey: vault,
    });
    // origin token mint
    remainingAccounts.push({
        isSigner: false,
        isWritable: true,
        pubkey: originToken,
    });
    // token_program
    remainingAccounts.push({
        isSigner: false,
        isWritable: true,
        pubkey: spl_token_1.TOKEN_PROGRAM_ID,
    });
    // associated_token_program
    remainingAccounts.push({
        isSigner: false,
        isWritable: true,
        pubkey: spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID,
    });
    // system_program
    remainingAccounts.push({
        isSigner: false,
        isWritable: true,
        pubkey: web3_js_1.SystemProgram.programId,
    });
    // event_authority in self-invoked CPIs (appended by Anchor with event_cpi macro).
    remainingAccounts.push({
        isSigner: false,
        isWritable: true,
        pubkey: eventAuthority,
    });
    // program
    remainingAccounts.push({
        isSigner: false,
        isWritable: true,
        pubkey: svmSpokeProgram.programId,
    });
    // Receive remote message on Solana.
    console.log("Receiving message on Solana...");
    const receiveMessageTx = await messageTransmitterProgram.methods
        .receiveMessage({
        message: Buffer.from(message.replace("0x", ""), "hex"),
        attestation: Buffer.from(attestation.replace("0x", ""), "hex"),
    })
        .accounts(receiveMessageAccounts)
        .remainingAccounts(remainingAccounts)
        .rpc();
    console.log("\nReceived remote message");
    console.log("Your transaction signature", receiveMessageTx);
    let routeAccount = await svmSpokeProgram.account.route.fetch(routePda);
    console.log("Updated deposit route state to: enabled =", routeAccount.enabled);
}
remoteHubPoolSetDepositRoute()
    .then(() => {
    process.exit(0);
})
    .catch((err) => {
    console.error(err);
    process.exit(1);
});
