UNPKG

hermes-swap-router-sdk

Version:

An sdk for routing swaps using Uniswap v2, Uniswap v3 and Balancer Stable Pools.

2,482 lines 110 kB
'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }

var abi = require('@ethersproject/abi');
var IApproveAndCallABI = _interopDefault(require('@uniswap/swap-router-contracts/artifacts/contracts/interfaces/IApproveAndCall.sol/IApproveAndCall.json'));
var hermesV2Sdk = require('hermes-v2-sdk');
var JSBI = _interopDefault(require('jsbi'));
var maiaCoreSdk = require('maia-core-sdk');
var invariant = _interopDefault(require('tiny-invariant'));
var IMulticallExtendedABI = _interopDefault(require('@uniswap/swap-router-contracts/artifacts/contracts/interfaces/IMulticallExtended.sol/IMulticallExtended.json'));
var IPeripheryPaymentsWithFeeExtendedABI = _interopDefault(require('@uniswap/swap-router-contracts/artifacts/contracts/interfaces/IPeripheryPaymentsWithFeeExtended.sol/IPeripheryPaymentsWithFeeExtended.json'));
var ISwapRouter02ABI = _interopDefault(require('@uniswap/swap-router-contracts/artifacts/contracts/interfaces/ISwapRouter02.sol/ISwapRouter02.json'));
var solidity = require('@ethersproject/solidity');

(function (ApprovalTypes) {
  ApprovalTypes[ApprovalTypes["NOT_REQUIRED"] = 0] = "NOT_REQUIRED";
  ApprovalTypes[ApprovalTypes["MAX"] = 1] = "MAX";
  ApprovalTypes[ApprovalTypes["MAX_MINUS_ONE"] = 2] = "MAX_MINUS_ONE";
  ApprovalTypes[ApprovalTypes["ZERO_THEN_MAX"] = 3] = "ZERO_THEN_MAX";
  ApprovalTypes[ApprovalTypes["ZERO_THEN_MAX_MINUS_ONE"] = 4] = "ZERO_THEN_MAX_MINUS_ONE";
})(exports.ApprovalTypes || (exports.ApprovalTypes = {}));
// type guard
function isMint(options) {
  return Object.keys(options).some(function (k) {
    return k === 'recipient';
  });
}
var ApproveAndCall = /*#__PURE__*/function () {
  /**
   * Cannot be constructed.
   */
  function ApproveAndCall() {}
  ApproveAndCall.encodeApproveMax = function encodeApproveMax(token) {
    return ApproveAndCall.INTERFACE.encodeFunctionData('approveMax', [token.address]);
  };
  ApproveAndCall.encodeApproveMaxMinusOne = function encodeApproveMaxMinusOne(token) {
    return ApproveAndCall.INTERFACE.encodeFunctionData('approveMaxMinusOne', [token.address]);
  };
  ApproveAndCall.encodeApproveZeroThenMax = function encodeApproveZeroThenMax(token) {
    return ApproveAndCall.INTERFACE.encodeFunctionData('approveZeroThenMax', [token.address]);
  };
  ApproveAndCall.encodeApproveZeroThenMaxMinusOne = function encodeApproveZeroThenMaxMinusOne(token) {
    return ApproveAndCall.INTERFACE.encodeFunctionData('approveZeroThenMaxMinusOne', [token.address]);
  };
  ApproveAndCall.encodeCallPositionManager = function encodeCallPositionManager(calldatas) {
    !(calldatas.length > 0) ?  invariant(false, 'NULL_CALLDATA')  : void 0;
    if (calldatas.length == 1) {
      return ApproveAndCall.INTERFACE.encodeFunctionData('callPositionManager', calldatas);
    } else {
      var encodedMulticall = hermesV2Sdk.NonfungiblePositionManager.INTERFACE.encodeFunctionData('multicall', [calldatas]);
      return ApproveAndCall.INTERFACE.encodeFunctionData('callPositionManager', [encodedMulticall]);
    }
  }
  /**
   * Encode adding liquidity to a position in the nft manager contract
   * @param position Forcasted position with expected amount out from swap
   * @param minimalPosition Forcasted position with custom minimal token amounts
   * @param addLiquidityOptions Options for adding liquidity
   * @param slippageTolerance Defines maximum slippage
   */;
  ApproveAndCall.encodeAddLiquidity = function encodeAddLiquidity(position, minimalPosition, addLiquidityOptions, slippageTolerance) {
    var _position$mintAmounts = position.mintAmountsWithSlippage(slippageTolerance),
      amount0Min = _position$mintAmounts.amount0,
      amount1Min = _position$mintAmounts.amount1;
    // position.mintAmountsWithSlippage() can create amounts not dependenable in scenarios
    // such as range orders. Allow the option to provide a position with custom minimum amounts
    // for these scenarios
    if (JSBI.lessThan(minimalPosition.amount0.quotient, amount0Min)) {
      amount0Min = minimalPosition.amount0.quotient;
    }
    if (JSBI.lessThan(minimalPosition.amount1.quotient, amount1Min)) {
      amount1Min = minimalPosition.amount1.quotient;
    }
    if (isMint(addLiquidityOptions)) {
      return ApproveAndCall.INTERFACE.encodeFunctionData('mint', [{
        token0: position.pool.token0.address,
        token1: position.pool.token1.address,
        fee: position.pool.fee,
        tickLower: position.tickLower,
        tickUpper: position.tickUpper,
        amount0Min: maiaCoreSdk.toHex(amount0Min),
        amount1Min: maiaCoreSdk.toHex(amount1Min),
        recipient: addLiquidityOptions.recipient
      }]);
    } else {
      return ApproveAndCall.INTERFACE.encodeFunctionData('increaseLiquidity', [{
        token0: position.pool.token0.address,
        token1: position.pool.token1.address,
        amount0Min: maiaCoreSdk.toHex(amount0Min),
        amount1Min: maiaCoreSdk.toHex(amount1Min),
        tokenId: maiaCoreSdk.toHex(addLiquidityOptions.tokenId)
      }]);
    }
  };
  ApproveAndCall.encodeApprove = function encodeApprove(token, approvalType) {
    switch (approvalType) {
      case exports.ApprovalTypes.MAX:
        return ApproveAndCall.encodeApproveMax(token.wrapped);
      case exports.ApprovalTypes.MAX_MINUS_ONE:
        return ApproveAndCall.encodeApproveMaxMinusOne(token.wrapped);
      case exports.ApprovalTypes.ZERO_THEN_MAX:
        return ApproveAndCall.encodeApproveZeroThenMax(token.wrapped);
      case exports.ApprovalTypes.ZERO_THEN_MAX_MINUS_ONE:
        return ApproveAndCall.encodeApproveZeroThenMaxMinusOne(token.wrapped);
      default:
        throw 'Error: invalid ApprovalType';
    }
  };
  return ApproveAndCall;
}();
ApproveAndCall.INTERFACE = /*#__PURE__*/new abi.Interface(IApproveAndCallABI.abi);

var MSG_SENDER = '0x0000000000000000000000000000000000000001';
var ADDRESS_THIS = '0x0000000000000000000000000000000000000002';
// = 1 << 23 or 100000000000000000000000
var V2_FEE_PATH_PLACEHOLDER = 8388608;
var ZERO_PERCENT = /*#__PURE__*/new maiaCoreSdk.Percent(maiaCoreSdk.ZERO);
var ONE_HUNDRED_PERCENT = /*#__PURE__*/new maiaCoreSdk.Percent(100, 100);

function _arrayLikeToArray(r, a) {
  (null == a || a > r.length) && (a = r.length);
  for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
  return n;
}
function asyncGeneratorStep(n, t, e, r, o, a, c) {
  try {
    var i = n[a](c),
      u = i.value;
  } catch (n) {
    return void e(n);
  }
  i.done ? t(u) : Promise.resolve(u).then(r, o);
}
function _asyncToGenerator(n) {
  return function () {
    var t = this,
      e = arguments;
    return new Promise(function (r, o) {
      var a = n.apply(t, e);
      function _next(n) {
        asyncGeneratorStep(a, r, o, _next, _throw, "next", n);
      }
      function _throw(n) {
        asyncGeneratorStep(a, r, o, _next, _throw, "throw", n);
      }
      _next(void 0);
    });
  };
}
function _defineProperties(e, r) {
  for (var t = 0; t < r.length; t++) {
    var o = r[t];
    o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o);
  }
}
function _createClass(e, r, t) {
  return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", {
    writable: !1
  }), e;
}
function _createForOfIteratorHelperLoose(r, e) {
  var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
  if (t) return (t = t.call(r)).next.bind(t);
  if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) {
    t && (r = t);
    var o = 0;
    return function () {
      return o >= r.length ? {
        done: !0
      } : {
        done: !1,
        value: r[o++]
      };
    };
  }
  throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function _extends() {
  return _extends = Object.assign ? Object.assign.bind() : function (n) {
    for (var e = 1; e < arguments.length; e++) {
      var t = arguments[e];
      for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]);
    }
    return n;
  }, _extends.apply(null, arguments);
}
function _inheritsLoose(t, o) {
  t.prototype = Object.create(o.prototype), t.prototype.constructor = t, _setPrototypeOf(t, o);
}
function _regeneratorRuntime() {
  _regeneratorRuntime = function () {
    return e;
  };
  var t,
    e = {},
    r = Object.prototype,
    n = r.hasOwnProperty,
    o = Object.defineProperty || function (t, e, r) {
      t[e] = r.value;
    },
    i = "function" == typeof Symbol ? Symbol : {},
    a = i.iterator || "@@iterator",
    c = i.asyncIterator || "@@asyncIterator",
    u = i.toStringTag || "@@toStringTag";
  function define(t, e, r) {
    return Object.defineProperty(t, e, {
      value: r,
      enumerable: !0,
      configurable: !0,
      writable: !0
    }), t[e];
  }
  try {
    define({}, "");
  } catch (t) {
    define = function (t, e, r) {
      return t[e] = r;
    };
  }
  function wrap(t, e, r, n) {
    var i = e && e.prototype instanceof Generator ? e : Generator,
      a = Object.create(i.prototype),
      c = new Context(n || []);
    return o(a, "_invoke", {
      value: makeInvokeMethod(t, r, c)
    }), a;
  }
  function tryCatch(t, e, r) {
    try {
      return {
        type: "normal",
        arg: t.call(e, r)
      };
    } catch (t) {
      return {
        type: "throw",
        arg: t
      };
    }
  }
  e.wrap = wrap;
  var h = "suspendedStart",
    l = "suspendedYield",
    f = "executing",
    s = "completed",
    y = {};
  function Generator() {}
  function GeneratorFunction() {}
  function GeneratorFunctionPrototype() {}
  var p = {};
  define(p, a, function () {
    return this;
  });
  var d = Object.getPrototypeOf,
    v = d && d(d(values([])));
  v && v !== r && n.call(v, a) && (p = v);
  var g = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(p);
  function defineIteratorMethods(t) {
    ["next", "throw", "return"].forEach(function (e) {
      define(t, e, function (t) {
        return this._invoke(e, t);
      });
    });
  }
  function AsyncIterator(t, e) {
    function invoke(r, o, i, a) {
      var c = tryCatch(t[r], t, o);
      if ("throw" !== c.type) {
        var u = c.arg,
          h = u.value;
        return h && "object" == typeof h && n.call(h, "__await") ? e.resolve(h.__await).then(function (t) {
          invoke("next", t, i, a);
        }, function (t) {
          invoke("throw", t, i, a);
        }) : e.resolve(h).then(function (t) {
          u.value = t, i(u);
        }, function (t) {
          return invoke("throw", t, i, a);
        });
      }
      a(c.arg);
    }
    var r;
    o(this, "_invoke", {
      value: function (t, n) {
        function callInvokeWithMethodAndArg() {
          return new e(function (e, r) {
            invoke(t, n, e, r);
          });
        }
        return r = r ? r.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg();
      }
    });
  }
  function makeInvokeMethod(e, r, n) {
    var o = h;
    return function (i, a) {
      if (o === f) throw Error("Generator is already running");
      if (o === s) {
        if ("throw" === i) throw a;
        return {
          value: t,
          done: !0
        };
      }
      for (n.method = i, n.arg = a;;) {
        var c = n.delegate;
        if (c) {
          var u = maybeInvokeDelegate(c, n);
          if (u) {
            if (u === y) continue;
            return u;
          }
        }
        if ("next" === n.method) n.sent = n._sent = n.arg;else if ("throw" === n.method) {
          if (o === h) throw o = s, n.arg;
          n.dispatchException(n.arg);
        } else "return" === n.method && n.abrupt("return", n.arg);
        o = f;
        var p = tryCatch(e, r, n);
        if ("normal" === p.type) {
          if (o = n.done ? s : l, p.arg === y) continue;
          return {
            value: p.arg,
            done: n.done
          };
        }
        "throw" === p.type && (o = s, n.method = "throw", n.arg = p.arg);
      }
    };
  }
  function maybeInvokeDelegate(e, r) {
    var n = r.method,
      o = e.iterator[n];
    if (o === t) return r.delegate = null, "throw" === n && e.iterator.return && (r.method = "return", r.arg = t, maybeInvokeDelegate(e, r), "throw" === r.method) || "return" !== n && (r.method = "throw", r.arg = new TypeError("The iterator does not provide a '" + n + "' method")), y;
    var i = tryCatch(o, e.iterator, r.arg);
    if ("throw" === i.type) return r.method = "throw", r.arg = i.arg, r.delegate = null, y;
    var a = i.arg;
    return a ? a.done ? (r[e.resultName] = a.value, r.next = e.nextLoc, "return" !== r.method && (r.method = "next", r.arg = t), r.delegate = null, y) : a : (r.method = "throw", r.arg = new TypeError("iterator result is not an object"), r.delegate = null, y);
  }
  function pushTryEntry(t) {
    var e = {
      tryLoc: t[0]
    };
    1 in t && (e.catchLoc = t[1]), 2 in t && (e.finallyLoc = t[2], e.afterLoc = t[3]), this.tryEntries.push(e);
  }
  function resetTryEntry(t) {
    var e = t.completion || {};
    e.type = "normal", delete e.arg, t.completion = e;
  }
  function Context(t) {
    this.tryEntries = [{
      tryLoc: "root"
    }], t.forEach(pushTryEntry, this), this.reset(!0);
  }
  function values(e) {
    if (e || "" === e) {
      var r = e[a];
      if (r) return r.call(e);
      if ("function" == typeof e.next) return e;
      if (!isNaN(e.length)) {
        var o = -1,
          i = function next() {
            for (; ++o < e.length;) if (n.call(e, o)) return next.value = e[o], next.done = !1, next;
            return next.value = t, next.done = !0, next;
          };
        return i.next = i;
      }
    }
    throw new TypeError(typeof e + " is not iterable");
  }
  return GeneratorFunction.prototype = GeneratorFunctionPrototype, o(g, "constructor", {
    value: GeneratorFunctionPrototype,
    configurable: !0
  }), o(GeneratorFunctionPrototype, "constructor", {
    value: GeneratorFunction,
    configurable: !0
  }), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, u, "GeneratorFunction"), e.isGeneratorFunction = function (t) {
    var e = "function" == typeof t && t.constructor;
    return !!e && (e === GeneratorFunction || "GeneratorFunction" === (e.displayName || e.name));
  }, e.mark = function (t) {
    return Object.setPrototypeOf ? Object.setPrototypeOf(t, GeneratorFunctionPrototype) : (t.__proto__ = GeneratorFunctionPrototype, define(t, u, "GeneratorFunction")), t.prototype = Object.create(g), t;
  }, e.awrap = function (t) {
    return {
      __await: t
    };
  }, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, c, function () {
    return this;
  }), e.AsyncIterator = AsyncIterator, e.async = function (t, r, n, o, i) {
    void 0 === i && (i = Promise);
    var a = new AsyncIterator(wrap(t, r, n, o), i);
    return e.isGeneratorFunction(r) ? a : a.next().then(function (t) {
      return t.done ? t.value : a.next();
    });
  }, defineIteratorMethods(g), define(g, u, "Generator"), define(g, a, function () {
    return this;
  }), define(g, "toString", function () {
    return "[object Generator]";
  }), e.keys = function (t) {
    var e = Object(t),
      r = [];
    for (var n in e) r.push(n);
    return r.reverse(), function next() {
      for (; r.length;) {
        var t = r.pop();
        if (t in e) return next.value = t, next.done = !1, next;
      }
      return next.done = !0, next;
    };
  }, e.values = values, Context.prototype = {
    constructor: Context,
    reset: function (e) {
      if (this.prev = 0, this.next = 0, this.sent = this._sent = t, this.done = !1, this.delegate = null, this.method = "next", this.arg = t, this.tryEntries.forEach(resetTryEntry), !e) for (var r in this) "t" === r.charAt(0) && n.call(this, r) && !isNaN(+r.slice(1)) && (this[r] = t);
    },
    stop: function () {
      this.done = !0;
      var t = this.tryEntries[0].completion;
      if ("throw" === t.type) throw t.arg;
      return this.rval;
    },
    dispatchException: function (e) {
      if (this.done) throw e;
      var r = this;
      function handle(n, o) {
        return a.type = "throw", a.arg = e, r.next = n, o && (r.method = "next", r.arg = t), !!o;
      }
      for (var o = this.tryEntries.length - 1; o >= 0; --o) {
        var i = this.tryEntries[o],
          a = i.completion;
        if ("root" === i.tryLoc) return handle("end");
        if (i.tryLoc <= this.prev) {
          var c = n.call(i, "catchLoc"),
            u = n.call(i, "finallyLoc");
          if (c && u) {
            if (this.prev < i.catchLoc) return handle(i.catchLoc, !0);
            if (this.prev < i.finallyLoc) return handle(i.finallyLoc);
          } else if (c) {
            if (this.prev < i.catchLoc) return handle(i.catchLoc, !0);
          } else {
            if (!u) throw Error("try statement without catch or finally");
            if (this.prev < i.finallyLoc) return handle(i.finallyLoc);
          }
        }
      }
    },
    abrupt: function (t, e) {
      for (var r = this.tryEntries.length - 1; r >= 0; --r) {
        var o = this.tryEntries[r];
        if (o.tryLoc <= this.prev && n.call(o, "finallyLoc") && this.prev < o.finallyLoc) {
          var i = o;
          break;
        }
      }
      i && ("break" === t || "continue" === t) && i.tryLoc <= e && e <= i.finallyLoc && (i = null);
      var a = i ? i.completion : {};
      return a.type = t, a.arg = e, i ? (this.method = "next", this.next = i.finallyLoc, y) : this.complete(a);
    },
    complete: function (t, e) {
      if ("throw" === t.type) throw t.arg;
      return "break" === t.type || "continue" === t.type ? this.next = t.arg : "return" === t.type ? (this.rval = this.arg = t.arg, this.method = "return", this.next = "end") : "normal" === t.type && e && (this.next = e), y;
    },
    finish: function (t) {
      for (var e = this.tryEntries.length - 1; e >= 0; --e) {
        var r = this.tryEntries[e];
        if (r.finallyLoc === t) return this.complete(r.completion, r.afterLoc), resetTryEntry(r), y;
      }
    },
    catch: function (t) {
      for (var e = this.tryEntries.length - 1; e >= 0; --e) {
        var r = this.tryEntries[e];
        if (r.tryLoc === t) {
          var n = r.completion;
          if ("throw" === n.type) {
            var o = n.arg;
            resetTryEntry(r);
          }
          return o;
        }
      }
      throw Error("illegal catch attempt");
    },
    delegateYield: function (e, r, n) {
      return this.delegate = {
        iterator: values(e),
        resultName: r,
        nextLoc: n
      }, "next" === this.method && (this.arg = t), y;
    }
  }, e;
}
function _setPrototypeOf(t, e) {
  return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) {
    return t.__proto__ = e, t;
  }, _setPrototypeOf(t, e);
}
function _toPrimitive(t, r) {
  if ("object" != typeof t || !t) return t;
  var e = t[Symbol.toPrimitive];
  if (void 0 !== e) {
    var i = e.call(t, r || "default");
    if ("object" != typeof i) return i;
    throw new TypeError("@@toPrimitive must return a primitive value.");
  }
  return ("string" === r ? String : Number)(t);
}
function _toPropertyKey(t) {
  var i = _toPrimitive(t, "string");
  return "symbol" == typeof i ? i : i + "";
}
function _unsupportedIterableToArray(r, a) {
  if (r) {
    if ("string" == typeof r) return _arrayLikeToArray(r, a);
    var t = {}.toString.call(r).slice(8, -1);
    return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
  }
}

