"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());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.transferFromSui = exports.transferFromAptos = exports.transferNearFromNear = exports.transferTokenFromNear = exports.transferFromAlgorand = exports.transferFromSolana = exports.transferNativeSol = exports.transferFromXpla = exports.transferFromTerra = exports.transferFromEthNative = exports.transferFromEth = exports.approveEth = exports.getAllowanceEth = void 0;
const sui_js_1 = require("@mysten/sui.js");
const spl_token_1 = require("@solana/spl-token");
const web3_js_1 = require("@solana/web3.js");
const terra_js_1 = require("@terra-money/terra.js");
const xpla_js_1 = require("@xpla/xpla.js");
const algosdk_1 = require("algosdk");
const bn_js_1 = __importDefault(require("bn.js"));
const __1 = require("..");
const algorand_1 = require("../algorand");
const aptos_1 = require("../aptos");
const bridge_1 = require("../bridge");
const cosmwasm_1 = require("../cosmwasm");
const ethers_contracts_1 = require("../ethers-contracts");
const tokenBridge_1 = require("../solana/tokenBridge");
const sui_1 = require("../sui");
const terra_1 = require("../terra");
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 transferFromTerra(walletAddress, tokenBridgeAddress, tokenAddress, amount, recipientChain, recipientAddress, relayerFee = "0", payload = null) {
    return __awaiter(this, void 0, void 0, function* () {
        const recipientChainId = utils_1.coalesceChainId(recipientChain);
        const nonce = Math.round(Math.random() * 100000);
        const isNativeAsset = terra_1.isNativeDenom(tokenAddress);
        const mk_initiate_transfer = (info) => payload
            ? {
                initiate_transfer_with_payload: {
                    asset: {
                        amount,
                        info,
                    },
                    recipient_chain: recipientChainId,
                    recipient: Buffer.from(recipientAddress).toString("base64"),
                    fee: relayerFee,
                    nonce: nonce,
                    payload: payload,
                },
            }
            : {
                initiate_transfer: {
                    asset: {
                        amount,
                        info,
                    },
                    recipient_chain: recipientChainId,
                    recipient: Buffer.from(recipientAddress).toString("base64"),
                    fee: relayerFee,
                    nonce: nonce,
                },
            };
        return isNativeAsset
            ? [
                new terra_js_1.MsgExecuteContract(walletAddress, tokenBridgeAddress, {
                    deposit_tokens: {},
                }, { [tokenAddress]: amount }),
                new terra_js_1.MsgExecuteContract(walletAddress, tokenBridgeAddress, mk_initiate_transfer({
                    native_token: {
                        denom: tokenAddress,
                    },
                }), {}),
            ]
            : [
                new terra_js_1.MsgExecuteContract(walletAddress, tokenAddress, {
                    increase_allowance: {
                        spender: tokenBridgeAddress,
                        amount: amount,
                        expires: {
                            never: {},
                        },
                    },
                }, {}),
                new terra_js_1.MsgExecuteContract(walletAddress, tokenBridgeAddress, mk_initiate_transfer({
                    token: {
                        contract_addr: tokenAddress,
                    },
                }), {}),
            ];
    });
}
exports.transferFromTerra = transferFromTerra;
function transferFromXpla(walletAddress, tokenBridgeAddress, tokenAddress, amount, recipientChain, recipientAddress, relayerFee = "0", payload = null) {
    const recipientChainId = utils_1.coalesceChainId(recipientChain);
    const nonce = Math.round(Math.random() * 100000);
    const isNativeAsset = cosmwasm_1.isNativeDenomXpla(tokenAddress);
    const createInitiateTransfer = (info) => payload
        ? {
            initiate_transfer_with_payload: {
                asset: {
                    amount,
                    info,
                },
                recipient_chain: recipientChainId,
                recipient: Buffer.from(recipientAddress).toString("base64"),
                fee: relayerFee,
                nonce,
                payload,
            },
        }
        : {
            initiate_transfer: {
                asset: {
                    amount,
                    info,
                },
                recipient_chain: recipientChainId,
                recipient: Buffer.from(recipientAddress).toString("base64"),
                fee: relayerFee,
                nonce,
            },
        };
    return isNativeAsset
        ? [
            new xpla_js_1.MsgExecuteContract(walletAddress, tokenBridgeAddress, {
                deposit_tokens: {},
            }, { [tokenAddress]: amount }),
            new xpla_js_1.MsgExecuteContract(walletAddress, tokenBridgeAddress, createInitiateTransfer({
                native_token: {
                    denom: tokenAddress,
                },
            }), {}),
        ]
        : [
            new xpla_js_1.MsgExecuteContract(walletAddress, tokenAddress, {
                increase_allowance: {
                    spender: tokenBridgeAddress,
                    amount: amount,
                    expires: {
                        never: {},
                    },
                },
            }, {}),
            new xpla_js_1.MsgExecuteContract(walletAddress, tokenBridgeAddress, createInitiateTransfer({
                token: {
                    contract_addr: tokenAddress,
                },
            }), {}),
        ];
}
exports.transferFromXpla = transferFromXpla;
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;
/**
 * Transfers an asset from Algorand to a receiver on another chain
 * @param client AlgodV2 client
 * @param tokenBridgeId Application ID of the token bridge
 * @param bridgeId Application ID of the core bridge
 * @param sender Sending account
 * @param assetId Asset index
 * @param qty Quantity to transfer
 * @param receiver Receiving account
 * @param chain Reeiving chain
 * @param fee Transfer fee
 * @param payload payload for payload3 transfers
 * @returns Sequence number of confirmation
 */
