@razorlabs/amm-sdk
Version:
🛠An SDK for building applications on top of RazorDEX.
744 lines (738 loc) • 30.3 kB
JavaScript
'use strict';
var tsSdk = require('@aptos-labs/ts-sdk');
var swapSdkCore = require('@razorlabs/swap-sdk-core');
var invariant5 = require('tiny-invariant');
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
var invariant5__default = /*#__PURE__*/_interopDefault(invariant5);
// src/constants.ts
var ZERO_PERCENT = new swapSdkCore.Percent("0");
var ONE_HUNDRED_PERCENT = new swapSdkCore.Percent("1");
var AMM_MODULE_ADDRESS = "0xc4e68f29fa608d2630d11513c8de731b09a975f2f75ea945160491b9bfd36992";
var AMM_SIGNER = "0xf317511756cb5bd755a7456ad900974645788926742b39d81771e17cd54b1c80";
var FACTORY_ADDRESS = `${AMM_MODULE_ADDRESS}::amm_factory`;
var FACTORY_ADDRESS_MAP = {
[swapSdkCore.ChainId.BARDOCK_TESTNET]: FACTORY_ADDRESS,
[swapSdkCore.ChainId.MAINNET]: FACTORY_ADDRESS
};
var WMOVE = {
[swapSdkCore.ChainId.BARDOCK_TESTNET]: new swapSdkCore.Token(
swapSdkCore.ChainId.BARDOCK_TESTNET,
tsSdk.AccountAddress.A.toStringLong(),
8,
"MOVE",
"Move Coin",
"https://movementlabs.xyz"
),
[swapSdkCore.ChainId.MAINNET]: new swapSdkCore.Token(
swapSdkCore.ChainId.MAINNET,
tsSdk.AccountAddress.A.toStringLong(),
8,
"MOVE",
"Move Coin",
"https://movementlabs.xyz"
)
};
var WNATIVE = {
[swapSdkCore.ChainId.BARDOCK_TESTNET]: WMOVE[swapSdkCore.ChainId.BARDOCK_TESTNET],
[swapSdkCore.ChainId.MAINNET]: WMOVE[swapSdkCore.ChainId.MAINNET]
};
var NATIVE = {
[swapSdkCore.ChainId.BARDOCK_TESTNET]: {
name: "Move Coin",
symbol: "MOVE",
decimals: 8
},
[swapSdkCore.ChainId.MAINNET]: {
name: "Move Coin",
symbol: "MOVE",
decimals: 8
}
};
// src/trade.ts
function isTradeBetter(tradeA, tradeB, minimumDelta = ZERO_PERCENT) {
if (tradeA && !tradeB) return false;
if (tradeB && !tradeA) return true;
if (!tradeA || !tradeB) return void 0;
if (tradeA.tradeType !== tradeB.tradeType || !tradeA.inputAmount.currency.equals(tradeB.inputAmount.currency) || !tradeA.outputAmount.currency.equals(tradeB.outputAmount.currency)) {
throw new Error("Trades are not comparable");
}
if (minimumDelta.equalTo(ZERO_PERCENT)) {
return tradeA.executionPrice.lessThan(tradeB.executionPrice);
}
return tradeA.executionPrice.asFraction.multiply(minimumDelta.add(ONE_HUNDRED_PERCENT)).lessThan(tradeB.executionPrice);
}
var _Move = class _Move extends swapSdkCore.NativeCurrency {
constructor(chainId) {
super(chainId, 8, "MOVE", "Move Coin");
}
get wrapped() {
const wmove = WMOVE[this.chainId];
invariant5__default.default(!!wmove, "WRAPPED");
return wmove;
}
static onChain(chainId) {
if (!this._moveCache[chainId]) {
this._moveCache[chainId] = new _Move(chainId);
}
return this._moveCache[chainId];
}
equals(other) {
return other.isNative && other.chainId === this.chainId;
}
};
_Move._moveCache = {};
var Move = _Move;
var getPairSeed = (token0, token1) => {
const validatedToken0Address = tsSdk.AccountAddress.from(token0.address).toStringLong();
const validatedToken1Address = tsSdk.AccountAddress.from(token1.address).toStringLong();
if (token0.sortsBefore(token1)) {
return validatedToken0Address + validatedToken1Address.slice(2);
} else {
return validatedToken1Address + validatedToken0Address.slice(2);
}
};
var computePairAddress = ({ tokenA, tokenB }) => {
const rawSeed = getPairSeed(tokenA, tokenB);
const seed = tsSdk.Hex.fromHexInput(rawSeed).toUint8Array();
const account = tsSdk.AccountAddress.from(AMM_SIGNER);
const address = tsSdk.createObjectAddress(account, seed);
return address.toString();
};
var Pair = class _Pair {
static getAddress(tokenA, tokenB) {
return computePairAddress({ tokenA, tokenB });
}
constructor(currencyAmountA, tokenAmountB) {
const tokenAmounts = currencyAmountA.currency.sortsBefore(tokenAmountB.currency) ? [currencyAmountA, tokenAmountB] : [tokenAmountB, currencyAmountA];
this.liquidityToken = new swapSdkCore.Token(
tokenAmounts[0].currency.chainId,
_Pair.getAddress(tokenAmounts[0].currency, tokenAmounts[1].currency),
8,
"RAZOR LP",
`Razor ${tokenAmounts[0].currency.symbol}-${tokenAmounts[1].currency.symbol} LP`
);
this.tokenAmounts = tokenAmounts;
}
/**
* Returns true if the token is either token0 or token1
* @param token to check
*/
involvesToken(token) {
return token.equals(this.token0) || token.equals(this.token1);
}
/**
* Returns the current mid price of the pair in terms of token0, i.e. the ratio of reserve1 to reserve0
*/
get token0Price() {
const result = this.tokenAmounts[1].divide(this.tokenAmounts[0]);
return new swapSdkCore.Price(this.token0, this.token1, result.denominator, result.numerator);
}
/**
* Returns the current mid price of the pair in terms of token1, i.e. the ratio of reserve0 to reserve1
*/
get token1Price() {
const result = this.tokenAmounts[0].divide(this.tokenAmounts[1]);
return new swapSdkCore.Price(this.token1, this.token0, result.denominator, result.numerator);
}
/**
* Return the price of the given token in terms of the other token in the pair.
* @param token token to return price of
*/
priceOf(token) {
invariant5__default.default(this.involvesToken(token), "TOKEN");
return token.equals(this.token0) ? this.token0Price : this.token1Price;
}
/**
* Returns the chain ID of the tokens in the pair.
*/
get chainId() {
return this.token0.chainId;
}
get token0() {
return this.tokenAmounts[0].currency;
}
get token1() {
return this.tokenAmounts[1].currency;
}
get reserve0() {
return this.tokenAmounts[0];
}
get reserve1() {
return this.tokenAmounts[1];
}
reserveOf(token) {
invariant5__default.default(this.involvesToken(token), "TOKEN");
return token.equals(this.token0) ? this.reserve0 : this.reserve1;
}
getOutputAmount(inputAmount) {
invariant5__default.default(this.involvesToken(inputAmount.currency), "TOKEN");
if (this.reserve0.quotient === swapSdkCore.ZERO || this.reserve1.quotient === swapSdkCore.ZERO) {
throw new swapSdkCore.InsufficientReservesError();
}
const inputReserve = this.reserveOf(inputAmount.currency);
const outputReserve = this.reserveOf(inputAmount.currency.equals(this.token0) ? this.token1 : this.token0);
const inputAmountWithFee = inputAmount.quotient * swapSdkCore._9975;
const numerator = inputAmountWithFee * outputReserve.quotient;
const denominator = inputReserve.quotient * swapSdkCore._10000 + inputAmountWithFee;
const outputAmount = swapSdkCore.CurrencyAmount.fromRawAmount(
inputAmount.currency.equals(this.token0) ? this.token1 : this.token0,
numerator / denominator
);
if (outputAmount.quotient === swapSdkCore.ZERO) {
throw new swapSdkCore.InsufficientInputAmountError();
}
return [outputAmount, new _Pair(inputReserve.add(inputAmount), outputReserve.subtract(outputAmount))];
}
getInputAmount(outputAmount) {
invariant5__default.default(this.involvesToken(outputAmount.currency), "TOKEN");
if (this.reserve0.quotient === swapSdkCore.ZERO || this.reserve1.quotient === swapSdkCore.ZERO || outputAmount.quotient >= this.reserveOf(outputAmount.currency).quotient) {
throw new swapSdkCore.InsufficientReservesError();
}
const outputReserve = this.reserveOf(outputAmount.currency);
const inputReserve = this.reserveOf(outputAmount.currency.equals(this.token0) ? this.token1 : this.token0);
const numerator = inputReserve.quotient * outputAmount.quotient * swapSdkCore._10000;
const denominator = outputReserve.quotient - outputAmount.quotient * swapSdkCore._9975;
const inputAmount = swapSdkCore.CurrencyAmount.fromRawAmount(
outputAmount.currency.equals(this.token0) ? this.token1 : this.token0,
numerator / denominator + swapSdkCore.ONE
);
return [inputAmount, new _Pair(inputReserve.add(inputAmount), outputReserve.subtract(outputAmount))];
}
getLiquidityMinted(totalSupply, tokenAmountA, tokenAmountB) {
invariant5__default.default(totalSupply.currency.equals(this.liquidityToken), "LIQUIDITY");
const tokenAmounts = tokenAmountA.currency.sortsBefore(tokenAmountB.currency) ? [tokenAmountA, tokenAmountB] : [tokenAmountB, tokenAmountA];
invariant5__default.default(tokenAmounts[0].currency.equals(this.token0) && tokenAmounts[1].currency.equals(this.token1), "TOKEN");
let liquidity;
if (totalSupply.quotient === swapSdkCore.ZERO) {
liquidity = swapSdkCore.sqrt(tokenAmounts[0].quotient * tokenAmounts[1].quotient) - swapSdkCore.MINIMUM_LIQUIDITY;
} else {
const amount0 = tokenAmounts[0].quotient * totalSupply.quotient / this.reserve0.quotient;
const amount1 = tokenAmounts[1].quotient * totalSupply.quotient / this.reserve1.quotient;
liquidity = amount0 <= amount1 ? amount0 : amount1;
}
if (!(liquidity > swapSdkCore.ZERO)) {
throw new swapSdkCore.InsufficientInputAmountError();
}
return swapSdkCore.CurrencyAmount.fromRawAmount(this.liquidityToken, liquidity);
}
getLiquidityValue(token, totalSupply, liquidity, feeOn = false, kLast) {
invariant5__default.default(this.involvesToken(token), "TOKEN");
invariant5__default.default(totalSupply.currency.equals(this.liquidityToken), "TOTAL_SUPPLY");
invariant5__default.default(liquidity.currency.equals(this.liquidityToken), "LIQUIDITY");
invariant5__default.default(liquidity.quotient <= totalSupply.quotient, "LIQUIDITY");
let totalSupplyAdjusted;
if (!feeOn) {
totalSupplyAdjusted = totalSupply;
} else {
invariant5__default.default(!!kLast, "K_LAST");
const kLastParsed = BigInt(kLast);
if (!(kLastParsed === swapSdkCore.ZERO)) {
const rootK = swapSdkCore.sqrt(this.reserve0.quotient * this.reserve1.quotient);
const rootKLast = swapSdkCore.sqrt(kLastParsed);
if (rootK > rootKLast) {
const numerator = totalSupply.quotient * (rootK - rootKLast);
const denominator = rootK * swapSdkCore.FIVE + rootKLast;
const feeLiquidity = numerator / denominator;
totalSupplyAdjusted = totalSupply.add(swapSdkCore.CurrencyAmount.fromRawAmount(this.liquidityToken, feeLiquidity));
} else {
totalSupplyAdjusted = totalSupply;
}
} else {
totalSupplyAdjusted = totalSupply;
}
}
if (totalSupply.equalTo(0)) {
return swapSdkCore.CurrencyAmount.fromRawAmount(token, 0);
}
return swapSdkCore.CurrencyAmount.fromRawAmount(
token,
liquidity.quotient * this.reserveOf(token).quotient / totalSupplyAdjusted.quotient
);
}
};
function getNetworkRPCUrl(chainId) {
switch (chainId) {
case swapSdkCore.ChainId.BARDOCK_TESTNET:
return "https://rpc.sentio.xyz/movement-testnet/v1";
default:
return "https://rpc.sentio.xyz/movement/v1";
}
}
function getNetworkIndexerUrl(chainId) {
switch (chainId) {
case swapSdkCore.ChainId.BARDOCK_TESTNET:
return "https://rpc.sentio.xyz/movement-testnet-indexer/v1/graphql";
default:
return "https://rpc.sentio.xyz/movement-indexer/v1/graphql";
}
}
function getDefaultProvider(chainId) {
const config = new tsSdk.AptosConfig({
fullnode: getNetworkRPCUrl(chainId),
indexer: getNetworkIndexerUrl(chainId)
});
const provider = new tsSdk.Aptos(config);
return provider;
}
var getReserves = (pairAddress) => {
return {
typeArguments: [],
functionArguments: [pairAddress],
function: `${AMM_MODULE_ADDRESS}::amm_pair::get_reserves`
};
};
var TOKEN_DECIMALS_CACHE = {
[swapSdkCore.ChainId.BARDOCK_TESTNET]: {}
};
var Fetcher = class {
/**
* Cannot be constructed.
*/
constructor() {
}
/**
* Fetch information for a given token on the given chain, using the given provider.
* @param chainId chain of the token
* @param address address of the token on the chain
* @param provider provider used to fetch the token
* @param symbol optional symbol of the token
* @param name optional name of the token
*/
static async fetchTokenData(chainId, address, provider = getDefaultProvider(chainId), symbol, name) {
const parsedDecimals = typeof TOKEN_DECIMALS_CACHE?.[chainId]?.[address] === "number" ? TOKEN_DECIMALS_CACHE[chainId][address] : await provider.fungibleAsset.getFungibleAssetMetadataByAssetType({ assetType: address }).then(({ decimals }) => {
TOKEN_DECIMALS_CACHE = {
...TOKEN_DECIMALS_CACHE,
[chainId]: {
...TOKEN_DECIMALS_CACHE?.[chainId],
[address]: decimals
}
};
return decimals;
});
return new swapSdkCore.Token(chainId, address, parsedDecimals, symbol, name);
}
/**
* Fetches information about a pair and constructs a pair from the given two tokens.
* @param tokenA first token
* @param tokenB second token
* @param provider the provider to use to fetch the data
*/
static async fetchPairData(tokenA, tokenB, provider = getDefaultProvider(tokenA.chainId)) {
invariant5__default.default(tokenA.chainId === tokenB.chainId, "CHAIN_ID");
const address = Pair.getAddress(tokenA, tokenB);
const [reserves0, reserves1, _] = await provider.view({
payload: getReserves(address)
});
const balances = tokenA.sortsBefore(tokenB) ? [reserves0, reserves1] : [reserves1, reserves0];
return new Pair(
swapSdkCore.CurrencyAmount.fromRawAmount(tokenA, balances[0]),
swapSdkCore.CurrencyAmount.fromRawAmount(tokenB, balances[1])
);
}
};
var Route = class {
constructor(pairs, input, output) {
this._midPrice = null;
invariant5__default.default(pairs.length > 0, "PAIRS");
const chainId = pairs[0].chainId;
invariant5__default.default(
pairs.every((pair) => pair.chainId === chainId),
"CHAIN_IDS"
);
const wrappedInput = input.wrapped;
invariant5__default.default(pairs[0].involvesToken(wrappedInput), "INPUT");
invariant5__default.default(typeof output === "undefined" || pairs[pairs.length - 1].involvesToken(output.wrapped), "OUTPUT");
const path = [wrappedInput];
for (const [i, pair] of pairs.entries()) {
const currentInput = path[i];
invariant5__default.default(currentInput.equals(pair.token0) || currentInput.equals(pair.token1), "PATH");
const output2 = currentInput.equals(pair.token0) ? pair.token1 : pair.token0;
path.push(output2);
}
this.pairs = pairs;
this.path = path;
this.input = input;
this.output = output;
}
get midPrice() {
if (this._midPrice !== null) return this._midPrice;
const prices = [];
for (const [i, pair] of this.pairs.entries()) {
prices.push(
this.path[i].equals(pair.token0) ? new swapSdkCore.Price(pair.reserve0.currency, pair.reserve1.currency, pair.reserve0.quotient, pair.reserve1.quotient) : new swapSdkCore.Price(pair.reserve1.currency, pair.reserve0.currency, pair.reserve1.quotient, pair.reserve0.quotient)
);
}
const reduced = prices.slice(1).reduce((accumulator, currentValue) => accumulator.multiply(currentValue), prices[0]);
return this._midPrice = new swapSdkCore.Price(this.input, this.output, reduced.denominator, reduced.numerator);
}
get chainId() {
return this.pairs[0].chainId;
}
};
function inputOutputComparator(a, b) {
invariant5__default.default(a.inputAmount.currency.equals(b.inputAmount.currency), "INPUT_CURRENCY");
invariant5__default.default(a.outputAmount.currency.equals(b.outputAmount.currency), "OUTPUT_CURRENCY");
if (a.outputAmount.equalTo(b.outputAmount)) {
if (a.inputAmount.equalTo(b.inputAmount)) {
return 0;
}
if (a.inputAmount.lessThan(b.inputAmount)) {
return -1;
}
return 1;
}
if (a.outputAmount.lessThan(b.outputAmount)) {
return 1;
}
return -1;
}
function tradeComparator(a, b) {
const ioComp = inputOutputComparator(a, b);
if (ioComp !== 0) {
return ioComp;
}
if (a.priceImpact.lessThan(b.priceImpact)) {
return -1;
}
if (a.priceImpact.greaterThan(b.priceImpact)) {
return 1;
}
return a.route.path.length - b.route.path.length;
}
var Trade = class _Trade {
/**
* Constructs an exact in trade with the given amount in and route
* @param route route of the exact in trade
* @param amountIn the amount being passed in
*/
static exactIn(route, amountIn) {
return new _Trade(route, amountIn, swapSdkCore.TradeType.EXACT_INPUT);
}
/**
* Constructs an exact out trade with the given amount out and route
* @param route route of the exact out trade
* @param amountOut the amount returned by the trade
*/
static exactOut(route, amountOut) {
return new _Trade(route, amountOut, swapSdkCore.TradeType.EXACT_OUTPUT);
}
constructor(route, amount, tradeType) {
this.route = route;
this.tradeType = tradeType;
const tokenAmounts = new Array(route.path.length);
if (tradeType === swapSdkCore.TradeType.EXACT_INPUT) {
invariant5__default.default(amount.currency.equals(route.input), "INPUT");
tokenAmounts[0] = amount.wrapped;
for (let i = 0; i < route.path.length - 1; i++) {
const pair = route.pairs[i];
const [outputAmount] = pair.getOutputAmount(tokenAmounts[i]);
tokenAmounts[i + 1] = outputAmount;
}
this.inputAmount = swapSdkCore.CurrencyAmount.fromFractionalAmount(route.input, amount.numerator, amount.denominator);
this.outputAmount = swapSdkCore.CurrencyAmount.fromFractionalAmount(
route.output,
tokenAmounts[tokenAmounts.length - 1].numerator,
tokenAmounts[tokenAmounts.length - 1].denominator
);
} else {
invariant5__default.default(amount.currency.equals(route.output), "OUTPUT");
tokenAmounts[tokenAmounts.length - 1] = amount.wrapped;
for (let i = route.path.length - 1; i > 0; i--) {
const pair = route.pairs[i - 1];
const [inputAmount] = pair.getInputAmount(tokenAmounts[i]);
tokenAmounts[i - 1] = inputAmount;
}
this.inputAmount = swapSdkCore.CurrencyAmount.fromFractionalAmount(
route.input,
tokenAmounts[0].numerator,
tokenAmounts[0].denominator
);
this.outputAmount = swapSdkCore.CurrencyAmount.fromFractionalAmount(route.output, amount.numerator, amount.denominator);
}
this.executionPrice = new swapSdkCore.Price(
this.inputAmount.currency,
this.outputAmount.currency,
this.inputAmount.quotient,
this.outputAmount.quotient
);
this.priceImpact = swapSdkCore.computePriceImpact(route.midPrice, this.inputAmount, this.outputAmount);
}
/**
* Get the minimum amount that must be received from this trade for the given slippage tolerance
* @param slippageTolerance tolerance of unfavorable slippage from the execution price of this trade
*/
minimumAmountOut(slippageTolerance) {
invariant5__default.default(!slippageTolerance.lessThan(swapSdkCore.ZERO), "SLIPPAGE_TOLERANCE");
if (this.tradeType === swapSdkCore.TradeType.EXACT_OUTPUT) {
return this.outputAmount;
}
const slippageAdjustedAmountOut = new swapSdkCore.Fraction(swapSdkCore.ONE).add(slippageTolerance).invert().multiply(this.outputAmount.quotient).quotient;
return swapSdkCore.CurrencyAmount.fromRawAmount(this.outputAmount.currency, slippageAdjustedAmountOut);
}
/**
* Get the maximum amount in that can be spent via this trade for the given slippage tolerance
* @param slippageTolerance tolerance of unfavorable slippage from the execution price of this trade
*/
maximumAmountIn(slippageTolerance) {
invariant5__default.default(!slippageTolerance.lessThan(swapSdkCore.ZERO), "SLIPPAGE_TOLERANCE");
if (this.tradeType === swapSdkCore.TradeType.EXACT_INPUT) {
return this.inputAmount;
}
const slippageAdjustedAmountIn = new swapSdkCore.Fraction(swapSdkCore.ONE).add(slippageTolerance).multiply(this.inputAmount.quotient).quotient;
return swapSdkCore.CurrencyAmount.fromRawAmount(this.inputAmount.currency, slippageAdjustedAmountIn);
}
/**
* Given a list of pairs, 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 pairs the pairs 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 pair
* @param currentPairs used in recursion; the current list of pairs
* @param currencyAmountIn used in recursion; the original value of the currencyAmountIn parameter
* @param bestTrades used in recursion; the current list of best trades
*/
static bestTradeExactIn(pairs, currencyAmountIn, currencyOut, { maxNumResults = 3, maxHops = 3 } = {}, currentPairs = [], nextAmountIn = currencyAmountIn, bestTrades = []) {
invariant5__default.default(pairs.length > 0, "PAIRS");
invariant5__default.default(maxHops > 0, "MAX_HOPS");
invariant5__default.default(currencyAmountIn === nextAmountIn || currentPairs.length > 0, "INVALID_RECURSION");
const amountIn = nextAmountIn.wrapped;
const tokenOut = currencyOut.wrapped;
for (let i = 0; i < pairs.length; i++) {
const pair = pairs[i];
if (!pair.token0.equals(amountIn.currency) && !pair.token1.equals(amountIn.currency)) continue;
if (pair.reserve0.equalTo(swapSdkCore.ZERO) || pair.reserve1.equalTo(swapSdkCore.ZERO)) continue;
let amountOut;
try {
;
[amountOut] = pair.getOutputAmount(amountIn);
} catch (error) {
if (error.isInsufficientInputAmountError) {
continue;
}
throw error;
}
if (amountOut.currency.equals(tokenOut)) {
swapSdkCore.sortedInsert(
bestTrades,
new _Trade(
new Route([...currentPairs, pair], currencyAmountIn.currency, currencyOut),
currencyAmountIn,
swapSdkCore.TradeType.EXACT_INPUT
),
maxNumResults,
tradeComparator
);
} else if (maxHops > 1 && pairs.length > 1) {
const pairsExcludingThisPair = pairs.slice(0, i).concat(pairs.slice(i + 1, pairs.length));
_Trade.bestTradeExactIn(
pairsExcludingThisPair,
currencyAmountIn,
currencyOut,
{
maxNumResults,
maxHops: maxHops - 1
},
[...currentPairs, pair],
amountOut,
bestTrades
);
}
}
return bestTrades;
}
/**
* Return the execution price after accounting for slippage tolerance
* @param slippageTolerance the allowed tolerated slippage
*/
worstExecutionPrice(slippageTolerance) {
return new swapSdkCore.Price(
this.inputAmount.currency,
this.outputAmount.currency,
this.maximumAmountIn(slippageTolerance).quotient,
this.minimumAmountOut(slippageTolerance).quotient
);
}
/**
* 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 nextAmountOut 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 currencyAmountOut used in recursion; the original value of the currencyAmountOut parameter
* @param bestTrades used in recursion; the current list of best trades
*/
static bestTradeExactOut(pairs, currencyIn, currencyAmountOut, { maxNumResults = 3, maxHops = 3 } = {}, currentPairs = [], nextAmountOut = currencyAmountOut, bestTrades = []) {
invariant5__default.default(pairs.length > 0, "PAIRS");
invariant5__default.default(maxHops > 0, "MAX_HOPS");
invariant5__default.default(currencyAmountOut === nextAmountOut || currentPairs.length > 0, "INVALID_RECURSION");
const amountOut = nextAmountOut.wrapped;
const tokenIn = currencyIn.wrapped;
for (let i = 0; i < pairs.length; i++) {
const pair = pairs[i];
if (!pair.token0.equals(amountOut.currency) && !pair.token1.equals(amountOut.currency)) continue;
if (pair.reserve0.equalTo(swapSdkCore.ZERO) || pair.reserve1.equalTo(swapSdkCore.ZERO)) continue;
let amountIn;
try {
;
[amountIn] = pair.getInputAmount(amountOut);
} catch (error) {
if (error.isInsufficientReservesError) {
continue;
}
throw error;
}
if (amountIn.currency.equals(tokenIn)) {
swapSdkCore.sortedInsert(
bestTrades,
new _Trade(
new Route([pair, ...currentPairs], currencyIn, currencyAmountOut.currency),
currencyAmountOut,
swapSdkCore.TradeType.EXACT_OUTPUT
),
maxNumResults,
tradeComparator
);
} else if (maxHops > 1 && pairs.length > 1) {
const pairsExcludingThisPair = pairs.slice(0, i).concat(pairs.slice(i + 1, pairs.length));
_Trade.bestTradeExactOut(
pairsExcludingThisPair,
currencyIn,
currencyAmountOut,
{
maxNumResults,
maxHops: maxHops - 1
},
[pair, ...currentPairs],
amountIn,
bestTrades
);
}
}
return bestTrades;
}
};
var _Native = class _Native extends swapSdkCore.NativeCurrency {
constructor({
chainId,
decimals,
name,
symbol
}) {
super(chainId, decimals, symbol, name);
}
get wrapped() {
const wnative = WNATIVE[this.chainId];
invariant5__default.default(!!wnative, "WRAPPED");
return wnative;
}
static onChain(chainId) {
if (chainId in this.cache) {
return this.cache[chainId];
}
invariant5__default.default(!!NATIVE[chainId], "NATIVE_CURRENCY");
const { decimals, name, symbol } = NATIVE[chainId];
return this.cache[chainId] = new _Native({ chainId, decimals, symbol, name });
}
equals(other) {
return other.isNative && other.chainId === this.chainId;
}
};
_Native.cache = {};
var Native = _Native;
var Router = class {
/**
* Cannot be constructed.
*/
constructor() {
}
/**
* Produces the on-chain method name to call and the hex encoded parameters to pass as arguments for a given trade.
* @param trade to produce call parameters for
* @param options options for the call parameters
*/
static swapCallParameters(trade, options) {
const moveIn = trade.inputAmount.currency.isNative;
const moveOut = trade.outputAmount.currency.isNative;
invariant5__default.default(!(moveIn && moveOut), "MOVE_IN_OUT");
invariant5__default.default(!("ttl" in options) || options.ttl > 0, "TTL");
const to = options.recipient;
const amountIn = Number(trade.maximumAmountIn(options.allowedSlippage).quotient.toString());
const amountOut = Number(trade.minimumAmountOut(options.allowedSlippage).quotient.toString());
const path = trade.route.path.map((token) => token.address);
const deadline = "ttl" in options ? Math.floor((/* @__PURE__ */ new Date()).getTime() / 1e3) + options.ttl : options.deadline;
let methodName;
let args;
switch (trade.tradeType) {
case swapSdkCore.TradeType.EXACT_INPUT:
if (moveIn) {
methodName = "swap_exact_move_for_tokens";
args = [amountIn, amountOut, path, to, deadline];
} else if (moveOut) {
methodName = "swap_exact_tokens_for_move";
args = [amountIn, amountOut, path, to, deadline];
} else {
methodName = "swap_exact_tokens_for_tokens";
args = [amountIn, amountOut, path, to, deadline];
}
break;
case swapSdkCore.TradeType.EXACT_OUTPUT:
if (moveIn) {
methodName = "swap_move_for_exact_tokens";
args = [amountIn, amountOut, path, to, deadline];
} else if (moveOut) {
methodName = "swap_tokens_for_exact_move";
args = [amountOut, amountIn, path, to, deadline];
} else {
methodName = "swap_tokens_for_exact_tokens";
args = [amountOut, amountIn, path, to, deadline];
}
break;
}
return {
methodName,
args
};
}
};
exports.AMM_MODULE_ADDRESS = AMM_MODULE_ADDRESS;
exports.AMM_SIGNER = AMM_SIGNER;
exports.FACTORY_ADDRESS = FACTORY_ADDRESS;
exports.FACTORY_ADDRESS_MAP = FACTORY_ADDRESS_MAP;
exports.Fetcher = Fetcher;
exports.Move = Move;
exports.NATIVE = NATIVE;
exports.Native = Native;
exports.ONE_HUNDRED_PERCENT = ONE_HUNDRED_PERCENT;
exports.Pair = Pair;
exports.Route = Route;
exports.Router = Router;
exports.Trade = Trade;
exports.WMOVE = WMOVE;
exports.WNATIVE = WNATIVE;
exports.ZERO_PERCENT = ZERO_PERCENT;
exports.computePairAddress = computePairAddress;
exports.getDefaultProvider = getDefaultProvider;
exports.getNetworkIndexerUrl = getNetworkIndexerUrl;
exports.getNetworkRPCUrl = getNetworkRPCUrl;
exports.getReserves = getReserves;
exports.inputOutputComparator = inputOutputComparator;
exports.isTradeBetter = isTradeBetter;
exports.tradeComparator = tradeComparator;
Object.keys(swapSdkCore).forEach(function (k) {
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
enumerable: true,
get: function () { return swapSdkCore[k]; }
});
});
//# sourceMappingURL=index.js.map
//# sourceMappingURL=index.js.map