import { contracts, amount } from "@wormhole-foundation/sdk-base";
import { isNative, isTokenId, nativeTokenId, } from "@wormhole-foundation/sdk-definitions";
import { TokenTransfer } from "../../protocols/tokenTransfer";
import { TransferState } from "../../types";
import { AutomaticRoute } from "../route";
export class AutomaticTokenBridgeRoute extends AutomaticRoute {
    NATIVE_GAS_DROPOFF_SUPPORTED = true;
    static meta = {
        name: "AutomaticTokenBridge",
    };
    static supportedNetworks() {
        return ["Mainnet", "Testnet"];
    }
    // get the list of chains this route supports
    static supportedChains(network) {
        if (contracts.tokenBridgeRelayerChains.has(network)) {
            return contracts.tokenBridgeRelayerChains.get(network);
        }
        return [];
    }
    // get the list of source tokens that are possible to send
    static async supportedSourceTokens(fromChain) {
        const atb = await fromChain.getAutomaticTokenBridge();
        const registered = await atb.getRegisteredTokens();
        return [
            nativeTokenId(fromChain.chain),
            ...registered.map((v) => {
                return { chain: fromChain.chain, address: v };
            }),
        ];
    }
    // get the list of destination tokens that may be recieved on the destination chain
    static async supportedDestinationTokens(sourceToken, fromChain, toChain) {
        return [await TokenTransfer.lookupDestinationToken(fromChain, toChain, sourceToken)];
    }
    static isProtocolSupported(chain) {
        return chain.supportsAutomaticTokenBridge();
    }
    getDefaultOptions() {
        return { nativeGas: 0.0 };
    }
    async isAvailable() {
        const atb = await this.request.fromChain.getAutomaticTokenBridge();
        if (isTokenId(this.request.source.id)) {
            return await atb.isRegisteredToken(this.request.source.id.address);
        }
        return true;
    }
    async validate(params) {
        try {
            const options = params.options ?? this.getDefaultOptions();
            if (options.nativeGas && (options.nativeGas > 1.0 || options.nativeGas < 0.0))
                throw new Error("Native gas must be between 0.0 and 1.0 (0% and 100%)");
            // If destination is native, max out the nativeGas requested
            const { destination } = this.request;
            if (isNative(destination.id.address) && options.nativeGas === 0.0)
                options.nativeGas = 1.0;
            const updatedParams = { ...params, options };
            const validatedParams = {
                ...updatedParams,
                normalizedParams: await this.normalizeTransferParams(updatedParams),
            };
            return { valid: true, params: validatedParams };
        }
        catch (e) {
            return { valid: false, params, error: e };
        }
    }
    async normalizeTransferParams(params) {
        const amt = this.request.parseAmount(params.amount);
        const inputToken = isNative(this.request.source.id.address)
            ? await this.request.fromChain.getNativeWrappedTokenId()
            : this.request.source.id;
        const atb = await this.request.fromChain.getAutomaticTokenBridge();
        const fee = await atb.getRelayerFee(this.request.from.address, this.request.to, inputToken.address);
        // Min amount is fee + 5%
        const minAmount = (fee * 105n) / 100n;
        if (amount.units(amt) < minAmount) {
            throw new Error(`Minimum amount is ${amount.display({
                amount: minAmount.toString(),
                decimals: amt.decimals,
            })}`);
        }
        const redeemableAmount = amount.units(amt) - fee;
        // Determine nativeGas
        let nativeGasAmount = 0n;
        if (params.options && params.options.nativeGas > 0) {
            const scale = 10000;
            const scaledGas = BigInt(params.options.nativeGas * scale);
            nativeGasAmount = (redeemableAmount * scaledGas) / BigInt(scale);
        }
        return {
            fee: amount.fromBaseUnits(fee, this.request.source.decimals),
            amount: amt,
            nativeGasAmount: amount.fromBaseUnits(nativeGasAmount, this.request.source.decimals),
        };
    }
    async quote(params) {
        try {
            let quote = await TokenTransfer.quoteTransfer(this.request.fromChain, this.request.toChain, this.toTransferDetails(params));
            return this.request.displayQuote(quote, params);
        }
        catch (e) {
            return {
                success: false,
                error: e,
            };
        }
    }
    async initiate(signer, quote) {
        const { params } = quote;
        const transfer = this.toTransferDetails(params);
        const txids = await TokenTransfer.transfer(this.request.fromChain, transfer, signer);
        const msg = await TokenTransfer.getTransferMessage(this.request.fromChain, txids[txids.length - 1].txid);
        return {
            from: transfer.from.chain,
            to: transfer.to.chain,
            state: TransferState.SourceFinalized,
            originTxs: txids,
            attestation: { id: msg },
        };
    }
    async *track(receipt, timeout) {
        yield* TokenTransfer.track(this.wh, receipt, timeout, this.request.fromChain, this.request.toChain);
    }
    toTransferDetails(params) {
        const transfer = {
            automatic: true,
            from: this.request.from,
            to: this.request.to,
            amount: amount.units(params.normalizedParams.amount),
            token: this.request.source.id,
            nativeGas: amount.units(params.normalizedParams.nativeGasAmount),
        };
        return transfer;
    }
}
//# sourceMappingURL=automatic.js.map