function transferFromAlgorand(client, tokenBridgeId, bridgeId, senderAddr, assetId, qty, receiver, chain, fee, payload = null) {
    return __awaiter(this, void 0, void 0, function* () {
        const recipientChainId = utils_1.coalesceChainId(chain);
        const tokenAddr = algosdk_1.getApplicationAddress(tokenBridgeId);
        const applAddr = bridge_1.getEmitterAddressAlgorand(tokenBridgeId);
        const txs = [];
        // "transferAsset"
        const { addr: emitterAddr, txs: emitterOptInTxs } = yield algorand_1.optin(client, senderAddr, bridgeId, BigInt(0), applAddr);
        txs.push(...emitterOptInTxs);
        let creator;
        let creatorAcctInfo;
        let wormhole = false;
        if (assetId !== BigInt(0)) {
            const assetInfo = yield client
                .getAssetByID(utils_1.safeBigIntToNumber(assetId))
                .do();
            creator = assetInfo["params"]["creator"];
            creatorAcctInfo = yield client.accountInformation(creator).do();
            const authAddr = creatorAcctInfo["auth-addr"];
            if (authAddr === tokenAddr) {
                wormhole = true;
            }
        }
        const params = yield client.getTransactionParams().do();
        const msgFee = yield algorand_1.getMessageFee(client, bridgeId);
        if (msgFee > 0) {
            const payTxn = algosdk_1.makePaymentTxnWithSuggestedParamsFromObject({
                from: senderAddr,
                suggestedParams: params,
                to: algosdk_1.getApplicationAddress(tokenBridgeId),
                amount: msgFee,
            });
            txs.push({ tx: payTxn, signer: null });
        }
        if (!wormhole) {
            const bNat = Buffer.from("native", "binary").toString("hex");
            // "creator"
            const result = yield algorand_1.optin(client, senderAddr, tokenBridgeId, assetId, bNat);
            creator = result.addr;
            txs.push(...result.txs);
        }
        if (assetId !== BigInt(0) &&
            !(yield algorand_1.assetOptinCheck(client, assetId, creator))) {
            // Looks like we need to optin
            const payTxn = algosdk_1.makePaymentTxnWithSuggestedParamsFromObject({
                from: senderAddr,
                to: creator,
                amount: 100000,
                suggestedParams: params,
            });
            txs.push({ tx: payTxn, signer: null });
            // The tokenid app needs to do the optin since it has signature authority
            const bOptin = utils_1.textToUint8Array("optin");
            let txn = algosdk_1.makeApplicationCallTxnFromObject({
                from: senderAddr,
                appIndex: utils_1.safeBigIntToNumber(tokenBridgeId),
                onComplete: algosdk_1.OnApplicationComplete.NoOpOC,
                appArgs: [bOptin, algosdk_1.bigIntToBytes(assetId, 8)],
                foreignAssets: [utils_1.safeBigIntToNumber(assetId)],
                accounts: [creator],
                suggestedParams: params,
            });
            txn.fee *= 2;
            txs.push({ tx: txn, signer: null });
        }
        const t = algosdk_1.makeApplicationCallTxnFromObject({
            from: senderAddr,
            appIndex: utils_1.safeBigIntToNumber(tokenBridgeId),
            onComplete: algosdk_1.OnApplicationComplete.NoOpOC,
            appArgs: [utils_1.textToUint8Array("nop")],
            suggestedParams: params,
        });
        txs.push({ tx: t, signer: null });
        let accounts = [];
        if (assetId === BigInt(0)) {
            const t = algosdk_1.makePaymentTxnWithSuggestedParamsFromObject({
                from: senderAddr,
                to: creator,
                amount: qty,
                suggestedParams: params,
            });
            txs.push({ tx: t, signer: null });
            accounts = [emitterAddr, creator, creator];
        }
        else {
            const t = algosdk_1.makeAssetTransferTxnWithSuggestedParamsFromObject({
                from: senderAddr,
                to: creator,
                suggestedParams: params,
                amount: qty,
                assetIndex: utils_1.safeBigIntToNumber(assetId),
            });
            txs.push({ tx: t, signer: null });
            accounts = [emitterAddr, creator, creatorAcctInfo["address"]];
        }
        let args = [
            utils_1.textToUint8Array("sendTransfer"),
            algosdk_1.bigIntToBytes(assetId, 8),
            algosdk_1.bigIntToBytes(qty, 8),
            utils_1.hexToUint8Array(receiver),
            algosdk_1.bigIntToBytes(recipientChainId, 8),
            algosdk_1.bigIntToBytes(fee, 8),
        ];
        if (payload !== null) {
            args.push(payload);
        }
        let acTxn = algosdk_1.makeApplicationCallTxnFromObject({
            from: senderAddr,
            appIndex: utils_1.safeBigIntToNumber(tokenBridgeId),
            onComplete: algosdk_1.OnApplicationComplete.NoOpOC,
            appArgs: args,
            foreignApps: [utils_1.safeBigIntToNumber(bridgeId)],
            foreignAssets: [utils_1.safeBigIntToNumber(assetId)],
            accounts: accounts,
            suggestedParams: params,
        });
        acTxn.fee *= 2;
        txs.push({ tx: acTxn, signer: null });
        return txs;
    });
}
exports.transferFromAlgorand = transferFromAlgorand;
function transferTokenFromNear(provider, account, coreBridge, tokenBridge, assetId, qty, receiver, chain, fee, payload = "") {
    return __awaiter(this, void 0, void 0, function* () {
        const isWrapped = __1.getIsWrappedAssetNear(tokenBridge, assetId);
        const messageFee = yield utils_1.callFunctionNear(provider, coreBridge, "message_fee", {});
        chain = utils_1.coalesceChainId(chain);
        if (isWrapped) {
            return [
                {
                    contractId: tokenBridge,
                    methodName: "send_transfer_wormhole_token",
                    args: {
                        token: assetId,
                        amount: qty.toString(10),
                        receiver: utils_1.uint8ArrayToHex(receiver),
                        chain,
                        fee: fee.toString(10),
                        payload: payload,
                        message_fee: messageFee,
                    },
                    attachedDeposit: new bn_js_1.default(messageFee + 1),
                    gas: new bn_js_1.default("100000000000000"),
                },
            ];
        }
        else {
            const options = [];
            const bal = yield utils_1.callFunctionNear(provider, assetId, "storage_balance_of", {
                account_id: tokenBridge,
            });
            if (bal === null) {
                // Looks like we have to stake some storage for this asset
                // for the token bridge...
                options.push({
                    contractId: assetId,
                    methodName: "storage_deposit",
                    args: { account_id: tokenBridge, registration_only: true },
                    gas: new bn_js_1.default("100000000000000"),
                    attachedDeposit: new bn_js_1.default("2000000000000000000000"), // 0.002 NEAR
                });
            }
            if (messageFee > 0) {
                const bank = yield utils_1.callFunctionNear(provider, tokenBridge, "bank_balance", {
                    acct: account,
                });
                if (!bank[0]) {
                    options.push({
                        contractId: tokenBridge,
                        methodName: "register_bank",
                        args: {},
                        gas: new bn_js_1.default("100000000000000"),
                        attachedDeposit: new bn_js_1.default("2000000000000000000000"), // 0.002 NEAR
                    });
                }
                if (bank[1] < messageFee) {
                    options.push({
                        contractId: tokenBridge,
                        methodName: "fill_bank",
                        args: {},
                        gas: new bn_js_1.default("100000000000000"),
                        attachedDeposit: new bn_js_1.default(messageFee),
                    });
                }
            }
            options.push({
                contractId: assetId,
                methodName: "ft_transfer_call",
                args: {
                    receiver_id: tokenBridge,
                    amount: qty.toString(10),
                    msg: JSON.stringify({
                        receiver: utils_1.uint8ArrayToHex(receiver),
                        chain,
                        fee: fee.toString(10),
                        payload: payload,
                        message_fee: messageFee,
                    }),
                },
                attachedDeposit: new bn_js_1.default(1),
                gas: new bn_js_1.default("100000000000000"),
            });
            return options;
        }
    });
}
exports.transferTokenFromNear = transferTokenFromNear;
function transferNearFromNear(provider, coreBridge, tokenBridge, qty, receiver, chain, fee, payload = "") {
    return __awaiter(this, void 0, void 0, function* () {
        const messageFee = yield utils_1.callFunctionNear(provider, coreBridge, "message_fee", {});
        return {
            contractId: tokenBridge,
            methodName: "send_transfer_near",
            args: {
                receiver: utils_1.uint8ArrayToHex(receiver),
                chain: utils_1.coalesceChainId(chain),
                fee: fee.toString(10),
                payload: payload,
                message_fee: messageFee,
            },
            attachedDeposit: new bn_js_1.default(qty.toString(10)).add(new bn_js_1.default(messageFee)),
            gas: new bn_js_1.default("100000000000000"),
        };
    });
}
exports.transferNearFromNear = transferNearFromNear;
/**
 * Transfer an asset on Aptos to another chain.
 * @param tokenBridgeAddress Address of token bridge
 * @param fullyQualifiedType Full qualified type of asset to transfer
 * @param amount Amount to send to recipient
 * @param recipientChain Target chain
 * @param recipient Recipient's address on target chain
 * @param relayerFee Fee to pay relayer
 * @param payload Payload3 data, leave undefined for basic token transfers
 * @returns Transaction payload
 */