/**
 * Represents a list of pools or pairs through which a swap can occur
 * @template TInput The input token
 * @template TOutput The output token
 */
var MixedRouteSDK = /*#__PURE__*/function () {
  /**
   * Creates an instance of route.
   * @param pools An array of `TPool` objects (pools or pairs), ordered by the route the swap will take
   * @param input The input token
   * @param output The output token
   */
  function MixedRouteSDK(pools, input, output) {
    this._midPrice = null;
    !(pools.length > 0) ?  invariant(false, 'POOLS')  : void 0;
    var chainId = pools[0].chainId;
    var allOnSameChain = pools.every(function (pool) {
      return pool.chainId === chainId;
    });
    !allOnSameChain ?  invariant(false, 'CHAIN_IDS')  : void 0;
    var wrappedInput = input.wrapped;
    !pools[0].involvesToken(wrappedInput) ?  invariant(false, 'INPUT')  : void 0;
    !pools[pools.length - 1].involvesToken(output.wrapped) ?  invariant(false, 'OUTPUT')  : void 0;
    /**
     * Normalizes token0-token1 order and selects the next token/fee step to add to the path
     * */
    var tokenPath = [wrappedInput];
    for (var _iterator = _createForOfIteratorHelperLoose(pools.entries()), _step; !(_step = _iterator()).done;) {
      var _step$value = _step.value,
        i = _step$value[0],
        pool = _step$value[1];
      var currentInputToken = tokenPath[i];
      !(currentInputToken.equals(pool.token0) || currentInputToken.equals(pool.token1)) ?  invariant(false, 'PATH')  : void 0;
      var nextToken = currentInputToken.equals(pool.token0) ? pool.token1 : pool.token0;
      tokenPath.push(nextToken);
    }
    this.pools = pools;
    this.path = tokenPath;
    this.input = input;
    this.output = output != null ? output : tokenPath[tokenPath.length - 1];
  }
  _createClass(MixedRouteSDK, [{
    key: "chainId",
    get: function get() {
      return this.pools[0].chainId;
    }
    /**
     * Returns the mid price of the route
     */
  }, {
    key: "midPrice",
    get: function get() {
      if (this._midPrice !== null) return this._midPrice;
      var price = this.pools.slice(1).reduce(function (_ref, pool) {
        var nextInput = _ref.nextInput,
          price = _ref.price;
        return nextInput.equals(pool.token0) ? {
          nextInput: pool.token1,
          price: price.multiply(pool.token0Price)
        } : {
          nextInput: pool.token0,
          price: price.multiply(pool.token1Price)
        };
      }, this.pools[0].token0.equals(this.input.wrapped) ? {
        nextInput: this.pools[0].token1,
        price: this.pools[0].token0Price
      } : {
        nextInput: this.pools[0].token0,
        price: this.pools[0].token1Price
      }).price;
      return this._midPrice = new maiaCoreSdk.Price(this.input, this.output, price.denominator, price.numerator);
    }
  }]);
  return MixedRouteSDK;
}();

/**
 * Trades comparator, an extension of the input output comparator that also considers other dimensions of the trade in ranking them
 * @template TInput The input token, either Ether or an ERC-20
 * @template TOutput The output token, either Ether or an ERC-20
 * @template TTradeType The trade type, either exact input or exact output
 * @param a The first trade to compare
 * @param b The second trade to compare
 * @returns A sorted ordering for two neighboring elements in a trade array
 */
function tradeComparator(a, b) {
  // must have same input and output token for comparison
  !a.inputAmount.currency.equals(b.inputAmount.currency) ?  invariant(false, 'INPUT_CURRENCY')  : void 0;
  !a.outputAmount.currency.equals(b.outputAmount.currency) ?  invariant(false, 'OUTPUT_CURRENCY')  : void 0;
  if (a.outputAmount.equalTo(b.outputAmount)) {
    if (a.inputAmount.equalTo(b.inputAmount)) {
      // consider the number of hops since each hop costs gas
      var aHops = a.swaps.reduce(function (total, cur) {
        return total + cur.route.path.length;
      }, 0);
      var bHops = b.swaps.reduce(function (total, cur) {
        return total + cur.route.path.length;
      }, 0);
      return aHops - bHops;
    }
    // trade A requires less input than trade B, so A should come first
    if (a.inputAmount.lessThan(b.inputAmount)) {
      return -1;
    } else {
      return 1;
    }
  } else {
    // tradeA has less output than trade B, so should come second
    if (a.outputAmount.lessThan(b.outputAmount)) {
      return 1;
    } else {
      return -1;
    }
  }
}
/**
 * Represents a trade executed against a set of routes where some percentage of the input is
 * split across each route.
 *
 * Each route has its own set of pools. Pools can not be re-used across routes.
 *
 * Does not account for slippage, i.e., changes in price environment that can occur between
 * the time the trade is submitted and when it is executed.
 * @notice This class is functionally the same as the `Trade` class in the `@uniswap/v3-sdk` package, aside from typing and some input validation.
 * @template TInput The input token, either Ether or an ERC-20
 * @template TOutput The output token, either Ether or an ERC-20
 * @template TTradeType The trade type, either exact input or exact output
 */
