"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.transferFromSolana = exports.transferNativeSol = exports.transferFromEthNative = exports.transferFromEth = exports.approveEth = exports.getAllowanceEth = void 0;
const spl_token_1 = require("@solana/spl-token");
const web3_js_1 = require("@solana/web3.js");
const ethers_contracts_1 = require("../ethers-contracts");
const tokenBridge_1 = require("../solana/tokenBridge");
const utils_1 = require("../utils");
function getAllowanceEth(tokenBridgeAddress, tokenAddress, signer) {
    return __awaiter(this, void 0, void 0, function* () {
        const token = ethers_contracts_1.TokenImplementation__factory.connect(tokenAddress, signer);
        const signerAddress = yield signer.getAddress();
        const allowance = yield token.allowance(signerAddress, tokenBridgeAddress);
        return allowance;
    });
}
exports.getAllowanceEth = getAllowanceEth;
function approveEth(tokenBridgeAddress, tokenAddress, signer, amount, overrides = {}) {
    return __awaiter(this, void 0, void 0, function* () {
        const token = ethers_contracts_1.TokenImplementation__factory.connect(tokenAddress, signer);
        return yield (yield token.approve(tokenBridgeAddress, amount, overrides)).wait();
    });
}
exports.approveEth = approveEth;
function transferFromEth(tokenBridgeAddress, signer, tokenAddress, amount, recipientChain, recipientAddress, relayerFee = 0, overrides = {}, payload = null) {
    return __awaiter(this, void 0, void 0, function* () {
        const recipientChainId = utils_1.coalesceChainId(recipientChain);
        const bridge = ethers_contracts_1.Bridge__factory.connect(tokenBridgeAddress, signer);
        const v = payload === null
            ? yield bridge.transferTokens(tokenAddress, amount, recipientChainId, recipientAddress, relayerFee, utils_1.createNonce(), overrides)
            : yield bridge.transferTokensWithPayload(tokenAddress, amount, recipientChainId, recipientAddress, utils_1.createNonce(), payload, overrides);
        const receipt = yield v.wait();
        return receipt;
    });
}
exports.transferFromEth = transferFromEth;
function transferFromEthNative(tokenBridgeAddress, signer, amount, recipientChain, recipientAddress, relayerFee = 0, overrides = {}, payload = null) {
    return __awaiter(this, void 0, void 0, function* () {
        const recipientChainId = utils_1.coalesceChainId(recipientChain);
        const bridge = ethers_contracts_1.Bridge__factory.connect(tokenBridgeAddress, signer);
        const v = payload === null
            ? yield bridge.wrapAndTransferETH(recipientChainId, recipientAddress, relayerFee, utils_1.createNonce(), Object.assign(Object.assign({}, overrides), { value: amount }))
            : yield bridge.wrapAndTransferETHWithPayload(recipientChainId, recipientAddress, utils_1.createNonce(), payload, Object.assign(Object.assign({}, overrides), { value: amount }));
        const receipt = yield v.wait();
        return receipt;
    });
}
exports.transferFromEthNative = transferFromEthNative;
function transferNativeSol(connection, bridgeAddress, tokenBridgeAddress, payerAddress, amount, targetAddress, targetChain, relayerFee = BigInt(0), payload = null, commitment) {
    return __awaiter(this, void 0, void 0, function* () {
        const rentBalance = yield spl_token_1.getMinimumBalanceForRentExemptAccount(connection, commitment);
        const payerPublicKey = new web3_js_1.PublicKey(payerAddress);
        const ancillaryKeypair = web3_js_1.Keypair.generate();
        //This will create a temporary account where the wSOL will be created.
        const createAncillaryAccountIx = web3_js_1.SystemProgram.createAccount({
            fromPubkey: payerPublicKey,
            newAccountPubkey: ancillaryKeypair.publicKey,
            lamports: rentBalance,
            space: spl_token_1.ACCOUNT_SIZE,
            programId: spl_token_1.TOKEN_PROGRAM_ID,
        });
        //Send in the amount of SOL which we want converted to wSOL
        const initialBalanceTransferIx = web3_js_1.SystemProgram.transfer({
            fromPubkey: payerPublicKey,
            lamports: amount,
            toPubkey: ancillaryKeypair.publicKey,
        });
        //Initialize the account as a WSOL account, with the original payerAddress as owner
        const initAccountIx = spl_token_1.createInitializeAccountInstruction(ancillaryKeypair.publicKey, spl_token_1.NATIVE_MINT, payerPublicKey);
        //Normal approve & transfer instructions, except that the wSOL is sent from the ancillary account.
        const approvalIx = tokenBridge_1.createApproveAuthoritySignerInstruction(tokenBridgeAddress, ancillaryKeypair.publicKey, payerPublicKey, amount);
        const message = web3_js_1.Keypair.generate();
        const nonce = utils_1.createNonce().readUInt32LE(0);
        const tokenBridgeTransferIx = payload !== null
            ? tokenBridge_1.createTransferNativeWithPayloadInstruction(tokenBridgeAddress, bridgeAddress, payerAddress, message.publicKey, ancillaryKeypair.publicKey, spl_token_1.NATIVE_MINT, nonce, amount, Buffer.from(targetAddress), utils_1.coalesceChainId(targetChain), payload)
            : tokenBridge_1.createTransferNativeInstruction(tokenBridgeAddress, bridgeAddress, payerAddress, message.publicKey, ancillaryKeypair.publicKey, spl_token_1.NATIVE_MINT, nonce, amount, relayerFee, Buffer.from(targetAddress), utils_1.coalesceChainId(targetChain));
        //Close the ancillary account for cleanup. Payer address receives any remaining funds
        const closeAccountIx = spl_token_1.createCloseAccountInstruction(ancillaryKeypair.publicKey, //account to close
        payerPublicKey, //Remaining funds destination
        payerPublicKey //authority
        );
        const { blockhash } = yield connection.getLatestBlockhash(commitment);
        const transaction = new web3_js_1.Transaction();
        transaction.recentBlockhash = blockhash;
        transaction.feePayer = payerPublicKey;
        transaction.add(createAncillaryAccountIx, initialBalanceTransferIx, initAccountIx, approvalIx, tokenBridgeTransferIx, closeAccountIx);
        transaction.partialSign(message, ancillaryKeypair);
        return transaction;
    });
}
exports.transferNativeSol = transferNativeSol;
function transferFromSolana(connection, bridgeAddress, tokenBridgeAddress, payerAddress, fromAddress, mintAddress, amount, targetAddress, targetChain, originAddress, originChain, fromOwnerAddress, relayerFee = BigInt(0), payload = null, commitment) {
    return __awaiter(this, void 0, void 0, function* () {
        const originChainId = originChain
            ? utils_1.coalesceChainId(originChain)
            : undefined;
        if (fromOwnerAddress === undefined) {
            fromOwnerAddress = payerAddress;
        }
        const nonce = utils_1.createNonce().readUInt32LE(0);
        const approvalIx = tokenBridge_1.createApproveAuthoritySignerInstruction(tokenBridgeAddress, fromAddress, fromOwnerAddress, amount);
        const message = web3_js_1.Keypair.generate();
        const isSolanaNative = originChainId === undefined || originChainId === utils_1.CHAIN_ID_SOLANA;
        if (!isSolanaNative && !originAddress) {
            return Promise.reject("originAddress is required when specifying originChain");
        }
        const tokenBridgeTransferIx = isSolanaNative
            ? payload !== null
                ? tokenBridge_1.createTransferNativeWithPayloadInstruction(tokenBridgeAddress, bridgeAddress, payerAddress, message.publicKey, fromAddress, mintAddress, nonce, amount, targetAddress, utils_1.coalesceChainId(targetChain), payload)
                : tokenBridge_1.createTransferNativeInstruction(tokenBridgeAddress, bridgeAddress, payerAddress, message.publicKey, fromAddress, mintAddress, nonce, amount, relayerFee, targetAddress, utils_1.coalesceChainId(targetChain))
            : payload !== null
                ? tokenBridge_1.createTransferWrappedWithPayloadInstruction(tokenBridgeAddress, bridgeAddress, payerAddress, message.publicKey, fromAddress, fromOwnerAddress, originChainId, originAddress, nonce, amount, targetAddress, utils_1.coalesceChainId(targetChain), payload)
                : tokenBridge_1.createTransferWrappedInstruction(tokenBridgeAddress, bridgeAddress, payerAddress, message.publicKey, fromAddress, fromOwnerAddress, originChainId, originAddress, nonce, amount, relayerFee, targetAddress, utils_1.coalesceChainId(targetChain));
        const transaction = new web3_js_1.Transaction().add(approvalIx, tokenBridgeTransferIx);
        const { blockhash } = yield connection.getLatestBlockhash(commitment);
        transaction.recentBlockhash = blockhash;
        transaction.feePayer = new web3_js_1.PublicKey(payerAddress);
        transaction.partialSign(message);
        return transaction;
    });
}
exports.transferFromSolana = transferFromSolana;
