"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.getForeignAssetAptos = exports.getForeignAssetSol = exports.getForeignAssetSolana = exports.getForeignAssetTerra = exports.getForeignAssetEth = void 0;
const anchor_1 = require("@project-serum/anchor");
const aptos_1 = require("aptos");
const utils_1 = require("ethers/lib/utils");
const js_base64_1 = require("js-base64");
const __1 = require("..");
const ethers_contracts_1 = require("../ethers-contracts");
const nftBridge_1 = require("../solana/nftBridge");
const utils_2 = require("../utils");
/**
 * Returns a foreign asset address on Ethereum for a provided native chain and asset address, AddressZero if it does not exist
 * @param nftBridgeAddress
 * @param provider
 * @param originChain
 * @param originAsset zero pad to 32 bytes
 * @returns
 */
function getForeignAssetEth(nftBridgeAddress, provider, originChain, originAsset) {
    return __awaiter(this, void 0, void 0, function* () {
        const originChainId = utils_2.coalesceChainId(originChain);
        const tokenBridge = ethers_contracts_1.NFTBridge__factory.connect(nftBridgeAddress, provider);
        try {
            if (originChainId === __1.CHAIN_ID_SOLANA) {
                // All NFTs from Solana are minted to the same address, the originAsset is encoded as the tokenId as
                // BigNumber.from(new PublicKey(originAsset).toBytes()).toString()
                const addr = yield tokenBridge.wrappedAsset(originChain, "0x0101010101010101010101010101010101010101010101010101010101010101");
                return addr;
            }
            return yield tokenBridge.wrappedAsset(originChainId, originAsset);
        }
        catch (e) {
            return null;
        }
    });
}
exports.getForeignAssetEth = getForeignAssetEth;
/**
 * Returns a foreign asset address on Terra for a provided native chain and asset address
 * @param nftBridgeAddress
 * @param client
 * @param originChain
 * @param originAsset
 * @returns
 */
function getForeignAssetTerra(nftBridgeAddress, client, originChain, originAsset) {
    return __awaiter(this, void 0, void 0, function* () {
        const originChainId = utils_2.coalesceChainId(originChain);
        try {
            const address = originChain == __1.CHAIN_ID_SOLANA
                ? "AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE="
                : js_base64_1.fromUint8Array(originAsset);
            const result = yield client.wasm.contractQuery(nftBridgeAddress, {
                wrapped_registry: {
                    chain: originChainId,
                    address,
                },
            });
            return result.address;
        }
        catch (e) {
            return null;
        }
    });
}
exports.getForeignAssetTerra = getForeignAssetTerra;
/**
 * Returns a foreign asset address on Solana for a provided native chain and asset address
 * @param nftBridgeAddress
 * @param originChain
 * @param originAsset zero pad to 32 bytes
 * @returns
 */
function getForeignAssetSolana(nftBridgeAddress, originChain, originAsset, tokenId) {
    return __awaiter(this, void 0, void 0, function* () {
        // we don't require NFT accounts to exist, so don't check them.
        return nftBridge_1.deriveWrappedMintKey(nftBridgeAddress, utils_2.coalesceChainId(originChain), originAsset, utils_1.isBytes(tokenId) ? BigInt(new anchor_1.BN(tokenId).toString()) : tokenId).toString();
    });
}
exports.getForeignAssetSolana = getForeignAssetSolana;
exports.getForeignAssetSol = getForeignAssetSolana;
/**
 * Get the token id of a foreign asset on Aptos. Tokens on Aptos are identified
 * by the tuple (creatorAddress, collectionName, tokenName, propertyVersion),
 * which this method returns as an object.
 *
 * This method also supports native assets, in which case it expects the token
 * hash (which can be obtained from `deriveTokenHashFromTokenId`).
 * @param client
 * @param nftBridgeAddress
 * @param originChain
 * @param originAddress External address of token on origin chain, or token hash
 * if origin chain is Aptos
 * @param tokenId Token id of token on origin chain, unnecessary if origin
 * chain is Aptos
 * @returns Unique token identifier on Aptos
 */
function getForeignAssetAptos(client, nftBridgeAddress, originChain, originAddress, tokenId) {
    return __awaiter(this, void 0, void 0, function* () {
        const originChainId = utils_2.coalesceChainId(originChain);
        if (originChainId === utils_2.CHAIN_ID_APTOS) {
            return utils_2.getTokenIdFromTokenHash(client, nftBridgeAddress, originAddress);
        }
        const creatorAddress = yield utils_2.deriveResourceAccountAddress(nftBridgeAddress, originChainId, originAddress);
        if (!creatorAddress) {
            throw new Error("Could not derive creator account address");
        }
        if (typeof tokenId === "bigint") {
            tokenId = utils_2.hexToUint8Array(BigInt(tokenId).toString(16).padStart(64, "0"));
        }
        if (!tokenId) {
            throw new Error("Invalid token ID");
        }
        const tokenIdAsUint8Array = new Uint8Array(tokenId);
        // Each creator account should contain a single collection that contains the
        // corresponding token creation events. Return if we find it in the first
        // page, otherwise reconstruct the token id from the first event.
        const PAGE_SIZE = 25;
        const events = (yield client.getEventsByEventHandle(creatorAddress, "0x3::token::Collections", "create_token_data_events", { limit: PAGE_SIZE }));
        const event = events.find((e) => utils_2.ensureHexPrefix(e.data.id.name) ===
            aptos_1.HexString.fromUint8Array(tokenIdAsUint8Array).hex());
        if (event) {
            return {
                token_data_id: event.data.id,
                property_version: "0", // property version always "0" for wrapped tokens
            };
        }
        // Skip pagination, reconstruct token id, and check to see if it exists
        try {
            const tokenIdObj = {
                token_data_id: Object.assign(Object.assign({}, events[0].data.id), { name: aptos_1.HexString.fromUint8Array(tokenIdAsUint8Array).noPrefix() }),
                property_version: "0",
            };
            yield new aptos_1.TokenClient(client).getTokenData(tokenIdObj.token_data_id.creator, tokenIdObj.token_data_id.collection, tokenIdObj.token_data_id.name);
            return tokenIdObj;
        }
        catch (e) {
            if (e instanceof aptos_1.ApiError &&
                e.status === 404 &&
                e.errorCode === "table_item_not_found") {
                return null;
            }
            throw e;
        }
    });
}
exports.getForeignAssetAptos = getForeignAssetAptos;