var MixedRouteTrade = /*#__PURE__*/function () {
  /**
   * Construct a trade by passing in the pre-computed property values
   * @param routes The routes through which the trade occurs
   * @param tradeType The type of trade, exact input or exact output
   */
  function MixedRouteTrade(_ref) {
    var routes = _ref.routes,
      tradeType = _ref.tradeType;
    var inputCurrency = routes[0].inputAmount.currency;
    var outputCurrency = routes[0].outputAmount.currency;
    !routes.every(function (_ref2) {
      var route = _ref2.route;
      return inputCurrency.wrapped.equals(route.input.wrapped);
    }) ?  invariant(false, 'INPUT_CURRENCY_MATCH')  : void 0;
    !routes.every(function (_ref3) {
      var route = _ref3.route;
      return outputCurrency.wrapped.equals(route.output.wrapped);
    }) ?  invariant(false, 'OUTPUT_CURRENCY_MATCH')  : void 0;
    var numPools = routes.map(function (_ref4) {
      var route = _ref4.route;
      return route.pools.length;
    }).reduce(function (total, cur) {
      return total + cur;
    }, 0);
    var poolAddressSet = new Set();
    for (var _iterator = _createForOfIteratorHelperLoose(routes), _step; !(_step = _iterator()).done;) {
      var route = _step.value.route;
      for (var _iterator2 = _createForOfIteratorHelperLoose(route.pools), _step2; !(_step2 = _iterator2()).done;) {
        var pool = _step2.value;
        pool instanceof hermesV2Sdk.Pool ? poolAddressSet.add(hermesV2Sdk.Pool.getAddress(pool.token0, pool.token1, pool.fee)) : poolAddressSet.add(hermesV2Sdk.Pair.getAddress(pool.token0, pool.token1));
      }
    }
    !(numPools == poolAddressSet.size) ?  invariant(false, 'POOLS_DUPLICATED')  : void 0;
    !(tradeType === hermesV2Sdk.TradeType.EXACT_INPUT) ?  invariant(false, 'TRADE_TYPE')  : void 0;
    this.swaps = routes;
    this.tradeType = tradeType;
  }
  /**
   * @deprecated Deprecated in favor of 'swaps' property. If the trade consists of multiple routes
   * this will return an error.
   *
   * When the trade consists of just a single route, this returns the route of the trade,
   * i.e. which pools the trade goes through.
   */
  /**
   * Constructs a trade by simulating swaps through the given route
   * @template TInput The input token, either Ether or an ERC-20.
   * @template TOutput The output token, either Ether or an ERC-20.
   * @template TTradeType The type of the trade, either exact in or exact out.
   * @param route route to swap through
   * @param amount the amount specified, either input or output, depending on tradeType
   * @param tradeType whether the trade is an exact input or exact output swap
   * @returns The route
   */
  MixedRouteTrade.fromRoute =
  /*#__PURE__*/
  function () {
    var _fromRoute = /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(route, amount, tradeType) {
      var amounts, i, pool, _yield$pool$getOutput, _outputAmount, inputAmount, outputAmount;
      return _regeneratorRuntime().wrap(function _callee$(_context) {
        while (1) switch (_context.prev = _context.next) {
          case 0:
            amounts = new Array(route.path.length);
            !(tradeType === hermesV2Sdk.TradeType.EXACT_INPUT) ?  invariant(false, 'TRADE_TYPE')  : void 0;
            !amount.currency.equals(route.input) ?  invariant(false, 'INPUT')  : void 0;
            amounts[0] = amount.wrapped;
            i = 0;
          case 5:
            if (!(i < route.path.length - 1)) {
              _context.next = 15;
              break;
            }
            pool = route.pools[i]; // TODO: If ComposableStablePoolWrapper and using underlying, update Wrapper's rate
            _context.next = 9;
            return pool.getOutputAmount(amounts[i]);
          case 9:
            _yield$pool$getOutput = _context.sent;
            _outputAmount = _yield$pool$getOutput[0];
            amounts[i + 1] = _outputAmount;
          case 12:
            i++;
            _context.next = 5;
            break;
          case 15:
            inputAmount = maiaCoreSdk.CurrencyAmount.fromFractionalAmount(route.input, amount.numerator, amount.denominator);
            outputAmount = maiaCoreSdk.CurrencyAmount.fromFractionalAmount(route.output, amounts[amounts.length - 1].numerator, amounts[amounts.length - 1].denominator);
            return _context.abrupt("return", new MixedRouteTrade({
              routes: [{
                inputAmount: inputAmount,
                outputAmount: outputAmount,
                route: route
              }],
              tradeType: tradeType
            }));
          case 18:
          case "end":
            return _context.stop();
        }
      }, _callee);
    }));
    function fromRoute(_x, _x2, _x3) {
      return _fromRoute.apply(this, arguments);
    }
    return fromRoute;
  }()
  /**
   * Constructs a trade from routes by simulating swaps
   *
   * @template TInput The input token, either Ether or an ERC-20.
   * @template TOutput The output token, either Ether or an ERC-20.
   * @template TTradeType The type of the trade, either exact in or exact out.
   * @param routes the routes to swap through and how much of the amount should be routed through each
   * @param tradeType whether the trade is an exact input or exact output swap
   * @returns The trade
   */
  ;
  MixedRouteTrade.fromRoutes =
  /*#__PURE__*/
  function () {
    var _fromRoutes = /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee2(routes, tradeType) {
      var populatedRoutes, _iterator3, _step3, _step3$value, route, amount, amounts, inputAmount, i, pool, _yield$pool$getOutput2, _outputAmount2, outputAmount;
      return _regeneratorRuntime().wrap(function _callee2$(_context2) {
        while (1) switch (_context2.prev = _context2.next) {
          case 0:
            populatedRoutes = [];
            !(tradeType === hermesV2Sdk.TradeType.EXACT_INPUT) ?  invariant(false, 'TRADE_TYPE')  : void 0;
            _iterator3 = _createForOfIteratorHelperLoose(routes);
          case 3:
            if ((_step3 = _iterator3()).done) {
              _context2.next = 24;
              break;
            }
            _step3$value = _step3.value, route = _step3$value.route, amount = _step3$value.amount;
            amounts = new Array(route.path.length);
            !amount.currency.equals(route.input) ?  invariant(false, 'INPUT')  : void 0;
            inputAmount = maiaCoreSdk.CurrencyAmount.fromFractionalAmount(route.input, amount.numerator, amount.denominator);
            amounts[0] = maiaCoreSdk.CurrencyAmount.fromFractionalAmount(route.input.wrapped, amount.numerator, amount.denominator);
            i = 0;
          case 10:
            if (!(i < route.path.length - 1)) {
              _context2.next = 20;
              break;
            }
            pool = route.pools[i]; // TODO: If ComposableStablePoolWrapper and using underlying, update Wrapper's rate
            _context2.next = 14;
            return pool.getOutputAmount(amounts[i]);
          case 14:
            _yield$pool$getOutput2 = _context2.sent;
            _outputAmount2 = _yield$pool$getOutput2[0];
            amounts[i + 1] = _outputAmount2;
          case 17:
            i++;
            _context2.next = 10;
            break;
          case 20:
            outputAmount = maiaCoreSdk.CurrencyAmount.fromFractionalAmount(route.output, amounts[amounts.length - 1].numerator, amounts[amounts.length - 1].denominator);
            populatedRoutes.push({
              route: route,
              inputAmount: inputAmount,
              outputAmount: outputAmount
            });
          case 22:
            _context2.next = 3;
            break;
          case 24:
            return _context2.abrupt("return", new MixedRouteTrade({
              routes: populatedRoutes,
              tradeType: tradeType
            }));
          case 25:
          case "end":
            return _context2.stop();
        }
      }, _callee2);
    }));
    function fromRoutes(_x4, _x5) {
      return _fromRoutes.apply(this, arguments);
    }
    return fromRoutes;
  }()
  /**
   * Creates a trade without computing the result of swapping through the route. Useful when you have simulated the trade
   * elsewhere and do not have any tick data
   * @template TInput The input token, either Ether or an ERC-20
   * @template TOutput The output token, either Ether or an ERC-20
   * @template TTradeType The type of the trade, either exact in or exact out
   * @param constructorArguments The arguments passed to the trade constructor
   * @returns The unchecked trade
   */
  ;
  MixedRouteTrade.createUncheckedTrade = function createUncheckedTrade(constructorArguments) {
    return new MixedRouteTrade(_extends({}, constructorArguments, {
      routes: [{
        inputAmount: constructorArguments.inputAmount,
        outputAmount: constructorArguments.outputAmount,
        route: constructorArguments.route
      }]
    }));
  }
  /**
   * Creates a trade without computing the result of swapping through the routes. Useful when you have simulated the trade
   * elsewhere and do not have any tick data
   * @template TInput The input token, either Ether or an ERC-20
   * @template TOutput The output token, either Ether or an ERC-20
   * @template TTradeType The type of the trade, either exact in or exact out
   * @param constructorArguments The arguments passed to the trade constructor
   * @returns The unchecked trade
   */;
  MixedRouteTrade.createUncheckedTradeWithMultipleRoutes = function createUncheckedTradeWithMultipleRoutes(constructorArguments) {
    return new MixedRouteTrade(constructorArguments);
  }
  /**
   * Get the minimum amount that must be received from this trade for the given slippage tolerance
   * @param slippageTolerance The tolerance of unfavorable slippage from the execution price of this trade
   * @returns The amount out
   */;
  var _proto = MixedRouteTrade.prototype;
  _proto.minimumAmountOut = function minimumAmountOut(slippageTolerance, amountOut) {
    if (amountOut === void 0) {
      amountOut = this.outputAmount;
    }
    !!slippageTolerance.lessThan(maiaCoreSdk.ZERO) ?  invariant(false, 'SLIPPAGE_TOLERANCE')  : void 0;
    /// does not support exactOutput, as enforced in the constructor
    var slippageAdjustedAmountOut = new maiaCoreSdk.Fraction(maiaCoreSdk.ONE).add(slippageTolerance).invert().multiply(amountOut.quotient).quotient;
    return maiaCoreSdk.CurrencyAmount.fromRawAmount(amountOut.currency, slippageAdjustedAmountOut);
  }
  /**
   * Get the maximum amount in that can be spent via this trade for the given slippage tolerance
   * @param slippageTolerance The tolerance of unfavorable slippage from the execution price of this trade
   * @returns The amount in
   */;
  _proto.maximumAmountIn = function maximumAmountIn(slippageTolerance, amountIn) {
    if (amountIn === void 0) {
      amountIn = this.inputAmount;
    }
    !!slippageTolerance.lessThan(maiaCoreSdk.ZERO) ?  invariant(false, 'SLIPPAGE_TOLERANCE')  : void 0;
    return amountIn;
    /// does not support exactOutput
  }
  /**
   * Return the execution price after accounting for slippage tolerance
   * @param slippageTolerance the allowed tolerated slippage
   * @returns The execution price
   */;
  _proto.worstExecutionPrice = function worstExecutionPrice(slippageTolerance) {
    return new maiaCoreSdk.Price(this.inputAmount.currency, this.outputAmount.currency, this.maximumAmountIn(slippageTolerance).quotient, this.minimumAmountOut(slippageTolerance).quotient);
  }
  /**
   * Given a list of pools, and a fixed amount in, returns the top `maxNumResults` trades that go from an input token
   * amount to an output token, making at most `maxHops` hops.
   * Note this does not consider aggregation, as routes are linear. It's possible a better route exists by splitting
   * the amount in among multiple routes.
   * @param pools the pools to consider in finding the best trade
   * @param nextAmountIn exact amount of input currency to spend
   * @param currencyOut the desired currency out
   * @param maxNumResults maximum number of results to return
   * @param maxHops maximum number of hops a returned trade can make, e.g. 1 hop goes through a single pool
   * @param currentPools used in recursion; the current list of pools
   * @param currencyAmountIn used in recursion; the original value of the currencyAmountIn parameter
   * @param bestTrades used in recursion; the current list of best trades
   * @returns The exact in trade
   */;
  MixedRouteTrade.bestTradeExactIn =
  /*#__PURE__*/
  function () {
    var _bestTradeExactIn = /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee3(pools, currencyAmountIn, currencyOut, // TODO: Increase maxHops (and maybe maxNumResults) to 5
    _temp,
    // used in recursion.
    currentPools, nextAmountIn, bestTrades) {
      var _ref5, _ref5$maxNumResults, maxNumResults, _ref5$maxHops, maxHops, amountIn, tokenOut, i, pool, amountOut, _yield$pool$getOutput3, poolsExcludingThisPool;
      return _regeneratorRuntime().wrap(function _callee3$(_context3) {
        while (1) switch (_context3.prev = _context3.next) {
          case 0:
            _ref5 = _temp === void 0 ? {} : _temp, _ref5$maxNumResults = _ref5.maxNumResults, maxNumResults = _ref5$maxNumResults === void 0 ? 3 : _ref5$maxNumResults, _ref5$maxHops = _ref5.maxHops, maxHops = _ref5$maxHops === void 0 ? 3 : _ref5$maxHops;
            if (currentPools === void 0) {
              currentPools = [];
            }
            if (nextAmountIn === void 0) {
              nextAmountIn = currencyAmountIn;
            }
            if (bestTrades === void 0) {
              bestTrades = [];
            }
            !(pools.length > 0) ?  invariant(false, 'POOLS')  : void 0;
            !(maxHops > 0) ?  invariant(false, 'MAX_HOPS')  : void 0;
            !(currencyAmountIn === nextAmountIn || currentPools.length > 0) ?  invariant(false, 'INVALID_RECURSION')  : void 0;
            amountIn = nextAmountIn.wrapped;
            tokenOut = currencyOut.wrapped;
            i = 0;
          case 10:
            if (!(i < pools.length)) {
              _context3.next = 49;
              break;
            }
            pool = pools[i]; // pool irrelevant
            if (!(!pool.token0.equals(amountIn.currency) && !pool.token1.equals(amountIn.currency))) {
              _context3.next = 14;
              break;
            }
            return _context3.abrupt("continue", 46);
          case 14:
            if (!(pool instanceof hermesV2Sdk.Pair)) {
              _context3.next = 17;
              break;
            }
            if (!(pool.reserve0.equalTo(maiaCoreSdk.ZERO) || pool.reserve1.equalTo(maiaCoreSdk.ZERO))) {
              _context3.next = 17;
              break;
            }
            return _context3.abrupt("continue", 46);
          case 17:
            amountOut = void 0;
            _context3.prev = 18;
            _context3.next = 22;
            return pool.getOutputAmount(amountIn);
          case 22:
            _yield$pool$getOutput3 = _context3.sent;
            amountOut = _yield$pool$getOutput3[0];
            _context3.next = 31;
            break;
          case 26:
            _context3.prev = 26;
            _context3.t0 = _context3["catch"](18);
            if (!_context3.t0.isInsufficientInputAmountError) {
              _context3.next = 30;
              break;
            }
            return _context3.abrupt("continue", 46);
          case 30:
            throw _context3.t0;
          case 31:
            if (!(amountOut.currency.isToken && amountOut.currency.equals(tokenOut))) {
              _context3.next = 42;
              break;
            }
            _context3.t1 = hermesV2Sdk.sortedInsert;
            _context3.t2 = bestTrades;
            _context3.next = 36;
            return MixedRouteTrade.fromRoute(new MixedRouteSDK([].concat(currentPools, [pool]), currencyAmountIn.currency, currencyOut), currencyAmountIn, hermesV2Sdk.TradeType.EXACT_INPUT);
          case 36:
            _context3.t3 = _context3.sent;
            _context3.t4 = maxNumResults;
            _context3.t5 = tradeComparator;
            (0, _context3.t1)(_context3.t2, _context3.t3, _context3.t4, _context3.t5);
            _context3.next = 46;
            break;
          case 42:
            if (!(maxHops > 1 && pools.length > 1)) {
              _context3.next = 46;
              break;
            }
            poolsExcludingThisPool = pools.slice(0, i).concat(pools.slice(i + 1, pools.length)); // otherwise, consider all the other paths that lead from this token as long as we have not exceeded maxHops
            _context3.next = 46;
            return MixedRouteTrade.bestTradeExactIn(poolsExcludingThisPool, currencyAmountIn, currencyOut, {
              maxNumResults: maxNumResults,
              maxHops: maxHops - 1
            }, [].concat(currentPools, [pool]), amountOut, bestTrades);
          case 46:
            i++;
            _context3.next = 10;
            break;
          case 49:
            return _context3.abrupt("return", bestTrades);
          case 50:
          case "end":
            return _context3.stop();
        }
      }, _callee3, null, [[18, 26]]);
    }));
    function bestTradeExactIn(_x6, _x7, _x8, _x9, _x10, _x11, _x12) {
      return _bestTradeExactIn.apply(this, arguments);
    }
    return bestTradeExactIn;
  }();
  _createClass(MixedRouteTrade, [{
    key: "route",
    get: function get() {
      !(this.swaps.length == 1) ?  invariant(false, 'MULTIPLE_ROUTES')  : void 0;
      return this.swaps[0].route;
    }
    /**
     * The input amount for the trade assuming no slippage.
     */
  }, {
    key: "inputAmount",
    get: function get() {
      if (this._inputAmount) {
        return this._inputAmount;
      }
      var inputCurrency = this.swaps[0].inputAmount.currency;
      var totalInputFromRoutes = this.swaps.map(function (_ref6) {
        var inputAmount = _ref6.inputAmount;
        return inputAmount;
      }).reduce(function (total, cur) {
        return total.add(cur);
      }, maiaCoreSdk.CurrencyAmount.fromRawAmount(inputCurrency, 0));
      this._inputAmount = totalInputFromRoutes;
      return this._inputAmount;
    }
    /**
     * The output amount for the trade assuming no slippage.
     */
  }, {
    key: "outputAmount",
    get: function get() {
      if (this._outputAmount) {
        return this._outputAmount;
      }
      var outputCurrency = this.swaps[0].outputAmount.currency;
      var totalOutputFromRoutes = this.swaps.map(function (_ref7) {
        var outputAmount = _ref7.outputAmount;
        return outputAmount;
      }).reduce(function (total, cur) {
        return total.add(cur);
      }, maiaCoreSdk.CurrencyAmount.fromRawAmount(outputCurrency, 0));
      this._outputAmount = totalOutputFromRoutes;
      return this._outputAmount;
    }
    /**
     * The price expressed in terms of output amount/input amount.
     */
  }, {
    key: "executionPrice",
    get: function get() {
      var _this$_executionPrice;
      return (_this$_executionPrice = this._executionPrice) != null ? _this$_executionPrice : this._executionPrice = new maiaCoreSdk.Price(this.inputAmount.currency, this.outputAmount.currency, this.inputAmount.quotient, this.outputAmount.quotient);
    }
    /**
     * Returns the percent difference between the route's mid price and the price impact
     */
  }, {
    key: "priceImpact",
    get: function get() {
      if (this._priceImpact) {
        return this._priceImpact;
      }
      var spotOutputAmount = maiaCoreSdk.CurrencyAmount.fromRawAmount(this.outputAmount.currency, 0);
      for (var _iterator4 = _createForOfIteratorHelperLoose(this.swaps), _step4; !(_step4 = _iterator4()).done;) {
        var _step4$value = _step4.value,
          route = _step4$value.route,
          inputAmount = _step4$value.inputAmount;
        var midPrice = route.midPrice;
        spotOutputAmount = spotOutputAmount.add(midPrice.quote(inputAmount));
      }
      var priceImpact = spotOutputAmount.subtract(this.outputAmount).divide(spotOutputAmount);
      this._priceImpact = new maiaCoreSdk.Percent(priceImpact.numerator, priceImpact.denominator);
      return this._priceImpact;
    }
  }]);
  return MixedRouteTrade;
}();

