Commit 221db315 authored by Moody Salem's avatar Moody Salem Committed by GitHub

Refactor core (#58)

* fix module reference

* modernize v2-sdk
parent 473ffb0e
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
"uniswap", "uniswap",
"ethereum" "ethereum"
], ],
"module": "dist/sdk.esm.js", "module": "dist/v2-sdk.esm.js",
"scripts": { "scripts": {
"lint": "tsdx lint src test", "lint": "tsdx lint src test",
"build": "tsdx build", "build": "tsdx build",
...@@ -22,20 +22,18 @@ ...@@ -22,20 +22,18 @@
"prepublishOnly": "tsdx build" "prepublishOnly": "tsdx build"
}, },
"dependencies": { "dependencies": {
"@uniswap/v2-core": "^1.0.0", "@uniswap/sdk-core": "^1.0.6",
"big.js": "^5.2.2",
"decimal.js-light": "^2.5.0",
"jsbi": "^3.1.1",
"tiny-invariant": "^1.1.0", "tiny-invariant": "^1.1.0",
"tiny-warning": "^1.0.3", "tiny-warning": "^1.0.3"
"toformat": "^2.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"@ethersproject/address": "^5.0.0-beta", "@ethersproject/address": "^5.0.0",
"@ethersproject/contracts": "^5.0.0-beta", "@ethersproject/contracts": "^5.0.0"
"@ethersproject/networks": "^5.0.0-beta", },
"@ethersproject/providers": "^5.0.0-beta", "optionalDependencies": {
"@ethersproject/solidity": "^5.0.0-beta" "@uniswap/v2-core": "^1.0.1",
"@ethersproject/networks": "^5.0.2",
"@ethersproject/providers": "^5.0.5"
}, },
"devDependencies": { "devDependencies": {
"@ethersproject/address": "^5.0.2", "@ethersproject/address": "^5.0.2",
...@@ -45,8 +43,8 @@ ...@@ -45,8 +43,8 @@
"@ethersproject/solidity": "^5.0.2", "@ethersproject/solidity": "^5.0.2",
"@types/big.js": "^4.0.5", "@types/big.js": "^4.0.5",
"@types/jest": "^24.0.25", "@types/jest": "^24.0.25",
"babel-plugin-transform-jsbi-to-bigint": "^1.3.1", "@uniswap/v2-core": "^1.0.1",
"tsdx": "^0.12.3" "tsdx": "^0.14.1"
}, },
"engines": { "engines": {
"node": ">=10" "node": ">=10"
......
import { INIT_CODE_HASH } from '../src/constants' import { INIT_CODE_HASH } from './constants'
import { bytecode } from '@uniswap/v2-core/build/UniswapV2Pair.json' import { bytecode } from '@uniswap/v2-core/build/UniswapV2Pair.json'
import { keccak256 } from '@ethersproject/solidity' import { keccak256 } from '@ethersproject/solidity'
......
import JSBI from 'jsbi' import JSBI from 'jsbi'
// exports for external consumption
export type BigintIsh = JSBI | bigint | string
export enum ChainId {
MAINNET = 1,
ROPSTEN = 3,
RINKEBY = 4,
GÖRLI = 5,
KOVAN = 42
}
export enum TradeType {
EXACT_INPUT,
EXACT_OUTPUT
}
export enum Rounding {
ROUND_DOWN,
ROUND_HALF_UP,
ROUND_UP
}
export const FACTORY_ADDRESS = '0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f' export const FACTORY_ADDRESS = '0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f'
export const INIT_CODE_HASH = '0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f' export const INIT_CODE_HASH = '0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f'
...@@ -29,22 +7,7 @@ export const INIT_CODE_HASH = '0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbe ...@@ -29,22 +7,7 @@ export const INIT_CODE_HASH = '0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbe
export const MINIMUM_LIQUIDITY = JSBI.BigInt(1000) export const MINIMUM_LIQUIDITY = JSBI.BigInt(1000)
// exports for internal consumption // exports for internal consumption
export const ZERO = JSBI.BigInt(0)
export const ONE = JSBI.BigInt(1)
export const TWO = JSBI.BigInt(2)
export const THREE = JSBI.BigInt(3) export const THREE = JSBI.BigInt(3)
export const FIVE = JSBI.BigInt(5) export const FIVE = JSBI.BigInt(5)
export const TEN = JSBI.BigInt(10)
export const _100 = JSBI.BigInt(100)
export const _997 = JSBI.BigInt(997) export const _997 = JSBI.BigInt(997)
export const _1000 = JSBI.BigInt(1000) export const _1000 = JSBI.BigInt(1000)
export enum SolidityType {
uint8 = 'uint8',
uint256 = 'uint256'
}
export const SOLIDITY_TYPE_MAXIMA = {
[SolidityType.uint8]: JSBI.BigInt('0xff'),
[SolidityType.uint256]: JSBI.BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff')
}
import { ChainId, WETH, Token, Fetcher } from '../src' import { ChainId, WETH, Token } from '@uniswap/sdk-core'
import { Fetcher } from './fetcher'
// TODO: replace the provider in these tests // TODO: replace the provider in these tests
describe.skip('data', () => { describe.skip('data', () => {
......
declare module 'toformat'
import JSBI from 'jsbi'
import { SolidityType } from '../constants'
import { validateSolidityTypeInstance } from '../utils'
/**
* A currency is any fungible financial instrument on Ethereum, including Ether and all ERC20 tokens.
*
* The only instance of the base class `Currency` is Ether.
*/
export class Currency {
public readonly decimals: number
public readonly symbol?: string
public readonly name?: string
/**
* The only instance of the base class `Currency`.
*/
public static readonly ETHER: Currency = new Currency(18, 'ETH', 'Ether')
/**
* Constructs an instance of the base class `Currency`. The only instance of the base class `Currency` is `Currency.ETHER`.
* @param decimals decimals of the currency
* @param symbol symbol of the currency
* @param name of the currency
*/
protected constructor(decimals: number, symbol?: string, name?: string) {
validateSolidityTypeInstance(JSBI.BigInt(decimals), SolidityType.uint8)
this.decimals = decimals
this.symbol = symbol
this.name = name
}
}
const ETHER = Currency.ETHER
export { ETHER }
import JSBI from 'jsbi'
import invariant from 'tiny-invariant' import invariant from 'tiny-invariant'
import { ChainId, WETH as _WETH, TradeType, Rounding, Token, TokenAmount, Pair, Route, Trade } from '../src' import { ChainId, WETH as _WETH, TradeType, Rounding, Token, TokenAmount } from '@uniswap/sdk-core'
import { Pair, Route, Trade } from '../index'
const ADDRESSES = [ const ADDRESSES = [
'0x0000000000000000000000000000000000000001', '0x0000000000000000000000000000000000000001',
...@@ -14,8 +16,8 @@ const DECIMAL_PERMUTATIONS: [number, number, number][] = [ ...@@ -14,8 +16,8 @@ const DECIMAL_PERMUTATIONS: [number, number, number][] = [
[18, 18, 18] [18, 18, 18]
] ]
function decimalize(amount: number, decimals: number): bigint { function decimalize(amount: number, decimals: number): JSBI {
return BigInt(amount) * BigInt(10) ** BigInt(decimals) return JSBI.multiply(JSBI.BigInt(amount), JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(decimals)))
} }
describe('entities', () => { describe('entities', () => {
...@@ -156,8 +158,10 @@ describe('entities', () => { ...@@ -156,8 +158,10 @@ describe('entities', () => {
new TokenAmount(tokens[1], decimalize(1, tokens[1].decimals)), new TokenAmount(tokens[1], decimalize(1, tokens[1].decimals)),
new TokenAmount( new TokenAmount(
WETH, WETH,
decimalize(10, WETH.decimals) + JSBI.add(
(tokens[1].decimals === 9 ? BigInt('30090280812437312') : BigInt('30090270812437322')) decimalize(10, WETH.decimals),
tokens[1].decimals === 9 ? JSBI.BigInt('30090280812437312') : JSBI.BigInt('30090270812437322')
)
) )
) )
], ],
......
import { currencyEquals } from '../token'
import { Currency, ETHER } from '../currency'
import invariant from 'tiny-invariant'
import JSBI from 'jsbi'
import _Big from 'big.js'
import toFormat from 'toformat'
import { BigintIsh, Rounding, TEN, SolidityType } from '../../constants'
import { parseBigintIsh, validateSolidityTypeInstance } from '../../utils'
import { Fraction } from './fraction'
const Big = toFormat(_Big)
export class CurrencyAmount extends Fraction {
public readonly currency: Currency
/**
* Helper that calls the constructor with the ETHER currency
* @param amount ether amount in wei
*/
public static ether(amount: BigintIsh): CurrencyAmount {
return new CurrencyAmount(ETHER, amount)
}
// amount _must_ be raw, i.e. in the native representation
protected constructor(currency: Currency, amount: BigintIsh) {
const parsedAmount = parseBigintIsh(amount)
validateSolidityTypeInstance(parsedAmount, SolidityType.uint256)
super(parsedAmount, JSBI.exponentiate(TEN, JSBI.BigInt(currency.decimals)))
this.currency = currency
}
public get raw(): JSBI {
return this.numerator
}
public add(other: CurrencyAmount): CurrencyAmount {
invariant(currencyEquals(this.currency, other.currency), 'TOKEN')
return new CurrencyAmount(this.currency, JSBI.add(this.raw, other.raw))
}
public subtract(other: CurrencyAmount): CurrencyAmount {
invariant(currencyEquals(this.currency, other.currency), 'TOKEN')
return new CurrencyAmount(this.currency, JSBI.subtract(this.raw, other.raw))
}
public toSignificant(
significantDigits: number = 6,
format?: object,
rounding: Rounding = Rounding.ROUND_DOWN
): string {
return super.toSignificant(significantDigits, format, rounding)
}
public toFixed(
decimalPlaces: number = this.currency.decimals,
format?: object,
rounding: Rounding = Rounding.ROUND_DOWN
): string {
invariant(decimalPlaces <= this.currency.decimals, 'DECIMALS')
return super.toFixed(decimalPlaces, format, rounding)
}
public toExact(format: object = { groupSeparator: '' }): string {
Big.DP = this.currency.decimals
return new Big(this.numerator.toString()).div(this.denominator.toString()).toFormat(format)
}
}
import invariant from 'tiny-invariant'
import JSBI from 'jsbi'
import _Decimal from 'decimal.js-light'
import _Big, { RoundingMode } from 'big.js'
import toFormat from 'toformat'
import { BigintIsh, Rounding } from '../../constants'
import { ONE } from '../../constants'
import { parseBigintIsh } from '../../utils'
const Decimal = toFormat(_Decimal)
const Big = toFormat(_Big)
const toSignificantRounding = {
[Rounding.ROUND_DOWN]: Decimal.ROUND_DOWN,
[Rounding.ROUND_HALF_UP]: Decimal.ROUND_HALF_UP,
[Rounding.ROUND_UP]: Decimal.ROUND_UP
}
const toFixedRounding = {
[Rounding.ROUND_DOWN]: RoundingMode.RoundDown,
[Rounding.ROUND_HALF_UP]: RoundingMode.RoundHalfUp,
[Rounding.ROUND_UP]: RoundingMode.RoundUp
}
export class Fraction {
public readonly numerator: JSBI
public readonly denominator: JSBI
public constructor(numerator: BigintIsh, denominator: BigintIsh = ONE) {
this.numerator = parseBigintIsh(numerator)
this.denominator = parseBigintIsh(denominator)
}
// performs floor division
public get quotient(): JSBI {
return JSBI.divide(this.numerator, this.denominator)
}
// remainder after floor division
public get remainder(): Fraction {
return new Fraction(JSBI.remainder(this.numerator, this.denominator), this.denominator)
}
public invert(): Fraction {
return new Fraction(this.denominator, this.numerator)
}
public add(other: Fraction | BigintIsh): Fraction {
const otherParsed = other instanceof Fraction ? other : new Fraction(parseBigintIsh(other))
if (JSBI.equal(this.denominator, otherParsed.denominator)) {
return new Fraction(JSBI.add(this.numerator, otherParsed.numerator), this.denominator)
}
return new Fraction(
JSBI.add(
JSBI.multiply(this.numerator, otherParsed.denominator),
JSBI.multiply(otherParsed.numerator, this.denominator)
),
JSBI.multiply(this.denominator, otherParsed.denominator)
)
}
public subtract(other: Fraction | BigintIsh): Fraction {
const otherParsed = other instanceof Fraction ? other : new Fraction(parseBigintIsh(other))
if (JSBI.equal(this.denominator, otherParsed.denominator)) {
return new Fraction(JSBI.subtract(this.numerator, otherParsed.numerator), this.denominator)
}
return new Fraction(
JSBI.subtract(
JSBI.multiply(this.numerator, otherParsed.denominator),
JSBI.multiply(otherParsed.numerator, this.denominator)
),
JSBI.multiply(this.denominator, otherParsed.denominator)
)
}
public lessThan(other: Fraction | BigintIsh): boolean {
const otherParsed = other instanceof Fraction ? other : new Fraction(parseBigintIsh(other))
return JSBI.lessThan(
JSBI.multiply(this.numerator, otherParsed.denominator),
JSBI.multiply(otherParsed.numerator, this.denominator)
)
}
public equalTo(other: Fraction | BigintIsh): boolean {
const otherParsed = other instanceof Fraction ? other : new Fraction(parseBigintIsh(other))
return JSBI.equal(
JSBI.multiply(this.numerator, otherParsed.denominator),
JSBI.multiply(otherParsed.numerator, this.denominator)
)
}
public greaterThan(other: Fraction | BigintIsh): boolean {
const otherParsed = other instanceof Fraction ? other : new Fraction(parseBigintIsh(other))
return JSBI.greaterThan(
JSBI.multiply(this.numerator, otherParsed.denominator),
JSBI.multiply(otherParsed.numerator, this.denominator)
)
}
public multiply(other: Fraction | BigintIsh): Fraction {
const otherParsed = other instanceof Fraction ? other : new Fraction(parseBigintIsh(other))
return new Fraction(
JSBI.multiply(this.numerator, otherParsed.numerator),
JSBI.multiply(this.denominator, otherParsed.denominator)
)
}
public divide(other: Fraction | BigintIsh): Fraction {
const otherParsed = other instanceof Fraction ? other : new Fraction(parseBigintIsh(other))
return new Fraction(
JSBI.multiply(this.numerator, otherParsed.denominator),
JSBI.multiply(this.denominator, otherParsed.numerator)
)
}
public toSignificant(
significantDigits: number,
format: object = { groupSeparator: '' },
rounding: Rounding = Rounding.ROUND_HALF_UP
): string {
invariant(Number.isInteger(significantDigits), `${significantDigits} is not an integer.`)
invariant(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)
}
public toFixed(
decimalPlaces: number,
format: object = { groupSeparator: '' },
rounding: Rounding = Rounding.ROUND_HALF_UP
): string {
invariant(Number.isInteger(decimalPlaces), `${decimalPlaces} is not an integer.`)
invariant(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)
}
}
export * from './fraction'
export * from './percent'
export * from './tokenAmount'
export * from './currencyAmount'
export * from './price'
import { Rounding, _100 } from '../../constants'
import { Fraction } from './fraction'
const _100_PERCENT = new Fraction(_100)
export class Percent extends Fraction {
public toSignificant(significantDigits: number = 5, format?: object, rounding?: Rounding): string {
return this.multiply(_100_PERCENT).toSignificant(significantDigits, format, rounding)
}
public toFixed(decimalPlaces: number = 2, format?: object, rounding?: Rounding): string {
return this.multiply(_100_PERCENT).toFixed(decimalPlaces, format, rounding)
}
}
import { Token } from '../token'
import { TokenAmount } from './tokenAmount'
import { currencyEquals } from '../token'
import invariant from 'tiny-invariant'
import JSBI from 'jsbi'
import { BigintIsh, Rounding, TEN } from '../../constants'
import { Currency } from '../currency'
import { Route } from '../route'
import { Fraction } from './fraction'
import { CurrencyAmount } from './currencyAmount'
export class Price extends Fraction {
public readonly baseCurrency: Currency // input i.e. denominator
public readonly quoteCurrency: Currency // output i.e. numerator
public readonly scalar: Fraction // used to adjust the raw fraction w/r/t the decimals of the {base,quote}Token
public static fromRoute(route: Route): Price {
const prices: Price[] = []
for (const [i, pair] of route.pairs.entries()) {
prices.push(
route.path[i].equals(pair.token0)
? new Price(pair.reserve0.currency, pair.reserve1.currency, pair.reserve0.raw, pair.reserve1.raw)
: new Price(pair.reserve1.currency, pair.reserve0.currency, pair.reserve1.raw, pair.reserve0.raw)
)
}
return prices.slice(1).reduce((accumulator, currentValue) => accumulator.multiply(currentValue), prices[0])
}
// denominator and numerator _must_ be raw, i.e. in the native representation
public constructor(baseCurrency: Currency, quoteCurrency: Currency, denominator: BigintIsh, numerator: BigintIsh) {
super(numerator, denominator)
this.baseCurrency = baseCurrency
this.quoteCurrency = quoteCurrency
this.scalar = new Fraction(
JSBI.exponentiate(TEN, JSBI.BigInt(baseCurrency.decimals)),
JSBI.exponentiate(TEN, JSBI.BigInt(quoteCurrency.decimals))
)
}
public get raw(): Fraction {
return new Fraction(this.numerator, this.denominator)
}
public get adjusted(): Fraction {
return super.multiply(this.scalar)
}
public invert(): Price {
return new Price(this.quoteCurrency, this.baseCurrency, this.numerator, this.denominator)
}
public multiply(other: Price): Price {
invariant(currencyEquals(this.quoteCurrency, other.baseCurrency), 'TOKEN')
const fraction = super.multiply(other)
return new Price(this.baseCurrency, other.quoteCurrency, fraction.denominator, fraction.numerator)
}
// performs floor division on overflow
public quote(currencyAmount: CurrencyAmount): CurrencyAmount {
invariant(currencyEquals(currencyAmount.currency, this.baseCurrency), 'TOKEN')
if (this.quoteCurrency instanceof Token) {
return new TokenAmount(this.quoteCurrency, super.multiply(currencyAmount.raw).quotient)
}
return CurrencyAmount.ether(super.multiply(currencyAmount.raw).quotient)
}
public toSignificant(significantDigits: number = 6, format?: object, rounding?: Rounding): string {
return this.adjusted.toSignificant(significantDigits, format, rounding)
}
public toFixed(decimalPlaces: number = 4, format?: object, rounding?: Rounding): string {
return this.adjusted.toFixed(decimalPlaces, format, rounding)
}
}
import { CurrencyAmount } from './currencyAmount'
import { Token } from '../token'
import invariant from 'tiny-invariant'
import JSBI from 'jsbi'
import { BigintIsh } from '../../constants'
export class TokenAmount extends CurrencyAmount {
public readonly token: Token
// amount _must_ be raw, i.e. in the native representation
public constructor(token: Token, amount: BigintIsh) {
super(token, amount)
this.token = token
}
public add(other: TokenAmount): TokenAmount {
invariant(this.token.equals(other.token), 'TOKEN')
return new TokenAmount(this.token, JSBI.add(this.raw, other.raw))
}
public subtract(other: TokenAmount): TokenAmount {
invariant(this.token.equals(other.token), 'TOKEN')
return new TokenAmount(this.token, JSBI.subtract(this.raw, other.raw))
}
}
export * from './token'
export * from './pair' export * from './pair'
export * from './route' export * from './route'
export * from './trade' export * from './trade'
export * from './currency'
export * from './fractions'
import { ChainId, Token, Pair, TokenAmount, WETH, Price } from '../src' import { ChainId, Token, TokenAmount, WETH, Price } from '@uniswap/sdk-core'
import { InsufficientInputAmountError } from '../errors'
import { Pair } from './pair'
describe('Pair', () => { describe('Pair', () => {
const USDC = new Token(ChainId.MAINNET, '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 18, 'USDC', 'USD Coin') const USDC = new Token(ChainId.MAINNET, '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 18, 'USDC', 'USD Coin')
...@@ -115,4 +117,108 @@ describe('Pair', () => { ...@@ -115,4 +117,108 @@ describe('Pair', () => {
new Pair(new TokenAmount(USDC, '100'), new TokenAmount(DAI, '100')).involvesToken(WETH[ChainId.MAINNET]) new Pair(new TokenAmount(USDC, '100'), new TokenAmount(DAI, '100')).involvesToken(WETH[ChainId.MAINNET])
).toEqual(false) ).toEqual(false)
}) })
describe('miscellaneous', () => {
it('getLiquidityMinted:0', async () => {
const tokenA = new Token(ChainId.RINKEBY, '0x0000000000000000000000000000000000000001', 18)
const tokenB = new Token(ChainId.RINKEBY, '0x0000000000000000000000000000000000000002', 18)
const pair = new Pair(new TokenAmount(tokenA, '0'), new TokenAmount(tokenB, '0'))
expect(() => {
pair.getLiquidityMinted(
new TokenAmount(pair.liquidityToken, '0'),
new TokenAmount(tokenA, '1000'),
new TokenAmount(tokenB, '1000')
)
}).toThrow(InsufficientInputAmountError)
expect(() => {
pair.getLiquidityMinted(
new TokenAmount(pair.liquidityToken, '0'),
new TokenAmount(tokenA, '1000000'),
new TokenAmount(tokenB, '1')
)
}).toThrow(InsufficientInputAmountError)
const liquidity = pair.getLiquidityMinted(
new TokenAmount(pair.liquidityToken, '0'),
new TokenAmount(tokenA, '1001'),
new TokenAmount(tokenB, '1001')
)
expect(liquidity.raw.toString()).toEqual('1')
})
it('getLiquidityMinted:!0', async () => {
const tokenA = new Token(ChainId.RINKEBY, '0x0000000000000000000000000000000000000001', 18)
const tokenB = new Token(ChainId.RINKEBY, '0x0000000000000000000000000000000000000002', 18)
const pair = new Pair(new TokenAmount(tokenA, '10000'), new TokenAmount(tokenB, '10000'))
expect(
pair
.getLiquidityMinted(
new TokenAmount(pair.liquidityToken, '10000'),
new TokenAmount(tokenA, '2000'),
new TokenAmount(tokenB, '2000')
)
.raw.toString()
).toEqual('2000')
})
it('getLiquidityValue:!feeOn', async () => {
const tokenA = new Token(ChainId.RINKEBY, '0x0000000000000000000000000000000000000001', 18)
const tokenB = new Token(ChainId.RINKEBY, '0x0000000000000000000000000000000000000002', 18)
const pair = new Pair(new TokenAmount(tokenA, '1000'), new TokenAmount(tokenB, '1000'))
{
const liquidityValue = pair.getLiquidityValue(
tokenA,
new TokenAmount(pair.liquidityToken, '1000'),
new TokenAmount(pair.liquidityToken, '1000'),
false
)
expect(liquidityValue.token.equals(tokenA)).toBe(true)
expect(liquidityValue.raw.toString()).toBe('1000')
}
// 500
{
const liquidityValue = pair.getLiquidityValue(
tokenA,
new TokenAmount(pair.liquidityToken, '1000'),
new TokenAmount(pair.liquidityToken, '500'),
false
)
expect(liquidityValue.token.equals(tokenA)).toBe(true)
expect(liquidityValue.raw.toString()).toBe('500')
}
// tokenB
{
const liquidityValue = pair.getLiquidityValue(
tokenB,
new TokenAmount(pair.liquidityToken, '1000'),
new TokenAmount(pair.liquidityToken, '1000'),
false
)
expect(liquidityValue.token.equals(tokenB)).toBe(true)
expect(liquidityValue.raw.toString()).toBe('1000')
}
})
it('getLiquidityValue:feeOn', async () => {
const tokenA = new Token(ChainId.RINKEBY, '0x0000000000000000000000000000000000000001', 18)
const tokenB = new Token(ChainId.RINKEBY, '0x0000000000000000000000000000000000000002', 18)
const pair = new Pair(new TokenAmount(tokenA, '1000'), new TokenAmount(tokenB, '1000'))
const liquidityValue = pair.getLiquidityValue(
tokenA,
new TokenAmount(pair.liquidityToken, '500'),
new TokenAmount(pair.liquidityToken, '500'),
true,
'250000' // 500 ** 2
)
expect(liquidityValue.token.equals(tokenA)).toBe(true)
expect(liquidityValue.raw.toString()).toBe('917') // ceiling(1000 - (500 * (1 / 6)))
})
})
}) })
import { Price } from './fractions/price' import { babylonianSqrt, BigintIsh, ChainId, ONE, Price, Token, TokenAmount, ZERO } from '@uniswap/sdk-core'
import { TokenAmount } from './fractions/tokenAmount'
import invariant from 'tiny-invariant' import invariant from 'tiny-invariant'
import JSBI from 'jsbi' import JSBI from 'jsbi'
import { pack, keccak256 } from '@ethersproject/solidity' import { pack, keccak256 } from '@ethersproject/solidity'
import { getCreate2Address } from '@ethersproject/address' import { getCreate2Address } from '@ethersproject/address'
import { import { FACTORY_ADDRESS, INIT_CODE_HASH, MINIMUM_LIQUIDITY, FIVE, _997, _1000 } from '../constants'
BigintIsh, import { parseBigintIsh } from '../utils'
FACTORY_ADDRESS,
INIT_CODE_HASH,
MINIMUM_LIQUIDITY,
ZERO,
ONE,
FIVE,
_997,
_1000,
ChainId
} from '../constants'
import { sqrt, parseBigintIsh } from '../utils'
import { InsufficientReservesError, InsufficientInputAmountError } from '../errors' import { InsufficientReservesError, InsufficientInputAmountError } from '../errors'
import { Token } from './token'
let PAIR_ADDRESS_CACHE: { [token0Address: string]: { [token1Address: string]: string } } = {} let PAIR_ADDRESS_CACHE: { [token0Address: string]: { [token1Address: string]: string } } = {}
...@@ -174,7 +161,10 @@ export class Pair { ...@@ -174,7 +161,10 @@ export class Pair {
let liquidity: JSBI let liquidity: JSBI
if (JSBI.equal(totalSupply.raw, ZERO)) { if (JSBI.equal(totalSupply.raw, ZERO)) {
liquidity = JSBI.subtract(sqrt(JSBI.multiply(tokenAmounts[0].raw, tokenAmounts[1].raw)), MINIMUM_LIQUIDITY) liquidity = JSBI.subtract(
babylonianSqrt(JSBI.multiply(tokenAmounts[0].raw, tokenAmounts[1].raw)),
MINIMUM_LIQUIDITY
)
} else { } else {
const amount0 = JSBI.divide(JSBI.multiply(tokenAmounts[0].raw, totalSupply.raw), this.reserve0.raw) const amount0 = JSBI.divide(JSBI.multiply(tokenAmounts[0].raw, totalSupply.raw), this.reserve0.raw)
const amount1 = JSBI.divide(JSBI.multiply(tokenAmounts[1].raw, totalSupply.raw), this.reserve1.raw) const amount1 = JSBI.divide(JSBI.multiply(tokenAmounts[1].raw, totalSupply.raw), this.reserve1.raw)
...@@ -205,8 +195,8 @@ export class Pair { ...@@ -205,8 +195,8 @@ export class Pair {
invariant(!!kLast, 'K_LAST') invariant(!!kLast, 'K_LAST')
const kLastParsed = parseBigintIsh(kLast) const kLastParsed = parseBigintIsh(kLast)
if (!JSBI.equal(kLastParsed, ZERO)) { if (!JSBI.equal(kLastParsed, ZERO)) {
const rootK = sqrt(JSBI.multiply(this.reserve0.raw, this.reserve1.raw)) const rootK = babylonianSqrt(JSBI.multiply(this.reserve0.raw, this.reserve1.raw))
const rootKLast = sqrt(kLastParsed) const rootKLast = babylonianSqrt(kLastParsed)
if (JSBI.greaterThan(rootK, rootKLast)) { if (JSBI.greaterThan(rootK, rootKLast)) {
const numerator = JSBI.multiply(totalSupply.raw, JSBI.subtract(rootK, rootKLast)) const numerator = JSBI.multiply(totalSupply.raw, JSBI.subtract(rootK, rootKLast))
const denominator = JSBI.add(JSBI.multiply(rootK, FIVE), rootKLast) const denominator = JSBI.add(JSBI.multiply(rootK, FIVE), rootKLast)
......
import { Token, WETH, ChainId, Pair, TokenAmount, Route, ETHER } from '../src' import { Token, WETH, ChainId, TokenAmount, ETHER } from '@uniswap/sdk-core'
import { Pair, Route } from './index'
describe('Route', () => { describe('Route', () => {
const token0 = new Token(ChainId.MAINNET, '0x0000000000000000000000000000000000000001', 18, 't0') const token0 = new Token(ChainId.MAINNET, '0x0000000000000000000000000000000000000001', 18, 't0')
......
import { ChainId } from '../constants' import { ChainId, Currency, ETHER, Price, Token, WETH } from '@uniswap/sdk-core'
import invariant from 'tiny-invariant' import invariant from 'tiny-invariant'
import { Currency, ETHER } from './currency'
import { Token, WETH } from './token'
import { Pair } from './pair' import { Pair } from './pair'
import { Price } from './fractions/price'
export class Route { export class Route {
public readonly pairs: Pair[] public readonly pairs: Pair[]
public readonly path: Token[] public readonly path: Token[]
public readonly input: Currency public readonly input: Currency
public readonly output: Currency public readonly output: Currency
public readonly midPrice: Price
public get midPrice(): Price {
const prices: Price[] = []
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.raw, pair.reserve1.raw)
: new Price(pair.reserve1.currency, pair.reserve0.currency, pair.reserve1.raw, pair.reserve0.raw)
)
}
return prices.slice(1).reduce((accumulator, currentValue) => accumulator.multiply(currentValue), prices[0])
}
public constructor(pairs: Pair[], input: Currency, output?: Currency) { public constructor(pairs: Pair[], input: Currency, output?: Currency) {
invariant(pairs.length > 0, 'PAIRS') invariant(pairs.length > 0, 'PAIRS')
...@@ -41,7 +49,6 @@ export class Route { ...@@ -41,7 +49,6 @@ export class Route {
this.pairs = pairs this.pairs = pairs
this.path = path this.path = path
this.midPrice = Price.fromRoute(this)
this.input = input this.input = input
this.output = output ?? path[path.length - 1] this.output = output ?? path[path.length - 1]
} }
......
import invariant from 'tiny-invariant'
import { ChainId } from '../constants'
import { validateAndParseAddress } from '../utils'
import { Currency } from './currency'
/**
* Represents an ERC20 token with a unique address and some metadata.
*/
export class Token extends Currency {
public readonly chainId: ChainId
public readonly address: string
public constructor(chainId: ChainId, address: string, decimals: number, symbol?: string, name?: string) {
super(decimals, symbol, name)
this.chainId = chainId
this.address = validateAndParseAddress(address)
}
/**
* Returns true if the two tokens are equivalent, i.e. have the same chainId and address.
* @param other other token to compare
*/
public equals(other: Token): boolean {
// short circuit on reference equality
if (this === other) {
return true
}
return 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
*/
public sortsBefore(other: Token): boolean {
invariant(this.chainId === other.chainId, 'CHAIN_IDS')
invariant(this.address !== other.address, 'ADDRESSES')
return this.address.toLowerCase() < other.address.toLowerCase()
}
}
/**
* Compares two currencies for equality
*/
export function currencyEquals(currencyA: Currency, currencyB: Currency): boolean {
if (currencyA instanceof Token && currencyB instanceof Token) {
return currencyA.equals(currencyB)
} else if (currencyA instanceof Token) {
return false
} else if (currencyB instanceof Token) {
return false
} else {
return currencyA === currencyB
}
}
export const WETH = {
[ChainId.MAINNET]: new Token(
ChainId.MAINNET,
'0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
18,
'WETH',
'Wrapped Ether'
),
[ChainId.ROPSTEN]: new Token(
ChainId.ROPSTEN,
'0xc778417E063141139Fce010982780140Aa0cD5Ab',
18,
'WETH',
'Wrapped Ether'
),
[ChainId.RINKEBY]: new Token(
ChainId.RINKEBY,
'0xc778417E063141139Fce010982780140Aa0cD5Ab',
18,
'WETH',
'Wrapped Ether'
),
[ChainId.GÖRLI]: new Token(ChainId.GÖRLI, '0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6', 18, 'WETH', 'Wrapped Ether'),
[ChainId.KOVAN]: new Token(ChainId.KOVAN, '0xd0A1E359811322d97991E03f863a0C30C2cF029C', 18, 'WETH', 'Wrapped Ether')
}
import { Pair } from './pair'
import { Route } from './route'
import { Trade } from './trade'
import JSBI from 'jsbi' import JSBI from 'jsbi'
import { import { ChainId, ETHER, CurrencyAmount, Percent, Token, TokenAmount, TradeType, WETH } from '@uniswap/sdk-core'
ChainId,
ETHER,
CurrencyAmount,
Pair,
Percent,
Route,
Token,
TokenAmount,
Trade,
TradeType,
WETH
} from '../src'
describe('Trade', () => { describe('Trade', () => {
const token0 = new Token(ChainId.MAINNET, '0x0000000000000000000000000000000000000001', 18, 't0') const token0 = new Token(ChainId.MAINNET, '0x0000000000000000000000000000000000000001', 18, 't0')
......
import {
ChainId,
Currency,
CurrencyAmount,
currencyEquals,
ETHER,
Fraction,
ONE,
Percent,
Price,
Token,
TokenAmount,
TradeType,
WETH,
ZERO
} from '@uniswap/sdk-core'
import invariant from 'tiny-invariant' import invariant from 'tiny-invariant'
import { ChainId, ONE, TradeType, ZERO } from '../constants'
import { sortedInsert } from '../utils' import { sortedInsert } from '../utils'
import { Currency, ETHER } from './currency'
import { CurrencyAmount } from './fractions/currencyAmount'
import { Fraction } from './fractions/fraction'
import { Percent } from './fractions/percent'
import { Price } from './fractions/price'
import { TokenAmount } from './fractions/tokenAmount'
import { Pair } from './pair' import { Pair } from './pair'
import { Route } from './route' import { Route } from './route'
import { currencyEquals, Token, WETH } from './token'
/** /**
* Returns the percent difference between the mid price and the execution price, i.e. price impact. * Returns the percent difference between the mid price and the execution price, i.e. price impact.
...@@ -194,7 +202,7 @@ export class Trade { ...@@ -194,7 +202,7 @@ export class Trade {
this.inputAmount.raw, this.inputAmount.raw,
this.outputAmount.raw this.outputAmount.raw
) )
this.nextMidPrice = Price.fromRoute(new Route(nextPairs, route.input)) this.nextMidPrice = new Route(nextPairs, route.input).midPrice
this.priceImpact = computePriceImpact(route.midPrice, this.inputAmount, this.outputAmount) this.priceImpact = computePriceImpact(route.midPrice, this.inputAmount, this.outputAmount)
} }
......
import { Contract } from '@ethersproject/contracts' import { Contract } from '@ethersproject/contracts'
import { getNetwork } from '@ethersproject/networks' import { getNetwork } from '@ethersproject/networks'
import { getDefaultProvider } from '@ethersproject/providers' import { getDefaultProvider } from '@ethersproject/providers'
import { TokenAmount } from './entities/fractions/tokenAmount' import { ChainId, Token, TokenAmount } from '@uniswap/sdk-core'
import { Pair } from './entities/pair' import { Pair } from './entities/pair'
import IUniswapV2Pair from '@uniswap/v2-core/build/IUniswapV2Pair.json' import IUniswapV2Pair from '@uniswap/v2-core/build/IUniswapV2Pair.json'
import invariant from 'tiny-invariant' import invariant from 'tiny-invariant'
import ERC20 from './abis/ERC20.json' import ERC20 from './abis/ERC20.json'
import { ChainId } from './constants'
import { Token } from './entities/token'
let TOKEN_DECIMALS_CACHE: { [chainId: number]: { [address: string]: number } } = { let TOKEN_DECIMALS_CACHE: { [chainId: number]: { [address: string]: number } } = {
[ChainId.MAINNET]: { [ChainId.MAINNET]: {
......
import JSBI from 'jsbi' import JSBI from 'jsbi'
export { JSBI } export { JSBI }
export { export { FACTORY_ADDRESS, INIT_CODE_HASH, MINIMUM_LIQUIDITY } from './constants'
BigintIsh,
ChainId,
TradeType,
Rounding,
FACTORY_ADDRESS,
INIT_CODE_HASH,
MINIMUM_LIQUIDITY
} from './constants'
export * from './errors' export * from './errors'
export * from './entities' export * from './entities'
......
import invariant from 'tiny-invariant'
import { ChainId, CurrencyAmount, ETHER, Pair, Percent, Route, Router, Token, TokenAmount, Trade, WETH } from '../src'
import JSBI from 'jsbi' import JSBI from 'jsbi'
import { Pair, Route, Trade } from './entities'
import { Router } from './router'
import invariant from 'tiny-invariant'
import { ChainId, CurrencyAmount, ETHER, Percent, Token, TokenAmount, WETH } from '@uniswap/sdk-core'
function checkDeadline(deadline: string[] | string): void { function checkDeadline(deadline: string[] | string): void {
expect(typeof deadline).toBe('string') expect(typeof deadline).toBe('string')
......
import { TradeType } from './constants' import { CurrencyAmount, ETHER, Percent, TradeType } from '@uniswap/sdk-core'
import { Trade } from 'entities'
import invariant from 'tiny-invariant' import invariant from 'tiny-invariant'
import { validateAndParseAddress } from './utils' import { validateAndParseAddress } from './utils'
import { CurrencyAmount, ETHER, Percent, Trade } from './entities'
/** /**
* Options for producing the arguments to send call to the router. * Options for producing the arguments to send call to the router.
......
...@@ -3,12 +3,7 @@ import warning from 'tiny-warning' ...@@ -3,12 +3,7 @@ import warning from 'tiny-warning'
import JSBI from 'jsbi' import JSBI from 'jsbi'
import { getAddress } from '@ethersproject/address' import { getAddress } from '@ethersproject/address'
import { BigintIsh, ZERO, ONE, TWO, THREE, SolidityType, SOLIDITY_TYPE_MAXIMA } from './constants' import { BigintIsh } from '@uniswap/sdk-core'
export function validateSolidityTypeInstance(value: JSBI, solidityType: SolidityType): void {
invariant(JSBI.greaterThanOrEqual(value, ZERO), `${value} is not a ${solidityType}.`)
invariant(JSBI.lessThanOrEqual(value, SOLIDITY_TYPE_MAXIMA[solidityType]), `${value} is not a ${solidityType}.`)
}
// warns if addresses are not checksummed // warns if addresses are not checksummed
export function validateAndParseAddress(address: string): string { export function validateAndParseAddress(address: string): string {
...@@ -22,29 +17,7 @@ export function validateAndParseAddress(address: string): string { ...@@ -22,29 +17,7 @@ export function validateAndParseAddress(address: string): string {
} }
export function parseBigintIsh(bigintIsh: BigintIsh): JSBI { export function parseBigintIsh(bigintIsh: BigintIsh): JSBI {
return bigintIsh instanceof JSBI return bigintIsh instanceof JSBI ? bigintIsh : JSBI.BigInt(bigintIsh)
? bigintIsh
: typeof bigintIsh === 'bigint'
? JSBI.BigInt(bigintIsh.toString())
: JSBI.BigInt(bigintIsh)
}
// mock the on-chain sqrt function
export function sqrt(y: JSBI): JSBI {
validateSolidityTypeInstance(y, SolidityType.uint256)
let z: JSBI = ZERO
let x: JSBI
if (JSBI.greaterThan(y, THREE)) {
z = y
x = JSBI.add(JSBI.divide(y, TWO), ONE)
while (JSBI.lessThan(x, z)) {
z = x
x = JSBI.divide(JSBI.add(JSBI.divide(y, x), x), TWO)
}
} else if (JSBI.notEqual(y, ZERO)) {
z = ONE
}
return z
} }
// given an array of items sorted by `comparator`, insert an item into its sort index and constrain the size to // given an array of items sorted by `comparator`, insert an item into its sort index and constrain the size to
......
import { Fraction } from '../src'
import JSBI from 'jsbi'
describe.only('Fraction', () => {
describe('#quotient', () => {
it('floor division', () => {
expect(new Fraction(JSBI.BigInt(8), JSBI.BigInt(3)).quotient).toEqual(JSBI.BigInt(2)) // one below
expect(new Fraction(JSBI.BigInt(12), JSBI.BigInt(4)).quotient).toEqual(JSBI.BigInt(3)) // exact
expect(new Fraction(JSBI.BigInt(16), JSBI.BigInt(5)).quotient).toEqual(JSBI.BigInt(3)) // one above
})
})
describe('#remainder', () => {
it('returns fraction after divison', () => {
expect(new Fraction(JSBI.BigInt(8), JSBI.BigInt(3)).remainder).toEqual(
new Fraction(JSBI.BigInt(2), JSBI.BigInt(3))
)
expect(new Fraction(JSBI.BigInt(12), JSBI.BigInt(4)).remainder).toEqual(
new Fraction(JSBI.BigInt(0), JSBI.BigInt(4))
)
expect(new Fraction(JSBI.BigInt(16), JSBI.BigInt(5)).remainder).toEqual(
new Fraction(JSBI.BigInt(1), JSBI.BigInt(5))
)
})
})
describe('#invert', () => {
it('flips num and denom', () => {
expect(new Fraction(JSBI.BigInt(5), JSBI.BigInt(10)).invert().numerator).toEqual(JSBI.BigInt(10))
expect(new Fraction(JSBI.BigInt(5), JSBI.BigInt(10)).invert().denominator).toEqual(JSBI.BigInt(5))
})
})
describe('#add', () => {
it('multiples denoms and adds nums', () => {
expect(new Fraction(JSBI.BigInt(1), JSBI.BigInt(10)).add(new Fraction(JSBI.BigInt(4), JSBI.BigInt(12)))).toEqual(
new Fraction(JSBI.BigInt(52), JSBI.BigInt(120))
)
})
it('same denom', () => {
expect(new Fraction(JSBI.BigInt(1), JSBI.BigInt(5)).add(new Fraction(JSBI.BigInt(2), JSBI.BigInt(5)))).toEqual(
new Fraction(JSBI.BigInt(3), JSBI.BigInt(5))
)
})
})
describe('#subtract', () => {
it('multiples denoms and subtracts nums', () => {
expect(
new Fraction(JSBI.BigInt(1), JSBI.BigInt(10)).subtract(new Fraction(JSBI.BigInt(4), JSBI.BigInt(12)))
).toEqual(new Fraction(JSBI.BigInt(-28), JSBI.BigInt(120)))
})
it('same denom', () => {
expect(
new Fraction(JSBI.BigInt(3), JSBI.BigInt(5)).subtract(new Fraction(JSBI.BigInt(2), JSBI.BigInt(5)))
).toEqual(new Fraction(JSBI.BigInt(1), JSBI.BigInt(5)))
})
})
describe('#lessThan', () => {
it('correct', () => {
expect(
new Fraction(JSBI.BigInt(1), JSBI.BigInt(10)).lessThan(new Fraction(JSBI.BigInt(4), JSBI.BigInt(12)))
).toBe(true)
expect(new Fraction(JSBI.BigInt(1), JSBI.BigInt(3)).lessThan(new Fraction(JSBI.BigInt(4), JSBI.BigInt(12)))).toBe(
false
)
expect(
new Fraction(JSBI.BigInt(5), JSBI.BigInt(12)).lessThan(new Fraction(JSBI.BigInt(4), JSBI.BigInt(12)))
).toBe(false)
})
})
describe('#equalTo', () => {
it('correct', () => {
expect(new Fraction(JSBI.BigInt(1), JSBI.BigInt(10)).equalTo(new Fraction(JSBI.BigInt(4), JSBI.BigInt(12)))).toBe(
false
)
expect(new Fraction(JSBI.BigInt(1), JSBI.BigInt(3)).equalTo(new Fraction(JSBI.BigInt(4), JSBI.BigInt(12)))).toBe(
true
)
expect(new Fraction(JSBI.BigInt(5), JSBI.BigInt(12)).equalTo(new Fraction(JSBI.BigInt(4), JSBI.BigInt(12)))).toBe(
false
)
})
})
describe('#greaterThan', () => {
it('correct', () => {
expect(
new Fraction(JSBI.BigInt(1), JSBI.BigInt(10)).greaterThan(new Fraction(JSBI.BigInt(4), JSBI.BigInt(12)))
).toBe(false)
expect(
new Fraction(JSBI.BigInt(1), JSBI.BigInt(3)).greaterThan(new Fraction(JSBI.BigInt(4), JSBI.BigInt(12)))
).toBe(false)
expect(
new Fraction(JSBI.BigInt(5), JSBI.BigInt(12)).greaterThan(new Fraction(JSBI.BigInt(4), JSBI.BigInt(12)))
).toBe(true)
})
})
describe('#multiplty', () => {
it('correct', () => {
expect(
new Fraction(JSBI.BigInt(1), JSBI.BigInt(10)).multiply(new Fraction(JSBI.BigInt(4), JSBI.BigInt(12)))
).toEqual(new Fraction(JSBI.BigInt(4), JSBI.BigInt(120)))
expect(
new Fraction(JSBI.BigInt(1), JSBI.BigInt(3)).multiply(new Fraction(JSBI.BigInt(4), JSBI.BigInt(12)))
).toEqual(new Fraction(JSBI.BigInt(4), JSBI.BigInt(36)))
expect(
new Fraction(JSBI.BigInt(5), JSBI.BigInt(12)).multiply(new Fraction(JSBI.BigInt(4), JSBI.BigInt(12)))
).toEqual(new Fraction(JSBI.BigInt(20), JSBI.BigInt(144)))
})
})
describe('#divide', () => {
it('correct', () => {
expect(
new Fraction(JSBI.BigInt(1), JSBI.BigInt(10)).divide(new Fraction(JSBI.BigInt(4), JSBI.BigInt(12)))
).toEqual(new Fraction(JSBI.BigInt(12), JSBI.BigInt(40)))
expect(
new Fraction(JSBI.BigInt(1), JSBI.BigInt(3)).divide(new Fraction(JSBI.BigInt(4), JSBI.BigInt(12)))
).toEqual(new Fraction(JSBI.BigInt(12), JSBI.BigInt(12)))
expect(
new Fraction(JSBI.BigInt(5), JSBI.BigInt(12)).divide(new Fraction(JSBI.BigInt(4), JSBI.BigInt(12)))
).toEqual(new Fraction(JSBI.BigInt(60), JSBI.BigInt(48)))
})
})
})
import { ChainId, Token, TokenAmount, Pair, InsufficientInputAmountError } from '../src'
import { sortedInsert } from '../src/utils'
describe('miscellaneous', () => {
it('getLiquidityMinted:0', async () => {
const tokenA = new Token(ChainId.RINKEBY, '0x0000000000000000000000000000000000000001', 18)
const tokenB = new Token(ChainId.RINKEBY, '0x0000000000000000000000000000000000000002', 18)
const pair = new Pair(new TokenAmount(tokenA, '0'), new TokenAmount(tokenB, '0'))
expect(() => {
pair.getLiquidityMinted(
new TokenAmount(pair.liquidityToken, '0'),
new TokenAmount(tokenA, '1000'),
new TokenAmount(tokenB, '1000')
)
}).toThrow(InsufficientInputAmountError)
expect(() => {
pair.getLiquidityMinted(
new TokenAmount(pair.liquidityToken, '0'),
new TokenAmount(tokenA, '1000000'),
new TokenAmount(tokenB, '1')
)
}).toThrow(InsufficientInputAmountError)
const liquidity = pair.getLiquidityMinted(
new TokenAmount(pair.liquidityToken, '0'),
new TokenAmount(tokenA, '1001'),
new TokenAmount(tokenB, '1001')
)
expect(liquidity.raw.toString()).toEqual('1')
})
it('getLiquidityMinted:!0', async () => {
const tokenA = new Token(ChainId.RINKEBY, '0x0000000000000000000000000000000000000001', 18)
const tokenB = new Token(ChainId.RINKEBY, '0x0000000000000000000000000000000000000002', 18)
const pair = new Pair(new TokenAmount(tokenA, '10000'), new TokenAmount(tokenB, '10000'))
expect(
pair
.getLiquidityMinted(
new TokenAmount(pair.liquidityToken, '10000'),
new TokenAmount(tokenA, '2000'),
new TokenAmount(tokenB, '2000')
)
.raw.toString()
).toEqual('2000')
})
it('getLiquidityValue:!feeOn', async () => {
const tokenA = new Token(ChainId.RINKEBY, '0x0000000000000000000000000000000000000001', 18)
const tokenB = new Token(ChainId.RINKEBY, '0x0000000000000000000000000000000000000002', 18)
const pair = new Pair(new TokenAmount(tokenA, '1000'), new TokenAmount(tokenB, '1000'))
{
const liquidityValue = pair.getLiquidityValue(
tokenA,
new TokenAmount(pair.liquidityToken, '1000'),
new TokenAmount(pair.liquidityToken, '1000'),
false
)
expect(liquidityValue.token.equals(tokenA)).toBe(true)
expect(liquidityValue.raw.toString()).toBe('1000')
}
// 500
{
const liquidityValue = pair.getLiquidityValue(
tokenA,
new TokenAmount(pair.liquidityToken, '1000'),
new TokenAmount(pair.liquidityToken, '500'),
false
)
expect(liquidityValue.token.equals(tokenA)).toBe(true)
expect(liquidityValue.raw.toString()).toBe('500')
}
// tokenB
{
const liquidityValue = pair.getLiquidityValue(
tokenB,
new TokenAmount(pair.liquidityToken, '1000'),
new TokenAmount(pair.liquidityToken, '1000'),
false
)
expect(liquidityValue.token.equals(tokenB)).toBe(true)
expect(liquidityValue.raw.toString()).toBe('1000')
}
})
it('getLiquidityValue:feeOn', async () => {
const tokenA = new Token(ChainId.RINKEBY, '0x0000000000000000000000000000000000000001', 18)
const tokenB = new Token(ChainId.RINKEBY, '0x0000000000000000000000000000000000000002', 18)
const pair = new Pair(new TokenAmount(tokenA, '1000'), new TokenAmount(tokenB, '1000'))
const liquidityValue = pair.getLiquidityValue(
tokenA,
new TokenAmount(pair.liquidityToken, '500'),
new TokenAmount(pair.liquidityToken, '500'),
true,
'250000' // 500 ** 2
)
expect(liquidityValue.token.equals(tokenA)).toBe(true)
expect(liquidityValue.raw.toString()).toBe('917') // ceiling(1000 - (500 * (1 / 6)))
})
describe('#sortedInsert', () => {
const comp = (a: number, b: number) => a - b
it('throws if maxSize is 0', () => {
expect(() => sortedInsert([], 1, 0, comp)).toThrow('MAX_SIZE_ZERO')
})
it('throws if items.length > maxSize', () => {
expect(() => sortedInsert([1, 2], 1, 1, comp)).toThrow('ITEMS_SIZE')
})
it('adds if empty', () => {
const arr: number[] = []
expect(sortedInsert(arr, 3, 2, comp)).toEqual(null)
expect(arr).toEqual([3])
})
it('adds if not full', () => {
const arr: number[] = [1, 5]
expect(sortedInsert(arr, 3, 3, comp)).toEqual(null)
expect(arr).toEqual([1, 3, 5])
})
it('adds if will not be full after', () => {
const arr: number[] = [1]
expect(sortedInsert(arr, 0, 3, comp)).toEqual(null)
expect(arr).toEqual([0, 1])
})
it('returns add if sorts after last', () => {
const arr = [1, 2, 3]
expect(sortedInsert(arr, 4, 3, comp)).toEqual(4)
expect(arr).toEqual([1, 2, 3])
})
it('removes from end if full', () => {
const arr = [1, 3, 4]
expect(sortedInsert(arr, 2, 3, comp)).toEqual(4)
expect(arr).toEqual([1, 2, 3])
})
it('uses comparator', () => {
const arr = [4, 2, 1]
expect(sortedInsert(arr, 3, 3, (a, b) => comp(a, b) * -1)).toEqual(1)
expect(arr).toEqual([4, 3, 2])
})
describe('maxSize of 1', () => {
it('empty add', () => {
const arr: number[] = []
expect(sortedInsert(arr, 3, 1, comp)).toEqual(null)
expect(arr).toEqual([3])
})
it('full add greater', () => {
const arr: number[] = [2]
expect(sortedInsert(arr, 3, 1, comp)).toEqual(3)
expect(arr).toEqual([2])
})
it('full add lesser', () => {
const arr: number[] = [4]
expect(sortedInsert(arr, 3, 1, comp)).toEqual(4)
expect(arr).toEqual([3])
})
})
})
})
import { ChainId, Token } from '../src'
describe('Token', () => {
const ADDRESS_ONE = '0x0000000000000000000000000000000000000001'
const ADDRESS_TWO = '0x0000000000000000000000000000000000000002'
describe('#equals', () => {
it('fails if address differs', () => {
expect(new Token(ChainId.MAINNET, ADDRESS_ONE, 18).equals(new Token(ChainId.MAINNET, ADDRESS_TWO, 18))).toBe(
false
)
})
it('false if chain id differs', () => {
expect(new Token(ChainId.ROPSTEN, ADDRESS_ONE, 18).equals(new Token(ChainId.MAINNET, ADDRESS_ONE, 18))).toBe(
false
)
})
it('true if only decimals differs', () => {
expect(new Token(ChainId.MAINNET, ADDRESS_ONE, 9).equals(new Token(ChainId.MAINNET, ADDRESS_ONE, 18))).toBe(true)
})
it('true if address is the same', () => {
expect(new Token(ChainId.MAINNET, ADDRESS_ONE, 18).equals(new Token(ChainId.MAINNET, ADDRESS_ONE, 18))).toBe(true)
})
it('true on reference equality', () => {
const token = new Token(ChainId.MAINNET, ADDRESS_ONE, 18)
expect(token.equals(token)).toBe(true)
})
it('true even if name/symbol/decimals differ', () => {
const tokenA = new Token(ChainId.MAINNET, ADDRESS_ONE, 9, 'abc', 'def')
const tokenB = new Token(ChainId.MAINNET, ADDRESS_ONE, 18, 'ghi', 'jkl')
expect(tokenA.equals(tokenB)).toBe(true)
})
})
})
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
"importHelpers": true, "importHelpers": true,
"declaration": true, "declaration": true,
"sourceMap": true, "sourceMap": true,
"rootDir": "./",
"strict": true, "strict": true,
"noImplicitAny": true, "noImplicitAny": true,
"strictNullChecks": true, "strictNullChecks": true,
......
This source diff could not be displayed because it is too large. You can view the blob instead.
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