function transferFromAptos(tokenBridgeAddress, fullyQualifiedType, amount, recipientChain, recipient, relayerFee = "0", payload = "") {
    if (payload) {
        // Currently unsupported
        return aptos_1.transferTokensWithPayload(tokenBridgeAddress, fullyQualifiedType, amount, recipientChain, recipient, relayerFee, utils_1.createNonce().readUInt32LE(0), payload);
    }
    return aptos_1.transferTokens(tokenBridgeAddress, fullyQualifiedType, amount, recipientChain, recipient, relayerFee, utils_1.createNonce().readUInt32LE(0));
}
exports.transferFromAptos = transferFromAptos;
function transferFromSui(provider, coreBridgeStateObjectId, tokenBridgeStateObjectId, coins, coinType, amount, recipientChain, recipient, feeAmount = BigInt(0), relayerFee = BigInt(0), payload = null) {
    return __awaiter(this, void 0, void 0, function* () {
        if (payload !== null) {
            throw new Error("Sui transfer with payload not implemented");
        }
        const [primaryCoin, ...mergeCoins] = coins.filter((coin) => sui_1.isSameType(coin.coinType, coinType));
        if (primaryCoin === undefined) {
            throw new Error(`Coins array doesn't contain any coins of type ${coinType}`);
        }
        const [coreBridgePackageId, tokenBridgePackageId] = yield Promise.all([
            sui_1.getPackageId(provider, coreBridgeStateObjectId),
            sui_1.getPackageId(provider, tokenBridgeStateObjectId),
        ]);
        const tx = new sui_js_1.TransactionBlock();
        const [transferCoin] = (() => {
            if (coinType === sui_js_1.SUI_TYPE_ARG) {
                return tx.splitCoins(tx.gas, [tx.pure(amount)]);
            }
            else {
                const primaryCoinInput = tx.object(primaryCoin.coinObjectId);
                if (mergeCoins.length) {
                    tx.mergeCoins(primaryCoinInput, mergeCoins.map((coin) => tx.object(coin.coinObjectId)));
                }
                return tx.splitCoins(primaryCoinInput, [tx.pure(amount)]);
            }
        })();
        const [feeCoin] = tx.splitCoins(tx.gas, [tx.pure(feeAmount)]);
        const [assetInfo] = tx.moveCall({
            target: `${tokenBridgePackageId}::state::verified_asset`,
            arguments: [tx.object(tokenBridgeStateObjectId)],
            typeArguments: [coinType],
        });
        const [transferTicket, dust] = tx.moveCall({
            target: `${tokenBridgePackageId}::transfer_tokens::prepare_transfer`,
            arguments: [
                assetInfo,
                transferCoin,
                tx.pure(utils_1.coalesceChainId(recipientChain)),
                tx.pure([...recipient]),
                tx.pure(relayerFee),
                tx.pure(utils_1.createNonce().readUInt32LE()),
            ],
            typeArguments: [coinType],
        });
        tx.moveCall({
            target: `${tokenBridgePackageId}::coin_utils::return_nonzero`,
            arguments: [dust],
            typeArguments: [coinType],
        });
        const [messageTicket] = tx.moveCall({
            target: `${tokenBridgePackageId}::transfer_tokens::transfer_tokens`,
            arguments: [tx.object(tokenBridgeStateObjectId), transferTicket],
            typeArguments: [coinType],
        });
        tx.moveCall({
            target: `${coreBridgePackageId}::publish_message::publish_message`,
            arguments: [
                tx.object(coreBridgeStateObjectId),
                feeCoin,
                messageTicket,
                tx.object(sui_js_1.SUI_CLOCK_OBJECT_ID),
            ],
        });
        return tx;
    });
}
exports.transferFromSui = transferFromSui;
