@pancakeswap/v2-sdk
Version:
An SDK for Pancakeswap V2
1,433 lines (1,427 loc) • 44.5 kB
JavaScript
'use strict';
var chains = require('@pancakeswap/chains');
var swapSdkCore = require('@pancakeswap/swap-sdk-core');
var swapSdkEvm = require('@pancakeswap/swap-sdk-evm');
var invariant3 = require('tiny-invariant');
var viem = require('viem');
var chains$1 = require('viem/chains');
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
var invariant3__default = /*#__PURE__*/_interopDefault(invariant3);
// src/constants.ts
var FACTORY_ADDRESS = "0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73";
var FACTORY_ADDRESS_ETH = "0x1097053Fd2ea711dad45caCcc45EfF7548fCB362";
var FACTORY_ADDRESS_MAP = {
[chains.ChainId.ETHEREUM]: FACTORY_ADDRESS_ETH,
[chains.ChainId.GOERLI]: FACTORY_ADDRESS_ETH,
[chains.ChainId.BSC]: FACTORY_ADDRESS,
[chains.ChainId.BSC_TESTNET]: "0x6725F303b657a9451d8BA641348b6761A6CC7a17",
[chains.ChainId.ARBITRUM_ONE]: "0x02a84c1b3BBD7401a5f7fa98a384EBC70bB5749E",
[chains.ChainId.ARBITRUM_GOERLI]: "0x333EAE459075b1d7dE8eb57997b5d4eee5F1070a",
[chains.ChainId.POLYGON_ZKEVM]: "0x02a84c1b3BBD7401a5f7fa98a384EBC70bB5749E",
[chains.ChainId.POLYGON_ZKEVM_TESTNET]: "0xBA40c83026213F9cbc79998752721a0312bdB74a",
[chains.ChainId.ZKSYNC]: "0xd03D8D566183F0086d8D09A84E1e30b58Dd5619d",
[chains.ChainId.ZKSYNC_TESTNET]: "0x48a33610Cd0E130af2024D55F67aE72a8C51aC27",
[chains.ChainId.LINEA]: "0x02a84c1b3BBD7401a5f7fa98a384EBC70bB5749E",
[chains.ChainId.LINEA_TESTNET]: "0xB6FAfd4ADbCd21cF665909767e0eD0D05709abfB",
[chains.ChainId.OPBNB]: "0x02a84c1b3BBD7401a5f7fa98a384EBC70bB5749E",
[chains.ChainId.OPBNB_TESTNET]: "0x776e4bD2f72de2176A59465e316335aaf8ed4E8F",
[chains.ChainId.BASE]: "0x02a84c1b3BBD7401a5f7fa98a384EBC70bB5749E",
[chains.ChainId.BASE_TESTNET]: "0x715303D2eF7dA7FFAbF637651D71FD11d41fAf7F",
[chains.ChainId.SCROLL_SEPOLIA]: "0x2B3C5df29F73dbF028BA82C33e0A5A6e5800F75e",
[chains.ChainId.SEPOLIA]: "0x1bdc540dEB9Ed1fA29964DeEcCc524A8f5e2198e",
[chains.ChainId.ARBITRUM_SEPOLIA]: "0x02a84c1b3BBD7401a5f7fa98a384EBC70bB5749E",
[chains.ChainId.BASE_SEPOLIA]: "0x02a84c1b3BBD7401a5f7fa98a384EBC70bB5749E",
[chains.ChainId.MONAD_TESTNET]: "0x82438CE666d9403e488bA720c7424434e8Aa47CD"
};
var INIT_CODE_HASH = "0x00fb7f630766e6a796048ea87d01acd3068e8ff67d078148a3fa3f4a84f69bd5";
var INIT_CODE_HASH_ETH = "0x57224589c67f3f30a6b0d7a1b54cf3153ab84563bc609ef41dfb34f8b2974d2d";
var INIT_CODE_HASH_MAP = {
[chains.ChainId.ETHEREUM]: INIT_CODE_HASH_ETH,
[chains.ChainId.GOERLI]: INIT_CODE_HASH_ETH,
[chains.ChainId.BSC]: INIT_CODE_HASH,
[chains.ChainId.BSC_TESTNET]: "0xd0d4c4cd0848c93cb4fd1f498d7013ee6bfb25783ea21593d5834f5d250ece66",
[chains.ChainId.ARBITRUM_ONE]: INIT_CODE_HASH_ETH,
[chains.ChainId.ARBITRUM_GOERLI]: INIT_CODE_HASH_ETH,
[chains.ChainId.POLYGON_ZKEVM]: INIT_CODE_HASH_ETH,
[chains.ChainId.POLYGON_ZKEVM_TESTNET]: INIT_CODE_HASH_ETH,
[chains.ChainId.ZKSYNC]: "0x0100045707a42494392b3558029b9869f865ff9df8f375dc1bf20b0555093f43",
[chains.ChainId.ZKSYNC_TESTNET]: "0x0100045707a42494392b3558029b9869f865ff9df8f375dc1bf20b0555093f43",
[chains.ChainId.LINEA]: INIT_CODE_HASH_ETH,
[chains.ChainId.LINEA_TESTNET]: INIT_CODE_HASH_ETH,
[chains.ChainId.OPBNB]: INIT_CODE_HASH_ETH,
[chains.ChainId.OPBNB_TESTNET]: INIT_CODE_HASH_ETH,
[chains.ChainId.BASE]: INIT_CODE_HASH_ETH,
[chains.ChainId.BASE_TESTNET]: "0xa5934690703a592a07e841ca29d5e5c79b5e22ed4749057bb216dc31100be1c0",
[chains.ChainId.SCROLL_SEPOLIA]: INIT_CODE_HASH_ETH,
[chains.ChainId.SEPOLIA]: INIT_CODE_HASH_ETH,
[chains.ChainId.ARBITRUM_SEPOLIA]: INIT_CODE_HASH_ETH,
[chains.ChainId.BASE_SEPOLIA]: INIT_CODE_HASH_ETH,
[chains.ChainId.MONAD_TESTNET]: "0xa5934690703a592a07e841ca29d5e5c79b5e22ed4749057bb216dc31100be1c0"
};
var PAIR_ADDRESS_CACHE = {};
var composeKey = (token0, token1) => `${token0.chainId}-${token0.address}-${token1.address}`;
function getCreate2Address(from_, salt_, initCodeHash) {
const from = viem.toBytes(viem.getAddress(from_));
const salt = viem.pad(viem.isBytes(salt_) ? salt_ : viem.toBytes(salt_), {
size: 32
});
return viem.getAddress(viem.slice(viem.keccak256(viem.concat([viem.toBytes("0xff"), from, salt, viem.toBytes(initCodeHash)])), 12));
}
var EMPTY_INPU_HASH = "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470";
var ZKSYNC_PREFIX = "0x2020dba91b30cc0006188af794c2fb30dd8520db7e2c088b7fc7c103c00ca494";
function getCreate2AddressZkSync(from, salt, initCodeHash) {
return viem.getAddress(
`0x${viem.keccak256(viem.concat([ZKSYNC_PREFIX, viem.pad(from, { size: 32 }), salt, initCodeHash, EMPTY_INPU_HASH])).slice(26)}`
);
}
var computePairAddress = ({
factoryAddress,
tokenA,
tokenB
}) => {
const [token0, token1] = tokenA.sortsBefore(tokenB) ? [tokenA, tokenB] : [tokenB, tokenA];
const key = composeKey(token0, token1);
if (PAIR_ADDRESS_CACHE?.[key] === void 0) {
const getCreate2Address_ = token0.chainId === chains.ChainId.ZKSYNC_TESTNET || token1.chainId === chains.ChainId.ZKSYNC ? getCreate2AddressZkSync : getCreate2Address;
PAIR_ADDRESS_CACHE = {
...PAIR_ADDRESS_CACHE,
[key]: getCreate2Address_(
factoryAddress,
viem.keccak256(viem.encodePacked(["address", "address"], [token0.address, token1.address])),
INIT_CODE_HASH_MAP[token0.chainId]
)
};
}
return PAIR_ADDRESS_CACHE[key];
};
var Pair = class {
static getAddress(tokenA, tokenB) {
return computePairAddress({
factoryAddress: FACTORY_ADDRESS_MAP[tokenA.chainId],
tokenA,
tokenB
});
}
constructor(currencyAmountA, tokenAmountB) {
const tokenAmounts = currencyAmountA.currency.sortsBefore(tokenAmountB.currency) ? [currencyAmountA, tokenAmountB] : [tokenAmountB, currencyAmountA];
this.liquidityToken = new swapSdkEvm.ERC20Token(
tokenAmounts[0].currency.chainId,
Pair.getAddress(tokenAmounts[0].currency, tokenAmounts[1].currency),
18,
"Cake-LP",
"Pancake LPs"
);
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) {
invariant3__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) {
invariant3__default.default(this.involvesToken(token), "TOKEN");
return token.equals(this.token0) ? this.reserve0 : this.reserve1;
}
getOutputAmount(inputAmount) {
invariant3__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) {
invariant3__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) {
invariant3__default.default(totalSupply.currency.equals(this.liquidityToken), "LIQUIDITY");
const tokenAmounts = tokenAmountA.currency.sortsBefore(tokenAmountB.currency) ? [tokenAmountA, tokenAmountB] : [tokenAmountB, tokenAmountA];
invariant3__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) {
invariant3__default.default(this.involvesToken(token), "TOKEN");
invariant3__default.default(totalSupply.currency.equals(this.liquidityToken), "TOTAL_SUPPLY");
invariant3__default.default(liquidity.currency.equals(this.liquidityToken), "LIQUIDITY");
invariant3__default.default(liquidity.quotient <= totalSupply.quotient, "LIQUIDITY");
let totalSupplyAdjusted;
if (!feeOn) {
totalSupplyAdjusted = totalSupply;
} else {
invariant3__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
);
}
};
var Route = class {
constructor(pairs, input, output) {
this._midPrice = null;
invariant3__default.default(pairs.length > 0, "PAIRS");
const { chainId } = pairs[0];
invariant3__default.default(
pairs.every((pair) => pair.chainId === chainId),
"CHAIN_IDS"
);
const wrappedInput = input.wrapped;
invariant3__default.default(pairs[0].involvesToken(wrappedInput), "INPUT");
invariant3__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];
invariant3__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) {
invariant3__default.default(a.inputAmount.currency.equals(b.inputAmount.currency), "INPUT_CURRENCY");
invariant3__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 {
/**
* 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) {
invariant3__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 {
invariant3__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) {
invariant3__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) {
invariant3__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 = []) {
invariant3__default.default(pairs.length > 0, "PAIRS");
invariant3__default.default(maxHops > 0, "MAX_HOPS");
invariant3__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 = []) {
invariant3__default.default(pairs.length > 0, "PAIRS");
invariant3__default.default(maxHops > 0, "MAX_HOPS");
invariant3__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;
}
};
// src/abis/IPancakePair.ts
var pancakePairV2ABI = [
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "owner",
type: "address"
},
{
indexed: true,
internalType: "address",
name: "spender",
type: "address"
},
{
indexed: false,
internalType: "uint256",
name: "value",
type: "uint256"
}
],
name: "Approval",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "sender",
type: "address"
},
{
indexed: false,
internalType: "uint256",
name: "amount0",
type: "uint256"
},
{
indexed: false,
internalType: "uint256",
name: "amount1",
type: "uint256"
},
{
indexed: true,
internalType: "address",
name: "to",
type: "address"
}
],
name: "Burn",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "sender",
type: "address"
},
{
indexed: false,
internalType: "uint256",
name: "amount0",
type: "uint256"
},
{
indexed: false,
internalType: "uint256",
name: "amount1",
type: "uint256"
}
],
name: "Mint",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "sender",
type: "address"
},
{
indexed: false,
internalType: "uint256",
name: "amount0In",
type: "uint256"
},
{
indexed: false,
internalType: "uint256",
name: "amount1In",
type: "uint256"
},
{
indexed: false,
internalType: "uint256",
name: "amount0Out",
type: "uint256"
},
{
indexed: false,
internalType: "uint256",
name: "amount1Out",
type: "uint256"
},
{
indexed: true,
internalType: "address",
name: "to",
type: "address"
}
],
name: "Swap",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "uint112",
name: "reserve0",
type: "uint112"
},
{
indexed: false,
internalType: "uint112",
name: "reserve1",
type: "uint112"
}
],
name: "Sync",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "from",
type: "address"
},
{
indexed: true,
internalType: "address",
name: "to",
type: "address"
},
{
indexed: false,
internalType: "uint256",
name: "value",
type: "uint256"
}
],
name: "Transfer",
type: "event"
},
{
constant: true,
inputs: [],
name: "DOMAIN_SEPARATOR",
outputs: [
{
internalType: "bytes32",
name: "",
type: "bytes32"
}
],
payable: false,
stateMutability: "view",
type: "function"
},
{
constant: true,
inputs: [],
name: "MINIMUM_LIQUIDITY",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
payable: false,
stateMutability: "pure",
type: "function"
},
{
constant: true,
inputs: [],
name: "PERMIT_TYPEHASH",
outputs: [
{
internalType: "bytes32",
name: "",
type: "bytes32"
}
],
payable: false,
stateMutability: "pure",
type: "function"
},
{
constant: true,
inputs: [
{
internalType: "address",
name: "owner",
type: "address"
},
{
internalType: "address",
name: "spender",
type: "address"
}
],
name: "allowance",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
payable: false,
stateMutability: "view",
type: "function"
},
{
constant: false,
inputs: [
{
internalType: "address",
name: "spender",
type: "address"
},
{
internalType: "uint256",
name: "value",
type: "uint256"
}
],
name: "approve",
outputs: [
{
internalType: "bool",
name: "",
type: "bool"
}
],
payable: false,
stateMutability: "nonpayable",
type: "function"
},
{
constant: true,
inputs: [
{
internalType: "address",
name: "owner",
type: "address"
}
],
name: "balanceOf",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
payable: false,
stateMutability: "view",
type: "function"
},
{
constant: false,
inputs: [
{
internalType: "address",
name: "to",
type: "address"
}
],
name: "burn",
outputs: [
{
internalType: "uint256",
name: "amount0",
type: "uint256"
},
{
internalType: "uint256",
name: "amount1",
type: "uint256"
}
],
payable: false,
stateMutability: "nonpayable",
type: "function"
},
{
constant: true,
inputs: [],
name: "decimals",
outputs: [
{
internalType: "uint8",
name: "",
type: "uint8"
}
],
payable: false,
stateMutability: "pure",
type: "function"
},
{
constant: true,
inputs: [],
name: "factory",
outputs: [
{
internalType: "address",
name: "",
type: "address"
}
],
payable: false,
stateMutability: "view",
type: "function"
},
{
constant: true,
inputs: [],
name: "getReserves",
outputs: [
{
internalType: "uint112",
name: "reserve0",
type: "uint112"
},
{
internalType: "uint112",
name: "reserve1",
type: "uint112"
},
{
internalType: "uint32",
name: "blockTimestampLast",
type: "uint32"
}
],
payable: false,
stateMutability: "view",
type: "function"
},
{
constant: false,
inputs: [
{
internalType: "address",
name: "",
type: "address"
},
{
internalType: "address",
name: "",
type: "address"
}
],
name: "initialize",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function"
},
{
constant: true,
inputs: [],
name: "kLast",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
payable: false,
stateMutability: "view",
type: "function"
},
{
constant: false,
inputs: [
{
internalType: "address",
name: "to",
type: "address"
}
],
name: "mint",
outputs: [
{
internalType: "uint256",
name: "liquidity",
type: "uint256"
}
],
payable: false,
stateMutability: "nonpayable",
type: "function"
},
{
constant: true,
inputs: [],
name: "name",
outputs: [
{
internalType: "string",
name: "",
type: "string"
}
],
payable: false,
stateMutability: "pure",
type: "function"
},
{
constant: true,
inputs: [
{
internalType: "address",
name: "owner",
type: "address"
}
],
name: "nonces",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
payable: false,
stateMutability: "view",
type: "function"
},
{
constant: false,
inputs: [
{
internalType: "address",
name: "owner",
type: "address"
},
{
internalType: "address",
name: "spender",
type: "address"
},
{
internalType: "uint256",
name: "value",
type: "uint256"
},
{
internalType: "uint256",
name: "deadline",
type: "uint256"
},
{
internalType: "uint8",
name: "v",
type: "uint8"
},
{
internalType: "bytes32",
name: "r",
type: "bytes32"
},
{
internalType: "bytes32",
name: "s",
type: "bytes32"
}
],
name: "permit",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function"
},
{
constant: true,
inputs: [],
name: "price0CumulativeLast",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
payable: false,
stateMutability: "view",
type: "function"
},
{
constant: true,
inputs: [],
name: "price1CumulativeLast",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
payable: false,
stateMutability: "view",
type: "function"
},
{
constant: false,
inputs: [
{
internalType: "address",
name: "to",
type: "address"
}
],
name: "skim",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function"
},
{
constant: false,
inputs: [
{
internalType: "uint256",
name: "amount0Out",
type: "uint256"
},
{
internalType: "uint256",
name: "amount1Out",
type: "uint256"
},
{
internalType: "address",
name: "to",
type: "address"
},
{
internalType: "bytes",
name: "data",
type: "bytes"
}
],
name: "swap",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function"
},
{
constant: true,
inputs: [],
name: "symbol",
outputs: [
{
internalType: "string",
name: "",
type: "string"
}
],
payable: false,
stateMutability: "pure",
type: "function"
},
{
constant: false,
inputs: [],
name: "sync",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function"
},
{
constant: true,
inputs: [],
name: "token0",
outputs: [
{
internalType: "address",
name: "",
type: "address"
}
],
payable: false,
stateMutability: "view",
type: "function"
},
{
constant: true,
inputs: [],
name: "token1",
outputs: [
{
internalType: "address",
name: "",
type: "address"
}
],
payable: false,
stateMutability: "view",
type: "function"
},
{
constant: true,
inputs: [],
name: "totalSupply",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
payable: false,
stateMutability: "view",
type: "function"
},
{
constant: false,
inputs: [
{
internalType: "address",
name: "to",
type: "address"
},
{
internalType: "uint256",
name: "value",
type: "uint256"
}
],
name: "transfer",
outputs: [
{
internalType: "bool",
name: "",
type: "bool"
}
],
payable: false,
stateMutability: "nonpayable",
type: "function"
},
{
constant: false,
inputs: [
{
internalType: "address",
name: "from",
type: "address"
},
{
internalType: "address",
name: "to",
type: "address"
},
{
internalType: "uint256",
name: "value",
type: "uint256"
}
],
name: "transferFrom",
outputs: [
{
internalType: "bool",
name: "",
type: "bool"
}
],
payable: false,
stateMutability: "nonpayable",
type: "function"
}
];
function toHex(currencyAmount) {
return `0x${currencyAmount.quotient.toString(16)}`;
}
var ZERO_HEX = "0x0";
var Router = class {
/**
* 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 etherIn = trade.inputAmount.currency.isNative;
const etherOut = trade.outputAmount.currency.isNative;
invariant3__default.default(!(etherIn && etherOut), "ETHER_IN_OUT");
invariant3__default.default(!("ttl" in options) || options.ttl > 0, "TTL");
const to = swapSdkEvm.validateAndParseAddress(options.recipient);
const amountIn = toHex(trade.maximumAmountIn(options.allowedSlippage));
const amountOut = toHex(trade.minimumAmountOut(options.allowedSlippage));
const path = trade.route.path.map((token) => token.address);
const deadline = "ttl" in options ? `0x${(Math.floor(( new Date()).getTime() / 1e3) + options.ttl).toString(16)}` : `0x${options.deadline.toString(16)}`;
const useFeeOnTransfer = Boolean(options.feeOnTransfer);
let methodName;
let args;
let value;
switch (trade.tradeType) {
case swapSdkCore.TradeType.EXACT_INPUT:
if (etherIn) {
methodName = useFeeOnTransfer ? "swapExactETHForTokensSupportingFeeOnTransferTokens" : "swapExactETHForTokens";
args = [amountOut, path, to, deadline];
value = amountIn;
} else if (etherOut) {
methodName = useFeeOnTransfer ? "swapExactTokensForETHSupportingFeeOnTransferTokens" : "swapExactTokensForETH";
args = [amountIn, amountOut, path, to, deadline];
value = ZERO_HEX;
} else {
methodName = useFeeOnTransfer ? "swapExactTokensForTokensSupportingFeeOnTransferTokens" : "swapExactTokensForTokens";
args = [amountIn, amountOut, path, to, deadline];
value = ZERO_HEX;
}
break;
case swapSdkCore.TradeType.EXACT_OUTPUT:
invariant3__default.default(!useFeeOnTransfer, "EXACT_OUT_FOT");
if (etherIn) {
methodName = "swapETHForExactTokens";
args = [amountOut, path, to, deadline];
value = amountIn;
} else if (etherOut) {
methodName = "swapTokensForExactETH";
args = [amountOut, amountIn, path, to, deadline];
value = ZERO_HEX;
} else {
methodName = "swapTokensForExactTokens";
args = [amountOut, amountIn, path, to, deadline];
value = ZERO_HEX;
}
break;
}
return {
methodName,
args,
value
};
}
};
var TOKEN_DECIMALS_CACHE = {
[chains.ChainId.BSC]: {}
};
var ethClient = viem.createPublicClient({ chain: chains$1.mainnet, transport: viem.http() });
var bscClient = viem.createPublicClient({ chain: chains$1.bsc, transport: viem.http() });
var bscTestnetClient = viem.createPublicClient({ chain: chains$1.bscTestnet, transport: viem.http() });
var goerliClient = viem.createPublicClient({ chain: chains$1.goerli, transport: viem.http() });
var getDefaultClient = (chainId) => {
switch (chainId) {
case chains.ChainId.ETHEREUM:
return ethClient;
case chains.ChainId.BSC:
return bscClient;
case chains.ChainId.BSC_TESTNET:
return bscTestnetClient;
case chains.ChainId.GOERLI:
return goerliClient;
default:
return bscClient;
}
};
var Fetcher = class {
/**
* Cannot be constructed.
*/
// eslint-disable-next-line no-useless-constructor,@typescript-eslint/no-empty-function
constructor() {
}
/**
* Fetch information for a given token on the given chain, using the given viem 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 symbol of the token
* @param name optional name of the token
*/
static async fetchTokenData(chainId, address, publicClient = getDefaultClient(chainId), symbol, name) {
const erc20 = viem.getContract({
abi: swapSdkEvm.erc20Abi,
address,
client: publicClient
});
const parsedDecimals = typeof TOKEN_DECIMALS_CACHE?.[chainId]?.[address] === "number" ? TOKEN_DECIMALS_CACHE[chainId][address] : await erc20.read.decimals().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, publicClient = getDefaultClient(tokenA.chainId)) {
invariant3__default.default(tokenA.chainId === tokenB.chainId, "CHAIN_ID");
const address = Pair.getAddress(tokenA, tokenB);
const pairContract = viem.getContract({
abi: pancakePairV2ABI,
address,
client: publicClient
});
const [reserves0, reserves1] = await pairContract.read.getReserves();
const balances = tokenA.sortsBefore(tokenB) ? [reserves0, reserves1] : [reserves1, reserves0];
return new Pair(
swapSdkCore.CurrencyAmount.fromRawAmount(tokenA, balances[0]),
swapSdkCore.CurrencyAmount.fromRawAmount(tokenB, balances[1])
);
}
};
function isTradeBetter(tradeA, tradeB, minimumDelta = swapSdkEvm.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(swapSdkEvm.ZERO_PERCENT)) {
return tradeA.executionPrice.lessThan(tradeB.executionPrice);
}
return tradeA.executionPrice.asFraction.multiply(minimumDelta.add(swapSdkEvm.ONE_HUNDRED_PERCENT)).lessThan(tradeB.executionPrice);
}
exports.FACTORY_ADDRESS = FACTORY_ADDRESS;
exports.FACTORY_ADDRESS_MAP = FACTORY_ADDRESS_MAP;
exports.Fetcher = Fetcher;
exports.INIT_CODE_HASH = INIT_CODE_HASH;
exports.INIT_CODE_HASH_MAP = INIT_CODE_HASH_MAP;
exports.Pair = Pair;
exports.Route = Route;
exports.Router = Router;
exports.Trade = Trade;
exports.computePairAddress = computePairAddress;
exports.inputOutputComparator = inputOutputComparator;
exports.isTradeBetter = isTradeBetter;
exports.pancakePairV2ABI = pancakePairV2ABI;
exports.tradeComparator = tradeComparator;
//# sourceMappingURL=out.js.map
//# sourceMappingURL=index.js.map