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());
    });
};
import { Token, TOKEN_PROGRAM_ID } from "@solana/spl-token";
import { Keypair, PublicKey, Transaction } from "@solana/web3.js";
import { MsgExecuteContract } from "@terra-money/terra.js";
import { NFTBridge__factory, NFTImplementation__factory, } from "../ethers-contracts";
import { getBridgeFeeIx, ixFromRust } from "../solana";
import { importNftWasm } from "../solana/wasm";
import { CHAIN_ID_SOLANA, coalesceChainId, createNonce } from "../utils";
export function transferFromEth(tokenBridgeAddress, signer, tokenAddress, tokenID, recipientChain, recipientAddress, overrides = {}) {
    return __awaiter(this, void 0, void 0, function* () {
        const recipientChainId = coalesceChainId(recipientChain);
        //TODO: should we check if token attestation exists on the target chain
        const token = NFTImplementation__factory.connect(tokenAddress, signer);
        yield (yield token.approve(tokenBridgeAddress, tokenID, overrides)).wait();
        const bridge = NFTBridge__factory.connect(tokenBridgeAddress, signer);
        const v = yield bridge.transferNFT(tokenAddress, tokenID, recipientChainId, recipientAddress, createNonce(), overrides);
        const receipt = yield v.wait();
        return receipt;
    });
}
export function transferFromSolana(connection, bridgeAddress, tokenBridgeAddress, payerAddress, fromAddress, mintAddress, targetAddress, targetChain, originAddress, originChain, originTokenId) {
    return __awaiter(this, void 0, void 0, function* () {
        const originChainId = originChain ? coalesceChainId(originChain) : undefined;
        const nonce = createNonce().readUInt32LE(0);
        const transferIx = yield getBridgeFeeIx(connection, bridgeAddress, payerAddress);
        const { transfer_native_ix, transfer_wrapped_ix, approval_authority_address, } = yield importNftWasm();
        const approvalIx = Token.createApproveInstruction(TOKEN_PROGRAM_ID, new PublicKey(fromAddress), new PublicKey(approval_authority_address(tokenBridgeAddress)), new PublicKey(payerAddress), [], Number(1));
        let messageKey = Keypair.generate();
        const isSolanaNative = originChain === undefined || originChain === CHAIN_ID_SOLANA;
        if (!isSolanaNative && (!originAddress || !originTokenId)) {
            throw new Error("originAddress and originTokenId are required when specifying originChain");
        }
        const ix = ixFromRust(isSolanaNative
            ? transfer_native_ix(tokenBridgeAddress, bridgeAddress, payerAddress, messageKey.publicKey.toString(), fromAddress, mintAddress, nonce, targetAddress, coalesceChainId(targetChain))
            : transfer_wrapped_ix(tokenBridgeAddress, bridgeAddress, payerAddress, messageKey.publicKey.toString(), fromAddress, payerAddress, originChainId, // checked by isSolanaNative
            originAddress, // checked by throw
            originTokenId, // checked by throw
            nonce, targetAddress, coalesceChainId(targetChain)));
        const transaction = new Transaction().add(transferIx, approvalIx, ix);
        const { blockhash } = yield connection.getRecentBlockhash();
        transaction.recentBlockhash = blockhash;
        transaction.feePayer = new PublicKey(payerAddress);
        transaction.partialSign(messageKey);
        return transaction;
    });
}
export function transferFromTerra(walletAddress, tokenBridgeAddress, tokenAddress, tokenID, recipientChain, recipientAddress) {
    return __awaiter(this, void 0, void 0, function* () {
        const recipientChainId = coalesceChainId(recipientChain);
        const nonce = Math.round(Math.random() * 100000);
        return [
            new MsgExecuteContract(walletAddress, tokenAddress, {
                approve: {
                    spender: tokenBridgeAddress,
                    token_id: tokenID,
                },
            }, {}),
            new MsgExecuteContract(walletAddress, tokenBridgeAddress, {
                initiate_transfer: {
                    contract_addr: tokenAddress,
                    token_id: tokenID,
                    recipient_chain: recipientChainId,
                    recipient: Buffer.from(recipientAddress).toString("base64"),
                    nonce: nonce,
                },
            }, {}),
        ];
    });
}
