Commit 05a05c5a authored by Noah Zinsmeister's avatar Noah Zinsmeister

throw errors for runtime parameters

remove public keyword from class functions

various small cleanups
parent cc692ef8
{ {
"plugins": [] // ["transform-jsbi-to-bigint"] "plugins": [] // "transform-jsbi-to-bigint"
} }
...@@ -10,19 +10,9 @@ ...@@ -10,19 +10,9 @@
}, },
{ {
"constant": true, "constant": true,
"inputs": [ "inputs": [{ "name": "", "type": "address" }],
{
"name": "",
"type": "address"
}
],
"name": "balanceOf", "name": "balanceOf",
"outputs": [ "outputs": [{ "name": "", "type": "uint256" }],
{
"name": "",
"type": "uint256"
}
],
"payable": false, "payable": false,
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
......
...@@ -36,3 +36,8 @@ export enum SolidityType { ...@@ -36,3 +36,8 @@ export enum SolidityType {
uint8 = 'uint8', uint8 = 'uint8',
uint256 = 'uint256' uint256 = 'uint256'
} }
export const SOLIDITY_TYPE_MAXIMA = {
[SolidityType.uint8]: JSBI.BigInt('0xff'),
[SolidityType.uint256]: JSBI.BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff')
}
...@@ -8,6 +8,7 @@ import { Contract } from '@ethersproject/contracts' ...@@ -8,6 +8,7 @@ import { Contract } from '@ethersproject/contracts'
import { FACTORY_ADDRESS, INIT_CODE_HASH, ZERO, ONE, _997, _1000 } from '../constants' import { FACTORY_ADDRESS, INIT_CODE_HASH, ZERO, ONE, _997, _1000 } from '../constants'
import ERC20 from '../abis/ERC20.json' import ERC20 from '../abis/ERC20.json'
import { InsufficientReservesError, InsufficientInputAmountError } from '../errors'
import { Token } from './token' import { Token } from './token'
import { TokenAmount } from './fractions/tokenAmount' import { TokenAmount } from './fractions/tokenAmount'
...@@ -18,8 +19,7 @@ export class Exchange { ...@@ -18,8 +19,7 @@ export class Exchange {
private readonly tokenAmounts: [TokenAmount, TokenAmount] private readonly tokenAmounts: [TokenAmount, TokenAmount]
static getAddress(tokenA: Token, tokenB: Token): string { static getAddress(tokenA: Token, tokenB: Token): string {
// performs the requisite safety checks const tokens = tokenA.sortsBefore(tokenB) ? [tokenA, tokenB] : [tokenB, tokenA] // does safety checks
const tokens: [Token, Token] = tokenA.sortsBefore(tokenB) ? [tokenA, tokenB] : [tokenB, tokenA]
if (CACHE?.[tokens[0].address]?.[tokens[1].address] === undefined) { if (CACHE?.[tokens[0].address]?.[tokens[1].address] === undefined) {
CACHE = { CACHE = {
...@@ -34,6 +34,7 @@ export class Exchange { ...@@ -34,6 +34,7 @@ export class Exchange {
} }
} }
} }
return CACHE[tokens[0].address][tokens[1].address] return CACHE[tokens[0].address][tokens[1].address]
} }
...@@ -42,78 +43,81 @@ export class Exchange { ...@@ -42,78 +43,81 @@ export class Exchange {
tokenB: Token, tokenB: Token,
provider = getDefaultProvider(getNetwork(tokenA.chainId)) provider = getDefaultProvider(getNetwork(tokenA.chainId))
): Promise<Exchange> { ): Promise<Exchange> {
const exchangeAddress = Exchange.getAddress(tokenA, tokenB) const address = Exchange.getAddress(tokenA, tokenB)
const balances = await Promise.all([ const balances = await Promise.all([
new Contract(tokenA.address, ERC20, provider).balanceOf(exchangeAddress), new Contract(tokenA.address, ERC20, provider).balanceOf(address),
new Contract(tokenB.address, ERC20, provider).balanceOf(exchangeAddress) new Contract(tokenB.address, ERC20, provider).balanceOf(address)
]) ])
return new Exchange(new TokenAmount(tokenA, balances[0]), new TokenAmount(tokenB, balances[1])) return new Exchange(new TokenAmount(tokenA, balances[0]), new TokenAmount(tokenB, balances[1]))
} }
constructor(tokenAmountA: TokenAmount, tokenAmountB: TokenAmount) { constructor(tokenAmountA: TokenAmount, tokenAmountB: TokenAmount) {
// performs the requisite safety checks const tokenAmounts = tokenAmountA.token.sortsBefore(tokenAmountB.token) // does safety checks
const tokenAmounts: [TokenAmount, TokenAmount] = tokenAmountA.token.sortsBefore(tokenAmountB.token)
? [tokenAmountA, tokenAmountB] ? [tokenAmountA, tokenAmountB]
: [tokenAmountB, tokenAmountA] : [tokenAmountB, tokenAmountA]
this.address = Exchange.getAddress(tokenAmounts[0].token, tokenAmounts[1].token) this.address = Exchange.getAddress(tokenAmounts[0].token, tokenAmounts[1].token)
this.tokenAmounts = tokenAmounts this.tokenAmounts = tokenAmounts as [TokenAmount, TokenAmount]
} }
public get reserve0(): TokenAmount { get token0(): Token {
return this.tokenAmounts[0] return this.tokenAmounts[0].token
} }
public get reserve1(): TokenAmount { get token1(): Token {
return this.tokenAmounts[1] return this.tokenAmounts[1].token
} }
public get token0(): Token { get reserve0(): TokenAmount {
return this.tokenAmounts[0].token return this.tokenAmounts[0]
} }
public get token1(): Token { get reserve1(): TokenAmount {
return this.tokenAmounts[1].token return this.tokenAmounts[1]
} }
public reserveOf(token: Token): TokenAmount { reserveOf(token: Token): TokenAmount {
invariant(token.equals(this.token0) || token.equals(this.token1), 'TOKEN') invariant(token.equals(this.token0) || token.equals(this.token1), 'TOKEN')
return token.equals(this.token0) ? this.reserve0 : this.reserve1 return token.equals(this.token0) ? this.reserve0 : this.reserve1
} }
public getOutputAmount(inputAmount: TokenAmount): [TokenAmount, Exchange] { getOutputAmount(inputAmount: TokenAmount): [TokenAmount, Exchange] {
invariant(inputAmount.token.equals(this.token0) || inputAmount.token.equals(this.token1), 'TOKEN') invariant(inputAmount.token.equals(this.token0) || inputAmount.token.equals(this.token1), 'TOKEN')
invariant(JSBI.greaterThan(inputAmount.raw, ZERO), 'ZERO') if (JSBI.equal(this.reserve0.raw, ZERO) || JSBI.equal(this.reserve1.raw, ZERO)) {
invariant(JSBI.greaterThan(this.reserve0.raw, ZERO), 'ZERO') throw new InsufficientReservesError()
invariant(JSBI.greaterThan(this.reserve1.raw, ZERO), 'ZERO') }
const inputReserve = this.reserveOf(inputAmount.token)
const inputReserve = inputAmount.token.equals(this.reserve0.token) ? this.reserve0 : this.reserve1 const outputReserve = this.reserveOf(inputAmount.token.equals(this.token0) ? this.token1 : this.token0)
const outputReserve = inputAmount.token.equals(this.reserve0.token) ? this.reserve1 : this.reserve0
const inputAmountWithFee = JSBI.multiply(inputAmount.raw, _997) const inputAmountWithFee = JSBI.multiply(inputAmount.raw, _997)
const numerator = JSBI.multiply(inputAmountWithFee, outputReserve.raw) const numerator = JSBI.multiply(inputAmountWithFee, outputReserve.raw)
const denominator = JSBI.add(JSBI.multiply(inputReserve.raw, _1000), inputAmountWithFee) const denominator = JSBI.add(JSBI.multiply(inputReserve.raw, _1000), inputAmountWithFee)
const output = new TokenAmount( const outputAmount = new TokenAmount(
inputAmount.token.equals(this.token0) ? this.token1 : this.token0, inputAmount.token.equals(this.token0) ? this.token1 : this.token0,
JSBI.divide(numerator, denominator) JSBI.divide(numerator, denominator)
) )
return [output, new Exchange(inputReserve.add(inputAmount), outputReserve.subtract(output))] if (JSBI.equal(outputAmount.raw, ZERO)) {
throw new InsufficientInputAmountError()
}
return [outputAmount, new Exchange(inputReserve.add(inputAmount), outputReserve.subtract(outputAmount))]
} }
public getInputAmount(outputAmount: TokenAmount): [TokenAmount, Exchange] { getInputAmount(outputAmount: TokenAmount): [TokenAmount, Exchange] {
invariant(outputAmount.token.equals(this.token0) || outputAmount.token.equals(this.token1), 'TOKEN') invariant(outputAmount.token.equals(this.token0) || outputAmount.token.equals(this.token1), 'TOKEN')
invariant(JSBI.greaterThan(outputAmount.raw, ZERO), 'ZERO') if (
invariant(JSBI.greaterThan(this.reserve0.raw, ZERO), 'ZERO') JSBI.equal(this.reserve0.raw, ZERO) ||
invariant(JSBI.greaterThan(this.reserve1.raw, ZERO), 'ZERO') JSBI.equal(this.reserve1.raw, ZERO) ||
invariant(JSBI.lessThan(outputAmount.raw, this.reserveOf(outputAmount.token).raw), 'INSUFFICIENT_RESERVE') JSBI.greaterThanOrEqual(outputAmount.raw, this.reserveOf(outputAmount.token).raw)
) {
throw new InsufficientReservesError()
}
const inputReserve = outputAmount.token.equals(this.reserve0.token) ? this.reserve1 : this.reserve0 const outputReserve = this.reserveOf(outputAmount.token)
const outputReserve = outputAmount.token.equals(this.reserve0.token) ? this.reserve0 : this.reserve1 const inputReserve = this.reserveOf(outputAmount.token.equals(this.token0) ? this.token1 : this.token0)
const numerator = JSBI.multiply(JSBI.multiply(inputReserve.raw, outputAmount.raw), _1000) const numerator = JSBI.multiply(JSBI.multiply(inputReserve.raw, outputAmount.raw), _1000)
const denominator = JSBI.multiply(JSBI.subtract(outputReserve.raw, outputAmount.raw), _997) const denominator = JSBI.multiply(JSBI.subtract(outputReserve.raw, outputAmount.raw), _997)
const input = new TokenAmount( const inputAmount = new TokenAmount(
outputAmount.token.equals(this.token0) ? this.token1 : this.token0, outputAmount.token.equals(this.token0) ? this.token1 : this.token0,
JSBI.add(JSBI.divide(numerator, denominator), ONE) JSBI.add(JSBI.divide(numerator, denominator), ONE)
) )
return [input, new Exchange(inputReserve.add(input), outputReserve.subtract(outputAmount))] return [inputAmount, new Exchange(inputReserve.add(inputAmount), outputReserve.subtract(outputAmount))]
} }
} }
...@@ -27,21 +27,21 @@ export class Fraction { ...@@ -27,21 +27,21 @@ export class Fraction {
public readonly numerator: JSBI public readonly numerator: JSBI
public readonly denominator: JSBI public readonly denominator: JSBI
public constructor(numerator: BigintIsh, denominator: BigintIsh = ONE) { constructor(numerator: BigintIsh, denominator: BigintIsh = ONE) {
this.numerator = parseBigintIsh(numerator) this.numerator = parseBigintIsh(numerator)
this.denominator = parseBigintIsh(denominator) this.denominator = parseBigintIsh(denominator)
} }
// performs floor division // performs floor division
public get quotient(): JSBI { get quotient(): JSBI {
return JSBI.divide(this.numerator, this.denominator) return JSBI.divide(this.numerator, this.denominator)
} }
public invert(): Fraction { invert(): Fraction {
return new Fraction(this.denominator, this.numerator) return new Fraction(this.denominator, this.numerator)
} }
public multiply(other: Fraction | BigintIsh): Fraction { multiply(other: Fraction | BigintIsh): Fraction {
const otherParsed = other instanceof Fraction ? other : new Fraction(parseBigintIsh(other)) const otherParsed = other instanceof Fraction ? other : new Fraction(parseBigintIsh(other))
return new Fraction( return new Fraction(
JSBI.multiply(this.numerator, otherParsed.numerator), JSBI.multiply(this.numerator, otherParsed.numerator),
...@@ -49,12 +49,12 @@ export class Fraction { ...@@ -49,12 +49,12 @@ export class Fraction {
) )
} }
public toSignificant( toSignificant(
significantDigits: number, significantDigits: number,
format: object = { groupSeparator: '' }, format: object = { groupSeparator: '' },
rounding: Rounding = Rounding.ROUND_HALF_UP rounding: Rounding = Rounding.ROUND_HALF_UP
): string { ): string {
invariant(Number.isInteger(significantDigits), `${significantDigits} is not a positive integer.`) invariant(Number.isInteger(significantDigits), `${significantDigits} is not an integer.`)
invariant(significantDigits > 0, `${significantDigits} is not positive.`) invariant(significantDigits > 0, `${significantDigits} is not positive.`)
Decimal.set({ precision: significantDigits + 1, rounding: toSignificantRounding[rounding] }) Decimal.set({ precision: significantDigits + 1, rounding: toSignificantRounding[rounding] })
...@@ -64,7 +64,7 @@ export class Fraction { ...@@ -64,7 +64,7 @@ export class Fraction {
return quotient.toFormat(quotient.decimalPlaces(), format) return quotient.toFormat(quotient.decimalPlaces(), format)
} }
public toFixed( toFixed(
decimalPlaces: number, decimalPlaces: number,
format: object = { groupSeparator: '' }, format: object = { groupSeparator: '' },
rounding: Rounding = Rounding.ROUND_HALF_UP rounding: Rounding = Rounding.ROUND_HALF_UP
......
...@@ -2,14 +2,14 @@ import { Rounding } from '../../types' ...@@ -2,14 +2,14 @@ import { Rounding } from '../../types'
import { _100 } from '../../constants' import { _100 } from '../../constants'
import { Fraction } from './fraction' import { Fraction } from './fraction'
const _100Percent = new Fraction(_100) const _100_PERCENT = new Fraction(_100)
export class Percent extends Fraction { export class Percent extends Fraction {
public toSignificant(significantDigits: number = 5, format?: object, rounding?: Rounding): string { toSignificant(significantDigits: number = 5, format?: object, rounding?: Rounding): string {
return this.multiply(_100Percent).toSignificant(significantDigits, format, rounding) return this.multiply(_100_PERCENT).toSignificant(significantDigits, format, rounding)
} }
public toFixed(decimalPlaces: number = 2, format?: object, rounding?: Rounding): string { toFixed(decimalPlaces: number = 2, format?: object, rounding?: Rounding): string {
return this.multiply(_100Percent).toSignificant(decimalPlaces, format, rounding) return this.multiply(_100_PERCENT).toFixed(decimalPlaces, format, rounding)
} }
} }
...@@ -11,7 +11,7 @@ import { TokenAmount } from './tokenAmount' ...@@ -11,7 +11,7 @@ import { TokenAmount } from './tokenAmount'
export class Price extends Fraction { export class Price extends Fraction {
public readonly baseToken: Token // input i.e. denominator public readonly baseToken: Token // input i.e. denominator
public readonly quoteToken: Token // output i.e. numerator public readonly quoteToken: Token // output i.e. numerator
public readonly scalar: Fraction // used to adjust the raw fraction w/r/t the decimals of the {base,quote}Tokens public readonly scalar: Fraction // used to adjust the raw fraction w/r/t the decimals of the {base,quote}Token
static fromRoute(route: Route): Price { static fromRoute(route: Route): Price {
const prices: Price[] = [] const prices: Price[] = []
...@@ -25,7 +25,7 @@ export class Price extends Fraction { ...@@ -25,7 +25,7 @@ export class Price extends Fraction {
return prices.slice(1).reduce((accumulator, currentValue) => accumulator.multiply(currentValue), prices[0]) return prices.slice(1).reduce((accumulator, currentValue) => accumulator.multiply(currentValue), prices[0])
} }
// denominator and numerator _must be_ scaled in units of the {base,quote}Tokens // denominator and numerator _must_ be raw, i.e. in the native representation
constructor(baseToken: Token, quoteToken: Token, denominator: BigintIsh, numerator: BigintIsh) { constructor(baseToken: Token, quoteToken: Token, denominator: BigintIsh, numerator: BigintIsh) {
super(numerator, denominator) super(numerator, denominator)
...@@ -37,35 +37,35 @@ export class Price extends Fraction { ...@@ -37,35 +37,35 @@ export class Price extends Fraction {
) )
} }
public get raw(): Fraction { get raw(): Fraction {
return new Fraction(this.numerator, this.denominator) return new Fraction(this.numerator, this.denominator)
} }
public get adjusted(): Fraction { get adjusted(): Fraction {
return super.multiply(this.scalar) return super.multiply(this.scalar)
} }
public invert(): Price { invert(): Price {
return new Price(this.quoteToken, this.baseToken, this.numerator, this.denominator) return new Price(this.quoteToken, this.baseToken, this.numerator, this.denominator)
} }
public multiply(other: Price): Price { multiply(other: Price): Price {
invariant(this.quoteToken.equals(other.baseToken), 'BASE') invariant(this.quoteToken.equals(other.baseToken), 'BASE')
const fraction = super.multiply(other) const fraction = super.multiply(other)
return new Price(this.baseToken, other.quoteToken, fraction.denominator, fraction.numerator) return new Price(this.baseToken, other.quoteToken, fraction.denominator, fraction.numerator)
} }
// performs floor division on overflow // performs floor division on overflow
public quote(tokenAmount: TokenAmount): TokenAmount { quote(tokenAmount: TokenAmount): TokenAmount {
invariant(tokenAmount.token.equals(this.baseToken), 'TOKEN') invariant(tokenAmount.token.equals(this.baseToken), 'TOKEN')
return new TokenAmount(this.quoteToken, super.multiply(tokenAmount.raw).quotient) return new TokenAmount(this.quoteToken, super.multiply(tokenAmount.raw).quotient)
} }
public toSignificant(significantDigits: number = 6, format?: object, rounding?: Rounding): string { toSignificant(significantDigits: number = 6, format?: object, rounding?: Rounding): string {
return this.adjusted.toSignificant(significantDigits, format, rounding) return this.adjusted.toSignificant(significantDigits, format, rounding)
} }
public toFixed(decimalPlaces: number = 6, format?: object, rounding?: Rounding): string { toFixed(decimalPlaces: number = 4, format?: object, rounding?: Rounding): string {
return this.adjusted.toFixed(decimalPlaces, format, rounding) return this.adjusted.toFixed(decimalPlaces, format, rounding)
} }
} }
...@@ -14,7 +14,7 @@ const Big = toFormat(_Big) ...@@ -14,7 +14,7 @@ const Big = toFormat(_Big)
export class TokenAmount extends Fraction { export class TokenAmount extends Fraction {
public readonly token: Token public readonly token: Token
// amount _must be_ scaled in units of the token // amount _must_ be raw, i.e. in the native representation
constructor(token: Token, amount: BigintIsh) { constructor(token: Token, amount: BigintIsh) {
const parsedAmount = parseBigintIsh(amount) const parsedAmount = parseBigintIsh(amount)
validateSolidityTypeInstance(parsedAmount, SolidityType.uint256) validateSolidityTypeInstance(parsedAmount, SolidityType.uint256)
...@@ -23,29 +23,25 @@ export class TokenAmount extends Fraction { ...@@ -23,29 +23,25 @@ export class TokenAmount extends Fraction {
this.token = token this.token = token
} }
public get raw(): JSBI { get raw(): JSBI {
return this.numerator return this.numerator
} }
public get adjusted(): Fraction { add(other: TokenAmount): TokenAmount {
return this
}
public add(other: TokenAmount): TokenAmount {
invariant(this.token.equals(other.token), 'TOKEN') invariant(this.token.equals(other.token), 'TOKEN')
return new TokenAmount(this.token, JSBI.add(this.raw, other.raw)) return new TokenAmount(this.token, JSBI.add(this.raw, other.raw))
} }
public subtract(other: TokenAmount): TokenAmount { subtract(other: TokenAmount): TokenAmount {
invariant(this.token.equals(other.token), 'TOKEN') invariant(this.token.equals(other.token), 'TOKEN')
return new TokenAmount(this.token, JSBI.subtract(this.raw, other.raw)) return new TokenAmount(this.token, JSBI.subtract(this.raw, other.raw))
} }
public toSignificant(significantDigits: number, format?: object, rounding: Rounding = Rounding.ROUND_DOWN): string { toSignificant(significantDigits: number = 6, format?: object, rounding: Rounding = Rounding.ROUND_DOWN): string {
return super.toSignificant(significantDigits, format, rounding) return super.toSignificant(significantDigits, format, rounding)
} }
public toFixed( toFixed(
decimalPlaces: number = this.token.decimals, decimalPlaces: number = this.token.decimals,
format?: object, format?: object,
rounding: Rounding = Rounding.ROUND_DOWN rounding: Rounding = Rounding.ROUND_DOWN
...@@ -54,7 +50,7 @@ export class TokenAmount extends Fraction { ...@@ -54,7 +50,7 @@ export class TokenAmount extends Fraction {
return super.toFixed(decimalPlaces, format, rounding) return super.toFixed(decimalPlaces, format, rounding)
} }
public toExact(format: object = { groupSeparator: '' }): string { toExact(format: object = { groupSeparator: '' }): string {
Big.DP = this.token.decimals Big.DP = this.token.decimals
return new Big(this.numerator.toString()).div(this.denominator.toString()).toFormat(format) return new Big(this.numerator.toString()).div(this.denominator.toString()).toFormat(format)
} }
......
...@@ -2,4 +2,5 @@ export * from './token' ...@@ -2,4 +2,5 @@ export * from './token'
export * from './exchange' export * from './exchange'
export * from './route' export * from './route'
export * from './trade' export * from './trade'
export * from './fractions' export * from './fractions'
...@@ -9,7 +9,7 @@ import ERC20 from '../abis/ERC20.json' ...@@ -9,7 +9,7 @@ import ERC20 from '../abis/ERC20.json'
import { validateAndParseAddress, validateSolidityTypeInstance } from '../utils' import { validateAndParseAddress, validateSolidityTypeInstance } from '../utils'
let CACHE: { [chainId: number]: { [address: string]: number } } = { let CACHE: { [chainId: number]: { [address: string]: number } } = {
1: { [ChainId.MAINNET]: {
'0xE0B7927c4aF23765Cb51314A0E0521A9645F0E2A': 9 // DGD '0xE0B7927c4aF23765Cb51314A0E0521A9645F0E2A': 9 // DGD
} }
} }
...@@ -54,13 +54,17 @@ export class Token { ...@@ -54,13 +54,17 @@ export class Token {
if (typeof name === 'string') this.name = name if (typeof name === 'string') this.name = name
} }
public equals(other: Token): boolean { equals(other: Token): boolean {
const equal = this.chainId === other.chainId && this.address === other.address const equal = this.chainId === other.chainId && this.address === other.address
if (equal) invariant(this.decimals === other.decimals, 'DECIMALS') if (equal) {
invariant(this.decimals === other.decimals, 'DECIMALS')
if (this.symbol && other.symbol) invariant(this.symbol === other.symbol, 'SYMBOL')
if (this.name && other.name) invariant(this.name === other.name, 'NAME')
}
return equal return equal
} }
public sortsBefore(other: Token): boolean { sortsBefore(other: Token): boolean {
invariant(this.chainId === other.chainId, 'CHAIN_IDS') invariant(this.chainId === other.chainId, 'CHAIN_IDS')
invariant(this.address !== other.address, 'ADDRESSES') invariant(this.address !== other.address, 'ADDRESSES')
return this.address.toLowerCase() < other.address.toLowerCase() return this.address.toLowerCase() < other.address.toLowerCase()
......
...@@ -8,11 +8,11 @@ import { Fraction, TokenAmount } from './fractions' ...@@ -8,11 +8,11 @@ import { Fraction, TokenAmount } from './fractions'
import { Price } from './fractions/price' import { Price } from './fractions/price'
import { Percent } from './fractions/percent' import { Percent } from './fractions/percent'
function getSlippage(inputAmount: TokenAmount, midPrice: Price, outputAmount: TokenAmount): Percent { function getSlippage(midPrice: Price, inputAmount: TokenAmount, outputAmount: TokenAmount): Percent {
const exactQuote = midPrice.raw.multiply(inputAmount.raw) const exactQuote = midPrice.raw.multiply(inputAmount.raw)
// calculate (outputAmount - exactQuote) / exactQuote // calculate (exactQuote - outputAmount) / exactQuote
const exactDifference = new Fraction( const exactDifference = new Fraction(
JSBI.subtract(JSBI.multiply(outputAmount.raw, exactQuote.denominator), exactQuote.numerator), JSBI.subtract(exactQuote.numerator, JSBI.multiply(outputAmount.raw, exactQuote.denominator)),
exactQuote.denominator exactQuote.denominator
) )
const slippage = exactDifference.multiply(exactQuote.invert()) const slippage = exactDifference.multiply(exactQuote.invert())
...@@ -20,11 +20,11 @@ function getSlippage(inputAmount: TokenAmount, midPrice: Price, outputAmount: To ...@@ -20,11 +20,11 @@ function getSlippage(inputAmount: TokenAmount, midPrice: Price, outputAmount: To
} }
function getPercentChange(referenceRate: Price, newRate: Price): Percent { function getPercentChange(referenceRate: Price, newRate: Price): Percent {
// calculate (newRate - referenceRate) / referenceRate // calculate (referenceRate - newRate) / referenceRate
const difference = new Fraction( const difference = new Fraction(
JSBI.subtract( JSBI.subtract(
JSBI.multiply(newRate.adjusted.numerator, referenceRate.adjusted.denominator), JSBI.multiply(referenceRate.adjusted.numerator, newRate.adjusted.denominator),
JSBI.multiply(referenceRate.adjusted.numerator, newRate.adjusted.denominator) JSBI.multiply(newRate.adjusted.numerator, referenceRate.adjusted.denominator)
), ),
JSBI.multiply(referenceRate.adjusted.denominator, newRate.adjusted.denominator) JSBI.multiply(referenceRate.adjusted.denominator, newRate.adjusted.denominator)
) )
...@@ -73,7 +73,7 @@ export class Trade { ...@@ -73,7 +73,7 @@ export class Trade {
this.executionPrice = new Price(route.input, route.output, inputAmount.raw, outputAmount.raw) this.executionPrice = new Price(route.input, route.output, inputAmount.raw, outputAmount.raw)
const nextMidPrice = Price.fromRoute(new Route(nextExchanges, route.input)) const nextMidPrice = Price.fromRoute(new Route(nextExchanges, route.input))
this.nextMidPrice = nextMidPrice this.nextMidPrice = nextMidPrice
this.slippage = getSlippage(inputAmount, route.midPrice, outputAmount) this.slippage = getSlippage(route.midPrice, inputAmount, outputAmount)
this.midPricePercentChange = getPercentChange(route.midPrice, nextMidPrice) this.midPricePercentChange = getPercentChange(route.midPrice, nextMidPrice)
} }
} }
export class InsufficientReservesError extends Error {
public constructor() {
super()
this.name = this.constructor.name
}
}
export class InsufficientInputAmountError extends Error {
public constructor() {
super()
this.name = this.constructor.name
}
}
...@@ -4,3 +4,4 @@ export { JSBI } ...@@ -4,3 +4,4 @@ export { JSBI }
export * from './types' export * from './types'
export * from './constants' export * from './constants'
export * from './entities' export * from './entities'
export * from './errors'
...@@ -4,14 +4,8 @@ import JSBI from 'jsbi' ...@@ -4,14 +4,8 @@ import JSBI from 'jsbi'
import { getAddress } from '@ethersproject/address' import { getAddress } from '@ethersproject/address'
import { BigintIsh } from './types' import { BigintIsh } from './types'
import { ZERO, SolidityType } from './constants' import { ZERO, SolidityType, SOLIDITY_TYPE_MAXIMA } from './constants'
const SOLIDITY_TYPE_MAXIMA = {
[SolidityType.uint8]: JSBI.BigInt('0xff'),
[SolidityType.uint256]: JSBI.BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff')
}
// only works for uint{8,256}
export function validateSolidityTypeInstance(value: JSBI, solidityType: SolidityType): void { export function validateSolidityTypeInstance(value: JSBI, solidityType: SolidityType): void {
invariant(JSBI.greaterThanOrEqual(value, ZERO), `${value} is not a ${solidityType}.`) invariant(JSBI.greaterThanOrEqual(value, ZERO), `${value} is not a ${solidityType}.`)
invariant(JSBI.lessThanOrEqual(value, SOLIDITY_TYPE_MAXIMA[solidityType]), `${value} is not a ${solidityType}.`) invariant(JSBI.lessThanOrEqual(value, SOLIDITY_TYPE_MAXIMA[solidityType]), `${value} is not a ${solidityType}.`)
......
...@@ -122,9 +122,9 @@ describe('entities', () => { ...@@ -122,9 +122,9 @@ describe('entities', () => {
expect(trade.nextMidPrice.toSignificant(18)).toEqual('1.38958368072925352') expect(trade.nextMidPrice.toSignificant(18)).toEqual('1.38958368072925352')
expect(trade.nextMidPrice.invert().toSignificant(18)).toEqual('0.71964') expect(trade.nextMidPrice.invert().toSignificant(18)).toEqual('0.71964')
expect(trade.slippage.toSignificant(18)).toEqual('-16.8751042187760547') expect(trade.slippage.toSignificant(18)).toEqual('16.8751042187760547')
expect(trade.midPricePercentChange.toSignificant(18)).toEqual('-30.5208159635373242') expect(trade.midPricePercentChange.toSignificant(18)).toEqual('30.5208159635373242')
}) })
it('TradeType.EXACT_OUTPUT', () => { it('TradeType.EXACT_OUTPUT', () => {
...@@ -144,9 +144,9 @@ describe('entities', () => { ...@@ -144,9 +144,9 @@ describe('entities', () => {
expect(trade.nextMidPrice.toSignificant(18)).toEqual('1.38958368072925352') expect(trade.nextMidPrice.toSignificant(18)).toEqual('1.38958368072925352')
expect(trade.nextMidPrice.invert().toSignificant(18)).toEqual('0.71964') expect(trade.nextMidPrice.invert().toSignificant(18)).toEqual('0.71964')
expect(trade.slippage.toSignificant(18)).toEqual('-16.8751042187760547') expect(trade.slippage.toSignificant(18)).toEqual('16.8751042187760547')
expect(trade.midPricePercentChange.toSignificant(18)).toEqual('-30.5208159635373242') expect(trade.midPricePercentChange.toSignificant(18)).toEqual('30.5208159635373242')
}) })
it('minimum TradeType.EXACT_INPUT', () => { it('minimum TradeType.EXACT_INPUT', () => {
...@@ -168,7 +168,7 @@ describe('entities', () => { ...@@ -168,7 +168,7 @@ describe('entities', () => {
const trade = new Trade(route, outputAmount, TradeType.EXACT_INPUT) const trade = new Trade(route, outputAmount, TradeType.EXACT_INPUT)
expect(trade.slippage.toSignificant(18)).toEqual( expect(trade.slippage.toSignificant(18)).toEqual(
tokens[1].decimals === 9 ? '-0.300000099400899902' : '-0.3000000000000001' tokens[1].decimals === 9 ? '0.300000099400899902' : '0.3000000000000001'
) )
} }
}) })
......
{ {
"include": ["src", "types", "test"], "include": ["src", "test"],
"compilerOptions": { "compilerOptions": {
"target": "es2018", "target": "es2018",
"module": "esnext", "module": "esnext",
......
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