(function (Protocol) {
  Protocol["V2"] = "V2";
  Protocol["V3"] = "V3";
  Protocol["BAL_STABLE"] = "BAL";
  Protocol["BAL_STABLE_WRAPPER"] = "ERC4626";
  Protocol["MIXED"] = "MIXED";
})(exports.Protocol || (exports.Protocol = {}));

// V2 route wrapper
var RouteV2 = /*#__PURE__*/function (_V2RouteSDK) {
  _inheritsLoose(RouteV2, _V2RouteSDK);
  function RouteV2(v2Route) {
    var _this;
    _this = _V2RouteSDK.call(this, v2Route.pairs, v2Route.input, v2Route.output) || this;
    _this.protocol = exports.Protocol.V2;
    _this.pools = _this.pairs;
    return _this;
  }
  return RouteV2;
}(hermesV2Sdk.V2RouteSDK);
// V3 route wrapper
var RouteV3 = /*#__PURE__*/function (_V3RouteSDK) {
  _inheritsLoose(RouteV3, _V3RouteSDK);
  function RouteV3(v3Route) {
    var _this2;
    _this2 = _V3RouteSDK.call(this, v3Route.pools, v3Route.input, v3Route.output) || this;
    _this2.protocol = exports.Protocol.V3;
    _this2.path = v3Route.tokenPath;
    return _this2;
  }
  return RouteV3;
}(hermesV2Sdk.Route);
// Balancer Composable Stable Pool route wrapper
var RouteStable = /*#__PURE__*/function (_V3RouteSDK2) {
  _inheritsLoose(RouteStable, _V3RouteSDK2);
  function RouteStable(stableRoute) {
    var _this3;
    _this3 = _V3RouteSDK2.call(this, stableRoute.pools, stableRoute.input, stableRoute.output) || this;
    _this3.protocol = exports.Protocol.BAL_STABLE;
    _this3.path = stableRoute.tokenPath;
    return _this3;
  }
  return RouteStable;
}(hermesV2Sdk.Route);
// Balancer Composable Stable Pool route wrapper
var RouteStableWrapper = /*#__PURE__*/function (_V3RouteSDK3) {
  _inheritsLoose(RouteStableWrapper, _V3RouteSDK3);
  function RouteStableWrapper(stableRoute) {
    var _this4;
    _this4 = _V3RouteSDK3.call(this, stableRoute.pools, stableRoute.input, stableRoute.output) || this;
    _this4.protocol = exports.Protocol.BAL_STABLE_WRAPPER;
    _this4.path = stableRoute.tokenPath;
    return _this4;
  }
  return RouteStableWrapper;
}(hermesV2Sdk.Route);
// Mixed route wrapper
var MixedRoute = /*#__PURE__*/function (_MixedRouteSDK) {
  _inheritsLoose(MixedRoute, _MixedRouteSDK);
  function MixedRoute(mixedRoute) {
    var _this5;
    _this5 = _MixedRouteSDK.call(this, mixedRoute.pools, mixedRoute.input, mixedRoute.output) || this;
    _this5.protocol = exports.Protocol.MIXED;
    return _this5;
  }
  return MixedRoute;
}(MixedRouteSDK);

