import { M as r, N as D, g as N, f as E, c as F, a as x, b as C } from "./types-iFgwQ7vG.mjs";
import { M as O, a as b, i as G, ad as R, f as w, ae as z, af as M, n as S, N as L, d as Q, W as v, ah as y, ai as W, aj as U, ak as I, m as P, al as A, am as B, u as _, ag as V, e as j } from "./platform-Cil_UtAQ.mjs";
class X extends O {
  static NATIVE_GAS_DROPOFF_SUPPORTED = !0;
  // Since we set the config on the static class, access it with this param
  // the MultiTokenNttExecutorRoute.config will always be empty
  staticConfig = (
    // @ts-ignore
    this.constructor.config
  );
  static config = { contracts: [] };
  static meta = { name: "MultiTokenNttExecutorRoute" };
  static supportedNetworks() {
    return r.resolveSupportedNetworks(this.config.contracts);
  }
  static supportedChains(t) {
    return r.resolveSupportedChains(this.config.contracts, t);
  }
  static async supportedDestinationTokens(t, e, i) {
    return [await r.getDestinationTokenId(t, e, i, this.config.contracts)];
  }
  static isProtocolSupported(t) {
    return t.supportsProtocol("MultiTokenNtt");
  }
  getDefaultOptions() {
    return {
      nativeGas: 0
    };
  }
  async isWrappedToken(t) {
    return await r.isWrappedToken(this.wh.getChain(t.chain), t, this.staticConfig.contracts);
  }
  async getOriginalToken(t) {
    return await r.getOriginalToken(this.wh.getChain(t.chain), t, this.staticConfig.contracts);
  }
  async validate(t, e) {
    if (t.fromChain.chain === t.toChain.chain)
      return {
        valid: !1,
        error: new Error("Source and destination chains must differ"),
        params: e
      };
    const i = e.options ?? this.getDefaultOptions();
    if (i.nativeGas !== void 0 && (i.nativeGas < 0 || i.nativeGas > 1))
      return {
        valid: !1,
        error: new Error("Invalid native gas percentage"),
        params: e
      };
    const a = b(e.amount, t.source.decimals), n = D.trimAmount(a, t.destination.decimals), o = r.resolveContracts(this.staticConfig.contracts, t.fromChain.chain), c = r.resolveContracts(this.staticConfig.contracts, t.toChain.chain), f = await t.fromChain.getProtocol("MultiTokenNtt", {
      multiTokenNtt: o
    }), h = G(t.source.id.address) ? await f.getWrappedNativeToken() : t.source.id, l = await f.getOriginalToken(h), m = await f.getSendTransceivers(t.toChain.chain), d = this.getReferrerFeeDbps(t), u = await this.estimateGasLimit(t, l);
    return { valid: !0, params: {
      amount: e.amount,
      normalizedParams: {
        amount: n,
        sourceContracts: o,
        destinationContracts: c,
        referrerFeeDbps: d,
        sourceTokenId: t.source.id,
        destinationTokenId: t.destination.id,
        originalTokenId: l,
        gasLimit: u,
        sendTransceivers: m
      },
      options: i
    } };
  }
  async quote(t, e) {
    const { fromChain: i, toChain: a } = t;
    try {
      const n = await this.fetchExecutorQuote(t, e), { deliveryPrice: o, transceiverInstructions: c } = await this.fetchDeliveryPriceAndInstructions(i, a, e), { remainingAmount: f, estimatedCost: h, gasDropOff: l, expires: m } = n, d = R(w(f, t.source.decimals), t.destination.decimals), u = {
        success: !0,
        params: e,
        sourceToken: {
          token: t.source.id,
          amount: e.normalizedParams.amount
        },
        destinationToken: {
          token: t.destination.id,
          amount: d
        },
        relayFee: {
          token: S(i.chain),
          amount: w(h + o, i.config.nativeTokenDecimals)
        },
        destinationNativeGas: w(l, a.config.nativeTokenDecimals),
        eta: z(t.fromChain.chain) + M * 1e3,
        expires: m,
        details: {
          ...n,
          deliveryPrice: o,
          transceiverInstructions: c
        }
      }, g = await a.getProtocol("MultiTokenNtt", {
        multiTokenNtt: e.normalizedParams.destinationContracts
      }), s = await g.getRateLimitDuration(), p = await r.checkRateLimit(g, i.chain, e.normalizedParams.originalTokenId, d, s);
      return p && (u.warnings = p), u;
    } catch (n) {
      return {
        success: !1,
        error: n instanceof Error ? n : new Error(String(n))
      };
    }
  }
  getReferrerFeeDbps(t) {
    let e = 0n;
    if (this.staticConfig.referrerFee && (e = this.staticConfig.referrerFee.feeDbps, this.staticConfig.referrerFee.perTokenOverrides)) {
      const i = L(t.source.id), a = this.staticConfig.referrerFee.perTokenOverrides[t.source.id.chain]?.[i];
      a?.referrerFeeDbps !== void 0 && (e = a.referrerFeeDbps);
    }
    return e;
  }
  getReferrerAddress(t) {
    let e = N(t.chain);
    const i = this.staticConfig.referrerFee;
    if (i) {
      const a = Q(t.chain), n = i.referrerAddresses?.[a] ?? "";
      n && (e = v.chainAddress(t.chain, n));
    }
    return e;
  }
  async validateCapabilities(t, e) {
    const i = await E(t.network), a = i[y(t.chain)];
    if (!a)
      throw new Error("Unsupported source chain");
    const n = i[y(e.chain)];
    if (!n || !n.requestPrefixes.includes("ERN1"))
      throw new Error("Unsupported destination chain");
    return { sourceCapabilities: a, destinationCapabilities: n };
  }
  calculateGasDropOff(t, e) {
    return e.options.nativeGas && t > 0n ? BigInt(Math.round(e.options.nativeGas * 100)) * t / 100n : 0n;
  }
  async estimateGasLimit(t, e) {
    return r.estimateGasLimit(t, e, this.staticConfig.contracts, this.staticConfig.referrerFee?.perTokenOverrides);
  }
  async fetchExecutorQuote(t, e) {
    const { fromChain: i, toChain: a } = t, n = this.getReferrerAddress(i), { referrerFee: o, remainingAmount: c, referrerFeeDbps: f } = F(e.normalizedParams.amount, e.normalizedParams.referrerFeeDbps ?? 0n, t.destination.decimals);
    if (c <= 0n)
      throw new Error("Amount after fee <= 0");
    const { destinationCapabilities: h } = await this.validateCapabilities(i, a), { recipient: l } = t, m = BigInt(h.gasDropOffLimit), d = this.calculateGasDropOff(m, e), u = [];
    u.push({
      request: {
        type: "GasInstruction",
        gasLimit: e.normalizedParams.gasLimit,
        msgValue: 0n
      }
    }), d > 0n && u.push({
      request: {
        type: "GasDropOffInstruction",
        dropOff: d,
        // If the recipient is undefined (e.g. the user hasn't connected their wallet yet),
        // we temporarily use a dummy address to fetch a quote.
        // The recipient address is validated later in the `initiate` method, which will throw if it's still missing.
        recipient: l ? l.address.toUniversalAddress() : new W(new Uint8Array(32))
      }
    });
    const g = U(I, {
      requests: u
    }), s = await x(i.network, i.chain, a.chain, P.encode(g, !0));
    if (!s.estimatedCost)
      throw new Error("No estimated cost");
    const p = BigInt(s.estimatedCost), T = P.decode(s.signedQuote), k = A(B, T);
    return {
      signedQuote: T,
      relayInstructions: g,
      estimatedCost: p,
      payeeAddress: k.quote.payeeAddress,
      referrer: n,
      referrerFee: o,
      remainingAmount: c,
      referrerFeeDbps: f,
      expires: k.quote.expiryTime,
      gasDropOff: d
    };
  }
  async fetchDeliveryPriceAndInstructions(t, e, i) {
    const a = await t.getProtocol("MultiTokenNtt", {
      multiTokenNtt: i.normalizedParams.sourceContracts
    }), n = await a.createTransceiverInstructions(e.chain, i.normalizedParams.gasLimit, this.staticConfig.axelarGasMultiplier);
    return { deliveryPrice: await a.quoteDeliveryPrice(e.chain, n), transceiverInstructions: n };
  }
  async initiate(t, e, i, a) {
    if (!i.details)
      throw new Error("Missing quote details");
    const { details: n, params: o } = i, { fromChain: c } = t;
    A(I, n.relayInstructions).requests.forEach(({ request: s }) => {
      if (s.type === "GasDropOffInstruction" && !s.recipient.equals(a.address.toUniversalAddress()))
        throw new Error("Gas drop-off recipient does not match");
    });
    const h = v.parseAddress(e.chain(), e.address()), m = (await c.getProtocol("MultiTokenNttWithExecutor", {
      multiTokenNtt: o.normalizedParams.sourceContracts
    })).transfer(h, a, t.source.id, _(o.normalizedParams.amount), n), d = await V(c, m, e);
    let u = 0;
    return (async () => {
      for (; u < 20; ) {
        try {
          const [s] = await C(c.network, d.at(-1).txid, c.chain);
          if (s)
            break;
        } catch {
        }
        u++, await new Promise((s) => setTimeout(s, 2e3));
      }
    })(), {
      from: c.chain,
      to: a.chain,
      state: j.SourceInitiated,
      originTxs: d,
      params: o,
      trackingInfo: { transceiverAttested: {} }
    };
  }
  async complete(t, e) {
    return await r.complete(t, this.wh.getChain(e.to), e);
  }
  async resume(t) {
    return await C(this.wh.network, t.txid, t.chain), await r.resume(t, this.wh, this.staticConfig.contracts);
  }
  async finalize(t, e) {
    return await r.finalize(t, this.wh.getChain(e.to), e);
  }
  async *track(t, e) {
    return yield* r.track(this.wh, t, !1, e);
  }
}
export {
  X as M
};
