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 { PublicKey } from "@solana/web3.js";
import { ethers } from "ethers";
import { arrayify, zeroPad } from "ethers/lib/utils";
import { decodeLocalState } from "../algorand";
import { TokenImplementation__factory } from "../ethers-contracts";
import { importTokenWasm } from "../solana/wasm";
import { buildNativeId, canonicalAddress, isNativeDenom } from "../terra";
import { CHAIN_ID_ALGORAND, CHAIN_ID_SOLANA, CHAIN_ID_TERRA, coalesceChainId, } from "../utils";
import { safeBigIntToNumber } from "../utils/bigint";
import { getIsWrappedAssetAlgorand, getIsWrappedAssetEth, } from "./getIsWrappedAsset";
/**
 * Returns a origin chain and asset address on {originChain} for a provided Wormhole wrapped address
 * @param tokenBridgeAddress
 * @param provider
 * @param wrappedAddress
 * @returns
 */
export function getOriginalAssetEth(tokenBridgeAddress, provider, wrappedAddress, lookupChain) {
    return __awaiter(this, void 0, void 0, function* () {
        const isWrapped = yield getIsWrappedAssetEth(tokenBridgeAddress, provider, wrappedAddress);
        if (isWrapped) {
            const token = TokenImplementation__factory.connect(wrappedAddress, provider);
            const chainId = (yield token.chainId()); // origin chain
            const assetAddress = yield token.nativeContract(); // origin address
            return {
                isWrapped: true,
                chainId,
                assetAddress: arrayify(assetAddress),
            };
        }
        return {
            isWrapped: false,
            chainId: coalesceChainId(lookupChain),
            assetAddress: zeroPad(arrayify(wrappedAddress), 32),
        };
    });
}
export function getOriginalAssetTerra(client, wrappedAddress) {
    return __awaiter(this, void 0, void 0, function* () {
        if (isNativeDenom(wrappedAddress)) {
            return {
                isWrapped: false,
                chainId: CHAIN_ID_TERRA,
                assetAddress: buildNativeId(wrappedAddress),
            };
        }
        try {
            const result = yield client.wasm.contractQuery(wrappedAddress, {
                wrapped_asset_info: {},
            });
            if (result) {
                return {
                    isWrapped: true,
                    chainId: result.asset_chain,
                    assetAddress: new Uint8Array(Buffer.from(result.asset_address, "base64")),
                };
            }
        }
        catch (e) { }
        return {
            isWrapped: false,
            chainId: CHAIN_ID_TERRA,
            assetAddress: zeroPad(canonicalAddress(wrappedAddress), 32),
        };
    });
}
/**
 * Returns a origin chain and asset address on {originChain} for a provided Wormhole wrapped address
 * @param connection
 * @param tokenBridgeAddress
 * @param mintAddress
 * @returns
 */
export function getOriginalAssetSol(connection, tokenBridgeAddress, mintAddress) {
    return __awaiter(this, void 0, void 0, function* () {
        if (mintAddress) {
            // TODO: share some of this with getIsWrappedAssetSol, like a getWrappedMetaAccountAddress or something
            const { parse_wrapped_meta, wrapped_meta_address } = yield importTokenWasm();
            const wrappedMetaAddress = wrapped_meta_address(tokenBridgeAddress, new PublicKey(mintAddress).toBytes());
            const wrappedMetaAddressPK = new PublicKey(wrappedMetaAddress);
            const wrappedMetaAccountInfo = yield connection.getAccountInfo(wrappedMetaAddressPK);
            if (wrappedMetaAccountInfo) {
                const parsed = parse_wrapped_meta(wrappedMetaAccountInfo.data);
                return {
                    isWrapped: true,
                    chainId: parsed.chain,
                    assetAddress: parsed.token_address,
                };
            }
        }
        try {
            return {
                isWrapped: false,
                chainId: CHAIN_ID_SOLANA,
                assetAddress: new PublicKey(mintAddress).toBytes(),
            };
        }
        catch (e) { }
        return {
            isWrapped: false,
            chainId: CHAIN_ID_SOLANA,
            assetAddress: new Uint8Array(32),
        };
    });
}
/**
 * Returns an origin chain and asset address on {originChain} for a provided Wormhole wrapped address
 * @param client Algodv2 client
 * @param tokenBridgeId Application ID of the token bridge
 * @param assetId Algorand asset index
 * @returns wrapped wormhole information structure
 */
export function getOriginalAssetAlgorand(client, tokenBridgeId, assetId) {
    return __awaiter(this, void 0, void 0, function* () {
        let retVal = {
            isWrapped: false,
            chainId: CHAIN_ID_ALGORAND,
            assetAddress: new Uint8Array(),
        };
        retVal.isWrapped = yield getIsWrappedAssetAlgorand(client, tokenBridgeId, assetId);
        if (!retVal.isWrapped) {
            retVal.assetAddress = zeroPad(arrayify(ethers.BigNumber.from(assetId)), 32);
            return retVal;
        }
        const assetInfo = yield client.getAssetByID(safeBigIntToNumber(assetId)).do();
        const lsa = assetInfo.params.creator;
        const dls = yield decodeLocalState(client, tokenBridgeId, lsa);
        const dlsBuffer = Buffer.from(dls);
        retVal.chainId = dlsBuffer.readInt16BE(92);
        retVal.assetAddress = new Uint8Array(dlsBuffer.slice(60, 60 + 32));
        return retVal;
    });
}
