bestTradeExactOut static method
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;
}