var Trade = /*#__PURE__*/function () {
  //  construct a trade across v2 and v3 routes from pre-computed amounts
  function Trade(_ref) {
    var v2Routes = _ref.v2Routes,
      v3Routes = _ref.v3Routes,
      stableRoutes = _ref.stableRoutes,
      stableWrapperRoutes = _ref.stableWrapperRoutes,
      tradeType = _ref.tradeType,
      mixedRoutes = _ref.mixedRoutes,
      allowDifferentTokenRoutes = _ref.allowDifferentTokenRoutes;
    this.swaps = [];
    this.routes = [];
    // wrap v2 routes
    for (var _iterator = _createForOfIteratorHelperLoose(v2Routes), _step; !(_step = _iterator()).done;) {
      var _step$value = _step.value,
        routev2 = _step$value.routev2,
        _inputAmount = _step$value.inputAmount,
        _outputAmount = _step$value.outputAmount;
      var _route = new RouteV2(routev2);
      this.routes.push(_route);
      this.swaps.push({
        route: _route,
        inputAmount: _inputAmount,
        outputAmount: _outputAmount
      });
    }
    // wrap v3 routes
    for (var _iterator2 = _createForOfIteratorHelperLoose(v3Routes), _step2; !(_step2 = _iterator2()).done;) {
      var _step2$value = _step2.value,
        routev3 = _step2$value.routev3,
        _inputAmount2 = _step2$value.inputAmount,
        _outputAmount2 = _step2$value.outputAmount;
      var _route2 = new RouteV3(routev3);
      this.routes.push(_route2);
      this.swaps.push({
        route: _route2,
        inputAmount: _inputAmount2,
        outputAmount: _outputAmount2
      });
    }
    // wrap stable routes
    for (var _iterator3 = _createForOfIteratorHelperLoose(stableRoutes), _step3; !(_step3 = _iterator3()).done;) {
      var _step3$value = _step3.value,
        routeStable = _step3$value.routeStable,
        _inputAmount3 = _step3$value.inputAmount,
        _outputAmount3 = _step3$value.outputAmount;
      var _route3 = new RouteStable(routeStable);
      this.routes.push(_route3);
      this.swaps.push({
        route: _route3,
        inputAmount: _inputAmount3,
        outputAmount: _outputAmount3
      });
    }
    // wrap stable wrapper routes
    for (var _iterator4 = _createForOfIteratorHelperLoose(stableWrapperRoutes), _step4; !(_step4 = _iterator4()).done;) {
      var _step4$value = _step4.value,
        routeStableWrapper = _step4$value.routeStableWrapper,
        _inputAmount4 = _step4$value.inputAmount,
        _outputAmount4 = _step4$value.outputAmount;
      var _route4 = new RouteStableWrapper(routeStableWrapper);
      this.routes.push(_route4);
      this.swaps.push({
        route: _route4,
        inputAmount: _inputAmount4,
        outputAmount: _outputAmount4
      });
    }
    // wrap mixedRoutes
    if (mixedRoutes) {
      for (var _iterator5 = _createForOfIteratorHelperLoose(mixedRoutes), _step5; !(_step5 = _iterator5()).done;) {
        var _step5$value = _step5.value,
          mixedRoute = _step5$value.mixedRoute,
          inputAmount = _step5$value.inputAmount,
          outputAmount = _step5$value.outputAmount;
        var route = new MixedRoute(mixedRoute);
        this.routes.push(route);
        this.swaps.push({
          route: route,
          inputAmount: inputAmount,
          outputAmount: outputAmount
        });
      }
    }
    if (this.swaps.length === 0) {
      throw new Error('No routes provided when calling Trade constructor');
    }
    this.tradeType = tradeType;
    if (!allowDifferentTokenRoutes) {
      // each route must have the same input and output currency
      var inputCurrency = this.swaps[0].inputAmount.currency;
      var outputCurrency = this.swaps[0].outputAmount.currency;
      !this.swaps.every(function (_ref2) {
        var route = _ref2.route;
        return inputCurrency.wrapped.equals(route.input.wrapped);
      }) ?  invariant(false, 'INPUT_CURRENCY_MATCH')  : void 0;
      !this.swaps.every(function (_ref3) {
        var route = _ref3.route;
        return outputCurrency.wrapped.equals(route.output.wrapped);
      }) ?  invariant(false, 'OUTPUT_CURRENCY_MATCH')  : void 0;
    }
    // pools must be unique inter protocols
    var numPools = this.swaps.map(function (_ref4) {
      var route = _ref4.route;
      return route.pools.length;
    }).reduce(function (total, cur) {
      return total + cur;
    }, 0);
    var poolAddressSet = new Set();
    for (var _iterator6 = _createForOfIteratorHelperLoose(this.swaps), _step6; !(_step6 = _iterator6()).done;) {
      var _route5 = _step6.value.route;
      for (var _iterator7 = _createForOfIteratorHelperLoose(_route5.pools), _step7; !(_step7 = _iterator7()).done;) {
        var pool = _step7.value;
        if (pool instanceof hermesV2Sdk.Pool) {
          poolAddressSet.add(hermesV2Sdk.Pool.getAddress(pool.token0, pool.token1, pool.fee));
        } else if (pool instanceof hermesV2Sdk.Pair) {
          var pair = pool;
          poolAddressSet.add(hermesV2Sdk.Pair.getAddress(pair.token0, pair.token1));
        } else if (pool instanceof hermesV2Sdk.ComposableStablePool) {
          poolAddressSet.add(pool.pool.id);
        } else if (pool instanceof hermesV2Sdk.ComposableStablePoolWrapper) {
          poolAddressSet.add(pool.vault().address);
        } else {
          throw new Error('Unexpected pool type in route when constructing trade object');
        }
      }
    }
    !(numPools == poolAddressSet.size) ?  invariant(false, 'POOLS_DUPLICATED')  : void 0;
  }
  var _proto = Trade.prototype;
  /**
   * Get the minimum amount that must be received from this trade for the given slippage tolerance
   * @param slippageTolerance The tolerance of unfavorable slippage from the execution price of this trade
   * @returns The amount out
   */
  _proto.minimumAmountOut = function minimumAmountOut(slippageTolerance, amountOut) {
    if (amountOut === void 0) {
      amountOut = this.outputAmount;
    }
    !!slippageTolerance.lessThan(maiaCoreSdk.ZERO) ?  invariant(false, 'SLIPPAGE_TOLERANCE')  : void 0;
    if (this.tradeType === hermesV2Sdk.TradeType.EXACT_OUTPUT) {
      return amountOut;
    } else {
      var slippageAdjustedAmountOut = new maiaCoreSdk.Fraction(maiaCoreSdk.ONE).add(slippageTolerance).invert().multiply(amountOut.quotient).quotient;
      return maiaCoreSdk.CurrencyAmount.fromRawAmount(amountOut.currency, slippageAdjustedAmountOut);
    }
  }
  /**
   * Get the maximum amount in that can be spent via this trade for the given slippage tolerance
   * @param slippageTolerance The tolerance of unfavorable slippage from the execution price of this trade
   * @returns The amount in
   */;
  _proto.maximumAmountIn = function maximumAmountIn(slippageTolerance, amountIn) {
    if (amountIn === void 0) {
      amountIn = this.inputAmount;
    }
    !!slippageTolerance.lessThan(maiaCoreSdk.ZERO) ?  invariant(false, 'SLIPPAGE_TOLERANCE')  : void 0;
    if (this.tradeType === hermesV2Sdk.TradeType.EXACT_INPUT) {
      return amountIn;
    } else {
      var slippageAdjustedAmountIn = new maiaCoreSdk.Fraction(maiaCoreSdk.ONE).add(slippageTolerance).multiply(amountIn.quotient).quotient;
      return maiaCoreSdk.CurrencyAmount.fromRawAmount(amountIn.currency, slippageAdjustedAmountIn);
    }
  }
  /**
   * Return the execution price after accounting for slippage tolerance
   * @param slippageTolerance the allowed tolerated slippage
   * @returns The execution price
   */;
  _proto.worstExecutionPrice = function worstExecutionPrice(slippageTolerance) {
    return new maiaCoreSdk.Price(this.inputAmount.currency, this.outputAmount.currency, this.maximumAmountIn(slippageTolerance).quotient, this.minimumAmountOut(slippageTolerance).quotient);
  };
  Trade.fromRoutes = /*#__PURE__*/function () {
    var _fromRoutes = /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(v2Routes, v3Routes, stableRoutes, stableWrapperRoutes, tradeType, mixedRoutes) {
      var populatedV2Routes, populatedV3Routes, populatedStableRoutes, populatedStableWrapperRoutes, populatedMixedRoutes, _iterator8, _step8, _step8$value, routev2, _amount, v2Trade, _inputAmount5, _outputAmount5, _iterator9, _step9, _step9$value, routev3, _amount2, v3Trade, _inputAmount6, _outputAmount6, _iterator10, _step10, _step10$value, routeStable, _amount3, stableTrade, _inputAmount7, _outputAmount7, _iterator11, _step11, _step11$value, routeStableWrapper, _amount4, stableWrapperTrade, _inputAmount8, _outputAmount8, _iterator12, _step12, _step12$value, mixedRoute, amount, mixedRouteTrade, inputAmount, outputAmount;
      return _regeneratorRuntime().wrap(function _callee$(_context) {
        while (1) switch (_context.prev = _context.next) {
          case 0:
            populatedV2Routes = [];
            populatedV3Routes = [];
            populatedStableRoutes = [];
            populatedStableWrapperRoutes = [];
            populatedMixedRoutes = [];
            for (_iterator8 = _createForOfIteratorHelperLoose(v2Routes); !(_step8 = _iterator8()).done;) {
              _step8$value = _step8.value, routev2 = _step8$value.routev2, _amount = _step8$value.amount;
              v2Trade = new hermesV2Sdk.V2Trade(routev2, _amount, tradeType);
              _inputAmount5 = v2Trade.inputAmount, _outputAmount5 = v2Trade.outputAmount;
              populatedV2Routes.push({
                routev2: routev2,
                inputAmount: _inputAmount5,
                outputAmount: _outputAmount5
              });
            }
            _iterator9 = _createForOfIteratorHelperLoose(v3Routes);
          case 7:
            if ((_step9 = _iterator9()).done) {
              _context.next = 16;
              break;
            }
            _step9$value = _step9.value, routev3 = _step9$value.routev3, _amount2 = _step9$value.amount;
            _context.next = 11;
            return hermesV2Sdk.Trade.fromRoute(routev3, _amount2, tradeType);
          case 11:
            v3Trade = _context.sent;
            _inputAmount6 = v3Trade.inputAmount, _outputAmount6 = v3Trade.outputAmount;
            populatedV3Routes.push({
              routev3: routev3,
              inputAmount: _inputAmount6,
              outputAmount: _outputAmount6
            });
          case 14:
            _context.next = 7;
            break;
          case 16:
            _iterator10 = _createForOfIteratorHelperLoose(stableRoutes);
          case 17:
            if ((_step10 = _iterator10()).done) {
              _context.next = 26;
              break;
            }
            _step10$value = _step10.value, routeStable = _step10$value.routeStable, _amount3 = _step10$value.amount;
            _context.next = 21;
            return hermesV2Sdk.Trade.fromRoute(routeStable, _amount3, tradeType);
          case 21:
            stableTrade = _context.sent;
            _inputAmount7 = stableTrade.inputAmount, _outputAmount7 = stableTrade.outputAmount;
            populatedStableRoutes.push({
              routeStable: routeStable,
              inputAmount: _inputAmount7,
              outputAmount: _outputAmount7
            });
          case 24:
            _context.next = 17;
            break;
          case 26:
            _iterator11 = _createForOfIteratorHelperLoose(stableWrapperRoutes);
          case 27:
            if ((_step11 = _iterator11()).done) {
              _context.next = 36;
              break;
            }
            _step11$value = _step11.value, routeStableWrapper = _step11$value.routeStableWrapper, _amount4 = _step11$value.amount;
            _context.next = 31;
            return hermesV2Sdk.Trade.fromRoute(routeStableWrapper, _amount4, tradeType);
          case 31:
            stableWrapperTrade = _context.sent;
            _inputAmount8 = stableWrapperTrade.inputAmount, _outputAmount8 = stableWrapperTrade.outputAmount;
            populatedStableWrapperRoutes.push({
              routeStableWrapper: routeStableWrapper,
              inputAmount: _inputAmount8,
              outputAmount: _outputAmount8
            });
          case 34:
            _context.next = 27;
            break;
          case 36:
            if (!mixedRoutes) {
              _context.next = 47;
              break;
            }
            _iterator12 = _createForOfIteratorHelperLoose(mixedRoutes);
          case 38:
            if ((_step12 = _iterator12()).done) {
              _context.next = 47;
              break;
            }
            _step12$value = _step12.value, mixedRoute = _step12$value.mixedRoute, amount = _step12$value.amount;
            _context.next = 42;
            return MixedRouteTrade.fromRoute(mixedRoute, amount, tradeType);
          case 42:
            mixedRouteTrade = _context.sent;
            inputAmount = mixedRouteTrade.inputAmount, outputAmount = mixedRouteTrade.outputAmount;
            populatedMixedRoutes.push({
              mixedRoute: mixedRoute,
              inputAmount: inputAmount,
              outputAmount: outputAmount
            });
          case 45:
            _context.next = 38;
            break;
          case 47:
            return _context.abrupt("return", new Trade({
              v2Routes: populatedV2Routes,
              v3Routes: populatedV3Routes,
              stableRoutes: populatedStableRoutes,
              stableWrapperRoutes: populatedStableWrapperRoutes,
              mixedRoutes: populatedMixedRoutes,
              tradeType: tradeType
            }));
          case 48:
          case "end":
            return _context.stop();
        }
      }, _callee);
    }));
    function fromRoutes(_x, _x2, _x3, _x4, _x5, _x6) {
      return _fromRoutes.apply(this, arguments);
    }
    return fromRoutes;
  }();
  Trade.fromRoute = /*#__PURE__*/function () {
    var _fromRoute = /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee2(route, amount, tradeType) {
      var v2Routes, v3Routes, stableRoutes, stableWrapperRoutes, mixedRoutes, v2Trade, inputAmount, outputAmount, v3Trade, _inputAmount9, _outputAmount9, stableTrade, _inputAmount10, _outputAmount10, stableWrapperTrade, _inputAmount11, _outputAmount11, mixedRouteTrade, _inputAmount12, _outputAmount12;
      return _regeneratorRuntime().wrap(function _callee2$(_context2) {
        while (1) switch (_context2.prev = _context2.next) {
          case 0:
            v2Routes = [];
            v3Routes = [];
            stableRoutes = [];
            stableWrapperRoutes = [];
            mixedRoutes = [];
            if (!(route instanceof hermesV2Sdk.V2RouteSDK)) {
              _context2.next = 11;
              break;
            }
            v2Trade = new hermesV2Sdk.V2Trade(route, amount, tradeType);
            inputAmount = v2Trade.inputAmount, outputAmount = v2Trade.outputAmount;
            v2Routes = [{
              routev2: route,
              inputAmount: inputAmount,
              outputAmount: outputAmount
            }];
            _context2.next = 48;
            break;
          case 11:
            if (!(route instanceof hermesV2Sdk.Route)) {
              _context2.next = 39;
              break;
            }
            if (!(Trade.routeProtocol(route) === exports.Protocol.V3)) {
              _context2.next = 20;
              break;
            }
            _context2.next = 15;
            return hermesV2Sdk.Trade.fromRoute(route, amount, tradeType);
          case 15:
            v3Trade = _context2.sent;
            _inputAmount9 = v3Trade.inputAmount, _outputAmount9 = v3Trade.outputAmount;
            v3Routes = [{
              routev3: route,
              inputAmount: _inputAmount9,
              outputAmount: _outputAmount9
            }];
            _context2.next = 37;
            break;
          case 20:
            if (!(Trade.routeProtocol(route) === exports.Protocol.BAL_STABLE)) {
              _context2.next = 28;
              break;
            }
            _context2.next = 23;
            return hermesV2Sdk.Trade.fromRoute(route, amount, tradeType);
          case 23:
            stableTrade = _context2.sent;
            _inputAmount10 = stableTrade.inputAmount, _outputAmount10 = stableTrade.outputAmount;
            stableRoutes = [{
              routeStable: route,
              inputAmount: _inputAmount10,
              outputAmount: _outputAmount10
            }];
            _context2.next = 37;
            break;
          case 28:
            if (!(Trade.routeProtocol(route) === exports.Protocol.BAL_STABLE_WRAPPER)) {
              _context2.next = 36;
              break;
            }
            _context2.next = 31;
            return hermesV2Sdk.Trade.fromRoute(route, amount, tradeType);
          case 31:
            stableWrapperTrade = _context2.sent;
            _inputAmount11 = stableWrapperTrade.inputAmount, _outputAmount11 = stableWrapperTrade.outputAmount;
            stableWrapperRoutes.push({
              routeStableWrapper: route,
              inputAmount: _inputAmount11,
              outputAmount: _outputAmount11
            });
            _context2.next = 37;
            break;
          case 36:
            throw new Error('Invalid route type');
          case 37:
            _context2.next = 48;
            break;
          case 39:
            if (!(route instanceof MixedRouteSDK)) {
              _context2.next = 47;
              break;
            }
            _context2.next = 42;
            return MixedRouteTrade.fromRoute(route, amount, tradeType);
          case 42:
            mixedRouteTrade = _context2.sent;
            _inputAmount12 = mixedRouteTrade.inputAmount, _outputAmount12 = mixedRouteTrade.outputAmount;
            mixedRoutes = [{
              mixedRoute: route,
              inputAmount: _inputAmount12,
              outputAmount: _outputAmount12
            }];
            _context2.next = 48;
            break;
          case 47:
            throw new Error('Invalid route type');
          case 48:
            return _context2.abrupt("return", new Trade({
              v2Routes: v2Routes,
              v3Routes: v3Routes,
              stableRoutes: stableRoutes,
              stableWrapperRoutes: stableWrapperRoutes,
              mixedRoutes: mixedRoutes,
              tradeType: tradeType
            }));
          case 49:
          case "end":
            return _context2.stop();
        }
      }, _callee2);
    }));
    function fromRoute(_x7, _x8, _x9) {
      return _fromRoute.apply(this, arguments);
    }
    return fromRoute;
  }();
  Trade.tradeProtocol = function tradeProtocol(trade) {
    return Trade.routeProtocol(trade.swaps[0].route);
  };
  Trade.routeProtocol = function routeProtocol(route) {
    if (route.pools.length == 0) throw new Error('INVALID_ROUTE');
    if (route.pools[0] instanceof hermesV2Sdk.Pool) return exports.Protocol.V3;else if (route.pools[0] instanceof hermesV2Sdk.ComposableStablePool) return exports.Protocol.BAL_STABLE;else if (route.pools[0] instanceof hermesV2Sdk.ComposableStablePoolWrapper) return exports.Protocol.BAL_STABLE_WRAPPER;
    throw new Error('UNSUPPORTED_PROTOCOL');
  };
  _createClass(Trade, [{
    key: "inputAmount",
    get: function get() {
      if (this._inputAmount) {
        return this._inputAmount;
      }
      var inputCurrency = this.swaps[0].inputAmount.currency;
      var totalInputFromRoutes = this.swaps.map(function (_ref5) {
        var inputAmount = _ref5.inputAmount;
        return inputAmount;
      }).reduce(function (total, cur) {
        return total.add(cur);
      }, maiaCoreSdk.CurrencyAmount.fromRawAmount(inputCurrency, 0));
      this._inputAmount = totalInputFromRoutes;
      return this._inputAmount;
    }
  }, {
    key: "outputAmount",
    get: function get() {
      if (this._outputAmount) {
        return this._outputAmount;
      }
      var outputCurrency = this.swaps[0].outputAmount.currency;
      var totalOutputFromRoutes = this.swaps.map(function (_ref6) {
        var outputAmount = _ref6.outputAmount;
        return outputAmount;
      }).reduce(function (total, cur) {
        return total.add(cur);
      }, maiaCoreSdk.CurrencyAmount.fromRawAmount(outputCurrency, 0));
      this._outputAmount = totalOutputFromRoutes;
      return this._outputAmount;
    }
    /**
     * The price expressed in terms of output amount/input amount.
     */
  }, {
    key: "executionPrice",
    get: function get() {
      var _this$_executionPrice;
      return (_this$_executionPrice = this._executionPrice) != null ? _this$_executionPrice : this._executionPrice = new maiaCoreSdk.Price(this.inputAmount.currency, this.outputAmount.currency, this.inputAmount.quotient, this.outputAmount.quotient);
    }
    /**
     * Returns the sell tax of the input token
     */
  }, {
    key: "inputTax",
    get: function get() {
      return ZERO_PERCENT;
      // TODO: Review if adding tax is needed
      // const inputCurrency = this.inputAmount.currency
      // if (inputCurrency.isNative || !inputCurrency.wrapped.sellFeeBps) return ZERO_PERCENT
      // return new Percent(inputCurrency.wrapped.sellFeeBps.toNumber(), 10000)
    }
    /**
     * Returns the buy tax of the output token
     */
  }, {
    key: "outputTax",
    get: function get() {
      return ZERO_PERCENT;
      // TODO: Review if adding tax is needed
      // const outputCurrency = this.outputAmount.currency
      // if (outputCurrency.isNative || !outputCurrency.wrapped.buyFeeBps) return ZERO_PERCENT
      // return new Percent(outputCurrency.wrapped.buyFeeBps.toNumber(), 10000)
    }
    /**
     * Returns the percent difference between the route's mid price and the expected execution price
     * In order to exclude token taxes from the price impact calculation, the spot price is calculated
     * using a ratio of values that go into the pools, which are the post-tax input amount and pre-tax output amount.
     */
  }, {
    key: "priceImpact",
    get: function get() {
      if (this._priceImpact) {
        return this._priceImpact;
      }
      // // returns 0% price impact even though this may be inaccurate as a swap may have occured.
      // // because we're unable to derive the pre-buy-tax amount, use 0% as a placeholder.
      // if (this.outputTax.equalTo(ONE_HUNDRED_PERCENT)) return ZERO_PERCENT
      var spotOutputAmount = maiaCoreSdk.CurrencyAmount.fromRawAmount(this.outputAmount.currency, 0);
      for (var _iterator13 = _createForOfIteratorHelperLoose(this.swaps), _step13; !(_step13 = _iterator13()).done;) {
        var _step13$value = _step13.value,
          route = _step13$value.route,
          inputAmount = _step13$value.inputAmount;
        var midPrice = route.midPrice;
        // const postTaxInputAmount = inputAmount.multiply(new Fraction(ONE).subtract(this.inputTax))
        spotOutputAmount = spotOutputAmount.add(midPrice.quote(inputAmount));
      }
      // if the total output of this trade is 0, then most likely the post-tax input was also 0, and therefore this swap
      // does not move the pools' market price
      if (spotOutputAmount.equalTo(maiaCoreSdk.ZERO)) return ZERO_PERCENT;
      // const preTaxOutputAmount = this.outputAmount.divide(new Fraction(ONE).subtract(this.outputTax))
      var priceImpact = spotOutputAmount.subtract(this.outputAmount).divide(spotOutputAmount);
      this._priceImpact = new maiaCoreSdk.Percent(priceImpact.numerator, priceImpact.denominator);
      return this._priceImpact;
    }
  }]);
  return Trade;
}();

