bestTradeExactOut static method

List<Trade> bestTradeExactOut(
  1. List<Pair> pairs,
  2. Currency currencyIn,
  3. CurrencyAmount currencyAmountOut, {
  4. int maxNumResults = 3,
  5. int maxHops = 3,
  6. List<Pair>? currentPairs,
  7. CurrencyAmount? originalAmountOut,
  8. List<Trade>? bestTrades,
})

similar to the above method but instead targets a fixed output amount given a list of pairs, and a fixed amount out, returns the top maxNumResults trades that go from an input token to an output token amount, 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 pairs the pairs to consider in finding the best trade @param currencyIn the currency to spend @param currencyAmountOut the exact amount of 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 pair @param currentPairs used in recursion; the current list of pairs @param originalAmountOut used in recursion; the original value of the currencyAmountOut parameter @param bestTrades used in recursion; the current list of best trades

Implementation

static List<Trade> bestTradeExactOut(
  List<Pair> pairs,
  Currency currencyIn,
  CurrencyAmount currencyAmountOut, {
  int maxNumResults = 3,
  int maxHops = 3,
  List<Pair>? currentPairs,
  CurrencyAmount? originalAmountOut,
  List<Trade>? bestTrades,
}) {
  originalAmountOut ??= currencyAmountOut;
  currentPairs ??= <Pair>[];
  bestTrades ??= <Trade>[];

  invariant(pairs.isNotEmpty, 'PAIRS');
  invariant(maxHops > 0, 'MAX_HOPS');
  invariant(originalAmountOut == currencyAmountOut || currentPairs.isNotEmpty,
      'INVALID_RECURSION');

  var chainId = currencyAmountOut is TokenAmount
      ? currencyAmountOut.token.chainId
      : currencyIn is Token
          ? currencyIn.chainId
          : null;

  invariant(chainId != null, 'CHAIN_ID');

  var amountOut = wrappedAmount(currencyAmountOut, chainId!.chain);
  var tokenIn = wrappedCurrency(currencyIn, chainId);

  for (var i = 0; i < pairs.length; i++) {
    final pair = pairs[i];

    // pair irrelevant
    if (pair.token0 != amountOut.token && pair.token1 != amountOut.token) {
      continue;
    }

    if (pair.reserve0.raw == BigInt.zero || pair.reserve1.raw == BigInt.zero) {
      continue;
    }

    late TokenAmount amountIn;
    try {
      amountIn = pair.getInputAmount(amountOut)[0];

      // input too low
    } on InsufficientReservesError catch (_) {
      continue;
    } catch (e) {
      rethrow;
    }

    // we have arrived at the output token, so this is the final trade of one of the paths
    if (amountIn.token == tokenIn) {
      sortedInsertTrade(
        bestTrades,
        Trade(
          Route([pair, ...currentPairs], currencyIn,
              output: originalAmountOut.currency),
          originalAmountOut,
          TradeType.EXACT_OUTPUT,
        ),
        maxNumResults,
      );
    } else if (maxHops > 1 && pairs.length > 1) {
      final dataLastPair = pairs.sublist(i + 1, pairs.length);
      final pairsExcludingThisPair = pairs.sublist(0, i) + dataLastPair;
      // otherwise, consider all the other paths that lead from this token as long as we have not exceeded maxHops
      Trade.bestTradeExactOut(
        pairsExcludingThisPair,
        currencyIn,
        amountIn,
        maxNumResults: maxNumResults,
        maxHops: maxHops - 1,
        currentPairs: [pair, ...currentPairs],
        originalAmountOut: originalAmountOut,
        bestTrades: bestTrades,
      );
    }
  }
  return bestTrades;
}