"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.transferFromAptos = exports.transferFromTerra = exports.transferFromSolana = exports.transferFromEth = void 0;
const anchor_1 = require("@project-serum/anchor");
const web3_js_1 = require("@solana/web3.js");
const terra_js_1 = require("@terra-money/terra.js");
const utils_1 = require("ethers/lib/utils");
const ethers_contracts_1 = require("../ethers-contracts");
const solana_1 = require("../solana");
const nftBridge_1 = require("../solana/nftBridge");
const utils_2 = require("../utils");
function transferFromEth(nftBridgeAddress, signer, tokenAddress, tokenID, recipientChain, recipientAddress, overrides = {}) {
    return __awaiter(this, void 0, void 0, function* () {
        const recipientChainId = utils_2.coalesceChainId(recipientChain);
        //TODO: should we check if token attestation exists on the target chain
        const token = ethers_contracts_1.NFTImplementation__factory.connect(tokenAddress, signer);
        yield (yield token.approve(nftBridgeAddress, tokenID, overrides)).wait();
        const bridge = ethers_contracts_1.NFTBridge__factory.connect(nftBridgeAddress, signer);
        const v = yield bridge.transferNFT(tokenAddress, tokenID, recipientChainId, recipientAddress, utils_2.createNonce(), overrides);
        const receipt = yield v.wait();
        return receipt;
    });
}
exports.transferFromEth = transferFromEth;
function transferFromSolana(connection, bridgeAddress, nftBridgeAddress, payerAddress, fromAddress, mintAddress, targetAddress, targetChain, originAddress, originChain, originTokenId, commitment) {
    return __awaiter(this, void 0, void 0, function* () {
        const originChainId = originChain
            ? utils_2.coalesceChainId(originChain)
            : undefined;
        const nonce = utils_2.createNonce().readUInt32LE(0);
        const transferIx = yield solana_1.createBridgeFeeTransferInstruction(connection, bridgeAddress, payerAddress);
        const approvalIx = nftBridge_1.createApproveAuthoritySignerInstruction(nftBridgeAddress, fromAddress, payerAddress);
        let message = web3_js_1.Keypair.generate();
        const isSolanaNative = originChain === undefined || originChain === utils_2.CHAIN_ID_SOLANA;
        if (!isSolanaNative && (!originAddress || !originTokenId)) {
            return Promise.reject("originAddress and originTokenId are required when specifying originChain");
        }
        const nftBridgeTransferIx = isSolanaNative
            ? nftBridge_1.createTransferNativeInstruction(nftBridgeAddress, bridgeAddress, payerAddress, message.publicKey, fromAddress, mintAddress, nonce, targetAddress, utils_2.coalesceChainId(targetChain))
            : nftBridge_1.createTransferWrappedInstruction(nftBridgeAddress, bridgeAddress, payerAddress, message.publicKey, fromAddress, payerAddress, originChainId, originAddress, utils_1.isBytes(originTokenId)
                ? BigInt(new anchor_1.BN(originTokenId).toString())
                : originTokenId, nonce, targetAddress, utils_2.coalesceChainId(targetChain));
        const transaction = new web3_js_1.Transaction().add(transferIx, approvalIx, nftBridgeTransferIx);
        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;
function transferFromTerra(walletAddress, nftBridgeAddress, tokenAddress, tokenID, recipientChain, recipientAddress) {
    return __awaiter(this, void 0, void 0, function* () {
        const recipientChainId = utils_2.coalesceChainId(recipientChain);
        const nonce = Math.round(Math.random() * 100000);
        return [
            new terra_js_1.MsgExecuteContract(walletAddress, tokenAddress, {
                approve: {
                    spender: nftBridgeAddress,
                    token_id: tokenID,
                },
            }, {}),
            new terra_js_1.MsgExecuteContract(walletAddress, nftBridgeAddress, {
                initiate_transfer: {
                    contract_addr: tokenAddress,
                    token_id: tokenID,
                    recipient_chain: recipientChainId,
                    recipient: Buffer.from(recipientAddress).toString("base64"),
                    nonce: nonce,
                },
            }, {}),
        ];
    });
}
exports.transferFromTerra = transferFromTerra;
function transferFromAptos(nftBridgeAddress, creatorAddress, collectionName, tokenName, propertyVersion, recipientChain, recipient) {
    const recipientChainId = utils_2.coalesceChainId(recipientChain);
    return {
        function: `${nftBridgeAddress}::transfer_nft::transfer_nft_entry`,
        type_arguments: [],
        arguments: [
            creatorAddress,
            collectionName,
            tokenName,
            propertyVersion,
            recipientChainId,
            recipient,
            utils_2.createNonce().readUInt32LE(0),
        ],
    };
}
exports.transferFromAptos = transferFromAptos;
