UNPKG

@razorlabs/swap-sdk-core

Version:

🛠 An SDK for building applications on top of RazorDEX.

526 lines (517 loc) • 19 kB
'use strict'; var invariant6 = require('tiny-invariant'); var _Decimal = require('decimal.js-light'); var _Big = require('big.js'); var toFormat = require('toformat'); function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } var invariant6__default = /*#__PURE__*/_interopDefault(invariant6); var _Decimal__default = /*#__PURE__*/_interopDefault(_Decimal); var _Big__default = /*#__PURE__*/_interopDefault(_Big); var toFormat__default = /*#__PURE__*/_interopDefault(toFormat); // 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 ChainId = /* @__PURE__ */ ((ChainId2) => { ChainId2[ChainId2["MAINNET"] = 126] = "MAINNET"; ChainId2[ChainId2["BARDOCK_TESTNET"] = 250] = "BARDOCK_TESTNET"; return ChainId2; })(ChainId || {}); 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 _9975 = 9975n; var _10000 = 10000n; var MaxU256 = BigInt("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); var MoveType = /* @__PURE__ */ ((MoveType3) => { MoveType3["u8"] = "u8"; MoveType3["u16"] = "u16"; MoveType3["u32"] = "u32"; MoveType3["u64"] = "u64"; MoveType3["u128"] = "u128"; MoveType3["u256"] = "u256"; return MoveType3; })(MoveType || {}); var MOVE_TYPE_MAXIMA = { ["u8" /* u8 */]: BigInt("0xff"), ["u16" /* u16 */]: BigInt("0xffff"), ["u32" /* u32 */]: BigInt("0xffffffff"), ["u64" /* u64 */]: BigInt("0xffffffffffffffff"), ["u128" /* u128 */]: BigInt("0xffffffffffffffffffffffffffffffff"), ["u256" /* u256 */]: BigInt("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") }; var BaseCurrency = class { /** * Constructs an instance of the base class `BaseCurrency`. * @param chainId the chain ID on which this currency resides * @param decimals decimals of the currency * @param symbol symbol of the currency * @param name of the currency */ constructor(chainId, decimals, symbol, name) { invariant6__default.default(Number.isSafeInteger(chainId), "CHAIN_ID"); invariant6__default.default(decimals >= 0 && decimals < 255 && Number.isInteger(decimals), "DECIMALS"); this.chainId = chainId; this.decimals = decimals; this.symbol = symbol; this.name = name; } }; 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 */) { invariant6__default.default(Number.isInteger(significantDigits), `${significantDigits} is not an integer.`); invariant6__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 */) { invariant6__default.default(Number.isInteger(decimalPlaces), `${decimalPlaces} is not an integer.`); invariant6__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/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); } }; var Big2 = toFormat__default.default(_Big__default.default); var CurrencyAmount = class _CurrencyAmount extends Fraction { /** * Returns a new currency amount instance from the unit-less amount of token, i.e. the raw amount * @param currency the currency in the amount * @param rawAmount the raw token or ether amount */ static fromRawAmount(currency, rawAmount) { return new _CurrencyAmount(currency, rawAmount); } /** * Construct a currency amount with a denominator that is not equal to 1 * @param currency the currency * @param numerator the numerator of the fractional token amount * @param denominator the denominator of the fractional token amount */ static fromFractionalAmount(currency, numerator, denominator) { return new _CurrencyAmount(currency, numerator, denominator); } constructor(currency, numerator, denominator) { super(numerator, denominator); invariant6__default.default(this.quotient <= MaxU256, "AMOUNT"); this.currency = currency; this.decimalScale = 10n ** BigInt(currency.decimals); } add(other) { invariant6__default.default(this.currency.equals(other.currency), "CURRENCY"); const added = super.add(other); return _CurrencyAmount.fromFractionalAmount(this.currency, added.numerator, added.denominator); } subtract(other) { invariant6__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 */) { invariant6__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) { invariant6__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) { invariant6__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); } }; // src/nativeCurrency.ts var NativeCurrency = class extends BaseCurrency { constructor() { super(...arguments); this.isNative = true; this.isToken = false; } }; var Token = class extends BaseCurrency { constructor(chainId, address, decimals, symbol, name, projectLink) { super(chainId, decimals, symbol, name); this.isNative = false; this.isToken = true; this.address = address; this.projectLink = projectLink; } /** * Returns true if the two tokens are equivalent, i.e. have the same chainId and address. * @param other other token to compare */ equals(other) { return other.isToken && this.chainId === other.chainId && this.address === other.address; } /** * Returns true if the address of this token sorts before the address of the other token * @param other other token to compare * @throws if the tokens have the same address * @throws if the tokens are on different chains */ sortsBefore(other) { invariant6__default.default(this.chainId === other.chainId, "CHAIN_IDS"); invariant6__default.default(this.address !== other.address, "ADDRESSES"); return this.address.toLowerCase() < other.address.toLowerCase(); } /** * Return this token, which does not need to be wrapped */ 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/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); } }; function validateMoveTypeInstance(value, moveType) { invariant6__default.default(value >= ZERO, `${value} is not a ${moveType}.`); invariant6__default.default(value <= MOVE_TYPE_MAXIMA[moveType], `${value} is not a ${moveType}.`); } function sqrt(y) { invariant6__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; } function sortedInsert(items, add, maxSize, comparator) { invariant6__default.default(maxSize > 0, "MAX_SIZE_ZERO"); invariant6__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 computePriceImpact(midPrice, inputAmount, outputAmount) { const quotedOutputAmount = midPrice.quote(inputAmount); const priceImpact = quotedOutputAmount.subtract(outputAmount).divide(quotedOutputAmount); return new Percent(priceImpact.numerator, priceImpact.denominator); } 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; }); } function getCurrencyAddress(currency) { if (currency.isNative) { return "0xA"; } return currency.address; } exports.BaseCurrency = BaseCurrency; exports.ChainId = ChainId; exports.CurrencyAmount = CurrencyAmount; exports.FIVE = FIVE; exports.Fraction = Fraction; exports.InsufficientInputAmountError = InsufficientInputAmountError; exports.InsufficientReservesError = InsufficientReservesError; exports.MINIMUM_LIQUIDITY = MINIMUM_LIQUIDITY; exports.MOVE_TYPE_MAXIMA = MOVE_TYPE_MAXIMA; exports.MaxU256 = MaxU256; exports.MoveType = MoveType; exports.NativeCurrency = NativeCurrency; exports.ONE = ONE; exports.Percent = Percent; exports.Price = Price; exports.Rounding = Rounding; exports.TEN = TEN; exports.THREE = THREE; exports.TWO = TWO; exports.Token = Token; exports.TradeType = TradeType; exports.ZERO = ZERO; exports._100 = _100; exports._10000 = _10000; exports._9975 = _9975; exports.computePriceImpact = computePriceImpact; exports.getCurrencyAddress = getCurrencyAddress; exports.getTokenComparator = getTokenComparator; exports.sortCurrencies = sortCurrencies; exports.sortedInsert = sortedInsert; exports.sqrt = sqrt; exports.validateMoveTypeInstance = validateMoveTypeInstance;