function validateAndParseBytes32(bytes32) {
  if (!bytes32.match(/^0x[0-9a-fA-F]{64}$/)) {
    throw new Error(bytes32 + " is not valid bytes32.");
  }
  return bytes32.toLowerCase();
}
var MulticallExtended = /*#__PURE__*/function () {
  /**
   * Cannot be constructed.
   */
  function MulticallExtended() {}
  MulticallExtended.encodeMulticall = function encodeMulticall(calldatas, validation) {
    // if there's no validation, we can just fall back to regular multicall
    if (typeof validation === 'undefined') {
      return hermesV2Sdk.Multicall.encodeMulticall(calldatas);
    }
    // if there is validation, we have to normalize calldatas
    if (!Array.isArray(calldatas)) {
      calldatas = [calldatas];
    }
    // this means the validation value should be a previousBlockhash
    if (typeof validation === 'string' && validation.startsWith('0x')) {
      var previousBlockhash = validateAndParseBytes32(validation);
      return MulticallExtended.INTERFACE.encodeFunctionData('multicall(bytes32,bytes[])', [previousBlockhash, calldatas]);
    } else {
      var deadline = maiaCoreSdk.toHex(validation);
      return MulticallExtended.INTERFACE.encodeFunctionData('multicall(uint256,bytes[])', [deadline, calldatas]);
    }
  };
  return MulticallExtended;
}();
MulticallExtended.INTERFACE = /*#__PURE__*/new abi.Interface(IMulticallExtendedABI.abi);

function encodeFeeBips(fee) {
  return maiaCoreSdk.toHex(fee.multiply(10000).quotient);
}
var PaymentsExtended = /*#__PURE__*/function () {
  /**
   * Cannot be constructed.
   */
  function PaymentsExtended() {}
  PaymentsExtended.encodeUnwrapWETH9 = function encodeUnwrapWETH9(amountMinimum, recipient, feeOptions) {
    // if there's a recipient, just pass it along
    if (typeof recipient === 'string') {
      return hermesV2Sdk.Payments.encodeUnwrapWETH9(amountMinimum, recipient, feeOptions);
    }
    if (!!feeOptions) {
      var feeBips = encodeFeeBips(feeOptions.fee);
      var feeRecipient = hermesV2Sdk.validateAndParseAddress(feeOptions.recipient);
      return PaymentsExtended.INTERFACE.encodeFunctionData('unwrapWETH9WithFee(uint256,uint256,address)', [maiaCoreSdk.toHex(amountMinimum), feeBips, feeRecipient]);
    } else {
      return PaymentsExtended.INTERFACE.encodeFunctionData('unwrapWETH9(uint256)', [maiaCoreSdk.toHex(amountMinimum)]);
    }
  };
  PaymentsExtended.encodeSweepToken = function encodeSweepToken(token, amountMinimum, recipient, feeOptions) {
    // if there's a recipient, just pass it along
    if (typeof recipient === 'string') {
      return hermesV2Sdk.Payments.encodeSweepToken(token, amountMinimum, recipient, feeOptions);
    }
    if (!!feeOptions) {
      var feeBips = encodeFeeBips(feeOptions.fee);
      var feeRecipient = hermesV2Sdk.validateAndParseAddress(feeOptions.recipient);
      return PaymentsExtended.INTERFACE.encodeFunctionData('sweepTokenWithFee(address,uint256,uint256,address)', [token.address, maiaCoreSdk.toHex(amountMinimum), feeBips, feeRecipient]);
    } else {
      return PaymentsExtended.INTERFACE.encodeFunctionData('sweepToken(address,uint256)', [token.address, maiaCoreSdk.toHex(amountMinimum)]);
    }
  };
  PaymentsExtended.encodePull = function encodePull(token, amount) {
    return PaymentsExtended.INTERFACE.encodeFunctionData('pull', [token.address, maiaCoreSdk.toHex(amount)]);
  };
  PaymentsExtended.encodeWrapETH = function encodeWrapETH(amount) {
    return PaymentsExtended.INTERFACE.encodeFunctionData('wrapETH', [maiaCoreSdk.toHex(amount)]);
  };
  return PaymentsExtended;
}();
PaymentsExtended.INTERFACE = /*#__PURE__*/new abi.Interface(IPeripheryPaymentsWithFeeExtendedABI.abi);

var UNIVERSAL_ROUTER_ADDRESS = function UNIVERSAL_ROUTER_ADDRESS(chainId) {
  switch (chainId) {
    case 42161:
      // arbitrum
      return '0x00000006f4dd9687c729a608a0ddb70400001095';
    case 11155111:
      // sepolia
      return '0x5181bd48C2070E1f850C4a1F59665f02E4617Cb0';
    case 1:
      // mainnet
      return maiaCoreSdk.ZERO_ADDRESS;
    case 10:
      // optimism
      return maiaCoreSdk.ZERO_ADDRESS;
    default:
      throw new Error("Universal Router not deployed on chain " + chainId);
  }
};

(function (EncodingType) {
  EncodingType["V2"] = "V2";
  EncodingType["V3"] = "V3";
  EncodingType["BAL_STABLE"] = "BAL";
  EncodingType["VAULT"] = "ERC4626";
})(exports.EncodingType || (exports.EncodingType = {}));
// Mapping of pool constructors to their corresponding EncodingType
var poolTypeToEncodingTypeMap = /*#__PURE__*/new Map([[hermesV2Sdk.Pool, exports.EncodingType.V3], [hermesV2Sdk.ComposableStablePool, exports.EncodingType.BAL_STABLE], [hermesV2Sdk.ComposableStablePoolWrapper, exports.EncodingType.VAULT], [hermesV2Sdk.Pair, exports.EncodingType.V2]]);
// Function to determine the type group of a pool
var getTypeGroup = function getTypeGroup(pool) {
  for (var _iterator = _createForOfIteratorHelperLoose(poolTypeToEncodingTypeMap), _step; !(_step = _iterator()).done;) {
    var _step$value = _step.value,
      poolConstructor = _step$value[0],
      encodingType = _step$value[1];
    if (pool instanceof poolConstructor) {
      return encodingType;
    }
  }
  throw new Error('UNRECOGNIZED POOL TYPE');
};
/**
 * Utility function to return each consecutive section of various types of pools in a MixedRoute
 * @param route
 * @returns a nested array of various types of pools in the order of the route
 */
var partitionMixedRouteByProtocol = function partitionMixedRouteByProtocol(route) {
  var pools = route.pools;
  return partitionPoolsByProtocol(pools);
};
function partitionPoolsByProtocol(pools) {
  var acc = [];
  var left = 0;
  var right = 0;
  while (right < pools.length) {
    var leftGroup = getTypeGroup(pools[left]);
    var rightGroup = getTypeGroup(pools[right]);
    if (leftGroup !== rightGroup) {
      acc.push(pools.slice(left, right));
      left = right;
    }
    right++;
    if (right === pools.length) {
      acc.push(pools.slice(left, right));
    }
  }
  return acc;
}
/**
 * Simple utility function to get the output of an array of Pools
 * @param pools
 * @param firstInputToken
 * @returns the output token of the last pool in the array
 */
var getOutputOfPools = function getOutputOfPools(pools, firstInputToken) {
  var _pools$reduce = pools.reduce(function (_ref, pool) {
      var inputToken = _ref.inputToken;
      if (!pool.involvesToken(inputToken)) throw new Error('PATH');
      var outputToken = pool.token0.equals(inputToken) ? pool.token1 : pool.token0;
      return {
        inputToken: outputToken
      };
    }, {
      inputToken: firstInputToken
    }),
    outputToken = _pools$reduce.inputToken;
  return outputToken;
};

var SWAP_ROUTER_INTERFACE = /*#__PURE__*/new abi.Interface(ISwapRouter02ABI.abi);

// Encoding function for a V2 Pool
function encodeV2Route(encodingParams) {
  var path = encodingParams.route.path.map(function (token) {
    return token.address;
  });
  var exactInputParams = [encodingParams.amountIn, encodingParams.amountOutMinimum, path, encodingParams.recipient];
  return SWAP_ROUTER_INTERFACE.encodeFunctionData('swapExactTokensForTokens', exactInputParams);
}
function encodeMixedV2Route(encodingParams) {
  var exactInputParams = [encodingParams.amountIn, encodingParams.amountOutMinimum, encodingParams.route.path.map(function (token) {
    return token.address;
  }), encodingParams.recipient];
  return SWAP_ROUTER_INTERFACE.encodeFunctionData('swapExactTokensForTokens', exactInputParams);
}

/**
 * Converts a route to a hex encoded path
 * @notice only supports exactIn route encodings
 * @param route the mixed path to convert to an encoded path
 * @returns the exactIn encoded path
 */
function encodeMixedRouteToPath(route) {
  var firstInputToken = route.input.wrapped;
  var _route$pools$reduce = route.pools.reduce(function (_ref, pool, index) {
      var inputToken = _ref.inputToken,
        path = _ref.path,
        types = _ref.types;
      var outputToken = pool.token0.equals(inputToken) ? pool.token1 : pool.token0;
      if (index === 0) {
        return {
          inputToken: outputToken,
          types: ['address', 'uint24', 'address'],
          path: [inputToken.address, pool instanceof hermesV2Sdk.Pool ? pool.fee : V2_FEE_PATH_PLACEHOLDER, outputToken.address]
        };
      } else {
        return {
          inputToken: outputToken,
          types: [].concat(types, ['uint24', 'address']),
          path: [].concat(path, [pool instanceof hermesV2Sdk.Pool ? pool.fee : V2_FEE_PATH_PLACEHOLDER, outputToken.address])
        };
      }
    }, {
      inputToken: firstInputToken,
      path: [],
      types: []
    }),
    path = _route$pools$reduce.path,
    types = _route$pools$reduce.types;
  return solidity.pack(types, path);
}

// Encoding function for a V3 Pool
function encodeV3Route(encodingParams) {
  var exactInputSingleParams = {
    tokenIn: encodingParams.route.path[0].address,
    tokenOut: encodingParams.route.path[1].address,
    fee: encodingParams.route.pools[0].fee,
    recipient: encodingParams.recipient,
    amountIn: encodingParams.amountIn,
    amountOutMinimum: encodingParams.amountOutMinimum,
    sqrtPriceLimitX96: 0
  };
  return SWAP_ROUTER_INTERFACE.encodeFunctionData('exactInputSingle', [exactInputSingleParams]);
}
function encodeMixedV3Route(encodingParams) {
  var path = encodeMixedRouteToPath(encodingParams.route);
  var exactInputParams = {
    path: path,
    // By default router holds funds until the last swap, then it is sent to the recipient
    // special case exists where we are unwrapping WETH output, in which case `routerMustCustody` is set to true
    // and router still holds the funds. That logic bundled into how the value of `recipient` is calculated
    recipient: encodingParams.recipient,
    amountIn: encodingParams.amountIn,
    amountOutMinimum: encodingParams.amountOutMinimum
  };
  return SWAP_ROUTER_INTERFACE.encodeFunctionData('exactInput', [exactInputParams]);
}

var _poolEncodingStrategi, _mixedPoolEncodingStr;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function unsupportedPoolType(_encodingParams) {
  throw new Error('UNSUPPORTED_SWAP_ROUTER_POOL_TYPE');
}
// Mapping of pool types to their encoding functions
var poolEncodingStrategies = (_poolEncodingStrategi = {}, _poolEncodingStrategi[exports.EncodingType.V3] = encodeV3Route, _poolEncodingStrategi[exports.EncodingType.V2] = encodeV2Route, _poolEncodingStrategi[exports.EncodingType.BAL_STABLE] = unsupportedPoolType, _poolEncodingStrategi[exports.EncodingType.VAULT] = unsupportedPoolType, _poolEncodingStrategi);
// Mapping of pool types to their encoding functions for mixed routes
var mixedPoolEncodingStrategies = (_mixedPoolEncodingStr = {}, _mixedPoolEncodingStr[exports.EncodingType.V3] = encodeMixedV3Route, _mixedPoolEncodingStr[exports.EncodingType.V2] = encodeMixedV2Route, _mixedPoolEncodingStr[exports.EncodingType.BAL_STABLE] = unsupportedPoolType, _mixedPoolEncodingStr[exports.EncodingType.VAULT] = unsupportedPoolType, _mixedPoolEncodingStr);
// Function to determine the encoding strategy for a given route
function getEncodingStrategyForRoute(route) {
  var typeGroup = getTypeGroup(route.pools[0]); // Assuming all pools in a route are of the same type
  var encodingStrategy = poolEncodingStrategies[typeGroup];
  if (!encodingStrategy) {
    throw new Error("No encoding strategy found for pool type: " + typeGroup);
  }
  return encodingStrategy;
}
// Function to determine the encoding strategy for a given route
function getEncodingStrategyForMixedRoute(route) {
  var typeGroup = getTypeGroup(route.pools[0]); // Assuming all pools in a route are of the same type
  var mixedEncodingStrategy = mixedPoolEncodingStrategies[typeGroup];
  if (!mixedEncodingStrategy) {
    throw new Error("No encoding strategy found for pool type: " + typeGroup);
  }
  return mixedEncodingStrategy;
}
/**
 * @notice Generates the calldata for a MixedRouteSwap. Since single hop routes are not MixedRoutes, we will
 *         instead generate them via the existing encodeV3Swap and encodeV2Swap methods.
 * @param trade The MixedRouteTrade to encode.
 * @param options SwapOptions to use for the trade.
 * @param routerMustCustody Flag for whether funds should be sent to the router
 * @param performAggregatedSlippageCheck Flag for whether we want to perform an aggregated slippage check
 * @returns A string array of calldatas for the trade.
 */
function encodeMixedRouteSwap(trade, options, routerMustCustody, performAggregatedSlippageCheck) {
  var calldatas = [];
  !(trade.tradeType === hermesV2Sdk.TradeType.EXACT_INPUT) ?  invariant(false, 'TRADE_TYPE')  : void 0;
  var _loop = function _loop() {
    var _step$value = _step.value,
      route = _step$value.route,
      inputAmount = _step$value.inputAmount,
      outputAmount = _step$value.outputAmount;
    var amountIn = maiaCoreSdk.toHex(trade.maximumAmountIn(options.slippageTolerance, inputAmount).quotient);
    var amountOut = maiaCoreSdk.toHex(trade.minimumAmountOut(options.slippageTolerance, outputAmount).quotient);
    // flag for whether the trade is single hop or not
    var singleHop = route.pools.length === 1;
    var recipient = routerMustCustody ? ADDRESS_THIS : typeof options.recipient === 'undefined' ? MSG_SENDER : hermesV2Sdk.validateAndParseAddress(options.recipient);
    if (singleHop) {
      var encodingParams = {
        route: route,
        amountIn: amountIn,
        amountOutMinimum: performAggregatedSlippageCheck ? '0' : amountOut,
        recipient: recipient
      };
      /// For single hop, since it isn't really a mixedRoute, we'll just mimic behavior of each pool type
      /// We don't use encodeV3Swap(), encodeV2Swap(), etc. because casting the trade to a Trade is overcomplex
      var encodeFunc = getEncodingStrategyForRoute(route);
      calldatas.push(encodeFunc(encodingParams));
    } else {
      var sections = partitionMixedRouteByProtocol(route);
      var isLastSectionInRoute = function isLastSectionInRoute(i) {
        return i === sections.length - 1;
      };
      var outputToken;
      var inputToken = route.input.wrapped;
      for (var i = 0; i < sections.length; i++) {
        var section = sections[i];
        /// Now, we get output of this section
        outputToken = getOutputOfPools(section, inputToken);
        var newRouteOriginal = new MixedRouteSDK([].concat(section), section[0].token0.equals(inputToken) ? section[0].token0 : section[0].token1, outputToken);
        var newRoute = new MixedRoute(newRouteOriginal);
        /// Previous output is now input
        inputToken = outputToken;
        var _encodingParams2 = {
          route: newRoute,
          amountIn: i === 0 ? amountIn : '0',
          amountOutMinimum: isLastSectionInRoute(i) ? amountOut : '0',
          recipient: isLastSectionInRoute(i) ? recipient : ADDRESS_THIS
        };
        var _encodeFunc = getEncodingStrategyForMixedRoute(newRoute);
        calldatas.push(_encodeFunc(_encodingParams2));
      }
    }
  };
  for (var _iterator = _createForOfIteratorHelperLoose(trade.swaps), _step; !(_step = _iterator()).done;) {
    _loop();
  }
  return calldatas;
}

/**
 * @notice Generates the calldata for a Swap with a V3 Route.
 * @param _trade The V3Trade to encode.
 * @param _options SwapOptions to use for the trade.
 * @param _routerMustCustody Flag for whether funds should be sent to the router
 * @param _performAggregatedSlippageCheck Flag for whether we want to perform an aggregated slippage check
 * @returns A string array of calldatas for the trade.
 */
function encodeStableSwap(_trade, _options, _routerMustCustody, _performAggregatedSlippageCheck) {
  throw new Error('UNSUPPORTED_SWAP_ROUTER_POOL_TYPE');
}

/**
 * @notice Generates the calldata for a Swap with a V2 Route.
 * @param trade The V2Trade to encode.
 * @param options SwapOptions to use for the trade.
 * @param routerMustCustody Flag for whether funds should be sent to the router
 * @param performAggregatedSlippageCheck Flag for whether we want to perform an aggregated slippage check
 * @returns A string array of calldatas for the trade.
 */
function encodeV2Swap(trade, options, routerMustCustody, performAggregatedSlippageCheck) {
  var amountIn = maiaCoreSdk.toHex(trade.maximumAmountIn(options.slippageTolerance).quotient);
  var amountOut = maiaCoreSdk.toHex(trade.minimumAmountOut(options.slippageTolerance).quotient);
  var path = trade.route.path.map(function (token) {
    return token.address;
  });
  var recipient = routerMustCustody ? ADDRESS_THIS : typeof options.recipient === 'undefined' ? MSG_SENDER : hermesV2Sdk.validateAndParseAddress(options.recipient);
  if (trade.tradeType === hermesV2Sdk.TradeType.EXACT_INPUT) {
    var exactInputParams = [amountIn, performAggregatedSlippageCheck ? 0 : amountOut, path, recipient];
    return SWAP_ROUTER_INTERFACE.encodeFunctionData('swapExactTokensForTokens', exactInputParams);
  } else {
    var exactOutputParams = [amountOut, amountIn, path, recipient];
    return SWAP_ROUTER_INTERFACE.encodeFunctionData('swapTokensForExactTokens', exactOutputParams);
  }
}

/**
 * @notice Generates the calldata for a Swap with a V3 Route.
 * @param trade The V3Trade to encode.
 * @param options SwapOptions to use for the trade.
 * @param routerMustCustody Flag for whether funds should be sent to the router
 * @param performAggregatedSlippageCheck Flag for whether we want to perform an aggregated slippage check
 * @returns A string array of calldatas for the trade.
 */
function encodeV3Swap(trade, options, routerMustCustody, performAggregatedSlippageCheck) {
  var calldatas = [];
  for (var _iterator = _createForOfIteratorHelperLoose(trade.swaps), _step; !(_step = _iterator()).done;) {
    var _step$value = _step.value,
      route = _step$value.route,
      inputAmount = _step$value.inputAmount,
      outputAmount = _step$value.outputAmount;
    var amountIn = maiaCoreSdk.toHex(trade.maximumAmountIn(options.slippageTolerance, inputAmount).quotient);
    var amountOut = maiaCoreSdk.toHex(trade.minimumAmountOut(options.slippageTolerance, outputAmount).quotient);
    // flag for whether the trade is single hop or not
    var singleHop = route.pools.length === 1;
    var recipient = routerMustCustody ? ADDRESS_THIS : typeof options.recipient === 'undefined' ? MSG_SENDER : hermesV2Sdk.validateAndParseAddress(options.recipient);
    if (singleHop) {
      if (trade.tradeType === hermesV2Sdk.TradeType.EXACT_INPUT) {
        var exactInputSingleParams = {
          tokenIn: route.tokenPath[0].address,
          tokenOut: route.tokenPath[1].address,
          fee: route.pools[0].fee,
          recipient: recipient,
          amountIn: amountIn,
          amountOutMinimum: performAggregatedSlippageCheck ? 0 : amountOut,
          sqrtPriceLimitX96: 0
        };
        calldatas.push(SWAP_ROUTER_INTERFACE.encodeFunctionData('exactInputSingle', [exactInputSingleParams]));
      } else {
        var exactOutputSingleParams = {
          tokenIn: route.tokenPath[0].address,
          tokenOut: route.tokenPath[1].address,
          fee: route.pools[0].fee,
          recipient: recipient,
          amountOut: amountOut,
          amountInMaximum: amountIn,
          sqrtPriceLimitX96: 0
        };
        calldatas.push(SWAP_ROUTER_INTERFACE.encodeFunctionData('exactOutputSingle', [exactOutputSingleParams]));
      }
    } else {
      var path = hermesV2Sdk.encodeRouteToPath(route, trade.tradeType === hermesV2Sdk.TradeType.EXACT_OUTPUT);
      if (trade.tradeType === hermesV2Sdk.TradeType.EXACT_INPUT) {
        var exactInputParams = {
          path: path,
          recipient: recipient,
          amountIn: amountIn,
          amountOutMinimum: performAggregatedSlippageCheck ? 0 : amountOut
        };
        calldatas.push(SWAP_ROUTER_INTERFACE.encodeFunctionData('exactInput', [exactInputParams]));
      } else {
        var exactOutputParams = {
          path: path,
          recipient: recipient,
          amountOut: amountOut,
          amountInMaximum: amountIn
        };
        calldatas.push(SWAP_ROUTER_INTERFACE.encodeFunctionData('exactOutput', [exactOutputParams]));
      }
    }
  }
  return calldatas;
}

/**
 * @notice Generates the calldata for a Swap with a V3 Route.
 * @param _trade The V3Trade to encode.
 * @param _options SwapOptions to use for the trade.
 * @param _routerMustCustody Flag for whether funds should be sent to the router
 * @param _performAggregatedSlippageCheck Flag for whether we want to perform an aggregated slippage check
 * @returns A string array of calldatas for the trade.
 */
function encodeVaultSwap(_trade, _options, _routerMustCustody, _performAggregatedSlippageCheck) {
  throw new Error('UNSUPPORTED_SWAP_ROUTER_POOL_TYPE');
}

/**
 * @notice Generates the calldata for a Swap with a V3 Route.
 * @param trade The V3Trade to encode.
 * @param options SwapOptions to use for the trade.
 * @param routerMustCustody Flag for whether funds should be sent to the router
 * @param performAggregatedSlippageCheck Flag for whether we want to perform an aggregated slippage check
 * @returns A string array of calldatas for the trade.
 */
function encodeSwap(trade, options, routerMustCustody, performAggregatedSlippageCheck) {
  var calldatas = [];
  var protocol = Trade.tradeProtocol(trade);
  if (protocol == exports.Protocol.V3) {
    for (var _iterator = _createForOfIteratorHelperLoose(encodeV3Swap(trade, options, routerMustCustody, performAggregatedSlippageCheck)), _step; !(_step = _iterator()).done;) {
      var calldata = _step.value;
      calldatas.push(calldata);
    }
  } else if (protocol == exports.Protocol.BAL_STABLE) {
    for (var _iterator2 = _createForOfIteratorHelperLoose(encodeStableSwap()), _step2; !(_step2 = _iterator2()).done;) {
      var _calldata = _step2.value;
      calldatas.push(_calldata);
    }
  } else if (protocol == exports.Protocol.BAL_STABLE_WRAPPER) {
    for (var _iterator3 = _createForOfIteratorHelperLoose(encodeVaultSwap()), _step3; !(_step3 = _iterator3()).done;) {
      var _calldata2 = _step3.value;
      calldatas.push(_calldata2);
    }
  } else {
    throw new Error('UNSUPPORTED_TRADE_PROTOCOL');
  }
  return calldatas;
}
function encodeSwaps(trades, options, isSwapAndAdd) {
  // If dealing with an instance of the aggregated Trade object, unbundle it to individual trade objects.
  if (trades instanceof Trade) {
    !trades.swaps.every(function (swap) {
      return swap.route.protocol == exports.Protocol.V3 || swap.route.protocol == exports.Protocol.V2 || swap.route.protocol == exports.Protocol.BAL_STABLE || swap.route.protocol == exports.Protocol.BAL_STABLE_WRAPPER || swap.route.protocol == exports.Protocol.MIXED;
    }) ?  invariant(false, 'UNSUPPORTED_PROTOCOL')  : void 0;
    var individualTrades = [];
    for (var _iterator4 = _createForOfIteratorHelperLoose(trades.swaps), _step4; !(_step4 = _iterator4()).done;) {
      var _step4$value = _step4.value,
        route = _step4$value.route,
        inputAmount = _step4$value.inputAmount,
        outputAmount = _step4$value.outputAmount;
      if (route.protocol == exports.Protocol.V2) {
        individualTrades.push(new hermesV2Sdk.V2Trade(route, trades.tradeType == hermesV2Sdk.TradeType.EXACT_INPUT ? inputAmount : outputAmount, trades.tradeType));
      } else if (route.protocol == exports.Protocol.V3) {
        individualTrades.push(hermesV2Sdk.Trade.createUncheckedTrade({
          route: route,
          inputAmount: inputAmount,
          outputAmount: outputAmount,
          tradeType: trades.tradeType
        }));
      } else if (route.protocol == exports.Protocol.MIXED) {
        individualTrades.push(
        /// we can change the naming of this function on MixedRouteTrade if needed
        MixedRouteTrade.createUncheckedTrade({
          route: route,
          inputAmount: inputAmount,
          outputAmount: outputAmount,
          tradeType: trades.tradeType
        }));
      } else if (route.protocol == exports.Protocol.BAL_STABLE) {
        individualTrades.push(hermesV2Sdk.Trade.createUncheckedTrade({
          route: route,
          inputAmount: inputAmount,
          outputAmount: outputAmount,
          tradeType: trades.tradeType
        }));
      } else if (route.protocol == exports.Protocol.BAL_STABLE_WRAPPER) {
        individualTrades.push(hermesV2Sdk.Trade.createUncheckedTrade({
          route: route,
          inputAmount: inputAmount,
          outputAmount: outputAmount,
          tradeType: trades.tradeType
        }));
      } else {
        throw new Error('UNSUPPORTED_TRADE_PROTOCOL');
      }
    }
    trades = individualTrades;
  }
  if (!Array.isArray(trades)) {
    trades = [trades];
  }
  var numberOfTrades = trades.reduce(function (numberOfTrades, trade) {
    return numberOfTrades + (trade instanceof hermesV2Sdk.Trade || trade instanceof MixedRouteTrade ? trade.swaps.length : 1);
  }, 0);
  var sampleTrade = trades[0];
  // All trades should have the same starting/ending currency and trade type
  !trades.every(function (trade) {
    return trade.inputAmount.currency.equals(sampleTrade.inputAmount.currency);
  }) ?  invariant(false, 'TOKEN_IN_DIFF')  : void 0;
  !trades.every(function (trade) {
    return trade.outputAmount.currency.equals(sampleTrade.outputAmount.currency);
  }) ?  invariant(false, 'TOKEN_OUT_DIFF')  : void 0;
  !trades.every(function (trade) {
    return trade.tradeType === sampleTrade.tradeType;
  }) ?  invariant(false, 'TRADE_TYPE_DIFF')  : void 0;
  var calldatas = [];
  var inputIsNative = sampleTrade.inputAmount.currency.isNative;
  var outputIsNative = sampleTrade.outputAmount.currency.isNative;
  // flag for whether we want to perform an aggregated slippage check
  //   1. when there are >2 exact input trades. this is only a heuristic,
  //      as it's still more gas-expensive even in this case, but has benefits
  //      in that the reversion probability is lower
  var performAggregatedSlippageCheck = sampleTrade.tradeType === hermesV2Sdk.TradeType.EXACT_INPUT && numberOfTrades > 2;
  // flag for whether funds should be send first to the router
  //   1. when receiving ETH (which much be unwrapped from WETH)
  //   2. when a fee on the output is being taken
  //   3. when performing swap and add
  //   4. when performing an aggregated slippage check
  var routerMustCustody = outputIsNative || !!options.fee || !!isSwapAndAdd || performAggregatedSlippageCheck;
  // encode permit if necessary
  if (options.inputTokenPermit) {
    !sampleTrade.inputAmount.currency.isToken ?  invariant(false, 'NON_TOKEN_PERMIT')  : void 0;
    calldatas.push(hermesV2Sdk.SelfPermit.encodePermit(sampleTrade.inputAmount.currency, options.inputTokenPermit));
  }
  for (var _iterator5 = _createForOfIteratorHelperLoose(trades), _step5; !(_step5 = _iterator5()).done;) {
    var trade = _step5.value;
    if (trade instanceof hermesV2Sdk.V2Trade) {
      calldatas.push(encodeV2Swap(trade, options, routerMustCustody, performAggregatedSlippageCheck));
    } else if (trade instanceof hermesV2Sdk.Trade) {
      for (var _iterator6 = _createForOfIteratorHelperLoose(encodeSwap(trade, options, routerMustCustody, performAggregatedSlippageCheck)), _step6; !(_step6 = _iterator6()).done;) {
        var calldata = _step6.value;
        calldatas.push(calldata);
      }
    } else if (trade instanceof MixedRouteTrade) {
      for (var _iterator7 = _createForOfIteratorHelperLoose(encodeMixedRouteSwap(trade, options, routerMustCustody, performAggregatedSlippageCheck)), _step7; !(_step7 = _iterator7()).done;) {
        var _calldata3 = _step7.value;
        calldatas.push(_calldata3);
      }
    } else {
      throw new Error('Unsupported trade object');
    }
  }
  var ZERO_IN = maiaCoreSdk.CurrencyAmount.fromRawAmount(sampleTrade.inputAmount.currency, 0);
  var ZERO_OUT = maiaCoreSdk.CurrencyAmount.fromRawAmount(sampleTrade.outputAmount.currency, 0);
  var minimumAmountOut = trades.reduce(function (sum, trade) {
    return sum.add(trade.minimumAmountOut(options.slippageTolerance));
  }, ZERO_OUT);
  var quoteAmountOut = trades.reduce(function (sum, trade) {
    return sum.add(trade.outputAmount);
  }, ZERO_OUT);
  var totalAmountIn = trades.reduce(function (sum, trade) {
    return sum.add(trade.maximumAmountIn(options.slippageTolerance));
  }, ZERO_IN);
  return {
    calldatas: calldatas,
    sampleTrade: sampleTrade,
    routerMustCustody: routerMustCustody,
    inputIsNative: inputIsNative,
    outputIsNative: outputIsNative,
    totalAmountIn: totalAmountIn,
    minimumAmountOut: minimumAmountOut,
    quoteAmountOut: quoteAmountOut
  };
}

