UNPKG

@razorlabs/dex-sdk

Version:

⚒️ An SDK for building applications on top of Razor DEX

1,424 lines (1,407 loc) 47.8 kB
'use strict'; var invariant11 = require('tiny-invariant'); var _Decimal = require('decimal.js-light'); var _Big = require('big.js'); var toFormat = require('toformat'); var tsSdk = require('@aptos-labs/ts-sdk'); function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } var invariant11__default = /*#__PURE__*/_interopDefault(invariant11); var _Decimal__default = /*#__PURE__*/_interopDefault(_Decimal); var _Big__default = /*#__PURE__*/_interopDefault(_Big); var toFormat__default = /*#__PURE__*/_interopDefault(toFormat); // src/chains.ts var ChainId = /* @__PURE__ */ ((ChainId2) => { ChainId2[ChainId2["MOVEMENT_SUZUKA"] = 27] = "MOVEMENT_SUZUKA"; ChainId2[ChainId2["MOVEMENT_IMOLA"] = 30732] = "MOVEMENT_IMOLA"; ChainId2[ChainId2["MOVEMENT_BAKU"] = 100] = "MOVEMENT_BAKU"; ChainId2[ChainId2["APTOS_MAINNET"] = 1] = "APTOS_MAINNET"; ChainId2[ChainId2["APTOS_TESTNET"] = 2] = "APTOS_TESTNET"; ChainId2[ChainId2["APTOS_DEVNET"] = 146] = "APTOS_DEVNET"; ChainId2[ChainId2["SUI_MAINNET"] = 1e3] = "SUI_MAINNET"; ChainId2[ChainId2["SUI_TESTNET"] = 1001] = "SUI_TESTNET"; ChainId2[ChainId2["SUI_DEVNET"] = 1002] = "SUI_DEVNET"; return ChainId2; })(ChainId || {}); var SUPPORTED_CHAINS = [ 146 /* APTOS_DEVNET */, 2 /* APTOS_TESTNET */ ]; var NativeCurrencyName = /* @__PURE__ */ ((NativeCurrencyName2) => { NativeCurrencyName2["MOVE"] = "MOVE"; NativeCurrencyName2["APT"] = "APT"; NativeCurrencyName2["SUI"] = "SUI"; return NativeCurrencyName2; })(NativeCurrencyName || {}); var Decimal = toFormat__default.default(_Decimal__default.default); var Big = toFormat__default.default(_Big__default.default); var toSignificantRounding = { [0 /* ROUND_DOWN */]: Decimal.ROUND_DOWN, [1 /* ROUND_HALF_UP */]: Decimal.ROUND_HALF_UP, [2 /* ROUND_UP */]: Decimal.ROUND_UP }; var toFixedRounding = { [0 /* ROUND_DOWN */]: 0 /* RoundDown */, [1 /* ROUND_HALF_UP */]: 1 /* RoundHalfUp */, [2 /* ROUND_UP */]: 3 /* RoundUp */ }; var Fraction = class _Fraction { constructor(numerator, denominator = 1n) { this.numerator = BigInt(numerator); this.denominator = BigInt(denominator); } static tryParseFraction(fractionish) { if (typeof fractionish === "bigint" || typeof fractionish === "number" || typeof fractionish === "string") return new _Fraction(fractionish); if ("numerator" in fractionish && "denominator" in fractionish) return fractionish; throw new Error("Could not parse fraction"); } // performs floor division get quotient() { return this.numerator / this.denominator; } // remainder after floor division get remainder() { return new _Fraction(this.numerator % this.denominator, this.denominator); } invert() { return new _Fraction(this.denominator, this.numerator); } add(other) { const otherParsed = _Fraction.tryParseFraction(other); if (this.denominator === otherParsed.denominator) { return new _Fraction( this.numerator + otherParsed.numerator, this.denominator ); } return new _Fraction( this.numerator * otherParsed.denominator + otherParsed.numerator * this.denominator, this.denominator * otherParsed.denominator ); } subtract(other) { const otherParsed = _Fraction.tryParseFraction(other); if (this.denominator === otherParsed.denominator) { return new _Fraction( this.numerator - otherParsed.numerator, this.denominator ); } return new _Fraction( this.numerator * otherParsed.denominator - otherParsed.numerator * this.denominator, this.denominator * otherParsed.denominator ); } lessThan(other) { const otherParsed = _Fraction.tryParseFraction(other); return this.numerator * otherParsed.denominator < otherParsed.numerator * this.denominator; } equalTo(other) { const otherParsed = _Fraction.tryParseFraction(other); return this.numerator * otherParsed.denominator === otherParsed.numerator * this.denominator; } greaterThan(other) { const otherParsed = _Fraction.tryParseFraction(other); return this.numerator * otherParsed.denominator > otherParsed.numerator * this.denominator; } multiply(other) { const otherParsed = _Fraction.tryParseFraction(other); return new _Fraction( this.numerator * otherParsed.numerator, this.denominator * otherParsed.denominator ); } divide(other) { const otherParsed = _Fraction.tryParseFraction(other); return new _Fraction( this.numerator * otherParsed.denominator, this.denominator * otherParsed.numerator ); } toSignificant(significantDigits, format = { groupSeparator: "" }, rounding = 1 /* ROUND_HALF_UP */) { invariant11__default.default( Number.isInteger(significantDigits), `${significantDigits} is not an integer.` ); invariant11__default.default(significantDigits > 0, `${significantDigits} is not positive.`); Decimal.set({ precision: significantDigits + 1, rounding: toSignificantRounding[rounding] }); const quotient = new Decimal(this.numerator.toString()).div(this.denominator.toString()).toSignificantDigits(significantDigits); return quotient.toFormat(quotient.decimalPlaces(), format); } toFixed(decimalPlaces, format = { groupSeparator: "" }, rounding = 1 /* ROUND_HALF_UP */) { invariant11__default.default( Number.isInteger(decimalPlaces), `${decimalPlaces} is not an integer.` ); invariant11__default.default(decimalPlaces >= 0, `${decimalPlaces} is negative.`); Big.DP = decimalPlaces; Big.RM = toFixedRounding[rounding]; return new Big(this.numerator.toString()).div(this.denominator.toString()).toFormat(decimalPlaces, format); } /** * Helper method for converting any super class back to a fraction */ get asFraction() { return new _Fraction(this.numerator, this.denominator); } }; // src/entities/fractions/percent.ts var ONE_HUNDRED = new Fraction(100n); function toPercent(fraction) { return new Percent(fraction.numerator, fraction.denominator); } var Percent = class extends Fraction { constructor() { super(...arguments); /** * This boolean prevents a fraction from being interpreted as a Percent */ this.isPercent = true; } add(other) { return toPercent(super.add(other)); } subtract(other) { return toPercent(super.subtract(other)); } multiply(other) { return toPercent(super.multiply(other)); } divide(other) { return toPercent(super.divide(other)); } toSignificant(significantDigits = 5, format, rounding) { return super.multiply(ONE_HUNDRED).toSignificant(significantDigits, format, rounding); } toFixed(decimalPlaces = 2, format, rounding) { return super.multiply(ONE_HUNDRED).toFixed(decimalPlaces, format, rounding); } }; Percent.toPercent = toPercent; var Big2 = toFormat__default.default(_Big__default.default); var CurrencyAmount = class _CurrencyAmount extends Fraction { static fromRawAmount(currency, rawAmount) { return new _CurrencyAmount(currency, rawAmount); } static fromFractionalAmount(currency, numerator, denominator) { return new _CurrencyAmount(currency, numerator, denominator); } constructor(currency, numerator, denominator) { super(numerator, denominator); invariant11__default.default(this.quotient <= MaxUint256, "AMOUNT"); this.currency = currency; this.decimalScale = 10n ** BigInt(currency.decimals); } add(other) { invariant11__default.default(this.currency.equals(other.currency), "CURRENCY"); const added = super.add(other); return _CurrencyAmount.fromFractionalAmount( this.currency, added.numerator, added.denominator ); } subtract(other) { invariant11__default.default(this.currency.equals(other.currency), "CURRENCY"); const subtracted = super.subtract(other); return _CurrencyAmount.fromFractionalAmount( this.currency, subtracted.numerator, subtracted.denominator ); } multiply(other) { const multiplied = super.multiply(other); return _CurrencyAmount.fromFractionalAmount( this.currency, multiplied.numerator, multiplied.denominator ); } divide(other) { const divided = super.divide(other); return _CurrencyAmount.fromFractionalAmount( this.currency, divided.numerator, divided.denominator ); } toSignificant(significantDigits = 6, format, rounding = 0 /* ROUND_DOWN */) { return super.divide(this.decimalScale).toSignificant(significantDigits, format, rounding); } toFixed(decimalPlaces = this.currency.decimals, format, rounding = 0 /* ROUND_DOWN */) { invariant11__default.default(decimalPlaces <= this.currency.decimals, "DECIMALS"); return super.divide(this.decimalScale).toFixed(decimalPlaces, format, rounding); } toExact(format = { groupSeparator: "" }) { Big2.DP = this.currency.decimals; return new Big2(this.quotient.toString()).div(this.decimalScale.toString()).toFormat(format); } get wrapped() { if (this.currency.isToken) return this; return _CurrencyAmount.fromFractionalAmount( this.currency.wrapped, this.numerator, this.denominator ); } }; var Price = class _Price extends Fraction { // used to adjust the raw fraction w/r/t the decimals of the {base,quote}Token /** * Construct a price, either with the base and quote currency amount, or the * @param args */ constructor(...args) { let baseCurrency; let quoteCurrency; let denominator; let numerator; if (args.length === 4) { [baseCurrency, quoteCurrency, denominator, numerator] = args; } else { const result = args[0].quoteAmount.divide(args[0].baseAmount); [baseCurrency, quoteCurrency, denominator, numerator] = [ args[0].baseAmount.currency, args[0].quoteAmount.currency, result.denominator, result.numerator ]; } super(numerator, denominator); this.baseCurrency = baseCurrency; this.quoteCurrency = quoteCurrency; this.scalar = new Fraction( 10n ** BigInt(baseCurrency.decimals), 10n ** BigInt(quoteCurrency.decimals) ); } /** * Flip the price, switching the base and quote currency */ invert() { return new _Price( this.quoteCurrency, this.baseCurrency, this.numerator, this.denominator ); } /** * Multiply the price by another price, returning a new price. The other price must have the same base currency as this price's quote currency * @param other the other price */ multiply(other) { invariant11__default.default(this.quoteCurrency.equals(other.baseCurrency), "TOKEN"); const fraction = super.multiply(other); return new _Price( this.baseCurrency, other.quoteCurrency, fraction.denominator, fraction.numerator ); } /** * Return the amount of quote currency corresponding to a given amount of the base currency * @param currencyAmount the amount of base currency to quote against the price */ quote(currencyAmount) { invariant11__default.default(currencyAmount.currency.equals(this.baseCurrency), "TOKEN"); const result = super.multiply(currencyAmount); return CurrencyAmount.fromFractionalAmount( this.quoteCurrency, result.numerator, result.denominator ); } /** * Get the value scaled by decimals for formatting * @private */ get adjustedForDecimals() { return super.multiply(this.scalar); } toSignificant(significantDigits = 6, format, rounding) { return this.adjustedForDecimals.toSignificant( significantDigits, format, rounding ); } toFixed(decimalPlaces = 4, format, rounding) { return this.adjustedForDecimals.toFixed(decimalPlaces, format, rounding); } }; var BaseCurrency = class { constructor(chainId, decimals, symbol, name) { invariant11__default.default(Number.isSafeInteger(chainId), "CHAIN_ID"); invariant11__default.default( decimals >= 0 && decimals < 255 && Number.isInteger(decimals), "DECIMALS" ); this.chainId = chainId; this.decimals = decimals; this.symbol = symbol; this.name = name; } }; // src/entities/nativeCurrency.ts var NativeCurrency = class extends BaseCurrency { constructor() { super(...arguments); this.isNative = true; this.isToken = false; this.isFungibleAsset = false; } }; var Token = class extends BaseCurrency { constructor(chainId, address, decimals, symbol, isFungibleAsset, name, projectLink) { super(chainId, decimals, symbol, name); this.isNative = false; this.isToken = true; this.address = address; this.isFungibleAsset = isFungibleAsset; this.projectLink = projectLink; } equals(other) { return other.isToken && this.chainId === other.chainId && this.address === other.address; } sortsBefore(other) { invariant11__default.default(this.chainId === other.chainId, "CHAIN_IDS"); invariant11__default.default(this.address !== other.address, "ADDRESSES"); return this.address.toLowerCase() < other.address.toLowerCase(); } get wrapped() { return this; } get serialize() { return { address: this.address, chainId: this.chainId, decimals: this.decimals, symbol: this.symbol, name: this.name, projectLink: this.projectLink }; } }; // src/constants.ts var TradeType = /* @__PURE__ */ ((TradeType2) => { TradeType2[TradeType2["EXACT_INPUT"] = 0] = "EXACT_INPUT"; TradeType2[TradeType2["EXACT_OUTPUT"] = 1] = "EXACT_OUTPUT"; return TradeType2; })(TradeType || {}); var Rounding = /* @__PURE__ */ ((Rounding2) => { Rounding2[Rounding2["ROUND_DOWN"] = 0] = "ROUND_DOWN"; Rounding2[Rounding2["ROUND_HALF_UP"] = 1] = "ROUND_HALF_UP"; Rounding2[Rounding2["ROUND_UP"] = 2] = "ROUND_UP"; return Rounding2; })(Rounding || {}); var MINIMUM_LIQUIDITY = 1000n; var ZERO = 0n; var ONE = 1n; var TWO = 2n; var THREE = 3n; var FIVE = 5n; var TEN = 10n; var _100 = 100n; var _997 = 997n; var _1000 = 1000n; var BASIS_POINTS = 10000n; var MaxUint256 = BigInt( "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" ); var ZERO_PERCENT = new Percent("0"); var ONE_HUNDRED_PERCENT = new Percent("1"); // src/addresses.ts var FACTORY_MODULE = "factory"; var ROUTER_MODULE = "router"; var PAIR_MODULE = "pair"; var LIBRARY_MODULE = "library"; var RESOURCE_ACCOUNT = "0xbe9c218a7a8fa9a176f55362111b1a41863acfc6d740d19441d2216c06bafd9d"; var V2_FACTORY_ADDRESS = `${RESOURCE_ACCOUNT}::${FACTORY_MODULE}`; var V2_ROUTER_ADDRESS = `${RESOURCE_ACCOUNT}::${ROUTER_MODULE}`; var V2_PAIR_ADDRESS = `${RESOURCE_ACCOUNT}::${PAIR_MODULE}`; var V2_LIBRARY_ADDRESS = `${RESOURCE_ACCOUNT}::${LIBRARY_MODULE}`; var HexString = class _HexString { /** * Creates new hex string from Buffer * @param buffer A buffer to convert * @returns New HexString */ static fromBuffer(buffer) { return _HexString.fromUint8Array(buffer); } /** * Creates new hex string from Uint8Array * @param arr Uint8Array to convert * @returns New HexString */ static fromUint8Array(arr) { return new _HexString(tsSdk.Hex.fromHexInput(arr).toString()); } /** * Ensures `hexString` is instance of `HexString` class * @param hexString String to check * @returns New HexString if `hexString` is regular string or `hexString` if it is HexString instance * @example * ``` * const regularString = "string"; * const hexString = new HexString("string"); // "0xstring" * HexString.ensure(regularString); // "0xstring" * HexString.ensure(hexString); // "0xstring" * ``` */ static ensure(hexString) { if (typeof hexString === "string") { return new _HexString(hexString); } return hexString; } /** * Creates new HexString instance from regular string. If specified string already starts with "0x" prefix, * it will not add another one * @param hexString String to convert * @example * ``` * const string = "string"; * new HexString(string); // "0xstring" * ``` */ constructor(hexString) { if (hexString.startsWith("0x")) { this.hexString = hexString; } else { this.hexString = `0x${hexString}`; } } /** * Getter for inner hexString * @returns Inner hex string */ hex() { return this.hexString; } /** * Getter for inner hexString without prefix * @returns Inner hex string without prefix * @example * ``` * const hexString = new HexString("string"); // "0xstring" * hexString.noPrefix(); // "string" * ``` */ noPrefix() { return this.hexString.slice(2); } /** * Overrides default `toString` method * @returns Inner hex string */ toString() { return this.hex(); } /** * Trimmes extra zeroes in the begining of a string * @returns Inner hexString without leading zeroes * @example * ``` * new HexString("0x000000string").toShortString(); // result = "0xstring" * ``` */ toShortString() { const trimmed = this.hexString.replace(/^0x0*/, ""); return `0x${trimmed}`; } /** * Converts hex string to a Uint8Array * @returns Uint8Array from inner hexString without prefix */ toUint8Array() { return Uint8Array.from(tsSdk.Hex.fromHexInput(this.noPrefix()).toUint8Array()); } }; // src/assets/coin.ts var Coin = class extends Token { constructor(chainId, address, decimals, symbol, name, projectLink) { super( chainId, new HexString(address).toShortString(), decimals, symbol, false, name, projectLink ); } sortsBefore(other) { return super.sortsBefore(other.wrapped); } equals(other) { return this.chainId === other.chainId && new HexString(this.address).toShortString() === new HexString(other.address).toShortString(); } }; var MOVE_COIN = "0x1::aptos_coin::AptosCoin"; var _MoveCoin = class _MoveCoin extends NativeCurrency { constructor(chainId) { super(chainId, 8, "MOVE", "Move Coin"); this.address = MOVE_COIN; this.structTag = tsSdk.parseTypeTag(MOVE_COIN); this.projectLink = "https://movementlabs.xyz"; } static onChain(chainId) { if (this._moveCache[chainId]) { return this._moveCache[chainId]; } const move = new _MoveCoin(chainId); this._moveCache[chainId] = move; return move; } equals(other) { if (other.chainId === this.chainId) { if (other.isNative || other.address === this.address) { return true; } return false; } return false; } get wrapped() { return new Coin( this.chainId, this.address, this.decimals, this.symbol, this.name, this.projectLink ); } sortsBefore(other) { return this.address.toLowerCase() < other.address.toLowerCase(); } get serialize() { return { address: this.address, chainId: this.chainId, decimals: this.decimals, symbol: this.symbol, name: this.name, projectLink: this.projectLink }; } }; _MoveCoin._moveCache = {}; var MoveCoin = _MoveCoin; // src/assets/fungibleAsset.ts var FungibleAsset = class extends Token { constructor(chainId, address, decimals, symbol, name, projectLink) { super( chainId, new HexString(address).toShortString(), decimals, symbol, true, name, projectLink ); } sortsBefore(other) { return super.sortsBefore(other.wrapped); } equals(other) { return this.chainId === other.chainId && new HexString(this.address).toShortString() === new HexString(other.address).toShortString(); } }; // src/amm/viewFunctions.ts var get_pair = (tokenA, tokenB) => { return { typeArguments: [], functionArguments: [tokenA, tokenB], function: `${V2_FACTORY_ADDRESS}::get_pair` }; }; // src/errors.ts var CAN_SET_PROTOTYPE = "setPrototypeOf" in Object; var InsufficientReservesError = class extends Error { constructor() { super(); this.isInsufficientReservesError = true; this.name = this.constructor.name; if (CAN_SET_PROTOTYPE) Object.setPrototypeOf(this, new.target.prototype); } }; var InsufficientInputAmountError = class extends Error { constructor() { super(); this.isInsufficientInputAmountError = true; this.name = this.constructor.name; if (CAN_SET_PROTOTYPE) Object.setPrototypeOf(this, new.target.prototype); } }; // src/utils/computePriceImpact.ts function computePriceImpact(midPrice, inputAmount, outputAmount) { const quotedOutputAmount = midPrice.quote(inputAmount); const priceImpact = quotedOutputAmount.subtract(outputAmount).divide(quotedOutputAmount); return new Percent(priceImpact.numerator, priceImpact.denominator); } function sortedInsert(items, add, maxSize, comparator) { invariant11__default.default(maxSize > 0, "MAX_SIZE_ZERO"); invariant11__default.default(items.length <= maxSize, "ITEMS_SIZE"); if (items.length === 0) { items.push(add); return null; } else { const isFull = items.length === maxSize; if (isFull && comparator(items[items.length - 1], add) <= 0) { return add; } let lo = 0, hi = items.length; while (lo < hi) { const mid = lo + hi >>> 1; if (comparator(items[mid], add) <= 0) { lo = mid + 1; } else { hi = mid; } } items.splice(lo, 0, add); return isFull ? items.pop() : null; } } function sqrt(y) { invariant11__default.default(y >= ZERO, "NEGATIVE"); let z = ZERO; let x; if (y > THREE) { z = y; x = y / TWO + ONE; while (x < z) { z = x; x = (y / x + x) / TWO; } } else if (y !== ZERO) { z = ONE; } return z; } // src/utils/index.ts function balanceComparator(balanceA, balanceB) { if (balanceA && balanceB) { return balanceA.greaterThan(balanceB) ? -1 : balanceA.equalTo(balanceB) ? 0 : 1; } if (balanceA && balanceA.greaterThan("0")) { return -1; } if (balanceB && balanceB.greaterThan("0")) { return 1; } return 0; } function getTokenComparator(balances) { return function sortTokens(tokenA, tokenB) { const balanceA = balances[tokenA.address]; const balanceB = balances[tokenB.address]; const balanceComp = balanceComparator(balanceA, balanceB); if (balanceComp !== 0) return balanceComp; if (tokenA.symbol && tokenB.symbol) { return tokenA.symbol.toLowerCase() < tokenB.symbol.toLowerCase() ? -1 : 1; } return tokenA.symbol ? -1 : tokenB.symbol ? -1 : 0; }; } function sortCurrencies(currencies) { return currencies.sort((a, b) => { if (a.isNative) { return -1; } if (b.isNative) { return 1; } return a.sortsBefore(b) ? -1 : 1; }); } // src/amm/pair.ts var aptosConfig = new tsSdk.AptosConfig({ network: tsSdk.Network.DEVNET }); var client = new tsSdk.Aptos(aptosConfig); var computePairAddress = async ({ tokenA, tokenB }) => { const [token0, token1] = tokenA.sortsBefore(tokenB) ? [tokenA, tokenB] : [tokenB, tokenA]; let address = await client.view({ payload: get_pair(token0.address, token1.address) }); return address[0]; }; var _Pair = class _Pair { static getCacheKey(tokenA, tokenB) { return tokenA.address < tokenB.address ? `${tokenA.address}-${tokenB.address}` : `${tokenB.address}-${tokenA.address}`; } static getAddress(tokenA, tokenB) { const cacheKey = this.getCacheKey(tokenA, tokenB); if (this.addressCache.has(cacheKey)) { return this.addressCache.get(cacheKey); } if (!this.addressComputationPromises.has(cacheKey)) { this.precomputeAddress(tokenA, tokenB); } return "pending_" + cacheKey; } static async precomputeAddress(tokenA, tokenB) { const cacheKey = this.getCacheKey(tokenA, tokenB); if (!this.addressCache.has(cacheKey) && !this.addressComputationPromises.has(cacheKey)) { const computationPromise = computePairAddress({ tokenA, tokenB }).then((addr) => { this.addressCache.set(cacheKey, addr); }).catch((error) => { console.error("Error precomputing pair address:", error); }).finally(() => { this.addressComputationPromises.delete(cacheKey); }); this.addressComputationPromises.set(cacheKey, computationPromise); await computationPromise; } } constructor(currencyAmountA, tokenAmountB) { const tokenAmounts = currencyAmountA.currency.sortsBefore( tokenAmountB.currency ) ? [currencyAmountA, tokenAmountB] : [tokenAmountB, currencyAmountA]; this.liquidityToken = new FungibleAsset( 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; } static async create(currencyAmountA, tokenAmountB) { const tokenAmounts = currencyAmountA.currency.sortsBefore( tokenAmountB.currency ) ? [currencyAmountA, tokenAmountB] : [tokenAmountB, currencyAmountA]; await _Pair.precomputeAddress( tokenAmounts[0].currency, tokenAmounts[1].currency ); return new _Pair(currencyAmountA, tokenAmountB); } /** * 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 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 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) { invariant11__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) { invariant11__default.default(this.involvesToken(token), "TOKEN"); return token.equals(this.token0) ? this.reserve0 : this.reserve1; } getOutputAmount(inputAmount) { invariant11__default.default(this.involvesToken(inputAmount.currency), "TOKEN"); if (this.reserve0.quotient === ZERO || this.reserve1.quotient === ZERO) { throw new InsufficientReservesError(); } const inputReserve = this.reserveOf(inputAmount.currency); const outputReserve = this.reserveOf( inputAmount.currency.equals(this.token0) ? this.token1 : this.token0 ); const inputAmountWithFee = inputAmount.quotient * _997; const numerator = inputAmountWithFee * outputReserve.quotient; const denominator = inputReserve.quotient * _1000 + inputAmountWithFee; const outputAmount = CurrencyAmount.fromRawAmount( inputAmount.currency.equals(this.token0) ? this.token1 : this.token0, numerator / denominator ); if (outputAmount.quotient === ZERO) { throw new InsufficientInputAmountError(); } return [ outputAmount, new _Pair( inputReserve.add(inputAmount), outputReserve.subtract(outputAmount) ) ]; } getInputAmount(outputAmount) { invariant11__default.default(this.involvesToken(outputAmount.currency), "TOKEN"); if (this.reserve0.quotient === ZERO || this.reserve1.quotient === ZERO || outputAmount.quotient >= this.reserveOf(outputAmount.currency).quotient) { throw new 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 * _1000; const denominator = (outputReserve.quotient - outputAmount.quotient) * _997; const inputAmount = CurrencyAmount.fromRawAmount( outputAmount.currency.equals(this.token0) ? this.token1 : this.token0, numerator / denominator + ONE ); return [ inputAmount, new _Pair( inputReserve.add(inputAmount), outputReserve.subtract(outputAmount) ) ]; } getLiquidityMinted(totalSupply, tokenAmountA, tokenAmountB) { invariant11__default.default(totalSupply.currency.equals(this.liquidityToken), "LIQUIDITY"); const tokenAmounts = tokenAmountA.currency.sortsBefore( tokenAmountB.currency ) ? [tokenAmountA, tokenAmountB] : [tokenAmountB, tokenAmountA]; invariant11__default.default( tokenAmounts[0].currency.equals(this.token0) && tokenAmounts[1].currency.equals(this.token1), "TOKEN" ); let liquidity; if (totalSupply.quotient === ZERO) { liquidity = sqrt(tokenAmounts[0].quotient * tokenAmounts[1].quotient) - 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 > ZERO)) { throw new InsufficientInputAmountError(); } return CurrencyAmount.fromRawAmount(this.liquidityToken, liquidity); } getLiquidityValue(token, totalSupply, liquidity, feeOn = false, kLast) { invariant11__default.default(this.involvesToken(token), "TOKEN"); invariant11__default.default(totalSupply.currency.equals(this.liquidityToken), "TOTAL_SUPPLY"); invariant11__default.default(liquidity.currency.equals(this.liquidityToken), "LIQUIDITY"); invariant11__default.default(liquidity.quotient <= totalSupply.quotient, "LIQUIDITY"); let totalSupplyAdjusted; if (!feeOn) { totalSupplyAdjusted = totalSupply; } else { invariant11__default.default(!!kLast, "K_LAST"); const kLastParsed = BigInt(kLast); if (!(kLastParsed === ZERO)) { const rootK = sqrt(this.reserve0.quotient * this.reserve1.quotient); const rootKLast = sqrt(kLastParsed); if (rootK > rootKLast) { const numerator = totalSupply.quotient * (rootK - rootKLast); const denominator = rootK * FIVE + rootKLast; const feeLiquidity = numerator / denominator; totalSupplyAdjusted = totalSupply.add( CurrencyAmount.fromRawAmount(this.liquidityToken, feeLiquidity) ); } else { totalSupplyAdjusted = totalSupply; } } else { totalSupplyAdjusted = totalSupply; } } return CurrencyAmount.fromRawAmount( token, liquidity.quotient * this.reserveOf(token).quotient / totalSupplyAdjusted.quotient ); } }; _Pair.addressCache = /* @__PURE__ */ new Map(); _Pair.addressComputationPromises = /* @__PURE__ */ new Map(); var Pair = _Pair; var Route = class { constructor(pairs, input, output) { this._midPrice = null; invariant11__default.default(pairs.length > 0, "PAIRS"); const chainId = pairs[0].chainId; invariant11__default.default( pairs.every((pair) => pair.chainId === chainId), "CHAIN_IDS" ); const wrappedInput = input.wrapped; invariant11__default.default(pairs[0].involvesToken(wrappedInput), "INPUT"); invariant11__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]; invariant11__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 Price( pair.reserve0.currency, pair.reserve1.currency, pair.reserve0.quotient, pair.reserve1.quotient ) : new 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 Price( this.input, this.output, reduced.denominator, reduced.numerator ); } get chainId() { return this.pairs[0].chainId; } }; 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) { invariant11__default.default(!("ttl" in options) || options.ttl > 0, "TTL"); const to = options.recipient; const amountIn = trade.maximumAmountIn(options.allowedSlippage).quotient.toString(); const amountOut = 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).toString(16)}` : `${options.deadline.toString(16)}`; const useFeeOnTransfer = Boolean(options.feeOnTransfer); let methodName; let args; let typeArgs; switch (trade.tradeType) { case 0 /* EXACT_INPUT */: methodName = useFeeOnTransfer ? "swapExactTokensForTokensSupportingFeeOnTransferTokens" : "swapExactTokensForTokens"; args = [amountIn, amountOut, path, to, deadline]; typeArgs = []; break; case 1 /* EXACT_OUTPUT */: invariant11__default.default(!useFeeOnTransfer, "EXACT_OUT_FOT"); methodName = "swapTokensForExactTokens"; args = [amountOut, amountIn, path, to, deadline]; typeArgs = []; break; } return { methodName, args, typeArgs }; } }; function inputOutputComparator(a, b) { invariant11__default.default( a.inputAmount.currency.equals(b.inputAmount.currency), "INPUT_CURRENCY" ); invariant11__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; } else { return 1; } } else { if (a.outputAmount.lessThan(b.outputAmount)) { return 1; } else { return -1; } } } function tradeComparator(a, b) { const ioComp = inputOutputComparator(a, b); if (ioComp !== 0) { return ioComp; } if (a.priceImpact.lessThan(b.priceImpact)) { return -1; } else 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, 0 /* 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, 1 /* EXACT_OUTPUT */); } constructor(route, amount, tradeType) { this.route = route; this.tradeType = tradeType; const tokenAmounts = new Array(route.path.length); if (tradeType === 0 /* EXACT_INPUT */) { invariant11__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 = CurrencyAmount.fromFractionalAmount( route.input, amount.numerator, amount.denominator ); this.outputAmount = CurrencyAmount.fromFractionalAmount( route.output, tokenAmounts[tokenAmounts.length - 1].numerator, tokenAmounts[tokenAmounts.length - 1].denominator ); } else { invariant11__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 = CurrencyAmount.fromFractionalAmount( route.input, tokenAmounts[0].numerator, tokenAmounts[0].denominator ); this.outputAmount = CurrencyAmount.fromFractionalAmount( route.output, amount.numerator, amount.denominator ); } this.executionPrice = new Price( this.inputAmount.currency, this.outputAmount.currency, this.inputAmount.quotient, this.outputAmount.quotient ); this.priceImpact = 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) { invariant11__default.default(!slippageTolerance.lessThan(ZERO), "SLIPPAGE_TOLERANCE"); if (this.tradeType === 1 /* EXACT_OUTPUT */) { return this.outputAmount; } else { const slippageAdjustedAmountOut = new Fraction(ONE).add(slippageTolerance).invert().multiply(this.outputAmount.quotient).quotient; return 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) { invariant11__default.default(!slippageTolerance.lessThan(ZERO), "SLIPPAGE_TOLERANCE"); if (this.tradeType === 0 /* EXACT_INPUT */) { return this.inputAmount; } else { const slippageAdjustedAmountIn = new Fraction(ONE).add(slippageTolerance).multiply(this.inputAmount.quotient).quotient; return 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 = []) { invariant11__default.default(pairs.length > 0, "PAIRS"); invariant11__default.default(maxHops > 0, "MAX_HOPS"); invariant11__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(ZERO) || pair.reserve1.equalTo(ZERO)) continue; let amountOut; try { [amountOut] = pair.getOutputAmount(amountIn); } catch (error) { if (error.isInsufficientInputAmountError) { continue; } throw error; } if (amountOut.currency.equals(tokenOut)) { sortedInsert( bestTrades, new _Trade( new Route( [...currentPairs, pair], currencyAmountIn.currency, currencyOut ), currencyAmountIn, 0 /* 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 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 = []) { invariant11__default.default(pairs.length > 0, "PAIRS"); invariant11__default.default(maxHops > 0, "MAX_HOPS"); invariant11__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(ZERO) || pair.reserve1.equalTo(ZERO)) continue; let amountIn; try { [amountIn] = pair.getInputAmount(amountOut); } catch (error) { if (error.isInsufficientReservesError) { continue; } throw error; } if (amountIn.currency.equals(tokenIn)) { sortedInsert( bestTrades, new _Trade( new Route( [pair, ...currentPairs], currencyIn, currencyAmountOut.currency ), currencyAmountOut, 1 /* 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; } }; exports.BASIS_POINTS = BASIS_POINTS; exports.BaseCurrency = BaseCurrency; exports.ChainId = ChainId; exports.Coin = Coin; exports.CurrencyAmount = CurrencyAmount; exports.FIVE = FIVE; exports.Fraction = Fraction; exports.FungibleAsset = FungibleAsset; exports.MINIMUM_LIQUIDITY = MINIMUM_LIQUIDITY; exports.MaxUint256 = MaxUint256; exports.MoveCoin = MoveCoin; exports.NativeCurrency = NativeCurrency; exports.NativeCurrencyName = NativeCurrencyName; exports.ONE = ONE; exports.ONE_HUNDRED_PERCENT = ONE_HUNDRED_PERCENT; exports.Pair = Pair; exports.Percent = Percent; exports.Price = Price; exports.Rounding = Rounding; exports.Route = Route; exports.Router = Router; exports.SUPPORTED_CHAINS = SUPPORTED_CHAINS; exports.TEN = TEN; exports.THREE = THREE; exports.TWO = TWO; exports.Token = Token; exports.Trade = Trade; exports.TradeType = TradeType; exports.V2_FACTORY_ADDRESS = V2_FACTORY_ADDRESS; exports.V2_LIBRARY_ADDRESS = V2_LIBRARY_ADDRESS; exports.V2_PAIR_ADDRESS = V2_PAIR_ADDRESS; exports.V2_ROUTER_ADDRESS = V2_ROUTER_ADDRESS; exports.ZERO = ZERO; exports.ZERO_PERCENT = ZERO_PERCENT; exports._100 = _100; exports._1000 = _1000; exports._997 = _997; exports.computePairAddress = computePairAddress; exports.computePriceImpact = computePriceImpact; exports.getTokenComparator = getTokenComparator; exports.get_pair = get_pair; exports.inputOutputComparator = inputOutputComparator; exports.sortCurrencies = sortCurrencies; exports.sortedInsert = sortedInsert; exports.sqrt = sqrt; exports.tradeComparator = tradeComparator;