Commit 563f413c authored by Noah Zinsmeister's avatar Noah Zinsmeister

add MVP implementation

parent ddea6611
......@@ -2,3 +2,4 @@
[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)
[![Actions Status](https://github.com/Uniswap/uniswap-sdk/workflows/CI/badge.svg)](https://github.com/Uniswap/uniswap-sdk)
[![npm version](https://img.shields.io/npm/v/@uniswap/sdk/next.svg)](https://www.npmjs.com/package/@uniswap/sdk/v/next)
{
"name": "@uniswap/sdk",
"license": "GPL-3.0-or-later",
"version": "2.0.0",
"version": "2.0.0-beta.0",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"files": [
......@@ -20,11 +20,19 @@
"test": "tsdx test"
},
"devDependencies": {
"@types/big.js": "^4.0.5",
"@types/jest": "^24.0.25",
"tsdx": "^0.12.1",
"tslib": "^1.10.0",
"typescript": "^3.7.4"
},
"dependencies": {
"@ethersproject/address": "^5.0.0-beta.134",
"big.js": "^5.2.2",
"decimal.js-light": "^2.5.0",
"tiny-invariant": "^1.0.6",
"toformat": "^2.0.0"
},
"prettier": {
"printWidth": 120,
"semi": false,
......
// exports for external consumption
export enum ChainId {
RINKEBY = 4
}
export const WETH = {
[ChainId.RINKEBY]: {
chainId: ChainId.RINKEBY,
address: '0xc778417E063141139Fce010982780140Aa0cD5Ab',
decimals: 18
}
}
export enum TradeType {
EXACT_INPUT,
EXACT_OUTPUT
}
// exports for internal consumption
export const ZERO = BigInt(0)
export const TEN = BigInt(10)
export const _1000 = BigInt(1000)
export const _997 = BigInt(997)
export enum SolidityType {
uint8,
uint256
}
declare module 'toformat'
import invariant from 'tiny-invariant'
import { SolidityType } from '../constants'
import { BigintIsh } from '../types'
import { parseBigintIsh } from '../utils/parseInputs'
import { validateSolidityTypeInstance } from '../utils/validateInputs'
import { Token } from './token'
export class Exchange {
public readonly pair: [Token, Token]
public readonly balances: [bigint, bigint]
static validate(pair: [Token, Token], balances: [bigint, bigint]) {
// validate components of an Exchange
balances.forEach(balance => validateSolidityTypeInstance(balance, SolidityType.uint256))
// validate conditions that must be true of an Exchange
const chainIds = pair.map(token => token.chainId)
invariant(chainIds[0] === chainIds[1], `${chainIds} are not equal.`)
const addresses = pair.map(token => token.address)
invariant(addresses[0] < addresses[1], `${addresses} are not ordered.`)
}
constructor(pair: [Token, Token], balances: [BigintIsh, BigintIsh]) {
const balancesParsed = balances.map(balance => parseBigintIsh(balance))
const inOrder = pair[0].address < pair[1].address
const orderedPair = (inOrder ? pair : pair.slice().reverse()) as [Token, Token]
const orderedBalances = (inOrder ? balancesParsed : balancesParsed.slice().reverse()) as [bigint, bigint]
Exchange.validate(orderedPair, orderedBalances)
this.pair = orderedPair
this.balances = orderedBalances
}
}
export * from './token'
export * from './exchange'
export * from './route'
export * from './trade'
import invariant from 'tiny-invariant'
import { TEN } from '../constants'
import { Fraction, BigintIsh } from '../types'
import { parseBigintIsh } from '../utils/parseInputs'
import { formatSignificant, formatFixed } from '../utils/formatOutputs'
import { Route } from './route'
export class Rate {
public readonly rate: Fraction
constructor(rate: Fraction) {
this.rate = rate
}
public formatSignificant(significantDigits = 6, invert = false, ...rest: any[]): string {
const fraction = (invert ? this.rate.slice().reverse() : this.rate) as Fraction
return formatSignificant(fraction, significantDigits, ...rest)
}
public formatFixed(decimalPlaces = 6, invert = false, ...rest: any[]): string {
const fraction = (invert ? this.rate.slice().reverse() : this.rate) as Fraction
return formatFixed(fraction, decimalPlaces, ...rest)
}
}
export class Price extends Rate {
public readonly scalar: Fraction // used to convert back to balances
static fromRoute(route: Route): Price {
const rates: Fraction[] = route.exchanges.map((exchange, i) => {
const input = route.path[i]
const baseIndex = input.address === exchange.pair[0].address ? 0 : 1
const quoteIndex = input.address === exchange.pair[0].address ? 1 : 0
return [
exchange.balances[quoteIndex] * TEN ** BigInt(exchange.pair[baseIndex].decimals),
exchange.balances[baseIndex] * TEN ** BigInt(exchange.pair[quoteIndex].decimals)
]
})
const rate: Fraction = [
rates.map(rate => rate[0]).reduce((accumulator, currentValue) => accumulator * currentValue, BigInt(1)),
rates.map(rate => rate[1]).reduce((accumulator, currentValue) => accumulator * currentValue, BigInt(1))
]
const scalar: Fraction = [TEN ** BigInt(route.output.decimals), TEN ** BigInt(route.input.decimals)]
return new Price(rate, scalar)
}
constructor(rate: Fraction, scalar: Fraction) {
super(rate)
this.scalar = scalar
}
public quote(amount: BigintIsh, invert = false): bigint {
const amountParsed = parseBigintIsh(amount)
invariant(amountParsed > 0, `${amountParsed} isn't positive.`)
const [numeratorRate, denominatorRate] = invert ? this.rate.slice().reverse() : this.rate
const [numeratorScalar, denominatorScalar] = invert
? (this.scalar as Fraction).slice().reverse()
: (this.scalar as Fraction)
return (amountParsed * numeratorRate * numeratorScalar) / (denominatorRate * denominatorScalar)
}
}
export class Percent extends Rate {
public formatSignificant(significantDigits = 6, ...rest: any[]): string {
return formatSignificant([this.rate[0] * BigInt(100), this.rate[1]], significantDigits, ...rest)
}
public formatFixed(decimalPlaces = 6, ...rest: any[]): string {
return formatFixed([this.rate[0] * BigInt(100), this.rate[1]], decimalPlaces, ...rest)
}
public formatSignificantRaw(...args: any[]) {
return super.formatSignificant(...args)
}
public formatFixedRaw(...args: any[]) {
return super.formatFixed(...args)
}
}
import invariant from 'tiny-invariant'
import { Token } from './token'
import { Exchange } from './exchange'
import { Price } from './rate'
export class Route {
public readonly exchanges: Exchange[]
public readonly path: Token[]
public readonly midPrice: Price
static validate(exchanges: Exchange[], input: Token): Token[] {
// validate components of a Route
invariant(exchanges.length > 0, `${exchanges} does not consist of at least 1 exchange.`)
// validate conditions that must be true of a Route
const chainIds = exchanges.flatMap(exchange => exchange.pair.map(token => token.chainId))
chainIds.forEach((chainId, _, array) => invariant(chainId === array[0], `${chainIds} are not all equal.`))
const path = [input]
exchanges.forEach((exchange, i) => {
const currentInput = path[i]
const addresses = exchange.pair.map(token => token.address)
invariant(addresses.includes(currentInput.address), `${addresses} does not contain ${input.address}.`)
const output = currentInput.address === addresses[0] ? exchange.pair[1] : exchange.pair[0]
path.push(output)
})
invariant(path.length === new Set(path).size, `${path} contains duplicate addresses.`)
return path
}
constructor(exchanges: Exchange[], input: Token) {
const path = Route.validate(exchanges, input)
this.exchanges = exchanges
this.path = path
this.midPrice = Price.fromRoute(this)
}
get input(): Token {
return this.path[0]
}
get output(): Token {
return this.path[this.path.length - 1]
}
}
import { SolidityType } from '../constants'
import { validateChainId, validateAddress, validateSolidityTypeInstance } from '../utils/validateInputs'
export class Token {
public readonly chainId: number
public readonly address: string
public readonly decimals: number
static validate(chainId: number, address: string, decimals: number) {
validateChainId(chainId)
validateAddress(address)
validateSolidityTypeInstance(BigInt(decimals), SolidityType.uint8)
}
constructor(chainId: number, address: string, decimals: number) {
Token.validate(chainId, address, decimals)
this.chainId = chainId
this.address = address
this.decimals = decimals
}
}
import invariant from 'tiny-invariant'
import { _997, _1000, SolidityType, TradeType } from '../constants'
import { BigintIsh, Fraction } from '../types'
import { parseBigintIsh } from '../utils/parseInputs'
import { validateSolidityTypeInstance } from '../utils/validateInputs'
import { Exchange } from './exchange'
import { Route } from './route'
import { Price, Percent } from './rate'
function getOutputAmount(inputAmount: bigint, inputReserve: bigint, outputReserve: bigint): bigint {
invariant(inputAmount > 0, `${inputAmount} is not positive.`)
invariant(inputReserve > 0, `${inputReserve} is not positive.`)
invariant(outputReserve > 0, `${outputReserve} is not positive.`)
const inputAmountWithFee = inputAmount * _997
const numerator = inputAmountWithFee * outputReserve
const denominator = inputReserve * _1000 + inputAmountWithFee
return numerator / denominator
}
function getInputAmount(outputAmount: bigint, inputReserve: bigint, outputReserve: bigint): bigint {
invariant(outputAmount > 0, `${outputAmount} is not positive.`)
invariant(inputReserve > 0, `${inputReserve} is not positive.`)
invariant(outputReserve > 0, `${outputReserve} is not positive.`)
const numerator = inputReserve * outputAmount * _1000
const denominator = (outputReserve - outputAmount) * _997
return numerator / denominator + BigInt(1)
}
function getSlippage(inputAmount: bigint, midPrice: Price, outputAmount: bigint): Percent {
const exactQuote: Fraction = [
inputAmount * midPrice.rate[0] * midPrice.scalar[0],
midPrice.rate[1] * midPrice.scalar[1]
]
const normalizedNumerator: Fraction = [outputAmount * exactQuote[1] - exactQuote[0], exactQuote[1]]
const invertedDenominator = exactQuote.slice().reverse() as Fraction
return new Percent([normalizedNumerator[0] * invertedDenominator[0], normalizedNumerator[1] * invertedDenominator[1]])
}
function getPercentChange(referenceRate: Price, newRate: Price): Percent {
const normalizedNumerator: Fraction = [
newRate.rate[0] * referenceRate.rate[1] - referenceRate.rate[0] * newRate.rate[1],
referenceRate.rate[1] * newRate.rate[1]
]
const invertedDenominator = referenceRate.rate.slice().reverse() as Fraction
return new Percent([normalizedNumerator[0] * invertedDenominator[0], normalizedNumerator[1] * invertedDenominator[1]])
}
export class Trade {
public readonly route: Route
public readonly inputAmount: bigint
public readonly outputAmount: bigint
public readonly tradeType: TradeType
public readonly executionPrice: Price
public readonly nextMidPrice: Price
public readonly slippage: Percent
public readonly midPricePercentChange: Percent
static validate(amount: bigint) {
validateSolidityTypeInstance(amount, SolidityType.uint256)
}
constructor(route: Route, amount: BigintIsh, tradeType: TradeType) {
const amountParsed = parseBigintIsh(amount)
Trade.validate(amountParsed)
const amounts: bigint[] = new Array(route.exchanges.length + 1)
const nextExchanges: Exchange[] = new Array(route.exchanges.length)
if (tradeType === TradeType.EXACT_INPUT) {
amounts[0] = amountParsed
route.exchanges.forEach((exchange, i) => {
const input = route.path[i]
const inputIndex = input.address === exchange.pair[0].address ? 0 : 1
const outputIndex = input.address === exchange.pair[0].address ? 1 : 0
const inputAmount = amounts[i]
const outputAmount = getOutputAmount(inputAmount, exchange.balances[inputIndex], exchange.balances[outputIndex])
amounts[i + 1] = outputAmount
const nextExchange = new Exchange(
[exchange.pair[inputIndex], exchange.pair[outputIndex]],
[exchange.balances[inputIndex] + inputAmount, exchange.balances[outputIndex] - outputAmount]
)
nextExchanges[i] = nextExchange
})
} else if (tradeType === TradeType.EXACT_OUTPUT) {
amounts[amounts.length - 1] = amountParsed
route.exchanges
.slice()
.reverse()
.forEach((exchange, i) => {
const inverseIndex = route.exchanges.length - 1 - i
const input = route.path[inverseIndex]
const inputIndex = input.address === exchange.pair[0].address ? 0 : 1
const outputIndex = input.address === exchange.pair[0].address ? 1 : 0
const outputAmount = amounts[inverseIndex + 1]
const inputAmount = getInputAmount(
outputAmount,
exchange.balances[inputIndex],
exchange.balances[outputIndex]
)
amounts[inverseIndex] = inputAmount
const nextExchange = new Exchange(
[exchange.pair[inputIndex], exchange.pair[outputIndex]],
[exchange.balances[inputIndex] + inputAmount, exchange.balances[outputIndex] - outputAmount]
)
nextExchanges[inverseIndex] = nextExchange
})
}
this.route = route
const inputAmount = amounts[0]
const outputAmount = amounts[amounts.length - 1]
this.inputAmount = inputAmount
this.outputAmount = outputAmount
this.tradeType = tradeType
const executionPrice = new Price(
[outputAmount * route.midPrice.scalar[1], inputAmount * route.midPrice.scalar[0]],
route.midPrice.scalar
)
this.executionPrice = executionPrice
this.slippage = getSlippage(inputAmount, route.midPrice, outputAmount)
const nextMidPrice = Price.fromRoute(new Route(nextExchanges, route.input))
this.nextMidPrice = nextMidPrice
this.midPricePercentChange = getPercentChange(route.midPrice, nextMidPrice)
}
}
export const ONE = BigInt(1)
export * from './types'
export { ChainId, WETH, TradeType } from './constants'
export * from './entities'
export type BigintIsh = bigint | string
export type Fraction = [bigint, bigint]
import invariant from 'tiny-invariant'
import _Decimal from 'decimal.js-light'
import _Big, { RoundingMode } from 'big.js'
import toFormat from 'toformat'
import { Fraction } from '../types'
const Decimal = toFormat(_Decimal)
const Big = toFormat(_Big)
export function formatSignificant(
[numerator, denominator]: Fraction,
significantDigits: number,
format: object = { groupSeparator: '' },
roundingMode?: number
): string {
invariant(Number.isInteger(significantDigits), `${significantDigits} is not an integer.`)
invariant(significantDigits > 0, `${significantDigits} isn't positive.`)
Decimal.set({ precision: significantDigits + 1, rounding: roundingMode ?? Decimal.ROUND_HALF_UP })
const quotient = new Decimal(numerator.toString()).div(denominator.toString()).toSignificantDigits(significantDigits)
return quotient.toFormat(quotient.decimalPlaces(), format)
}
export function formatFixed(
[numerator, denominator]: Fraction,
decimalPlaces: number,
format: object = { groupSeparator: '' },
roundingMode?: RoundingMode
): string {
invariant(Number.isInteger(decimalPlaces), `${decimalPlaces} is not an integer.`)
invariant(decimalPlaces >= 0, `${decimalPlaces} is negative.`)
Big.DP = decimalPlaces
Big.RM = roundingMode ?? RoundingMode.RoundHalfUp
return new Big(numerator.toString()).div(denominator.toString()).toFormat(decimalPlaces, format)
}
import { BigintIsh } from '../types'
export function parseBigintIsh(bigintIsh: BigintIsh): bigint {
return typeof bigintIsh === 'string' ? BigInt(bigintIsh) : bigintIsh
}
import invariant from 'tiny-invariant'
import { getAddress } from '@ethersproject/address'
import { ZERO, ChainId, SolidityType } from '../constants'
export function validateChainId(chainId: number) {
invariant(!!ChainId[chainId], `${chainId} is not a supported chainId.`)
}
export function validateAddress(address: string) {
try {
if (address !== getAddress(address)) {
throw Error('Address is not properly checksummed.')
}
} catch (error) {
invariant(false, `${address} is an invalid address. ${error}`)
}
}
const SolidityTypeMaxima = {
[SolidityType.uint8]: BigInt(2 ** 8 - 1),
[SolidityType.uint256]: BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff')
}
export function validateSolidityTypeInstance(value: bigint, solidityType: SolidityType) {
invariant(value >= ZERO, `${value.toString()} is negative.`)
invariant(value <= SolidityTypeMaxima[solidityType], `${value.toString()} is too large.`)
}
import { ONE } from '../src'
describe('blah', () => {
it('works', () => {
expect(ONE.toString()).toEqual('1')
})
})
import { ChainId, WETH as _WETH, TradeType, Token, Exchange, Route, Trade } from '../src'
const ADDRESSES = [
'0x0000000000000000000000000000000000000000',
'0x0000000000000000000000000000000000000001',
'0x0000000000000000000000000000000000000002'
]
const CHAIN_ID = ChainId.RINKEBY
const WETH = _WETH[ChainId.RINKEBY]
function getTokens(n: number, decimals: number | number[]) {
return ADDRESSES.slice(0, n).map(
(address, i) => new Token(CHAIN_ID, address, typeof decimals === 'number' ? decimals : decimals[i])
)
}
function decimalize(amount: number, decimal: number): bigint {
return BigInt(amount) * BigInt(10) ** BigInt(decimal)
}
describe('entities', () => {
;[
[0, 0, 0],
[0, 9, 18],
[18, 18, 18]
].forEach(decimals => {
describe(`decimals: ${decimals}`, () => {
let tokens: Token[]
it('Token', () => {
tokens = getTokens(3, decimals)
tokens.forEach((token, i) => {
expect(token.chainId).toEqual(CHAIN_ID)
expect(token.address).toEqual(ADDRESSES[i])
expect(token.decimals).toEqual(decimals[i])
})
})
let exchanges: Exchange[]
it('Exchange', () => {
const pairs: [Token, Token][] = [
[tokens[0], tokens[1]],
[tokens[1], tokens[2]],
[tokens[2], WETH]
]
const balances: [bigint, bigint][] = [
[decimalize(1, pairs[0][0].decimals), decimalize(1, pairs[0][1].decimals)],
[decimalize(1, pairs[1][0].decimals), decimalize(1, pairs[1][1].decimals)],
[decimalize(1, pairs[2][0].decimals), decimalize(1234, pairs[2][1].decimals)]
]
exchanges = [
new Exchange(pairs[0], balances[0]),
new Exchange(pairs[1], balances[1]),
new Exchange(pairs[2], balances[2])
]
})
let route: Route
it('Route', () => {
route = new Route(exchanges, tokens[0])
expect(route.path).toEqual(tokens.concat([WETH]))
expect(route.input).toEqual(tokens[0])
expect(route.output).toEqual(WETH)
})
it('Rate via Route.marketRate', () => {
expect(route.midPrice.quote(decimalize(1, route.input.decimals))).toEqual(
decimalize(1234, route.output.decimals)
)
expect(route.midPrice.quote(decimalize(1234, route.output.decimals), true)).toEqual(
decimalize(1, route.input.decimals)
)
expect(route.midPrice.formatSignificant(1)).toEqual('1000')
expect(route.midPrice.formatSignificant(2)).toEqual('1200')
expect(route.midPrice.formatSignificant(3)).toEqual('1230')
expect(route.midPrice.formatSignificant(4)).toEqual('1234')
expect(route.midPrice.formatSignificant(5)).toEqual('1234')
expect(route.midPrice.formatSignificant(4, undefined, { groupSeparator: ',' })).toEqual('1,234')
expect(route.midPrice.formatSignificant(1, true)).toEqual('0.0008')
expect(route.midPrice.formatSignificant(2, true)).toEqual('0.00081')
expect(route.midPrice.formatSignificant(3, true)).toEqual('0.00081')
expect(route.midPrice.formatSignificant(4, true)).toEqual('0.0008104')
expect(route.midPrice.formatSignificant(4, true, undefined, 1)).toEqual('0.0008103')
expect(route.midPrice.formatSignificant(5, true)).toEqual('0.00081037')
expect(route.midPrice.formatFixed(0)).toEqual('1234')
expect(route.midPrice.formatFixed(1)).toEqual('1234.0')
expect(route.midPrice.formatFixed(2)).toEqual('1234.00')
expect(route.midPrice.formatFixed(2, undefined, { groupSeparator: ',' })).toEqual('1,234.00')
expect(route.midPrice.formatFixed(0, true)).toEqual('0')
expect(route.midPrice.formatFixed(1, true)).toEqual('0.0')
expect(route.midPrice.formatFixed(4, true)).toEqual('0.0008')
expect(route.midPrice.formatFixed(5, true)).toEqual('0.00081')
expect(route.midPrice.formatFixed(6, true)).toEqual('0.000810')
expect(route.midPrice.formatFixed(7, true)).toEqual('0.0008104')
expect(route.midPrice.formatFixed(7, true, undefined, 0)).toEqual('0.0008103')
expect(route.midPrice.formatFixed(8, true)).toEqual('0.00081037')
})
describe('Trade', () => {
it('TradeType.EXACT_INPUT', () => {
const exchanges = [
new Exchange([tokens[1], WETH], [decimalize(5, tokens[1].decimals), decimalize(10, WETH.decimals)])
]
const route = new Route(exchanges, tokens[1])
const inputAmount = decimalize(1, tokens[1].decimals)
const trade = new Trade(route, inputAmount, TradeType.EXACT_INPUT)
expect(trade.inputAmount).toEqual(inputAmount)
expect(trade.outputAmount).toEqual(BigInt('1662497915624478906'))
expect(trade.executionPrice.formatSignificant(18)).toEqual('1.66249791562447891')
expect(trade.executionPrice.formatSignificant(18, true)).toEqual('0.601504513540621866')
expect(trade.executionPrice.quote(inputAmount)).toEqual(trade.outputAmount)
expect(trade.executionPrice.quote(trade.outputAmount, true)).toEqual(inputAmount)
expect(trade.nextMidPrice.formatSignificant(18)).toEqual('1.38958368072925352')
expect(trade.nextMidPrice.formatSignificant(18, true)).toEqual('0.71964')
expect(trade.slippage.formatSignificant(18)).toEqual('-16.8751042187760547')
expect(trade.slippage.formatSignificantRaw(18)).toEqual('-0.168751042187760547')
expect(trade.midPricePercentChange.formatSignificant(18)).toEqual('-30.5208159635373242')
expect(trade.midPricePercentChange.formatSignificantRaw(18)).toEqual('-0.305208159635373242')
})
it('TradeType.EXACT_OUTPUT', () => {
const exchanges = [
new Exchange([tokens[1], WETH], [decimalize(5, tokens[1].decimals), decimalize(10, WETH.decimals)])
]
const route = new Route(exchanges, tokens[1])
const outputAmount = BigInt('1662497915624478906')
const trade = new Trade(route, outputAmount, TradeType.EXACT_OUTPUT)
expect(trade.inputAmount).toEqual(decimalize(1, tokens[1].decimals))
expect(trade.outputAmount).toEqual(outputAmount)
// TODO think about inverse execution price?
expect(trade.executionPrice.formatSignificant(18)).toEqual('1.66249791562447891')
expect(trade.executionPrice.formatSignificant(18, true)).toEqual('0.601504513540621866')
expect(trade.executionPrice.quote(trade.inputAmount)).toEqual(outputAmount)
expect(trade.executionPrice.quote(outputAmount, true)).toEqual(trade.inputAmount)
expect(trade.nextMidPrice.formatSignificant(18)).toEqual('1.38958368072925352')
expect(trade.nextMidPrice.formatSignificant(18, true)).toEqual('0.71964')
expect(trade.slippage.formatSignificant(18)).toEqual('-16.8751042187760547')
expect(trade.slippage.formatSignificantRaw(18)).toEqual('-0.168751042187760547')
expect(trade.midPricePercentChange.formatSignificant(18)).toEqual('-30.5208159635373242')
expect(trade.midPricePercentChange.formatSignificantRaw(18)).toEqual('-0.305208159635373242')
})
it('minimum TradeType.EXACT_INPUT', () => {
if ([9, 18].includes(tokens[1].decimals)) {
const exchanges = [
new Exchange(
[tokens[1], WETH],
[
decimalize(1, tokens[1].decimals),
decimalize(10, WETH.decimals) +
(tokens[1].decimals === 9 ? BigInt('30090280812437312') : BigInt('30090270812437322'))
]
)
]
const route = new Route(exchanges, tokens[1])
const trade = new Trade(route, '1', TradeType.EXACT_INPUT)
expect(trade.slippage.formatSignificant(18)).toEqual(
tokens[1].decimals === 9 ? '-0.300000099400899902' : '-0.3000000000000001'
)
expect(trade.slippage.formatSignificantRaw(18)).toEqual(
tokens[1].decimals === 9 ? '-0.00300000099400899902' : '-0.003000000000000001'
)
}
})
})
})
})
})
{
"include": ["src", "types", "test"],
"compilerOptions": {
"target": "es5",
"target": "es2016",
"module": "esnext",
"lib": ["dom", "esnext", "esnext.bigint"],
"importHelpers": true,
......
......@@ -741,6 +741,62 @@
exec-sh "^0.3.2"
minimist "^1.2.0"
"@ethersproject/address@^5.0.0-beta.134":
version "5.0.0-beta.134"
resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.0.0-beta.134.tgz#9c1790c87b763dc547ac12e2dbc9fa78d0799a71"
integrity sha512-FHhUVJTUIg2pXvOOhIt8sB1cQbcwrzZKzf9CPV7JM1auli20nGoYhyMFYGK7u++GXzTMJduIkU1OwlIBupewDw==
dependencies:
"@ethersproject/bignumber" ">=5.0.0-beta.130"
"@ethersproject/bytes" ">=5.0.0-beta.129"
"@ethersproject/keccak256" ">=5.0.0-beta.127"
"@ethersproject/logger" ">=5.0.0-beta.129"
"@ethersproject/rlp" ">=5.0.0-beta.126"
bn.js "^4.4.0"
"@ethersproject/bignumber@>=5.0.0-beta.130":
version "5.0.0-beta.135"
resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.0.0-beta.135.tgz#9d464df8967f5d314d109497e4f25ab82314c098"
integrity sha512-7Tw2NgHzK7o+70bwyoaIZCbRycz+saWNU0sLOYnis3qYXwYsdTL+Rm0PMGA2v4jyHJt7BPS2pxGww+akVXbX+w==
dependencies:
"@ethersproject/bytes" ">=5.0.0-beta.129"
"@ethersproject/logger" ">=5.0.0-beta.129"
"@ethersproject/properties" ">=5.0.0-beta.131"
bn.js "^4.4.0"
"@ethersproject/bytes@>=5.0.0-beta.129":
version "5.0.0-beta.134"
resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.0.0-beta.134.tgz#eb6b9a200be02d6d539f90086fbfad26ec63d0d5"
integrity sha512-2BULy5x0BuHPRzuGjEYWndJieDaRAaZUbMk53fZjH4yjhKMHDGADQQOtaSD4wN+H183YOXMGdtlCrRGJ1N+uNg==
dependencies:
"@ethersproject/logger" ">=5.0.0-beta.129"
"@ethersproject/keccak256@>=5.0.0-beta.127":
version "5.0.0-beta.131"
resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.0.0-beta.131.tgz#b5778723ee75208065b9b9ad30c71d480f41bb31"
integrity sha512-KQnqMwGV0IMOjAr/UTFO8DuLrmN1uaMvcV3zh9hiXhh3rCuY+WXdeUh49w1VQ94kBKmaP0qfGb7z4SdhUWUHjw==
dependencies:
"@ethersproject/bytes" ">=5.0.0-beta.129"
js-sha3 "0.5.7"
"@ethersproject/logger@>=5.0.0-beta.129":
version "5.0.0-beta.133"
resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.0.0-beta.133.tgz#2d62d495ed413c7045054d4f99a0fb4920079b2e"
integrity sha512-1ISf7rFKFbMHlEB37JS7Oy3FgFlvzF2Ze2uFZMJHGKp9xgDvFy1VHNMBM1KrJPK4AqCZXww0//e2keLsN3g/Cw==
"@ethersproject/properties@>=5.0.0-beta.131":
version "5.0.0-beta.136"
resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.0.0-beta.136.tgz#4834f6eeb4d66aa9d2bb4d8b7a8517077df3eb63"
integrity sha512-hK/fPtXjbcKeQyZV6rojDobToKNZ7bNYNY+jrUZRB9B5fStmU4veEwfP80xXB2WSNfZF6jRKdmmwmGj8QeHP0Q==
dependencies:
"@ethersproject/logger" ">=5.0.0-beta.129"
"@ethersproject/rlp@>=5.0.0-beta.126":
version "5.0.0-beta.131"
resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.0.0-beta.131.tgz#4a0c0c314e26ed7f01be6bca16308d629a8022d2"
integrity sha512-sUJUGbywlnuk2frkSWzWiGenTrwOnrKQaNKJqjCGmK35x0WIzcR4/1gC6jWa0hpWJT6Seq6J6SCT5CS+ZWCFNw==
dependencies:
"@ethersproject/bytes" ">=5.0.0-beta.129"
"@jest/console@^24.7.1", "@jest/console@^24.9.0":
version "24.9.0"
resolved "https://registry.yarnpkg.com/@jest/console/-/console-24.9.0.tgz#79b1bc06fb74a8cfb01cbdedf945584b1b9707f0"
......@@ -937,6 +993,11 @@
dependencies:
"@babel/types" "^7.3.0"
"@types/big.js@^4.0.5":
version "4.0.5"
resolved "https://registry.yarnpkg.com/@types/big.js/-/big.js-4.0.5.tgz#62c61697646269e39191f24e55e8272f05f21fc0"
integrity sha512-D9KFrAt05FDSqLo7PU9TDHfDgkarlwdkuwFsg7Zm4xl62tTNaz+zN+Tkcdx2wGLBbSMf8BnoMhOVeUGUaJfLKg==
"@types/eslint-visitor-keys@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d"
......@@ -1493,6 +1554,11 @@ bcrypt-pbkdf@^1.0.0:
dependencies:
tweetnacl "^0.14.3"
big.js@^5.2.2:
version "5.2.2"
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
binary-extensions@^1.0.0:
version "1.13.1"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65"
......@@ -1510,6 +1576,11 @@ bluebird@3.5.5:
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f"
integrity sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==
bn.js@^4.4.0:
version "4.11.8"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f"
integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
......@@ -1933,6 +2004,11 @@ decamelize@^1.2.0:
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
decimal.js-light@^2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/decimal.js-light/-/decimal.js-light-2.5.0.tgz#ca7faf504c799326df94b0ab920424fdfc125348"
integrity sha512-b3VJCbd2hwUpeRGG3Toob+CRo8W22xplipNhP3tN7TSVB/cyMX71P1vM2Xjc9H74uV6dS2hDDmo/rHq8L87Upg==
decode-uri-component@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
......@@ -3537,6 +3613,11 @@ js-levenshtein@^1.1.3:
resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d"
integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==
js-sha3@0.5.7:
version "0.5.7"
resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7"
integrity sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
......@@ -5422,6 +5503,11 @@ tiny-glob@^0.2.6:
globalyzer "^0.1.0"
globrex "^0.1.1"
tiny-invariant@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.0.6.tgz#b3f9b38835e36a41c843a3b0907a5a7b3755de73"
integrity sha512-FOyLWWVjG+aC0UqG76V53yAWdXfH8bO6FNmyZOuUrzDzK8DI3/JRY25UD7+g49JWM1LXwymsKERB+DzI0dTEQA==
tmp@^0.0.33:
version "0.0.33"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
......@@ -5469,6 +5555,11 @@ to-regex@^3.0.1, to-regex@^3.0.2:
regex-not "^1.0.2"
safe-regex "^1.1.0"
toformat@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/toformat/-/toformat-2.0.0.tgz#7a043fd2dfbe9021a4e36e508835ba32056739d8"
integrity sha512-03SWBVop6nU8bpyZCx7SodpYznbZF5R4ljwNLBcTQzKOD9xuihRo/psX58llS1BMFhhAI08H3luot5GoXJz2pQ==
tough-cookie@^2.3.3, tough-cookie@^2.3.4:
version "2.5.0"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment