@razorlabs/dex-sdk
Version:
⚒️ An SDK for building applications on top of Razor DEX
1,424 lines (1,407 loc) • 47.8 kB
JavaScript
'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;