"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Wormhole = void 0;
const sdk_base_1 = require("@wormhole-foundation/sdk-base");
const sdk_definitions_1 = require("@wormhole-foundation/sdk-definitions");
const circle_api_1 = require("./circle-api");
const config_1 = require("./config");
const cctpTransfer_1 = require("./protocols/cctpTransfer");
const tokenTransfer_1 = require("./protocols/tokenTransfer");
const resolver_1 = require("./routes/resolver");
const tasks_1 = require("./tasks");
const whscan_api_1 = require("./whscan-api");
class Wormhole {
    _network;
    _platforms;
    _chains;
    config;
    constructor(network, platforms, config) {
        this._network = network;
        this.config = (0, config_1.applyOverrides)(network, config);
        this._chains = new Map();
        this._platforms = new Map();
        for (const p of platforms) {
            this._platforms.set(p._platform, new p(network));
        }
    }
    get network() {
        return this._network;
    }
    /**
     * Creates a CircleTransfer object to move Native USDC from one chain to another
     * @param amount the amount to transfer
     * @param from the address to transfer from
     * @param to the address to transfer to
     * @param automatic whether to use automatic delivery
     * @param payload the payload to send with the transfer
     * @param nativeGas the amount of native gas to send with the transfer
     * @returns the CircleTransfer object
     * @throws Errors if the chain or protocol is not supported
     */
    async circleTransfer(amount, from, to, automatic, payload, nativeGas) {
        if (automatic && payload)
            throw new Error("Payload with automatic delivery is not supported");
        if (!sdk_base_1.circle.isCircleChain(this.network, from.chain) ||
            !sdk_base_1.circle.isCircleChain(this.network, to.chain) ||
            !sdk_base_1.circle.isCircleSupported(this.network, from.chain) ||
            !sdk_base_1.circle.isCircleSupported(this.network, to.chain))
            throw new Error(`Network and chain not supported: ${this.network} ${from.chain} `);
        // ensure the amount is > fee + native gas
        if (automatic) {
            const acb = await this.getChain(from.chain).getAutomaticCircleBridge();
            const relayerFee = await acb.getRelayerFee(to.chain);
            const minAmount = relayerFee + (nativeGas ? nativeGas : 0n);
            if (amount < minAmount)
                throw new Error(`Amount must be > ${minAmount} (relayerFee + nativeGas)`);
        }
        return await cctpTransfer_1.CircleTransfer.from(this, {
            amount,
            from,
            to,
            automatic,
            payload,
            nativeGas,
        });
    }
    /**
     * Creates a TokenTransfer object to move a token from one chain to another
     * @param token the token to transfer
     * @param amount the amount to transfer
     * @param from the address to transfer from
     * @param to the address to transfer to
     * @param automatic whether to use automatic delivery
     * @param payload the payload to send with the transfer
     * @param nativeGas the amount of native gas to send with the transfer
     * @returns the TokenTransfer object
     * @throws Errors if the chain or protocol is not supported
     */
    async tokenTransfer(token, amount, from, to, automatic, payload, nativeGas) {
        return await tokenTransfer_1.TokenTransfer.from(this, {
            token,
            amount,
            from,
            to,
            automatic,
            payload,
            nativeGas,
        });
    }
    /**
     * Gets a RouteResolver configured with the routes passed
     * @param routes the list RouteConstructors to use
     * @returns the RouteResolver
     */
    resolver(routes) {
        return new resolver_1.RouteResolver(this, routes);
    }
    /**
     * Gets the contract addresses for a given chain
     * @param chain the chain name
     * @returns the contract addresses
     */
    getContracts(chain) {
        return this.config.chains[chain]?.contracts;
    }
    /**
     * Returns the platform object, i.e. the class with platform-specific logic and methods
     * @param chain the platform name
     * @returns the platform context class
     * @throws Errors if platform is not found
     */
    getPlatform(platformName) {
        const platform = this._platforms.get(platformName);
        if (!platform)
            throw new Error(`Not able to retrieve platform ${platform}`);
        return platform;
    }
    /**
     * Returns the chain "context", i.e. the class with chain-specific logic and methods
     * @param chain the chain name
     * @returns the chain context class
     * @throws Errors if context is not found
     */
    getChain(chain) {
        const platform = (0, sdk_base_1.chainToPlatform)(chain);
        if (!this._chains.has(chain))
            this._chains.set(chain, this.getPlatform(platform).getChain(chain));
        return this._chains.get(chain);
    }
    /**
     * Gets the TokenId for a token representation on any chain
     *  These are the Wormhole wrapped token addresses, not necessarily
     *  the cannonical version of that token
     *
     * @param chain The chain name to get the wrapped token address
     * @param tokenId The Token ID (chain/address) of the original token
     * @returns The TokenId on the given chain, null if it does not exist
     * @throws Errors if the chain is not supported or the token does not exist
     */
    async getWrappedAsset(chain, token) {
        const ctx = this.getChain(chain);
        const tb = await ctx.getTokenBridge();
        return { chain, address: await tb.getWrappedAsset(token) };
    }
    /**
     *  Taking the original TokenId for some wrapped token chain
     *  These are the Wormhole wrapped token addresses, not necessarily
     *  the cannonical version of that token
     *
     * @param tokenId The Token ID of the token we're looking up the original asset for
     * @returns The Original TokenId corresponding to the token id passed,
     * @throws Errors if the chain is not supported or the token does not exist
     */
    async getOriginalAsset(token) {
        const ctx = this.getChain(token.chain);
        const tb = await ctx.getTokenBridge();
        return await tb.getOriginalAsset(token.address);
    }
    /**
     * Gets the number of decimals for a token on a given chain
     *
     * @param chain The chain name or id of the token/representation
     * @param token The token address
     * @returns The number of decimals
     */
    async getDecimals(chain, token) {
        const ctx = this.getChain(chain);
        return await ctx.getDecimals(token);
    }
    /**
     * Fetches the balance of a given token for a wallet
     *
     * @param walletAddress The wallet address
     * @param tokenId The token ID (its home chain and address on the home chain)
     * @param chain The chain name or id
     * @returns The token balance of the wormhole asset as a BigNumber
     */
    async getBalance(chain, token, walletAddress) {
        const ctx = this.getChain(chain);
        return ctx.getBalance(walletAddress, token);
    }
    /**
     * Gets the associated token account for chains that require it (only Solana currently).
     *
     * @param token the TokenId of the token to get the token account for
     * @param recipient the address of the primary account that may require a separate token account
     * @returns
     */
    async getTokenAccount(recipient, token) {
        return this.getChain(recipient.chain).getTokenAccount(recipient.address, token.address);
    }
    /**
     * Gets the Raw VAA Bytes from the API or Guardian RPC, finality must be met before the VAA will be available.
     *
     * @param wormholeMessageId The WormholeMessageId corresponding to the VAA to be fetched
     * @param timeout The total amount of time to wait for the VAA to be available
     * @returns The VAA bytes if available
     * @throws Errors if the VAA is not available after the retries
     */
    async getVaaBytes(wormholeMessageId, timeout = config_1.DEFAULT_TASK_TIMEOUT) {
        return await (0, whscan_api_1.getVaaBytesWithRetry)(this.config.api, wormholeMessageId, timeout);
    }
    /**
     * Gets a VAA from the API or Guardian RPC, finality must be met before the VAA will be available.
     *
     * @param id The WormholeMessageId or Transaction hash corresponding to the VAA to be fetched
     * @param decodeAs The VAA type to decode the bytes as
     * @param timeout The total amount of time to wait for the VAA to be available
     * @returns The VAA if available
     * @throws Errors if the VAA is not available after the retries
     */
    async getVaa(id, decodeAs, timeout = config_1.DEFAULT_TASK_TIMEOUT) {
        if (typeof id === "string")
            return await (0, whscan_api_1.getVaaByTxHashWithRetry)(this.config.api, id, decodeAs, timeout);
        return await (0, whscan_api_1.getVaaWithRetry)(this.config.api, id, decodeAs, timeout);
    }
    /**
     * Gets the CircleAttestation corresponding to the message hash logged in the transfer transaction.
     * @param msgHash  The keccak256 hash of the message emitted by the circle contract
     * @param timeout The total amount of time to wait for the VAA to be available
     * @returns The CircleAttestation as a string, if available
     * @throws Errors if the CircleAttestation is not available after the retries
     */
    async getCircleAttestation(msgHash, timeout = config_1.DEFAULT_TASK_TIMEOUT) {
        return (0, circle_api_1.getCircleAttestationWithRetry)(this.config.circleAPI, msgHash, timeout);
    }
    /**
     * Get the status of a transaction, identified by the chain, emitter address, and sequence number
     *
     * @param id the message id for the Wormhole Message to get transaction status for or originating Transaction hash
     * @returns the TransactionStatus
     */
    async getTransactionStatus(id, timeout = config_1.DEFAULT_TASK_TIMEOUT) {
        let msgid;
        // No txid endpoint exists to get the status by txhash yet
        if (typeof id === "string") {
            const vaa = await (0, whscan_api_1.getVaaByTxHashWithRetry)(this.config.api, id, "Uint8Array", timeout);
            if (!vaa)
                return null;
            msgid = { emitter: vaa.emitterAddress, chain: vaa.emitterChain, sequence: vaa.sequence };
        }
        else {
            msgid = id;
        }
        return await (0, whscan_api_1.getTransactionStatusWithRetry)(this.config.api, msgid, timeout);
    }
    /**
     * Get recent transactions for an address
     *
     * @param address the string formatted address to get transactions for
     * @returns the TransactionStatus
     */
    async getTransactionsForAddress(address, pageSize = 50, page = 0) {
        return (0, whscan_api_1.getTxsByAddress)(this.config.api, address, pageSize, page);
    }
    /**
     * Parse an address from its canonincal string format to a NativeAddress
     *
     * @param chain The chain the address is for
     * @param address The address in canonical string format
     * @returns The address in the NativeAddress format
     */
    static parseAddress(chain, address) {
        return (0, sdk_definitions_1.toNative)(chain, address);
    }
    /**
     * Return a string in the canonical chain format representing the address
     * of a token or account
     *
     * @param chainAddress The ChainAddress or TokenId to get a string address
     * @returns The string address in canonical format for the chain
     */
    static canonicalAddress(chainAddress) {
        return (0, sdk_definitions_1.canonicalAddress)(chainAddress);
    }
    /**
     * Parse an address from its canonincal string format to a NativeAddress
     *
     * @param chain The chain the address is for
     * @param address The native address in canonical string format
     * @returns The ChainAddress
     */
    static chainAddress(chain, address) {
        return { chain, address: Wormhole.parseAddress(chain, address) };
    }
    /**
     * Parse an address from its canonincal string format to a NativeAddress
     *
     * @param chain The chain the address is for
     * @param address The native address in canonical string format or the string "native"
     * @returns The ChainAddress
     */
    static tokenId(chain, address) {
        return (0, sdk_definitions_1.isNative)(address) ? (0, sdk_definitions_1.nativeTokenId)(chain) : this.chainAddress(chain, address);
    }
    /**
     * Parses all relevant information from a transaction given the sending tx hash and sending chain
     *
     * @param chain The sending chain name or context
     * @param tx The sending transaction hash
     * @returns The parsed WormholeMessageId
     */
    static async parseMessageFromTx(chain, txid, timeout = config_1.DEFAULT_TASK_TIMEOUT) {
        const task = async () => {
            const msgs = await chain.parseTransaction(txid);
            // possible the node we hit does not have this data yet
            // return null to signal retry
            if (msgs.length === 0)
                return null;
            return msgs;
        };
        const parsed = await (0, tasks_1.retry)(task, chain.config.blockTime, timeout, "WormholeCore:ParseMessageFromTransaction");
        if (!parsed)
            throw new Error(`No WormholeMessageId found for ${txid}`);
        return parsed;
    }
}
exports.Wormhole = Wormhole;
//# sourceMappingURL=wormhole.js.map