var REFUND_ETH_PRICE_IMPACT_THRESHOLD = /*#__PURE__*/new maiaCoreSdk.Percent( /*#__PURE__*/JSBI.BigInt(50), /*#__PURE__*/JSBI.BigInt(100));
/**
 * Represents the Uniswap V2 + V3 SwapRouter02, and has static methods for helping execute trades.
 */
var SwapRouter = /*#__PURE__*/function () {
  /**
   * Cannot be constructed.
   */
  function SwapRouter() {}
  /**
   * Produces the on-chain method name to call and the hex encoded parameters to pass as arguments for a given trade.
   * @param trades to produce call parameters for
   * @param options options for the call parameters
   */
  SwapRouter.swapCallParameters = function swapCallParameters(trades, options) {
    var _encodeSwaps = encodeSwaps(trades, options),
      calldatas = _encodeSwaps.calldatas,
      sampleTrade = _encodeSwaps.sampleTrade,
      routerMustCustody = _encodeSwaps.routerMustCustody,
      inputIsNative = _encodeSwaps.inputIsNative,
      outputIsNative = _encodeSwaps.outputIsNative,
      totalAmountIn = _encodeSwaps.totalAmountIn,
      minimumAmountOut = _encodeSwaps.minimumAmountOut;
    // unwrap or sweep
    if (routerMustCustody) {
      if (outputIsNative) {
        calldatas.push(PaymentsExtended.encodeUnwrapWETH9(minimumAmountOut.quotient, options.recipient, options.fee));
      } else {
        calldatas.push(PaymentsExtended.encodeSweepToken(sampleTrade.outputAmount.currency.wrapped, minimumAmountOut.quotient, options.recipient, options.fee));
      }
    }
    // must refund when paying in ETH: either with an uncertain input amount OR if there's a chance of a partial fill.
    // unlike ERC20's, the full ETH value must be sent in the transaction, so the rest must be refunded.
    if (inputIsNative && (sampleTrade.tradeType === hermesV2Sdk.TradeType.EXACT_OUTPUT || SwapRouter.riskOfPartialFill(trades))) {
      calldatas.push(hermesV2Sdk.Payments.encodeRefundETH());
    }
    return {
      calldata: MulticallExtended.encodeMulticall(calldatas, options.deadlineOrPreviousBlockhash),
      value: maiaCoreSdk.toHex(inputIsNative ? totalAmountIn.quotient : maiaCoreSdk.ZERO)
    };
  }
  /**
   * Produces the on-chain method name to call and the hex encoded parameters to pass as arguments for a given trade.
   * @param trades to produce call parameters for
   * @param options options for the call parameters
   */;
  SwapRouter.swapAndAddCallParameters = function swapAndAddCallParameters(trades, options, position, addLiquidityOptions, tokenInApprovalType, tokenOutApprovalType) {
    var _position$amount0$quo, _position$amount, _position$amount1$quo, _position$amount2;
    var _encodeSwaps2 = encodeSwaps(trades, options, true),
      calldatas = _encodeSwaps2.calldatas,
      inputIsNative = _encodeSwaps2.inputIsNative,
      outputIsNative = _encodeSwaps2.outputIsNative,
      sampleTrade = _encodeSwaps2.sampleTrade,
      totalAmountSwapped = _encodeSwaps2.totalAmountIn,
      quoteAmountOut = _encodeSwaps2.quoteAmountOut,
      minimumAmountOut = _encodeSwaps2.minimumAmountOut;
    // encode output token permit if necessary
    if (options.outputTokenPermit) {
      !quoteAmountOut.currency.isToken ?  invariant(false, 'NON_TOKEN_PERMIT_OUTPUT')  : void 0;
      calldatas.push(hermesV2Sdk.SelfPermit.encodePermit(quoteAmountOut.currency, options.outputTokenPermit));
    }
    var chainId = sampleTrade.route.chainId;
    var zeroForOne = position.pool.token0.wrapped.address === totalAmountSwapped.currency.wrapped.address;
    var _SwapRouter$getPositi = SwapRouter.getPositionAmounts(position, zeroForOne),
      positionAmountIn = _SwapRouter$getPositi.positionAmountIn,
      positionAmountOut = _SwapRouter$getPositi.positionAmountOut;
    // if tokens are native they will be converted to WETH9
    var tokenIn = inputIsNative ? hermesV2Sdk.WETH9[chainId] : positionAmountIn.currency.wrapped;
    var tokenOut = outputIsNative ? hermesV2Sdk.WETH9[chainId] : positionAmountOut.currency.wrapped;
    // if swap output does not make up whole outputTokenBalanceDesired, pull in remaining tokens for adding liquidity
    var amountOutRemaining = positionAmountOut.subtract(quoteAmountOut.wrapped);
    if (amountOutRemaining.greaterThan(maiaCoreSdk.CurrencyAmount.fromRawAmount(positionAmountOut.currency, 0))) {
      // if output is native, this means the remaining portion is included as native value in the transaction
      // and must be wrapped. Otherwise, pull in remaining ERC20 token.
      outputIsNative ? calldatas.push(PaymentsExtended.encodeWrapETH(amountOutRemaining.quotient)) : calldatas.push(PaymentsExtended.encodePull(tokenOut, amountOutRemaining.quotient));
    }
    // if input is native, convert to WETH9, else pull ERC20 token
    inputIsNative ? calldatas.push(PaymentsExtended.encodeWrapETH(positionAmountIn.quotient)) : calldatas.push(PaymentsExtended.encodePull(tokenIn, positionAmountIn.quotient));
    // approve token balances to NFTManager
    if (tokenInApprovalType !== exports.ApprovalTypes.NOT_REQUIRED) calldatas.push(ApproveAndCall.encodeApprove(tokenIn, tokenInApprovalType));
    if (tokenOutApprovalType !== exports.ApprovalTypes.NOT_REQUIRED) calldatas.push(ApproveAndCall.encodeApprove(tokenOut, tokenOutApprovalType));
    // represents a position with token amounts resulting from a swap with maximum slippage
    // hence the minimal amount out possible.
    var minimalPosition = hermesV2Sdk.Position.fromAmounts({
      pool: position.pool,
      tickLower: position.tickLower,
      tickUpper: position.tickUpper,
      amount0: zeroForOne ? (_position$amount0$quo = (_position$amount = position.amount0) == null ? void 0 : _position$amount.quotient.toString()) != null ? _position$amount0$quo : '0' : minimumAmountOut.quotient.toString(),
      amount1: zeroForOne ? minimumAmountOut.quotient.toString() : (_position$amount1$quo = (_position$amount2 = position.amount1) == null ? void 0 : _position$amount2.quotient.toString()) != null ? _position$amount1$quo : '0',
      useFullPrecision: false
    });
    // encode NFTManager add liquidity
    calldatas.push(ApproveAndCall.encodeAddLiquidity(position, minimalPosition, addLiquidityOptions, options.slippageTolerance));
    // sweep remaining tokens
    inputIsNative ? calldatas.push(PaymentsExtended.encodeUnwrapWETH9(maiaCoreSdk.ZERO)) : calldatas.push(PaymentsExtended.encodeSweepToken(tokenIn, maiaCoreSdk.ZERO));
    outputIsNative ? calldatas.push(PaymentsExtended.encodeUnwrapWETH9(maiaCoreSdk.ZERO)) : calldatas.push(PaymentsExtended.encodeSweepToken(tokenOut, maiaCoreSdk.ZERO));
    var value;
    if (inputIsNative) {
      value = totalAmountSwapped.wrapped.add(positionAmountIn.wrapped).quotient;
    } else if (outputIsNative) {
      value = amountOutRemaining.quotient;
    } else {
      value = maiaCoreSdk.ZERO;
    }
    return {
      calldata: MulticallExtended.encodeMulticall(calldatas, options.deadlineOrPreviousBlockhash),
      value: value.toString()
    };
  }
  // if price impact is very high, there's a chance of hitting max/min prices resulting in a partial fill of the swap
  ;
  SwapRouter.riskOfPartialFill = function riskOfPartialFill(trades) {
    if (Array.isArray(trades)) {
      return trades.some(function (trade) {
        return SwapRouter.v3TradeWithHighPriceImpact(trade);
      });
    } else {
      return SwapRouter.v3TradeWithHighPriceImpact(trades);
    }
  };
  SwapRouter.v3TradeWithHighPriceImpact = function v3TradeWithHighPriceImpact(trade) {
    return !(trade instanceof hermesV2Sdk.V2Trade) && trade.priceImpact.greaterThan(REFUND_ETH_PRICE_IMPACT_THRESHOLD);
  };
  SwapRouter.getPositionAmounts = function getPositionAmounts(position, zeroForOne) {
    var _position$mintAmounts;
    var _ref = (_position$mintAmounts = position.mintAmounts) != null ? _position$mintAmounts : {
        amount0: 0,
        amount1: 0
      },
      amount0 = _ref.amount0,
      amount1 = _ref.amount1;
    var currencyAmount0 = maiaCoreSdk.CurrencyAmount.fromRawAmount(position.pool.token0, amount0);
    var currencyAmount1 = maiaCoreSdk.CurrencyAmount.fromRawAmount(position.pool.token1, amount1);
    var _ref2 = zeroForOne ? [currencyAmount0, currencyAmount1] : [currencyAmount1, currencyAmount0],
      positionAmountIn = _ref2[0],
      positionAmountOut = _ref2[1];
    return {
      positionAmountIn: positionAmountIn,
      positionAmountOut: positionAmountOut
    };
  };
  return SwapRouter;
}();
SwapRouter.INTERFACE = SWAP_ROUTER_INTERFACE;

exports.ADDRESS_THIS = ADDRESS_THIS;
exports.ApproveAndCall = ApproveAndCall;
exports.MSG_SENDER = MSG_SENDER;
exports.MixedRoute = MixedRoute;
exports.MixedRouteSDK = MixedRouteSDK;
exports.MixedRouteTrade = MixedRouteTrade;
exports.MulticallExtended = MulticallExtended;
exports.ONE_HUNDRED_PERCENT = ONE_HUNDRED_PERCENT;
exports.PaymentsExtended = PaymentsExtended;
exports.RouteStable = RouteStable;
exports.RouteStableWrapper = RouteStableWrapper;
exports.RouteV2 = RouteV2;
exports.RouteV3 = RouteV3;
exports.SwapRouter = SwapRouter;
exports.Trade = Trade;
exports.UNIVERSAL_ROUTER_ADDRESS = UNIVERSAL_ROUTER_ADDRESS;
exports.V2_FEE_PATH_PLACEHOLDER = V2_FEE_PATH_PLACEHOLDER;
exports.ZERO_PERCENT = ZERO_PERCENT;
exports.encodeMixedRouteToPath = encodeMixedRouteToPath;
exports.getOutputOfPools = getOutputOfPools;
exports.getTypeGroup = getTypeGroup;
exports.isMint = isMint;
exports.partitionMixedRouteByProtocol = partitionMixedRouteByProtocol;
exports.partitionPoolsByProtocol = partitionPoolsByProtocol;
exports.tradeComparator = tradeComparator;
//# sourceMappingURL=hermes-swap-router-sdk.cjs.development.js.map