Commit 6e86d3c7 authored by Moody Salem's avatar Moody Salem

use the new sdk-core version

parent 6440270c
import JSBI from 'jsbi' import JSBI from 'jsbi'
import invariant from 'tiny-invariant' import invariant from 'tiny-invariant'
import { ChainId, WETH9 as _WETH9, TradeType, Rounding, Token, CurrencyAmount } from '@uniswap/sdk-core' import { ChainId, WETH9 as _WETH9, TradeType, Token, CurrencyAmount } from '@uniswap/sdk-core'
import { Pair, Route, Trade } from '../index' import { Pair, Route, Trade } from '../index'
const ADDRESSES = [ const ADDRESSES = [
...@@ -24,95 +24,70 @@ describe('entities', () => { ...@@ -24,95 +24,70 @@ describe('entities', () => {
DECIMAL_PERMUTATIONS.forEach(decimals => { DECIMAL_PERMUTATIONS.forEach(decimals => {
describe(`decimals permutation: ${decimals}`, () => { describe(`decimals permutation: ${decimals}`, () => {
let tokens: Token[] let tokens: Token[]
it('Token', () => { beforeAll(() => {
tokens = ADDRESSES.map((address, i) => new Token(CHAIN_ID, address, decimals[i])) tokens = ADDRESSES.map((address, i) => new Token(CHAIN_ID, address, decimals[i]))
tokens.forEach((token, i) => {
expect(token.chainId).toEqual(CHAIN_ID)
expect(token.address).toEqual(ADDRESSES[i])
expect(token.decimals).toEqual(decimals[i])
})
}) })
let pairs: Pair[] let pairs: Pair[]
it('Pair', () => { it('Pair', () => {
pairs = [ pairs = [
new Pair( new Pair(
new CurrencyAmount(tokens[0], decimalize(1, tokens[0].decimals)), CurrencyAmount.fromRawAmount(tokens[0], decimalize(1, tokens[0].decimals)),
new CurrencyAmount(tokens[1], decimalize(1, tokens[1].decimals)) CurrencyAmount.fromRawAmount(tokens[1], decimalize(1, tokens[1].decimals))
), ),
new Pair( new Pair(
new CurrencyAmount(tokens[1], decimalize(1, tokens[1].decimals)), CurrencyAmount.fromRawAmount(tokens[1], decimalize(1, tokens[1].decimals)),
new CurrencyAmount(tokens[2], decimalize(1, tokens[2].decimals)) CurrencyAmount.fromRawAmount(tokens[2], decimalize(1, tokens[2].decimals))
), ),
new Pair( new Pair(
new CurrencyAmount(tokens[2], decimalize(1, tokens[2].decimals)), CurrencyAmount.fromRawAmount(tokens[2], decimalize(1, tokens[2].decimals)),
new CurrencyAmount(WETH9, decimalize(1234, WETH9.decimals)) CurrencyAmount.fromRawAmount(WETH9, decimalize(1234, WETH9.decimals))
) )
] ]
}) })
let route: Route let route: Route<Token, Token>
it('Route', () => { it('Route', () => {
route = new Route(pairs, tokens[0]) route = new Route(pairs, tokens[0], WETH9)
expect(route.pairs).toEqual(pairs) expect(route.pairs).toEqual(pairs)
expect(route.path).toEqual(tokens.concat([WETH9])) expect(route.path).toEqual(tokens.concat([WETH9]))
expect(route.input).toEqual(tokens[0]) expect(route.input).toEqual(tokens[0])
expect(route.output).toEqual(WETH9) expect(route.output).toEqual(WETH9)
}) })
it('Price:Route.midPrice', () => { it('#midPrice', () => {
invariant(route.input.isToken) invariant(route.input.isToken)
invariant(route.output.isToken) invariant(route.output.isToken)
expect(route.midPrice.quote(new CurrencyAmount(route.input, decimalize(1, route.input.decimals)))).toEqual(
new CurrencyAmount(route.output, decimalize(1234, route.output.decimals))
)
expect( expect(
route.midPrice.invert().quote(new CurrencyAmount(route.output, decimalize(1234, route.output.decimals))) route.midPrice.quote(CurrencyAmount.fromRawAmount(route.input, decimalize(1, route.input.decimals))).toExact()
).toEqual(new CurrencyAmount(route.input, decimalize(1, route.input.decimals))) ).toEqual(CurrencyAmount.fromRawAmount(route.output, decimalize(1234, route.output.decimals)).toExact())
expect(
route.midPrice
.invert()
.quote(CurrencyAmount.fromRawAmount(route.output, decimalize(1234, route.output.decimals)))
.toExact()
).toEqual(CurrencyAmount.fromRawAmount(route.input, decimalize(1, route.input.decimals)).toExact())
expect(route.midPrice.toSignificant(1)).toEqual('1000')
expect(route.midPrice.toSignificant(2)).toEqual('1200')
expect(route.midPrice.toSignificant(3)).toEqual('1230')
expect(route.midPrice.toSignificant(4)).toEqual('1234')
expect(route.midPrice.toSignificant(5)).toEqual('1234')
expect(route.midPrice.toSignificant(5, { groupSeparator: ',' })).toEqual('1,234')
expect(route.midPrice.invert().toSignificant(1)).toEqual('0.0008')
expect(route.midPrice.invert().toSignificant(2)).toEqual('0.00081')
expect(route.midPrice.invert().toSignificant(3)).toEqual('0.00081')
expect(route.midPrice.invert().toSignificant(4)).toEqual('0.0008104')
expect(route.midPrice.invert().toSignificant(4, undefined, Rounding.ROUND_DOWN)).toEqual('0.0008103')
expect(route.midPrice.invert().toSignificant(5)).toEqual('0.00081037') expect(route.midPrice.invert().toSignificant(5)).toEqual('0.00081037')
expect(route.midPrice.toFixed(0)).toEqual('1234')
expect(route.midPrice.toFixed(1)).toEqual('1234.0')
expect(route.midPrice.toFixed(2)).toEqual('1234.00') expect(route.midPrice.toFixed(2)).toEqual('1234.00')
expect(route.midPrice.toFixed(2, { groupSeparator: ',' })).toEqual('1,234.00')
expect(route.midPrice.invert().toFixed(0)).toEqual('0')
expect(route.midPrice.invert().toFixed(1)).toEqual('0.0')
expect(route.midPrice.invert().toFixed(2)).toEqual('0.00')
expect(route.midPrice.invert().toFixed(3)).toEqual('0.001')
expect(route.midPrice.invert().toFixed(4)).toEqual('0.0008')
expect(route.midPrice.invert().toFixed(5)).toEqual('0.00081')
expect(route.midPrice.invert().toFixed(6)).toEqual('0.000810')
expect(route.midPrice.invert().toFixed(7)).toEqual('0.0008104')
expect(route.midPrice.invert().toFixed(7, undefined, Rounding.ROUND_DOWN)).toEqual('0.0008103')
expect(route.midPrice.invert().toFixed(8)).toEqual('0.00081037') expect(route.midPrice.invert().toFixed(8)).toEqual('0.00081037')
}) })
describe('Trade', () => { describe('Trade', () => {
let route: Route let route: Route<Token, Token>
it('TradeType.EXACT_INPUT', () => { it('TradeType.EXACT_INPUT', () => {
route = new Route( route = new Route(
[ [
new Pair( new Pair(
new CurrencyAmount(tokens[1], decimalize(5, tokens[1].decimals)), CurrencyAmount.fromRawAmount(tokens[1], decimalize(5, tokens[1].decimals)),
new CurrencyAmount(WETH9, decimalize(10, WETH9.decimals)) CurrencyAmount.fromRawAmount(WETH9, decimalize(10, WETH9.decimals))
) )
], ],
tokens[1] tokens[1],
WETH9
) )
const inputAmount = new CurrencyAmount(tokens[1], decimalize(1, tokens[1].decimals)) const inputAmount = CurrencyAmount.fromRawAmount(tokens[1], decimalize(1, tokens[1].decimals))
const expectedOutputAmount = new CurrencyAmount(WETH9, '1662497915624478906') const expectedOutputAmount = CurrencyAmount.fromRawAmount(WETH9, '1662497915624478906')
const trade = new Trade(route, inputAmount, TradeType.EXACT_INPUT) const trade = new Trade(route, inputAmount, TradeType.EXACT_INPUT)
expect(trade.route).toEqual(route) expect(trade.route).toEqual(route)
expect(trade.tradeType).toEqual(TradeType.EXACT_INPUT) expect(trade.tradeType).toEqual(TradeType.EXACT_INPUT)
...@@ -121,18 +96,15 @@ describe('entities', () => { ...@@ -121,18 +96,15 @@ describe('entities', () => {
expect(trade.executionPrice.toSignificant(18)).toEqual('1.66249791562447891') expect(trade.executionPrice.toSignificant(18)).toEqual('1.66249791562447891')
expect(trade.executionPrice.invert().toSignificant(18)).toEqual('0.601504513540621866') expect(trade.executionPrice.invert().toSignificant(18)).toEqual('0.601504513540621866')
expect(trade.executionPrice.quote(inputAmount)).toEqual(expectedOutputAmount) expect(trade.executionPrice.quote(inputAmount).quotient).toEqual(expectedOutputAmount.quotient)
expect(trade.executionPrice.invert().quote(expectedOutputAmount)).toEqual(inputAmount) expect(trade.executionPrice.invert().quote(expectedOutputAmount).quotient).toEqual(inputAmount.quotient)
expect(trade.nextMidPrice.toSignificant(18)).toEqual('1.38958368072925352')
expect(trade.nextMidPrice.invert().toSignificant(18)).toEqual('0.71964')
expect(trade.priceImpact.toSignificant(18)).toEqual('16.8751042187760547') expect(trade.priceImpact.toSignificant(18)).toEqual('16.8751042187760547')
}) })
it('TradeType.EXACT_OUTPUT', () => { it('TradeType.EXACT_OUTPUT', () => {
const outputAmount = new CurrencyAmount(WETH9, '1662497915624478906') const outputAmount = CurrencyAmount.fromRawAmount(WETH9, '1662497915624478906')
const expectedInputAmount = new CurrencyAmount(tokens[1], decimalize(1, tokens[1].decimals)) const expectedInputAmount = CurrencyAmount.fromRawAmount(tokens[1], decimalize(1, tokens[1].decimals))
const trade = new Trade(route, outputAmount, TradeType.EXACT_OUTPUT) const trade = new Trade(route, outputAmount, TradeType.EXACT_OUTPUT)
expect(trade.route).toEqual(route) expect(trade.route).toEqual(route)
expect(trade.tradeType).toEqual(TradeType.EXACT_OUTPUT) expect(trade.tradeType).toEqual(TradeType.EXACT_OUTPUT)
...@@ -141,11 +113,8 @@ describe('entities', () => { ...@@ -141,11 +113,8 @@ describe('entities', () => {
expect(trade.executionPrice.toSignificant(18)).toEqual('1.66249791562447891') expect(trade.executionPrice.toSignificant(18)).toEqual('1.66249791562447891')
expect(trade.executionPrice.invert().toSignificant(18)).toEqual('0.601504513540621866') expect(trade.executionPrice.invert().toSignificant(18)).toEqual('0.601504513540621866')
expect(trade.executionPrice.quote(expectedInputAmount)).toEqual(outputAmount) expect(trade.executionPrice.quote(expectedInputAmount).quotient).toEqual(outputAmount.quotient)
expect(trade.executionPrice.invert().quote(outputAmount)).toEqual(expectedInputAmount) expect(trade.executionPrice.invert().quote(outputAmount).quotient).toEqual(expectedInputAmount.quotient)
expect(trade.nextMidPrice.toSignificant(18)).toEqual('1.38958368072925352')
expect(trade.nextMidPrice.invert().toSignificant(18)).toEqual('0.71964')
expect(trade.priceImpact.toSignificant(18)).toEqual('16.8751042187760547') expect(trade.priceImpact.toSignificant(18)).toEqual('16.8751042187760547')
}) })
...@@ -155,8 +124,8 @@ describe('entities', () => { ...@@ -155,8 +124,8 @@ describe('entities', () => {
const route = new Route( const route = new Route(
[ [
new Pair( new Pair(
new CurrencyAmount(tokens[1], decimalize(1, tokens[1].decimals)), CurrencyAmount.fromRawAmount(tokens[1], decimalize(1, tokens[1].decimals)),
new CurrencyAmount( CurrencyAmount.fromRawAmount(
WETH9, WETH9,
JSBI.add( JSBI.add(
decimalize(10, WETH9.decimals), decimalize(10, WETH9.decimals),
...@@ -165,9 +134,10 @@ describe('entities', () => { ...@@ -165,9 +134,10 @@ describe('entities', () => {
) )
) )
], ],
tokens[1] tokens[1],
WETH9
) )
const outputAmount = new CurrencyAmount(tokens[1], '1') const outputAmount = CurrencyAmount.fromRawAmount(tokens[1], '1')
const trade = new Trade(route, outputAmount, TradeType.EXACT_INPUT) const trade = new Trade(route, outputAmount, TradeType.EXACT_INPUT)
expect(trade.priceImpact.toSignificant(18)).toEqual( expect(trade.priceImpact.toSignificant(18)).toEqual(
...@@ -176,12 +146,6 @@ describe('entities', () => { ...@@ -176,12 +146,6 @@ describe('entities', () => {
} }
}) })
}) })
it('CurrencyAmount', () => {
const amount = new CurrencyAmount(WETH9, '1234567000000000000000')
expect(amount.toExact()).toEqual('1234.567')
expect(amount.toExact({ groupSeparator: ',' })).toEqual('1,234.567')
})
}) })
}) })
}) })
...@@ -44,7 +44,11 @@ describe('Pair', () => { ...@@ -44,7 +44,11 @@ describe('Pair', () => {
describe('constructor', () => { describe('constructor', () => {
it('cannot be used for tokens on different chains', () => { it('cannot be used for tokens on different chains', () => {
expect( expect(
() => new Pair(new CurrencyAmount(USDC, '100'), new CurrencyAmount(WETH9[ChainId.RINKEBY], '100')) () =>
new Pair(
CurrencyAmount.fromRawAmount(USDC, '100'),
CurrencyAmount.fromRawAmount(WETH9[ChainId.RINKEBY], '100')
)
).toThrow('CHAIN_IDS') ).toThrow('CHAIN_IDS')
}) })
}) })
...@@ -57,61 +61,69 @@ describe('Pair', () => { ...@@ -57,61 +61,69 @@ describe('Pair', () => {
describe('#token0', () => { describe('#token0', () => {
it('always is the token that sorts before', () => { it('always is the token that sorts before', () => {
expect(new Pair(new CurrencyAmount(USDC, '100'), new CurrencyAmount(DAI, '100')).token0).toEqual(DAI) expect(
expect(new Pair(new CurrencyAmount(DAI, '100'), new CurrencyAmount(USDC, '100')).token0).toEqual(DAI) new Pair(CurrencyAmount.fromRawAmount(USDC, '100'), CurrencyAmount.fromRawAmount(DAI, '100')).token0
).toEqual(DAI)
expect(
new Pair(CurrencyAmount.fromRawAmount(DAI, '100'), CurrencyAmount.fromRawAmount(USDC, '100')).token0
).toEqual(DAI)
}) })
}) })
describe('#token1', () => { describe('#token1', () => {
it('always is the token that sorts after', () => { it('always is the token that sorts after', () => {
expect(new Pair(new CurrencyAmount(USDC, '100'), new CurrencyAmount(DAI, '100')).token1).toEqual(USDC) expect(
expect(new Pair(new CurrencyAmount(DAI, '100'), new CurrencyAmount(USDC, '100')).token1).toEqual(USDC) new Pair(CurrencyAmount.fromRawAmount(USDC, '100'), CurrencyAmount.fromRawAmount(DAI, '100')).token1
).toEqual(USDC)
expect(
new Pair(CurrencyAmount.fromRawAmount(DAI, '100'), CurrencyAmount.fromRawAmount(USDC, '100')).token1
).toEqual(USDC)
}) })
}) })
describe('#reserve0', () => { describe('#reserve0', () => {
it('always comes from the token that sorts before', () => { it('always comes from the token that sorts before', () => {
expect(new Pair(new CurrencyAmount(USDC, '100'), new CurrencyAmount(DAI, '101')).reserve0).toEqual( expect(
new CurrencyAmount(DAI, '101') new Pair(CurrencyAmount.fromRawAmount(USDC, '100'), CurrencyAmount.fromRawAmount(DAI, '101')).reserve0
) ).toEqual(CurrencyAmount.fromRawAmount(DAI, '101'))
expect(new Pair(new CurrencyAmount(DAI, '101'), new CurrencyAmount(USDC, '100')).reserve0).toEqual( expect(
new CurrencyAmount(DAI, '101') new Pair(CurrencyAmount.fromRawAmount(DAI, '101'), CurrencyAmount.fromRawAmount(USDC, '100')).reserve0
) ).toEqual(CurrencyAmount.fromRawAmount(DAI, '101'))
}) })
}) })
describe('#reserve1', () => { describe('#reserve1', () => {
it('always comes from the token that sorts after', () => { it('always comes from the token that sorts after', () => {
expect(new Pair(new CurrencyAmount(USDC, '100'), new CurrencyAmount(DAI, '101')).reserve1).toEqual( expect(
new CurrencyAmount(USDC, '100') new Pair(CurrencyAmount.fromRawAmount(USDC, '100'), CurrencyAmount.fromRawAmount(DAI, '101')).reserve1
) ).toEqual(CurrencyAmount.fromRawAmount(USDC, '100'))
expect(new Pair(new CurrencyAmount(DAI, '101'), new CurrencyAmount(USDC, '100')).reserve1).toEqual( expect(
new CurrencyAmount(USDC, '100') new Pair(CurrencyAmount.fromRawAmount(DAI, '101'), CurrencyAmount.fromRawAmount(USDC, '100')).reserve1
) ).toEqual(CurrencyAmount.fromRawAmount(USDC, '100'))
}) })
}) })
describe('#token0Price', () => { describe('#token0Price', () => {
it('returns price of token0 in terms of token1', () => { it('returns price of token0 in terms of token1', () => {
expect(new Pair(new CurrencyAmount(USDC, '101'), new CurrencyAmount(DAI, '100')).token0Price).toEqual( expect(
new Price(DAI, USDC, '100', '101') new Pair(CurrencyAmount.fromRawAmount(USDC, '101'), CurrencyAmount.fromRawAmount(DAI, '100')).token0Price
) ).toEqual(new Price(DAI, USDC, '100', '101'))
expect(new Pair(new CurrencyAmount(DAI, '100'), new CurrencyAmount(USDC, '101')).token0Price).toEqual( expect(
new Price(DAI, USDC, '100', '101') new Pair(CurrencyAmount.fromRawAmount(DAI, '100'), CurrencyAmount.fromRawAmount(USDC, '101')).token0Price
) ).toEqual(new Price(DAI, USDC, '100', '101'))
}) })
}) })
describe('#token1Price', () => { describe('#token1Price', () => {
it('returns price of token1 in terms of token0', () => { it('returns price of token1 in terms of token0', () => {
expect(new Pair(new CurrencyAmount(USDC, '101'), new CurrencyAmount(DAI, '100')).token1Price).toEqual( expect(
new Price(USDC, DAI, '101', '100') new Pair(CurrencyAmount.fromRawAmount(USDC, '101'), CurrencyAmount.fromRawAmount(DAI, '100')).token1Price
) ).toEqual(new Price(USDC, DAI, '101', '100'))
expect(new Pair(new CurrencyAmount(DAI, '100'), new CurrencyAmount(USDC, '101')).token1Price).toEqual( expect(
new Price(USDC, DAI, '101', '100') new Pair(CurrencyAmount.fromRawAmount(DAI, '100'), CurrencyAmount.fromRawAmount(USDC, '101')).token1Price
) ).toEqual(new Price(USDC, DAI, '101', '100'))
}) })
}) })
describe('#priceOf', () => { describe('#priceOf', () => {
const pair = new Pair(new CurrencyAmount(USDC, '101'), new CurrencyAmount(DAI, '100')) const pair = new Pair(CurrencyAmount.fromRawAmount(USDC, '101'), CurrencyAmount.fromRawAmount(DAI, '100'))
it('returns price of token in terms of other token', () => { it('returns price of token in terms of other token', () => {
expect(pair.priceOf(DAI)).toEqual(pair.token0Price) expect(pair.priceOf(DAI)).toEqual(pair.token0Price)
expect(pair.priceOf(USDC)).toEqual(pair.token1Price) expect(pair.priceOf(USDC)).toEqual(pair.token1Price)
...@@ -124,136 +136,151 @@ describe('Pair', () => { ...@@ -124,136 +136,151 @@ describe('Pair', () => {
describe('#reserveOf', () => { describe('#reserveOf', () => {
it('returns reserves of the given token', () => { it('returns reserves of the given token', () => {
expect(new Pair(new CurrencyAmount(USDC, '100'), new CurrencyAmount(DAI, '101')).reserveOf(USDC)).toEqual( expect(
new CurrencyAmount(USDC, '100') new Pair(CurrencyAmount.fromRawAmount(USDC, '100'), CurrencyAmount.fromRawAmount(DAI, '101')).reserveOf(USDC)
) ).toEqual(CurrencyAmount.fromRawAmount(USDC, '100'))
expect(new Pair(new CurrencyAmount(DAI, '101'), new CurrencyAmount(USDC, '100')).reserveOf(USDC)).toEqual( expect(
new CurrencyAmount(USDC, '100') new Pair(CurrencyAmount.fromRawAmount(DAI, '101'), CurrencyAmount.fromRawAmount(USDC, '100')).reserveOf(USDC)
) ).toEqual(CurrencyAmount.fromRawAmount(USDC, '100'))
}) })
it('throws if not in the pair', () => { it('throws if not in the pair', () => {
expect(() => expect(() =>
new Pair(new CurrencyAmount(DAI, '101'), new CurrencyAmount(USDC, '100')).reserveOf(WETH9[ChainId.MAINNET]) new Pair(CurrencyAmount.fromRawAmount(DAI, '101'), CurrencyAmount.fromRawAmount(USDC, '100')).reserveOf(
WETH9[ChainId.MAINNET]
)
).toThrow('TOKEN') ).toThrow('TOKEN')
}) })
}) })
describe('#chainId', () => { describe('#chainId', () => {
it('returns the token0 chainId', () => { it('returns the token0 chainId', () => {
expect(new Pair(new CurrencyAmount(USDC, '100'), new CurrencyAmount(DAI, '100')).chainId).toEqual(ChainId.MAINNET) expect(
expect(new Pair(new CurrencyAmount(DAI, '100'), new CurrencyAmount(USDC, '100')).chainId).toEqual(ChainId.MAINNET) new Pair(CurrencyAmount.fromRawAmount(USDC, '100'), CurrencyAmount.fromRawAmount(DAI, '100')).chainId
).toEqual(ChainId.MAINNET)
expect(
new Pair(CurrencyAmount.fromRawAmount(DAI, '100'), CurrencyAmount.fromRawAmount(USDC, '100')).chainId
).toEqual(ChainId.MAINNET)
}) })
}) })
describe('#involvesToken', () => { describe('#involvesToken', () => {
expect(new Pair(new CurrencyAmount(USDC, '100'), new CurrencyAmount(DAI, '100')).involvesToken(USDC)).toEqual(true)
expect(new Pair(new CurrencyAmount(USDC, '100'), new CurrencyAmount(DAI, '100')).involvesToken(DAI)).toEqual(true)
expect( expect(
new Pair(new CurrencyAmount(USDC, '100'), new CurrencyAmount(DAI, '100')).involvesToken(WETH9[ChainId.MAINNET]) new Pair(CurrencyAmount.fromRawAmount(USDC, '100'), CurrencyAmount.fromRawAmount(DAI, '100')).involvesToken(USDC)
).toEqual(true)
expect(
new Pair(CurrencyAmount.fromRawAmount(USDC, '100'), CurrencyAmount.fromRawAmount(DAI, '100')).involvesToken(DAI)
).toEqual(true)
expect(
new Pair(CurrencyAmount.fromRawAmount(USDC, '100'), CurrencyAmount.fromRawAmount(DAI, '100')).involvesToken(
WETH9[ChainId.MAINNET]
)
).toEqual(false) ).toEqual(false)
}) })
describe('miscellaneous', () => { describe('miscellaneous', () => {
it('getLiquidityMinted:0', async () => { it('getLiquidityMinted:0', async () => {
const tokenA = new Token(ChainId.RINKEBY, '0x0000000000000000000000000000000000000001', 18) const tokenA = new Token(ChainId.RINKEBY, '0x0000000000000000000000000000000000000001', 18)
const tokenB = new Token(ChainId.RINKEBY, '0x0000000000000000000000000000000000000002', 18) const tokenB = new Token(ChainId.RINKEBY, '0x0000000000000000000000000000000000000002', 18)
const pair = new Pair(new CurrencyAmount(tokenA, '0'), new CurrencyAmount(tokenB, '0')) const pair = new Pair(CurrencyAmount.fromRawAmount(tokenA, '0'), CurrencyAmount.fromRawAmount(tokenB, '0'))
expect(() => { expect(() => {
pair.getLiquidityMinted( pair.getLiquidityMinted(
new CurrencyAmount(pair.liquidityToken, '0'), CurrencyAmount.fromRawAmount(pair.liquidityToken, '0'),
new CurrencyAmount(tokenA, '1000'), CurrencyAmount.fromRawAmount(tokenA, '1000'),
new CurrencyAmount(tokenB, '1000') CurrencyAmount.fromRawAmount(tokenB, '1000')
) )
}).toThrow(InsufficientInputAmountError) }).toThrow(InsufficientInputAmountError)
expect(() => { expect(() => {
pair.getLiquidityMinted( pair.getLiquidityMinted(
new CurrencyAmount(pair.liquidityToken, '0'), CurrencyAmount.fromRawAmount(pair.liquidityToken, '0'),
new CurrencyAmount(tokenA, '1000000'), CurrencyAmount.fromRawAmount(tokenA, '1000000'),
new CurrencyAmount(tokenB, '1') CurrencyAmount.fromRawAmount(tokenB, '1')
) )
}).toThrow(InsufficientInputAmountError) }).toThrow(InsufficientInputAmountError)
const liquidity = pair.getLiquidityMinted( const liquidity = pair.getLiquidityMinted(
new CurrencyAmount(pair.liquidityToken, '0'), CurrencyAmount.fromRawAmount(pair.liquidityToken, '0'),
new CurrencyAmount(tokenA, '1001'), CurrencyAmount.fromRawAmount(tokenA, '1001'),
new CurrencyAmount(tokenB, '1001') CurrencyAmount.fromRawAmount(tokenB, '1001')
) )
expect(liquidity.raw.toString()).toEqual('1') expect(liquidity.quotient.toString()).toEqual('1')
}) })
it('getLiquidityMinted:!0', async () => { it('getLiquidityMinted:!0', async () => {
const tokenA = new Token(ChainId.RINKEBY, '0x0000000000000000000000000000000000000001', 18) const tokenA = new Token(ChainId.RINKEBY, '0x0000000000000000000000000000000000000001', 18)
const tokenB = new Token(ChainId.RINKEBY, '0x0000000000000000000000000000000000000002', 18) const tokenB = new Token(ChainId.RINKEBY, '0x0000000000000000000000000000000000000002', 18)
const pair = new Pair(new CurrencyAmount(tokenA, '10000'), new CurrencyAmount(tokenB, '10000')) const pair = new Pair(
CurrencyAmount.fromRawAmount(tokenA, '10000'),
CurrencyAmount.fromRawAmount(tokenB, '10000')
)
expect( expect(
pair pair
.getLiquidityMinted( .getLiquidityMinted(
new CurrencyAmount(pair.liquidityToken, '10000'), CurrencyAmount.fromRawAmount(pair.liquidityToken, '10000'),
new CurrencyAmount(tokenA, '2000'), CurrencyAmount.fromRawAmount(tokenA, '2000'),
new CurrencyAmount(tokenB, '2000') CurrencyAmount.fromRawAmount(tokenB, '2000')
) )
.raw.toString() .quotient.toString()
).toEqual('2000') ).toEqual('2000')
}) })
it('getLiquidityValue:!feeOn', async () => { it('getLiquidityValue:!feeOn', async () => {
const tokenA = new Token(ChainId.RINKEBY, '0x0000000000000000000000000000000000000001', 18) const tokenA = new Token(ChainId.RINKEBY, '0x0000000000000000000000000000000000000001', 18)
const tokenB = new Token(ChainId.RINKEBY, '0x0000000000000000000000000000000000000002', 18) const tokenB = new Token(ChainId.RINKEBY, '0x0000000000000000000000000000000000000002', 18)
const pair = new Pair(new CurrencyAmount(tokenA, '1000'), new CurrencyAmount(tokenB, '1000')) const pair = new Pair(CurrencyAmount.fromRawAmount(tokenA, '1000'), CurrencyAmount.fromRawAmount(tokenB, '1000'))
{ {
const liquidityValue = pair.getLiquidityValue( const liquidityValue = pair.getLiquidityValue(
tokenA, tokenA,
new CurrencyAmount(pair.liquidityToken, '1000'), CurrencyAmount.fromRawAmount(pair.liquidityToken, '1000'),
new CurrencyAmount(pair.liquidityToken, '1000'), CurrencyAmount.fromRawAmount(pair.liquidityToken, '1000'),
false false
) )
expect(currencyEquals(liquidityValue.currency, tokenA)).toBe(true) expect(currencyEquals(liquidityValue.currency, tokenA)).toBe(true)
expect(liquidityValue.raw.toString()).toBe('1000') expect(liquidityValue.quotient.toString()).toBe('1000')
} }
// 500 // 500
{ {
const liquidityValue = pair.getLiquidityValue( const liquidityValue = pair.getLiquidityValue(
tokenA, tokenA,
new CurrencyAmount(pair.liquidityToken, '1000'), CurrencyAmount.fromRawAmount(pair.liquidityToken, '1000'),
new CurrencyAmount(pair.liquidityToken, '500'), CurrencyAmount.fromRawAmount(pair.liquidityToken, '500'),
false false
) )
expect(currencyEquals(liquidityValue.currency, tokenA)).toBe(true) expect(currencyEquals(liquidityValue.currency, tokenA)).toBe(true)
expect(liquidityValue.raw.toString()).toBe('500') expect(liquidityValue.quotient.toString()).toBe('500')
} }
// tokenB // tokenB
{ {
const liquidityValue = pair.getLiquidityValue( const liquidityValue = pair.getLiquidityValue(
tokenB, tokenB,
new CurrencyAmount(pair.liquidityToken, '1000'), CurrencyAmount.fromRawAmount(pair.liquidityToken, '1000'),
new CurrencyAmount(pair.liquidityToken, '1000'), CurrencyAmount.fromRawAmount(pair.liquidityToken, '1000'),
false false
) )
expect(currencyEquals(liquidityValue.currency, tokenB)).toBe(true) expect(currencyEquals(liquidityValue.currency, tokenB)).toBe(true)
expect(liquidityValue.raw.toString()).toBe('1000') expect(liquidityValue.quotient.toString()).toBe('1000')
} }
}) })
it('getLiquidityValue:feeOn', async () => { it('getLiquidityValue:feeOn', async () => {
const tokenA = new Token(ChainId.RINKEBY, '0x0000000000000000000000000000000000000001', 18) const tokenA = new Token(ChainId.RINKEBY, '0x0000000000000000000000000000000000000001', 18)
const tokenB = new Token(ChainId.RINKEBY, '0x0000000000000000000000000000000000000002', 18) const tokenB = new Token(ChainId.RINKEBY, '0x0000000000000000000000000000000000000002', 18)
const pair = new Pair(new CurrencyAmount(tokenA, '1000'), new CurrencyAmount(tokenB, '1000')) const pair = new Pair(CurrencyAmount.fromRawAmount(tokenA, '1000'), CurrencyAmount.fromRawAmount(tokenB, '1000'))
const liquidityValue = pair.getLiquidityValue( const liquidityValue = pair.getLiquidityValue(
tokenA, tokenA,
new CurrencyAmount(pair.liquidityToken, '500'), CurrencyAmount.fromRawAmount(pair.liquidityToken, '500'),
new CurrencyAmount(pair.liquidityToken, '500'), CurrencyAmount.fromRawAmount(pair.liquidityToken, '500'),
true, true,
'250000' // 500 ** 2 '250000' // 500 ** 2
) )
expect(currencyEquals(liquidityValue.currency, tokenA)).toBe(true) expect(currencyEquals(liquidityValue.currency, tokenA)).toBe(true)
expect(liquidityValue.raw.toString()).toBe('917') // ceiling(1000 - (500 * (1 / 6))) expect(liquidityValue.quotient.toString()).toBe('917') // ceiling(1000 - (500 * (1 / 6)))
}) })
}) })
}) })
...@@ -25,18 +25,16 @@ export const computePairAddress = ({ ...@@ -25,18 +25,16 @@ export const computePairAddress = ({
} }
export class Pair { export class Pair {
public readonly liquidityToken: Token public readonly liquidityToken: Token
private readonly tokenAmounts: [CurrencyAmount, CurrencyAmount] private readonly tokenAmounts: [CurrencyAmount<Token>, CurrencyAmount<Token>]
public static getAddress(tokenA: Token, tokenB: Token): string { public static getAddress(tokenA: Token, tokenB: Token): string {
return computePairAddress({ factoryAddress: FACTORY_ADDRESS, tokenA, tokenB }) return computePairAddress({ factoryAddress: FACTORY_ADDRESS, tokenA, tokenB })
} }
public constructor(currencyAmountA: CurrencyAmount, tokenAmountB: CurrencyAmount) { public constructor(currencyAmountA: CurrencyAmount<Token>, tokenAmountB: CurrencyAmount<Token>) {
invariant(currencyAmountA.currency.isToken && tokenAmountB.currency.isToken, 'TOKEN')
const tokenAmounts = currencyAmountA.currency.sortsBefore(tokenAmountB.currency) // does safety checks const tokenAmounts = currencyAmountA.currency.sortsBefore(tokenAmountB.currency) // does safety checks
? [currencyAmountA, tokenAmountB] ? [currencyAmountA, tokenAmountB]
: [tokenAmountB, currencyAmountA] : [tokenAmountB, currencyAmountA]
invariant(tokenAmounts[0].currency.isToken && tokenAmounts[1].currency.isToken, 'TOKEN')
this.liquidityToken = new Token( this.liquidityToken = new Token(
tokenAmounts[0].currency.chainId, tokenAmounts[0].currency.chainId,
Pair.getAddress(tokenAmounts[0].currency, tokenAmounts[1].currency), Pair.getAddress(tokenAmounts[0].currency, tokenAmounts[1].currency),
...@@ -44,7 +42,7 @@ export class Pair { ...@@ -44,7 +42,7 @@ export class Pair {
'UNI-V2', 'UNI-V2',
'Uniswap V2' 'Uniswap V2'
) )
this.tokenAmounts = tokenAmounts as [CurrencyAmount, CurrencyAmount] this.tokenAmounts = tokenAmounts as [CurrencyAmount<Token>, CurrencyAmount<Token>]
} }
/** /**
...@@ -58,22 +56,24 @@ export class Pair { ...@@ -58,22 +56,24 @@ export class Pair {
/** /**
* Returns the current mid price of the pair in terms of token0, i.e. the ratio of reserve1 to reserve0 * Returns the current mid price of the pair in terms of token0, i.e. the ratio of reserve1 to reserve0
*/ */
public get token0Price(): Price { public get token0Price(): Price<Token, Token> {
return new Price(this.token0, this.token1, this.tokenAmounts[0].raw, this.tokenAmounts[1].raw) const result = this.tokenAmounts[1].divide(this.tokenAmounts[0])
return new Price(this.token0, this.token1, result.denominator, result.numerator)
} }
/** /**
* Returns the current mid price of the pair in terms of token1, i.e. the ratio of reserve0 to reserve1 * Returns the current mid price of the pair in terms of token1, i.e. the ratio of reserve0 to reserve1
*/ */
public get token1Price(): Price { public get token1Price(): Price<Token, Token> {
return new Price(this.token1, this.token0, this.tokenAmounts[1].raw, this.tokenAmounts[0].raw) const result = this.tokenAmounts[0].divide(this.tokenAmounts[1])
return new Price(this.token1, this.token0, result.denominator, result.numerator)
} }
/** /**
* Return the price of the given token in terms of the other token in the pair. * Return the price of the given token in terms of the other token in the pair.
* @param token token to return price of * @param token token to return price of
*/ */
public priceOf(token: Token): Price { public priceOf(token: Token): Price<Token, Token> {
invariant(this.involvesToken(token), 'TOKEN') invariant(this.involvesToken(token), 'TOKEN')
return token.equals(this.token0) ? this.token0Price : this.token1Price return token.equals(this.token0) ? this.token0Price : this.token1Price
} }
...@@ -86,63 +86,61 @@ export class Pair { ...@@ -86,63 +86,61 @@ export class Pair {
} }
public get token0(): Token { public get token0(): Token {
invariant(this.tokenAmounts[0].currency.isToken)
return this.tokenAmounts[0].currency return this.tokenAmounts[0].currency
} }
public get token1(): Token { public get token1(): Token {
invariant(this.tokenAmounts[1].currency.isToken)
return this.tokenAmounts[1].currency return this.tokenAmounts[1].currency
} }
public get reserve0(): CurrencyAmount { public get reserve0(): CurrencyAmount<Token> {
return this.tokenAmounts[0] return this.tokenAmounts[0]
} }
public get reserve1(): CurrencyAmount { public get reserve1(): CurrencyAmount<Token> {
return this.tokenAmounts[1] return this.tokenAmounts[1]
} }
public reserveOf(token: Token): CurrencyAmount { public reserveOf(token: Token): CurrencyAmount<Token> {
invariant(this.involvesToken(token), 'TOKEN') invariant(this.involvesToken(token), 'TOKEN')
return token.equals(this.token0) ? this.reserve0 : this.reserve1 return token.equals(this.token0) ? this.reserve0 : this.reserve1
} }
public getOutputAmount(inputAmount: CurrencyAmount): [CurrencyAmount, Pair] { public getOutputAmount(inputAmount: CurrencyAmount<Token>): [CurrencyAmount<Token>, Pair] {
invariant(inputAmount.currency.isToken && this.involvesToken(inputAmount.currency), 'TOKEN') invariant(this.involvesToken(inputAmount.currency), 'TOKEN')
if (JSBI.equal(this.reserve0.raw, ZERO) || JSBI.equal(this.reserve1.raw, ZERO)) { if (JSBI.equal(this.reserve0.quotient, ZERO) || JSBI.equal(this.reserve1.quotient, ZERO)) {
throw new InsufficientReservesError() throw new InsufficientReservesError()
} }
const inputReserve = this.reserveOf(inputAmount.currency) const inputReserve = this.reserveOf(inputAmount.currency)
const outputReserve = this.reserveOf(inputAmount.currency.equals(this.token0) ? this.token1 : this.token0) const outputReserve = this.reserveOf(inputAmount.currency.equals(this.token0) ? this.token1 : this.token0)
const inputAmountWithFee = JSBI.multiply(inputAmount.raw, _997) const inputAmountWithFee = JSBI.multiply(inputAmount.quotient, _997)
const numerator = JSBI.multiply(inputAmountWithFee, outputReserve.raw) const numerator = JSBI.multiply(inputAmountWithFee, outputReserve.quotient)
const denominator = JSBI.add(JSBI.multiply(inputReserve.raw, _1000), inputAmountWithFee) const denominator = JSBI.add(JSBI.multiply(inputReserve.quotient, _1000), inputAmountWithFee)
const outputAmount = new CurrencyAmount( const outputAmount = CurrencyAmount.fromRawAmount(
inputAmount.currency.equals(this.token0) ? this.token1 : this.token0, inputAmount.currency.equals(this.token0) ? this.token1 : this.token0,
JSBI.divide(numerator, denominator) JSBI.divide(numerator, denominator)
) )
if (JSBI.equal(outputAmount.raw, ZERO)) { if (JSBI.equal(outputAmount.quotient, ZERO)) {
throw new InsufficientInputAmountError() throw new InsufficientInputAmountError()
} }
return [outputAmount, new Pair(inputReserve.add(inputAmount), outputReserve.subtract(outputAmount))] return [outputAmount, new Pair(inputReserve.add(inputAmount), outputReserve.subtract(outputAmount))]
} }
public getInputAmount(outputAmount: CurrencyAmount): [CurrencyAmount, Pair] { public getInputAmount(outputAmount: CurrencyAmount<Token>): [CurrencyAmount<Token>, Pair] {
invariant(outputAmount.currency.isToken && this.involvesToken(outputAmount.currency), 'TOKEN') invariant(this.involvesToken(outputAmount.currency), 'TOKEN')
if ( if (
JSBI.equal(this.reserve0.raw, ZERO) || JSBI.equal(this.reserve0.quotient, ZERO) ||
JSBI.equal(this.reserve1.raw, ZERO) || JSBI.equal(this.reserve1.quotient, ZERO) ||
JSBI.greaterThanOrEqual(outputAmount.raw, this.reserveOf(outputAmount.currency).raw) JSBI.greaterThanOrEqual(outputAmount.quotient, this.reserveOf(outputAmount.currency).quotient)
) { ) {
throw new InsufficientReservesError() throw new InsufficientReservesError()
} }
const outputReserve = this.reserveOf(outputAmount.currency) const outputReserve = this.reserveOf(outputAmount.currency)
const inputReserve = this.reserveOf(outputAmount.currency.equals(this.token0) ? this.token1 : this.token0) const inputReserve = this.reserveOf(outputAmount.currency.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.quotient, outputAmount.quotient), _1000)
const denominator = JSBI.multiply(JSBI.subtract(outputReserve.raw, outputAmount.raw), _997) const denominator = JSBI.multiply(JSBI.subtract(outputReserve.quotient, outputAmount.quotient), _997)
const inputAmount = new CurrencyAmount( const inputAmount = CurrencyAmount.fromRawAmount(
outputAmount.currency.equals(this.token0) ? this.token1 : this.token0, outputAmount.currency.equals(this.token0) ? this.token1 : this.token0,
JSBI.add(JSBI.divide(numerator, denominator), ONE) JSBI.add(JSBI.divide(numerator, denominator), ONE)
) )
...@@ -150,60 +148,59 @@ export class Pair { ...@@ -150,60 +148,59 @@ export class Pair {
} }
public getLiquidityMinted( public getLiquidityMinted(
totalSupply: CurrencyAmount, totalSupply: CurrencyAmount<Token>,
tokenAmountA: CurrencyAmount, tokenAmountA: CurrencyAmount<Token>,
tokenAmountB: CurrencyAmount tokenAmountB: CurrencyAmount<Token>
): CurrencyAmount { ): CurrencyAmount<Token> {
invariant(totalSupply.currency.isToken && totalSupply.currency.equals(this.liquidityToken), 'LIQUIDITY') invariant(totalSupply.currency.equals(this.liquidityToken), 'LIQUIDITY')
const tokenAmounts = const tokenAmounts = tokenAmountA.currency.sortsBefore(tokenAmountB.currency) // does safety checks
tokenAmountA.currency.isToken &&
tokenAmountB.currency.isToken &&
tokenAmountA.currency.sortsBefore(tokenAmountB.currency) // does safety checks
? [tokenAmountA, tokenAmountB] ? [tokenAmountA, tokenAmountB]
: [tokenAmountB, tokenAmountA] : [tokenAmountB, tokenAmountA]
invariant(tokenAmounts[0].currency.isToken && tokenAmounts[1].currency.isToken)
invariant(tokenAmounts[0].currency.equals(this.token0) && tokenAmounts[1].currency.equals(this.token1), 'TOKEN') invariant(tokenAmounts[0].currency.equals(this.token0) && tokenAmounts[1].currency.equals(this.token1), 'TOKEN')
let liquidity: JSBI let liquidity: JSBI
if (JSBI.equal(totalSupply.raw, ZERO)) { if (JSBI.equal(totalSupply.quotient, ZERO)) {
liquidity = JSBI.subtract(sqrt(JSBI.multiply(tokenAmounts[0].raw, tokenAmounts[1].raw)), MINIMUM_LIQUIDITY) liquidity = JSBI.subtract(
sqrt(JSBI.multiply(tokenAmounts[0].quotient, tokenAmounts[1].quotient)),
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].quotient, totalSupply.quotient), this.reserve0.quotient)
const amount1 = JSBI.divide(JSBI.multiply(tokenAmounts[1].raw, totalSupply.raw), this.reserve1.raw) const amount1 = JSBI.divide(JSBI.multiply(tokenAmounts[1].quotient, totalSupply.quotient), this.reserve1.quotient)
liquidity = JSBI.lessThanOrEqual(amount0, amount1) ? amount0 : amount1 liquidity = JSBI.lessThanOrEqual(amount0, amount1) ? amount0 : amount1
} }
if (!JSBI.greaterThan(liquidity, ZERO)) { if (!JSBI.greaterThan(liquidity, ZERO)) {
throw new InsufficientInputAmountError() throw new InsufficientInputAmountError()
} }
return new CurrencyAmount(this.liquidityToken, liquidity) return CurrencyAmount.fromRawAmount(this.liquidityToken, liquidity)
} }
public getLiquidityValue( public getLiquidityValue(
token: Token, token: Token,
totalSupply: CurrencyAmount, totalSupply: CurrencyAmount<Token>,
liquidity: CurrencyAmount, liquidity: CurrencyAmount<Token>,
feeOn: boolean = false, feeOn: boolean = false,
kLast?: BigintIsh kLast?: BigintIsh
): CurrencyAmount { ): CurrencyAmount<Token> {
invariant(this.involvesToken(token), 'TOKEN') invariant(this.involvesToken(token), 'TOKEN')
invariant(totalSupply.currency.isToken && totalSupply.currency.equals(this.liquidityToken), 'TOTAL_SUPPLY') invariant(totalSupply.currency.equals(this.liquidityToken), 'TOTAL_SUPPLY')
invariant(liquidity.currency.isToken && liquidity.currency.equals(this.liquidityToken), 'LIQUIDITY') invariant(liquidity.currency.equals(this.liquidityToken), 'LIQUIDITY')
invariant(JSBI.lessThanOrEqual(liquidity.raw, totalSupply.raw), 'LIQUIDITY') invariant(JSBI.lessThanOrEqual(liquidity.quotient, totalSupply.quotient), 'LIQUIDITY')
let totalSupplyAdjusted: CurrencyAmount let totalSupplyAdjusted: CurrencyAmount<Token>
if (!feeOn) { if (!feeOn) {
totalSupplyAdjusted = totalSupply totalSupplyAdjusted = totalSupply
} else { } else {
invariant(!!kLast, 'K_LAST') invariant(!!kLast, 'K_LAST')
const kLastParsed = JSBI.BigInt(kLast) const kLastParsed = JSBI.BigInt(kLast)
if (!JSBI.equal(kLastParsed, ZERO)) { if (!JSBI.equal(kLastParsed, ZERO)) {
const rootK = sqrt(JSBI.multiply(this.reserve0.raw, this.reserve1.raw)) const rootK = sqrt(JSBI.multiply(this.reserve0.quotient, this.reserve1.quotient))
const rootKLast = sqrt(kLastParsed) const rootKLast = sqrt(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.quotient, JSBI.subtract(rootK, rootKLast))
const denominator = JSBI.add(JSBI.multiply(rootK, FIVE), rootKLast) const denominator = JSBI.add(JSBI.multiply(rootK, FIVE), rootKLast)
const feeLiquidity = JSBI.divide(numerator, denominator) const feeLiquidity = JSBI.divide(numerator, denominator)
totalSupplyAdjusted = totalSupply.add(new CurrencyAmount(this.liquidityToken, feeLiquidity)) totalSupplyAdjusted = totalSupply.add(CurrencyAmount.fromRawAmount(this.liquidityToken, feeLiquidity))
} else { } else {
totalSupplyAdjusted = totalSupply totalSupplyAdjusted = totalSupply
} }
...@@ -212,9 +209,9 @@ export class Pair { ...@@ -212,9 +209,9 @@ export class Pair {
} }
} }
return new CurrencyAmount( return CurrencyAmount.fromRawAmount(
token, token,
JSBI.divide(JSBI.multiply(liquidity.raw, this.reserveOf(token).raw), totalSupplyAdjusted.raw) JSBI.divide(JSBI.multiply(liquidity.quotient, this.reserveOf(token).quotient), totalSupplyAdjusted.quotient)
) )
} }
} }
...@@ -5,12 +5,12 @@ describe('Route', () => { ...@@ -5,12 +5,12 @@ describe('Route', () => {
const token0 = new Token(ChainId.MAINNET, '0x0000000000000000000000000000000000000001', 18, 't0') const token0 = new Token(ChainId.MAINNET, '0x0000000000000000000000000000000000000001', 18, 't0')
const token1 = new Token(ChainId.MAINNET, '0x0000000000000000000000000000000000000002', 18, 't1') const token1 = new Token(ChainId.MAINNET, '0x0000000000000000000000000000000000000002', 18, 't1')
const weth = WETH9[ChainId.MAINNET] const weth = WETH9[ChainId.MAINNET]
const pair_0_1 = new Pair(new CurrencyAmount(token0, '100'), new CurrencyAmount(token1, '200')) const pair_0_1 = new Pair(CurrencyAmount.fromRawAmount(token0, '100'), CurrencyAmount.fromRawAmount(token1, '200'))
const pair_0_weth = new Pair(new CurrencyAmount(token0, '100'), new CurrencyAmount(weth, '100')) const pair_0_weth = new Pair(CurrencyAmount.fromRawAmount(token0, '100'), CurrencyAmount.fromRawAmount(weth, '100'))
const pair_1_weth = new Pair(new CurrencyAmount(token1, '175'), new CurrencyAmount(weth, '100')) const pair_1_weth = new Pair(CurrencyAmount.fromRawAmount(token1, '175'), CurrencyAmount.fromRawAmount(weth, '100'))
it('constructs a path from the tokens', () => { it('constructs a path from the tokens', () => {
const route = new Route([pair_0_1], token0) const route = new Route([pair_0_1], token0, token1)
expect(route.pairs).toEqual([pair_0_1]) expect(route.pairs).toEqual([pair_0_1])
expect(route.path).toEqual([token0, token1]) expect(route.path).toEqual([token0, token1])
expect(route.input).toEqual(token0) expect(route.input).toEqual(token0)
...@@ -19,14 +19,14 @@ describe('Route', () => { ...@@ -19,14 +19,14 @@ describe('Route', () => {
}) })
it('can have a token as both input and output', () => { it('can have a token as both input and output', () => {
const route = new Route([pair_0_weth, pair_0_1, pair_1_weth], weth) const route = new Route([pair_0_weth, pair_0_1, pair_1_weth], weth, weth)
expect(route.pairs).toEqual([pair_0_weth, pair_0_1, pair_1_weth]) expect(route.pairs).toEqual([pair_0_weth, pair_0_1, pair_1_weth])
expect(route.input).toEqual(weth) expect(route.input).toEqual(weth)
expect(route.output).toEqual(weth) expect(route.output).toEqual(weth)
}) })
it('supports ether input', () => { it('supports ether input', () => {
const route = new Route([pair_0_weth], ETHER) const route = new Route([pair_0_weth], ETHER, token0)
expect(route.pairs).toEqual([pair_0_weth]) expect(route.pairs).toEqual([pair_0_weth])
expect(route.input).toEqual(ETHER) expect(route.input).toEqual(ETHER)
expect(route.output).toEqual(token0) expect(route.output).toEqual(token0)
......
import { ChainId, Currency, ETHER, Price, Token, WETH9 } from '@uniswap/sdk-core'
import invariant from 'tiny-invariant' import invariant from 'tiny-invariant'
import { ChainId, Currency, Price, Token, wrappedCurrency } from '@uniswap/sdk-core'
import { Pair } from './pair' import { Pair } from './pair'
export class Route { export class Route<TInput extends Currency, TOutput extends Currency> {
public readonly pairs: Pair[] public readonly pairs: Pair[]
public readonly path: Token[] public readonly path: Token[]
public readonly input: Currency public readonly input: TInput
public readonly output: Currency public readonly output: TOutput
public get midPrice(): Price { public constructor(pairs: Pair[], input: TInput, output: TOutput) {
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) {
invariant(pairs.length > 0, 'PAIRS') invariant(pairs.length > 0, 'PAIRS')
const chainId: ChainId | number = pairs[0].chainId const chainId: ChainId | number = pairs[0].chainId
invariant( invariant(
...@@ -29,20 +17,14 @@ export class Route { ...@@ -29,20 +17,14 @@ export class Route {
'CHAIN_IDS' 'CHAIN_IDS'
) )
const weth: Token | undefined = WETH9[chainId as ChainId] const wrappedInput = wrappedCurrency(input, chainId)
invariant(pairs[0].involvesToken(wrappedInput), 'INPUT')
invariant(
(input.isToken && pairs[0].involvesToken(input)) || (input === ETHER && weth && pairs[0].involvesToken(weth)),
'INPUT'
)
invariant( invariant(
typeof output === 'undefined' || typeof output === 'undefined' || pairs[pairs.length - 1].involvesToken(wrappedCurrency(output, chainId)),
(output.isToken && pairs[pairs.length - 1].involvesToken(output)) ||
(output === ETHER && weth && pairs[pairs.length - 1].involvesToken(weth)),
'OUTPUT' 'OUTPUT'
) )
const path: Token[] = [input.isToken ? input : weth] const path: Token[] = [wrappedInput]
for (const [i, pair] of pairs.entries()) { for (const [i, pair] of pairs.entries()) {
const currentInput = path[i] const currentInput = path[i]
invariant(currentInput.equals(pair.token0) || currentInput.equals(pair.token1), 'PATH') invariant(currentInput.equals(pair.token0) || currentInput.equals(pair.token1), 'PATH')
...@@ -53,7 +35,23 @@ export class Route { ...@@ -53,7 +35,23 @@ export class Route {
this.pairs = pairs this.pairs = pairs
this.path = path this.path = path
this.input = input this.input = input
this.output = output ?? path[path.length - 1] this.output = output
}
private _midPrice: Price<TInput, TOutput> | null = null
public get midPrice(): Price<TInput, TOutput> {
if (this._midPrice !== null) return this._midPrice
const prices: Price<Currency, Currency>[] = []
for (const [i, pair] of this.pairs.entries()) {
prices.push(
this.path[i].equals(pair.token0)
? new Price(pair.reserve0.currency, pair.reserve1.currency, pair.reserve0.quotient, pair.reserve1.quotient)
: new Price(pair.reserve1.currency, pair.reserve0.currency, pair.reserve1.quotient, pair.reserve0.quotient)
)
}
const reduced = prices.slice(1).reduce((accumulator, currentValue) => accumulator.multiply(currentValue), prices[0])
return (this._midPrice = new Price(this.input, this.output, reduced.denominator, reduced.numerator))
} }
public get chainId(): ChainId | number { public get chainId(): ChainId | number {
......
...@@ -11,36 +11,39 @@ describe('Trade', () => { ...@@ -11,36 +11,39 @@ describe('Trade', () => {
const token3 = new Token(ChainId.MAINNET, '0x0000000000000000000000000000000000000004', 18, 't3') const token3 = new Token(ChainId.MAINNET, '0x0000000000000000000000000000000000000004', 18, 't3')
const pair_0_1 = new Pair( const pair_0_1 = new Pair(
new CurrencyAmount(token0, JSBI.BigInt(1000)), CurrencyAmount.fromRawAmount(token0, JSBI.BigInt(1000)),
new CurrencyAmount(token1, JSBI.BigInt(1000)) CurrencyAmount.fromRawAmount(token1, JSBI.BigInt(1000))
) )
const pair_0_2 = new Pair( const pair_0_2 = new Pair(
new CurrencyAmount(token0, JSBI.BigInt(1000)), CurrencyAmount.fromRawAmount(token0, JSBI.BigInt(1000)),
new CurrencyAmount(token2, JSBI.BigInt(1100)) CurrencyAmount.fromRawAmount(token2, JSBI.BigInt(1100))
)
const pair_0_3 = new Pair(
CurrencyAmount.fromRawAmount(token0, JSBI.BigInt(1000)),
CurrencyAmount.fromRawAmount(token3, JSBI.BigInt(900))
) )
const pair_0_3 = new Pair(new CurrencyAmount(token0, JSBI.BigInt(1000)), new CurrencyAmount(token3, JSBI.BigInt(900)))
const pair_1_2 = new Pair( const pair_1_2 = new Pair(
new CurrencyAmount(token1, JSBI.BigInt(1200)), CurrencyAmount.fromRawAmount(token1, JSBI.BigInt(1200)),
new CurrencyAmount(token2, JSBI.BigInt(1000)) CurrencyAmount.fromRawAmount(token2, JSBI.BigInt(1000))
) )
const pair_1_3 = new Pair( const pair_1_3 = new Pair(
new CurrencyAmount(token1, JSBI.BigInt(1200)), CurrencyAmount.fromRawAmount(token1, JSBI.BigInt(1200)),
new CurrencyAmount(token3, JSBI.BigInt(1300)) CurrencyAmount.fromRawAmount(token3, JSBI.BigInt(1300))
) )
const pair_weth_0 = new Pair( const pair_weth_0 = new Pair(
new CurrencyAmount(WETH9[ChainId.MAINNET], JSBI.BigInt(1000)), CurrencyAmount.fromRawAmount(WETH9[ChainId.MAINNET], JSBI.BigInt(1000)),
new CurrencyAmount(token0, JSBI.BigInt(1000)) CurrencyAmount.fromRawAmount(token0, JSBI.BigInt(1000))
) )
const empty_pair_0_1 = new Pair( const empty_pair_0_1 = new Pair(
new CurrencyAmount(token0, JSBI.BigInt(0)), CurrencyAmount.fromRawAmount(token0, JSBI.BigInt(0)),
new CurrencyAmount(token1, JSBI.BigInt(0)) CurrencyAmount.fromRawAmount(token1, JSBI.BigInt(0))
) )
it('can be constructed with ETHER as input', () => { it('can be constructed with ETHER as input', () => {
const trade = new Trade( const trade = new Trade(
new Route([pair_weth_0], ETHER), new Route([pair_weth_0], ETHER, token0),
CurrencyAmount.ether(JSBI.BigInt(100)), CurrencyAmount.ether(JSBI.BigInt(100)),
TradeType.EXACT_INPUT TradeType.EXACT_INPUT
) )
...@@ -50,7 +53,7 @@ describe('Trade', () => { ...@@ -50,7 +53,7 @@ describe('Trade', () => {
it('can be constructed with ETHER as input for exact output', () => { it('can be constructed with ETHER as input for exact output', () => {
const trade = new Trade( const trade = new Trade(
new Route([pair_weth_0], ETHER, token0), new Route([pair_weth_0], ETHER, token0),
new CurrencyAmount(token0, JSBI.BigInt(100)), CurrencyAmount.fromRawAmount(token0, JSBI.BigInt(100)),
TradeType.EXACT_OUTPUT TradeType.EXACT_OUTPUT
) )
expect(trade.inputAmount.currency).toEqual(ETHER) expect(trade.inputAmount.currency).toEqual(ETHER)
...@@ -69,7 +72,7 @@ describe('Trade', () => { ...@@ -69,7 +72,7 @@ describe('Trade', () => {
it('can be constructed with ETHER as output for exact input', () => { it('can be constructed with ETHER as output for exact input', () => {
const trade = new Trade( const trade = new Trade(
new Route([pair_weth_0], token0, ETHER), new Route([pair_weth_0], token0, ETHER),
new CurrencyAmount(token0, JSBI.BigInt(100)), CurrencyAmount.fromRawAmount(token0, JSBI.BigInt(100)),
TradeType.EXACT_INPUT TradeType.EXACT_INPUT
) )
expect(trade.inputAmount.currency).toEqual(token0) expect(trade.inputAmount.currency).toEqual(token0)
...@@ -78,41 +81,45 @@ describe('Trade', () => { ...@@ -78,41 +81,45 @@ describe('Trade', () => {
describe('#bestTradeExactIn', () => { describe('#bestTradeExactIn', () => {
it('throws with empty pairs', () => { it('throws with empty pairs', () => {
expect(() => Trade.bestTradeExactIn([], new CurrencyAmount(token0, JSBI.BigInt(100)), token2)).toThrow('PAIRS') expect(() => Trade.bestTradeExactIn([], CurrencyAmount.fromRawAmount(token0, JSBI.BigInt(100)), token2)).toThrow(
'PAIRS'
)
}) })
it('throws with max hops of 0', () => { it('throws with max hops of 0', () => {
expect(() => expect(() =>
Trade.bestTradeExactIn([pair_0_2], new CurrencyAmount(token0, JSBI.BigInt(100)), token2, { maxHops: 0 }) Trade.bestTradeExactIn([pair_0_2], CurrencyAmount.fromRawAmount(token0, JSBI.BigInt(100)), token2, {
maxHops: 0
})
).toThrow('MAX_HOPS') ).toThrow('MAX_HOPS')
}) })
it('provides best route', () => { it('provides best route', () => {
const result = Trade.bestTradeExactIn( const result = Trade.bestTradeExactIn(
[pair_0_1, pair_0_2, pair_1_2], [pair_0_1, pair_0_2, pair_1_2],
new CurrencyAmount(token0, JSBI.BigInt(100)), CurrencyAmount.fromRawAmount(token0, JSBI.BigInt(100)),
token2 token2
) )
expect(result).toHaveLength(2) expect(result).toHaveLength(2)
expect(result[0].route.pairs).toHaveLength(1) // 0 -> 2 at 10:11 expect(result[0].route.pairs).toHaveLength(1) // 0 -> 2 at 10:11
expect(result[0].route.path).toEqual([token0, token2]) expect(result[0].route.path).toEqual([token0, token2])
expect(result[0].inputAmount).toEqual(new CurrencyAmount(token0, JSBI.BigInt(100))) expect(result[0].inputAmount).toEqual(CurrencyAmount.fromRawAmount(token0, JSBI.BigInt(100)))
expect(result[0].outputAmount).toEqual(new CurrencyAmount(token2, JSBI.BigInt(99))) expect(result[0].outputAmount).toEqual(CurrencyAmount.fromRawAmount(token2, JSBI.BigInt(99)))
expect(result[1].route.pairs).toHaveLength(2) // 0 -> 1 -> 2 at 12:12:10 expect(result[1].route.pairs).toHaveLength(2) // 0 -> 1 -> 2 at 12:12:10
expect(result[1].route.path).toEqual([token0, token1, token2]) expect(result[1].route.path).toEqual([token0, token1, token2])
expect(result[1].inputAmount).toEqual(new CurrencyAmount(token0, JSBI.BigInt(100))) expect(result[1].inputAmount).toEqual(CurrencyAmount.fromRawAmount(token0, JSBI.BigInt(100)))
expect(result[1].outputAmount).toEqual(new CurrencyAmount(token2, JSBI.BigInt(69))) expect(result[1].outputAmount).toEqual(CurrencyAmount.fromRawAmount(token2, JSBI.BigInt(69)))
}) })
it('doesnt throw for zero liquidity pairs', () => { it('doesnt throw for zero liquidity pairs', () => {
expect( expect(
Trade.bestTradeExactIn([empty_pair_0_1], new CurrencyAmount(token0, JSBI.BigInt(100)), token1) Trade.bestTradeExactIn([empty_pair_0_1], CurrencyAmount.fromRawAmount(token0, JSBI.BigInt(100)), token1)
).toHaveLength(0) ).toHaveLength(0)
}) })
it('respects maxHops', () => { it('respects maxHops', () => {
const result = Trade.bestTradeExactIn( const result = Trade.bestTradeExactIn(
[pair_0_1, pair_0_2, pair_1_2], [pair_0_1, pair_0_2, pair_1_2],
new CurrencyAmount(token0, JSBI.BigInt(10)), CurrencyAmount.fromRawAmount(token0, JSBI.BigInt(10)),
token2, token2,
{ maxHops: 1 } { maxHops: 1 }
) )
...@@ -124,19 +131,19 @@ describe('Trade', () => { ...@@ -124,19 +131,19 @@ describe('Trade', () => {
it('insufficient input for one pair', () => { it('insufficient input for one pair', () => {
const result = Trade.bestTradeExactIn( const result = Trade.bestTradeExactIn(
[pair_0_1, pair_0_2, pair_1_2], [pair_0_1, pair_0_2, pair_1_2],
new CurrencyAmount(token0, JSBI.BigInt(1)), CurrencyAmount.fromRawAmount(token0, JSBI.BigInt(1)),
token2 token2
) )
expect(result).toHaveLength(1) expect(result).toHaveLength(1)
expect(result[0].route.pairs).toHaveLength(1) // 0 -> 2 at 10:11 expect(result[0].route.pairs).toHaveLength(1) // 0 -> 2 at 10:11
expect(result[0].route.path).toEqual([token0, token2]) expect(result[0].route.path).toEqual([token0, token2])
expect(result[0].outputAmount).toEqual(new CurrencyAmount(token2, JSBI.BigInt(1))) expect(result[0].outputAmount).toEqual(CurrencyAmount.fromRawAmount(token2, JSBI.BigInt(1)))
}) })
it('respects n', () => { it('respects n', () => {
const result = Trade.bestTradeExactIn( const result = Trade.bestTradeExactIn(
[pair_0_1, pair_0_2, pair_1_2], [pair_0_1, pair_0_2, pair_1_2],
new CurrencyAmount(token0, JSBI.BigInt(10)), CurrencyAmount.fromRawAmount(token0, JSBI.BigInt(10)),
token2, token2,
{ maxNumResults: 1 } { maxNumResults: 1 }
) )
...@@ -147,7 +154,7 @@ describe('Trade', () => { ...@@ -147,7 +154,7 @@ describe('Trade', () => {
it('no path', () => { it('no path', () => {
const result = Trade.bestTradeExactIn( const result = Trade.bestTradeExactIn(
[pair_0_1, pair_0_3, pair_1_3], [pair_0_1, pair_0_3, pair_1_3],
new CurrencyAmount(token0, JSBI.BigInt(10)), CurrencyAmount.fromRawAmount(token0, JSBI.BigInt(10)),
token2 token2
) )
expect(result).toHaveLength(0) expect(result).toHaveLength(0)
...@@ -170,7 +177,7 @@ describe('Trade', () => { ...@@ -170,7 +177,7 @@ describe('Trade', () => {
it('works for ETHER currency output', () => { it('works for ETHER currency output', () => {
const result = Trade.bestTradeExactIn( const result = Trade.bestTradeExactIn(
[pair_weth_0, pair_0_1, pair_0_3, pair_1_3], [pair_weth_0, pair_0_1, pair_0_3, pair_1_3],
new CurrencyAmount(token3, JSBI.BigInt(100)), CurrencyAmount.fromRawAmount(token3, JSBI.BigInt(100)),
ETHER ETHER
) )
expect(result).toHaveLength(2) expect(result).toHaveLength(2)
...@@ -186,8 +193,8 @@ describe('Trade', () => { ...@@ -186,8 +193,8 @@ describe('Trade', () => {
describe('#maximumAmountIn', () => { describe('#maximumAmountIn', () => {
describe('tradeType = EXACT_INPUT', () => { describe('tradeType = EXACT_INPUT', () => {
const exactIn = new Trade( const exactIn = new Trade(
new Route([pair_0_1, pair_1_2], token0), new Route([pair_0_1, pair_1_2], token0, token2),
new CurrencyAmount(token0, JSBI.BigInt(100)), CurrencyAmount.fromRawAmount(token0, JSBI.BigInt(100)),
TradeType.EXACT_INPUT TradeType.EXACT_INPUT
) )
it('throws if less than 0', () => { it('throws if less than 0', () => {
...@@ -200,20 +207,20 @@ describe('Trade', () => { ...@@ -200,20 +207,20 @@ describe('Trade', () => {
}) })
it('returns exact if nonzero', () => { it('returns exact if nonzero', () => {
expect(exactIn.maximumAmountIn(new Percent(JSBI.BigInt(0), JSBI.BigInt(100)))).toEqual( expect(exactIn.maximumAmountIn(new Percent(JSBI.BigInt(0), JSBI.BigInt(100)))).toEqual(
new CurrencyAmount(token0, JSBI.BigInt(100)) CurrencyAmount.fromRawAmount(token0, JSBI.BigInt(100))
) )
expect(exactIn.maximumAmountIn(new Percent(JSBI.BigInt(5), JSBI.BigInt(100)))).toEqual( expect(exactIn.maximumAmountIn(new Percent(JSBI.BigInt(5), JSBI.BigInt(100)))).toEqual(
new CurrencyAmount(token0, JSBI.BigInt(100)) CurrencyAmount.fromRawAmount(token0, JSBI.BigInt(100))
) )
expect(exactIn.maximumAmountIn(new Percent(JSBI.BigInt(200), JSBI.BigInt(100)))).toEqual( expect(exactIn.maximumAmountIn(new Percent(JSBI.BigInt(200), JSBI.BigInt(100)))).toEqual(
new CurrencyAmount(token0, JSBI.BigInt(100)) CurrencyAmount.fromRawAmount(token0, JSBI.BigInt(100))
) )
}) })
}) })
describe('tradeType = EXACT_OUTPUT', () => { describe('tradeType = EXACT_OUTPUT', () => {
const exactOut = new Trade( const exactOut = new Trade(
new Route([pair_0_1, pair_1_2], token0), new Route([pair_0_1, pair_1_2], token0, token2),
new CurrencyAmount(token2, JSBI.BigInt(100)), CurrencyAmount.fromRawAmount(token2, JSBI.BigInt(100)),
TradeType.EXACT_OUTPUT TradeType.EXACT_OUTPUT
) )
...@@ -227,13 +234,13 @@ describe('Trade', () => { ...@@ -227,13 +234,13 @@ describe('Trade', () => {
}) })
it('returns slippage amount if nonzero', () => { it('returns slippage amount if nonzero', () => {
expect(exactOut.maximumAmountIn(new Percent(JSBI.BigInt(0), JSBI.BigInt(100)))).toEqual( expect(exactOut.maximumAmountIn(new Percent(JSBI.BigInt(0), JSBI.BigInt(100)))).toEqual(
new CurrencyAmount(token0, JSBI.BigInt(156)) CurrencyAmount.fromRawAmount(token0, JSBI.BigInt(156))
) )
expect(exactOut.maximumAmountIn(new Percent(JSBI.BigInt(5), JSBI.BigInt(100)))).toEqual( expect(exactOut.maximumAmountIn(new Percent(JSBI.BigInt(5), JSBI.BigInt(100)))).toEqual(
new CurrencyAmount(token0, JSBI.BigInt(163)) CurrencyAmount.fromRawAmount(token0, JSBI.BigInt(163))
) )
expect(exactOut.maximumAmountIn(new Percent(JSBI.BigInt(200), JSBI.BigInt(100)))).toEqual( expect(exactOut.maximumAmountIn(new Percent(JSBI.BigInt(200), JSBI.BigInt(100)))).toEqual(
new CurrencyAmount(token0, JSBI.BigInt(468)) CurrencyAmount.fromRawAmount(token0, JSBI.BigInt(468))
) )
}) })
}) })
...@@ -242,8 +249,8 @@ describe('Trade', () => { ...@@ -242,8 +249,8 @@ describe('Trade', () => {
describe('#minimumAmountOut', () => { describe('#minimumAmountOut', () => {
describe('tradeType = EXACT_INPUT', () => { describe('tradeType = EXACT_INPUT', () => {
const exactIn = new Trade( const exactIn = new Trade(
new Route([pair_0_1, pair_1_2], token0), new Route([pair_0_1, pair_1_2], token0, token2),
new CurrencyAmount(token0, JSBI.BigInt(100)), CurrencyAmount.fromRawAmount(token0, JSBI.BigInt(100)),
TradeType.EXACT_INPUT TradeType.EXACT_INPUT
) )
it('throws if less than 0', () => { it('throws if less than 0', () => {
...@@ -256,20 +263,20 @@ describe('Trade', () => { ...@@ -256,20 +263,20 @@ describe('Trade', () => {
}) })
it('returns exact if nonzero', () => { it('returns exact if nonzero', () => {
expect(exactIn.minimumAmountOut(new Percent(JSBI.BigInt(0), JSBI.BigInt(100)))).toEqual( expect(exactIn.minimumAmountOut(new Percent(JSBI.BigInt(0), JSBI.BigInt(100)))).toEqual(
new CurrencyAmount(token2, JSBI.BigInt(69)) CurrencyAmount.fromRawAmount(token2, JSBI.BigInt(69))
) )
expect(exactIn.minimumAmountOut(new Percent(JSBI.BigInt(5), JSBI.BigInt(100)))).toEqual( expect(exactIn.minimumAmountOut(new Percent(JSBI.BigInt(5), JSBI.BigInt(100)))).toEqual(
new CurrencyAmount(token2, JSBI.BigInt(65)) CurrencyAmount.fromRawAmount(token2, JSBI.BigInt(65))
) )
expect(exactIn.minimumAmountOut(new Percent(JSBI.BigInt(200), JSBI.BigInt(100)))).toEqual( expect(exactIn.minimumAmountOut(new Percent(JSBI.BigInt(200), JSBI.BigInt(100)))).toEqual(
new CurrencyAmount(token2, JSBI.BigInt(23)) CurrencyAmount.fromRawAmount(token2, JSBI.BigInt(23))
) )
}) })
}) })
describe('tradeType = EXACT_OUTPUT', () => { describe('tradeType = EXACT_OUTPUT', () => {
const exactOut = new Trade( const exactOut = new Trade(
new Route([pair_0_1, pair_1_2], token0), new Route([pair_0_1, pair_1_2], token0, token2),
new CurrencyAmount(token2, JSBI.BigInt(100)), CurrencyAmount.fromRawAmount(token2, JSBI.BigInt(100)),
TradeType.EXACT_OUTPUT TradeType.EXACT_OUTPUT
) )
...@@ -283,13 +290,13 @@ describe('Trade', () => { ...@@ -283,13 +290,13 @@ describe('Trade', () => {
}) })
it('returns slippage amount if nonzero', () => { it('returns slippage amount if nonzero', () => {
expect(exactOut.minimumAmountOut(new Percent(JSBI.BigInt(0), JSBI.BigInt(100)))).toEqual( expect(exactOut.minimumAmountOut(new Percent(JSBI.BigInt(0), JSBI.BigInt(100)))).toEqual(
new CurrencyAmount(token2, JSBI.BigInt(100)) CurrencyAmount.fromRawAmount(token2, JSBI.BigInt(100))
) )
expect(exactOut.minimumAmountOut(new Percent(JSBI.BigInt(5), JSBI.BigInt(100)))).toEqual( expect(exactOut.minimumAmountOut(new Percent(JSBI.BigInt(5), JSBI.BigInt(100)))).toEqual(
new CurrencyAmount(token2, JSBI.BigInt(100)) CurrencyAmount.fromRawAmount(token2, JSBI.BigInt(100))
) )
expect(exactOut.minimumAmountOut(new Percent(JSBI.BigInt(200), JSBI.BigInt(100)))).toEqual( expect(exactOut.minimumAmountOut(new Percent(JSBI.BigInt(200), JSBI.BigInt(100)))).toEqual(
new CurrencyAmount(token2, JSBI.BigInt(100)) CurrencyAmount.fromRawAmount(token2, JSBI.BigInt(100))
) )
}) })
}) })
...@@ -298,8 +305,8 @@ describe('Trade', () => { ...@@ -298,8 +305,8 @@ describe('Trade', () => {
describe('#worstExecutionPrice', () => { describe('#worstExecutionPrice', () => {
describe('tradeType = EXACT_INPUT', () => { describe('tradeType = EXACT_INPUT', () => {
const exactIn = new Trade( const exactIn = new Trade(
new Route([pair_0_1, pair_1_2], token0), new Route([pair_0_1, pair_1_2], token0, token2),
new CurrencyAmount(token0, 100), CurrencyAmount.fromRawAmount(token0, 100),
TradeType.EXACT_INPUT TradeType.EXACT_INPUT
) )
it('throws if less than 0', () => { it('throws if less than 0', () => {
...@@ -316,8 +323,8 @@ describe('Trade', () => { ...@@ -316,8 +323,8 @@ describe('Trade', () => {
}) })
describe('tradeType = EXACT_OUTPUT', () => { describe('tradeType = EXACT_OUTPUT', () => {
const exactOut = new Trade( const exactOut = new Trade(
new Route([pair_0_1, pair_1_2], token0), new Route([pair_0_1, pair_1_2], token0, token2),
new CurrencyAmount(token2, 100), CurrencyAmount.fromRawAmount(token2, 100),
TradeType.EXACT_OUTPUT TradeType.EXACT_OUTPUT
) )
...@@ -337,11 +344,15 @@ describe('Trade', () => { ...@@ -337,11 +344,15 @@ describe('Trade', () => {
describe('#bestTradeExactOut', () => { describe('#bestTradeExactOut', () => {
it('throws with empty pairs', () => { it('throws with empty pairs', () => {
expect(() => Trade.bestTradeExactOut([], token0, new CurrencyAmount(token2, JSBI.BigInt(100)))).toThrow('PAIRS') expect(() => Trade.bestTradeExactOut([], token0, CurrencyAmount.fromRawAmount(token2, JSBI.BigInt(100)))).toThrow(
'PAIRS'
)
}) })
it('throws with max hops of 0', () => { it('throws with max hops of 0', () => {
expect(() => expect(() =>
Trade.bestTradeExactOut([pair_0_2], token0, new CurrencyAmount(token2, JSBI.BigInt(100)), { maxHops: 0 }) Trade.bestTradeExactOut([pair_0_2], token0, CurrencyAmount.fromRawAmount(token2, JSBI.BigInt(100)), {
maxHops: 0
})
).toThrow('MAX_HOPS') ).toThrow('MAX_HOPS')
}) })
...@@ -349,22 +360,22 @@ describe('Trade', () => { ...@@ -349,22 +360,22 @@ describe('Trade', () => {
const result = Trade.bestTradeExactOut( const result = Trade.bestTradeExactOut(
[pair_0_1, pair_0_2, pair_1_2], [pair_0_1, pair_0_2, pair_1_2],
token0, token0,
new CurrencyAmount(token2, JSBI.BigInt(100)) CurrencyAmount.fromRawAmount(token2, JSBI.BigInt(100))
) )
expect(result).toHaveLength(2) expect(result).toHaveLength(2)
expect(result[0].route.pairs).toHaveLength(1) // 0 -> 2 at 10:11 expect(result[0].route.pairs).toHaveLength(1) // 0 -> 2 at 10:11
expect(result[0].route.path).toEqual([token0, token2]) expect(result[0].route.path).toEqual([token0, token2])
expect(result[0].inputAmount).toEqual(new CurrencyAmount(token0, JSBI.BigInt(101))) expect(result[0].inputAmount).toEqual(CurrencyAmount.fromRawAmount(token0, JSBI.BigInt(101)))
expect(result[0].outputAmount).toEqual(new CurrencyAmount(token2, JSBI.BigInt(100))) expect(result[0].outputAmount).toEqual(CurrencyAmount.fromRawAmount(token2, JSBI.BigInt(100)))
expect(result[1].route.pairs).toHaveLength(2) // 0 -> 1 -> 2 at 12:12:10 expect(result[1].route.pairs).toHaveLength(2) // 0 -> 1 -> 2 at 12:12:10
expect(result[1].route.path).toEqual([token0, token1, token2]) expect(result[1].route.path).toEqual([token0, token1, token2])
expect(result[1].inputAmount).toEqual(new CurrencyAmount(token0, JSBI.BigInt(156))) expect(result[1].inputAmount).toEqual(CurrencyAmount.fromRawAmount(token0, JSBI.BigInt(156)))
expect(result[1].outputAmount).toEqual(new CurrencyAmount(token2, JSBI.BigInt(100))) expect(result[1].outputAmount).toEqual(CurrencyAmount.fromRawAmount(token2, JSBI.BigInt(100)))
}) })
it('doesnt throw for zero liquidity pairs', () => { it('doesnt throw for zero liquidity pairs', () => {
expect( expect(
Trade.bestTradeExactOut([empty_pair_0_1], token1, new CurrencyAmount(token1, JSBI.BigInt(100))) Trade.bestTradeExactOut([empty_pair_0_1], token1, CurrencyAmount.fromRawAmount(token1, JSBI.BigInt(100)))
).toHaveLength(0) ).toHaveLength(0)
}) })
...@@ -372,7 +383,7 @@ describe('Trade', () => { ...@@ -372,7 +383,7 @@ describe('Trade', () => {
const result = Trade.bestTradeExactOut( const result = Trade.bestTradeExactOut(
[pair_0_1, pair_0_2, pair_1_2], [pair_0_1, pair_0_2, pair_1_2],
token0, token0,
new CurrencyAmount(token2, JSBI.BigInt(10)), CurrencyAmount.fromRawAmount(token2, JSBI.BigInt(10)),
{ maxHops: 1 } { maxHops: 1 }
) )
expect(result).toHaveLength(1) expect(result).toHaveLength(1)
...@@ -384,7 +395,7 @@ describe('Trade', () => { ...@@ -384,7 +395,7 @@ describe('Trade', () => {
const result = Trade.bestTradeExactOut( const result = Trade.bestTradeExactOut(
[pair_0_1, pair_0_2, pair_1_2], [pair_0_1, pair_0_2, pair_1_2],
token0, token0,
new CurrencyAmount(token2, JSBI.BigInt(1200)) CurrencyAmount.fromRawAmount(token2, JSBI.BigInt(1200))
) )
expect(result).toHaveLength(0) expect(result).toHaveLength(0)
}) })
...@@ -393,7 +404,7 @@ describe('Trade', () => { ...@@ -393,7 +404,7 @@ describe('Trade', () => {
const result = Trade.bestTradeExactOut( const result = Trade.bestTradeExactOut(
[pair_0_1, pair_0_2, pair_1_2], [pair_0_1, pair_0_2, pair_1_2],
token0, token0,
new CurrencyAmount(token2, JSBI.BigInt(1050)) CurrencyAmount.fromRawAmount(token2, JSBI.BigInt(1050))
) )
expect(result).toHaveLength(1) expect(result).toHaveLength(1)
}) })
...@@ -402,7 +413,7 @@ describe('Trade', () => { ...@@ -402,7 +413,7 @@ describe('Trade', () => {
const result = Trade.bestTradeExactOut( const result = Trade.bestTradeExactOut(
[pair_0_1, pair_0_2, pair_1_2], [pair_0_1, pair_0_2, pair_1_2],
token0, token0,
new CurrencyAmount(token2, JSBI.BigInt(10)), CurrencyAmount.fromRawAmount(token2, JSBI.BigInt(10)),
{ maxNumResults: 1 } { maxNumResults: 1 }
) )
...@@ -413,7 +424,7 @@ describe('Trade', () => { ...@@ -413,7 +424,7 @@ describe('Trade', () => {
const result = Trade.bestTradeExactOut( const result = Trade.bestTradeExactOut(
[pair_0_1, pair_0_3, pair_1_3], [pair_0_1, pair_0_3, pair_1_3],
token0, token0,
new CurrencyAmount(token2, JSBI.BigInt(10)) CurrencyAmount.fromRawAmount(token2, JSBI.BigInt(10))
) )
expect(result).toHaveLength(0) expect(result).toHaveLength(0)
}) })
...@@ -422,7 +433,7 @@ describe('Trade', () => { ...@@ -422,7 +433,7 @@ describe('Trade', () => {
const result = Trade.bestTradeExactOut( const result = Trade.bestTradeExactOut(
[pair_weth_0, pair_0_1, pair_0_3, pair_1_3], [pair_weth_0, pair_0_1, pair_0_3, pair_1_3],
ETHER, ETHER,
new CurrencyAmount(token3, JSBI.BigInt(100)) CurrencyAmount.fromRawAmount(token3, JSBI.BigInt(100))
) )
expect(result).toHaveLength(2) expect(result).toHaveLength(2)
expect(result[0].inputAmount.currency).toEqual(ETHER) expect(result[0].inputAmount.currency).toEqual(ETHER)
......
...@@ -3,43 +3,33 @@ import { ...@@ -3,43 +3,33 @@ import {
Currency, Currency,
CurrencyAmount, CurrencyAmount,
currencyEquals, currencyEquals,
ETHER,
Fraction, Fraction,
Percent, Percent,
Price, Price,
sortedInsert, sortedInsert,
Token, wrappedCurrency,
TradeType, TradeType,
WETH9 wrappedCurrencyAmount
} from '@uniswap/sdk-core' } from '@uniswap/sdk-core'
import { computePriceImpact, Token } from '../../../../sdk-core'
import { ONE, ZERO } from '../constants' import { ONE, ZERO } from '../constants'
import invariant from 'tiny-invariant' import invariant from 'tiny-invariant'
import { Pair } from './pair' import { Pair } from './pair'
import { Route } from './route' import { Route } from './route'
/**
* Returns the percent difference between the mid price and the execution price, i.e. price impact.
* @param midPrice mid price before the trade
* @param inputAmount the input amount of the trade
* @param outputAmount the output amount of the trade
*/
function computePriceImpact(midPrice: Price, inputAmount: CurrencyAmount, outputAmount: CurrencyAmount): Percent {
const exactQuote = midPrice.raw.multiply(inputAmount.raw)
// calculate slippage := (exactQuote - outputAmount) / exactQuote
const slippage = exactQuote.subtract(outputAmount.raw).divide(exactQuote)
return new Percent(slippage.numerator, slippage.denominator)
}
// minimal interface so the input output comparator may be shared across types // minimal interface so the input output comparator may be shared across types
interface InputOutput { interface InputOutput<TInput extends Currency, TOutput extends Currency> {
readonly inputAmount: CurrencyAmount readonly inputAmount: CurrencyAmount<TInput>
readonly outputAmount: CurrencyAmount readonly outputAmount: CurrencyAmount<TOutput>
} }
// comparator function that allows sorting trades by their output amounts, in decreasing order, and then input amounts // comparator function that allows sorting trades by their output amounts, in decreasing order, and then input amounts
// in increasing order. i.e. the best trades have the most outputs for the least inputs and are sorted first // in increasing order. i.e. the best trades have the most outputs for the least inputs and are sorted first
export function inputOutputComparator(a: InputOutput, b: InputOutput): number { export function inputOutputComparator<TInput extends Currency, TOutput extends Currency>(
a: InputOutput<TInput, TOutput>,
b: InputOutput<TInput, TOutput>
): number {
// must have same input and output token for comparison // must have same input and output token for comparison
invariant(currencyEquals(a.inputAmount.currency, b.inputAmount.currency), 'INPUT_CURRENCY') invariant(currencyEquals(a.inputAmount.currency, b.inputAmount.currency), 'INPUT_CURRENCY')
invariant(currencyEquals(a.outputAmount.currency, b.outputAmount.currency), 'OUTPUT_CURRENCY') invariant(currencyEquals(a.outputAmount.currency, b.outputAmount.currency), 'OUTPUT_CURRENCY')
...@@ -64,7 +54,10 @@ export function inputOutputComparator(a: InputOutput, b: InputOutput): number { ...@@ -64,7 +54,10 @@ export function inputOutputComparator(a: InputOutput, b: InputOutput): number {
} }
// extension of the input output comparator that also considers other dimensions of the trade in ranking them // extension of the input output comparator that also considers other dimensions of the trade in ranking them
export function tradeComparator(a: Trade, b: Trade) { export function tradeComparator<TInput extends Currency, TOutput extends Currency, TTradeType extends TradeType>(
a: Trade<TInput, TOutput, TTradeType>,
b: Trade<TInput, TOutput, TTradeType>
) {
const ioComp = inputOutputComparator(a, b) const ioComp = inputOutputComparator(a, b)
if (ioComp !== 0) { if (ioComp !== 0) {
return ioComp return ioComp
...@@ -88,52 +81,31 @@ export interface BestTradeOptions { ...@@ -88,52 +81,31 @@ export interface BestTradeOptions {
maxHops?: number maxHops?: number
} }
/**
* Given a currency amount and a chain ID, returns the equivalent representation as the token amount.
* In other words, if the currency is ETHER, returns the WETH9 token amount for the given chain. Otherwise, returns
* the input currency amount.
*/
function wrappedAmount(currencyAmount: CurrencyAmount, chainId: ChainId): CurrencyAmount {
if (currencyAmount.currency.isToken) return currencyAmount
if (currencyAmount.currency.isEther) return new CurrencyAmount(WETH9[chainId], currencyAmount.raw)
throw new Error('CURRENCY')
}
function wrappedCurrency(currency: Currency, chainId: ChainId): Token {
if (currency.isToken) return currency
if (currency === ETHER) return WETH9[chainId]
throw new Error('CURRENCY')
}
/** /**
* Represents a trade executed against a list of pairs. * Represents a trade executed against a list of pairs.
* Does not account for slippage, i.e. trades that front run this trade and move the price. * Does not account for slippage, i.e. trades that front run this trade and move the price.
*/ */
export class Trade { export class Trade<TInput extends Currency, TOutput extends Currency, TTradeType extends TradeType> {
/** /**
* The route of the trade, i.e. which pairs the trade goes through. * The route of the trade, i.e. which pairs the trade goes through and the input/output currencies.
*/ */
public readonly route: Route public readonly route: Route<TInput, TOutput>
/** /**
* The type of the trade, either exact in or exact out. * The type of the trade, either exact in or exact out.
*/ */
public readonly tradeType: TradeType public readonly tradeType: TTradeType
/** /**
* The input amount for the trade assuming no slippage. * The input amount for the trade assuming no slippage.
*/ */
public readonly inputAmount: CurrencyAmount public readonly inputAmount: CurrencyAmount<TInput>
/** /**
* The output amount for the trade assuming no slippage. * The output amount for the trade assuming no slippage.
*/ */
public readonly outputAmount: CurrencyAmount public readonly outputAmount: CurrencyAmount<TOutput>
/** /**
* The price expressed in terms of output amount/input amount. * The price expressed in terms of output amount/input amount.
*/ */
public readonly executionPrice: Price public readonly executionPrice: Price<TInput, TOutput>
/**
* The mid price after the trade executes assuming no slippage.
*/
public readonly nextMidPrice: Price
/** /**
* The percent difference between the mid price before the trade and the trade execution price. * The percent difference between the mid price before the trade and the trade execution price.
*/ */
...@@ -144,7 +116,10 @@ export class Trade { ...@@ -144,7 +116,10 @@ export class Trade {
* @param route route of the exact in trade * @param route route of the exact in trade
* @param amountIn the amount being passed in * @param amountIn the amount being passed in
*/ */
public static exactIn(route: Route, amountIn: CurrencyAmount): Trade { public static exactIn<TInput extends Currency, TOutput extends Currency>(
route: Route<TInput, TOutput>,
amountIn: CurrencyAmount<TInput>
): Trade<TInput, TOutput, TradeType.EXACT_INPUT> {
return new Trade(route, amountIn, TradeType.EXACT_INPUT) return new Trade(route, amountIn, TradeType.EXACT_INPUT)
} }
...@@ -153,54 +128,57 @@ export class Trade { ...@@ -153,54 +128,57 @@ export class Trade {
* @param route route of the exact out trade * @param route route of the exact out trade
* @param amountOut the amount returned by the trade * @param amountOut the amount returned by the trade
*/ */
public static exactOut(route: Route, amountOut: CurrencyAmount): Trade { public static exactOut<TInput extends Currency, TOutput extends Currency>(
route: Route<TInput, TOutput>,
amountOut: CurrencyAmount<TOutput>
): Trade<TInput, TOutput, TradeType.EXACT_OUTPUT> {
return new Trade(route, amountOut, TradeType.EXACT_OUTPUT) return new Trade(route, amountOut, TradeType.EXACT_OUTPUT)
} }
public constructor(route: Route, amount: CurrencyAmount, tradeType: TradeType) { public constructor(
const amounts: CurrencyAmount[] = new Array(route.path.length) route: Route<TInput, TOutput>,
const nextPairs: Pair[] = new Array(route.pairs.length) amount: TTradeType extends TradeType.EXACT_INPUT ? CurrencyAmount<TInput> : CurrencyAmount<TOutput>,
tradeType: TTradeType
) {
this.route = route
this.tradeType = tradeType
const tokenAmounts: CurrencyAmount<Token>[] = new Array(route.path.length)
if (tradeType === TradeType.EXACT_INPUT) { if (tradeType === TradeType.EXACT_INPUT) {
invariant(currencyEquals(amount.currency, route.input), 'INPUT') invariant(currencyEquals(amount.currency, route.input), 'INPUT')
amounts[0] = wrappedAmount(amount, route.chainId) tokenAmounts[0] = wrappedCurrencyAmount(amount, route.chainId)
for (let i = 0; i < route.path.length - 1; i++) { for (let i = 0; i < route.path.length - 1; i++) {
const pair = route.pairs[i] const pair = route.pairs[i]
const [outputAmount, nextPair] = pair.getOutputAmount(amounts[i]) const [outputAmount] = pair.getOutputAmount(tokenAmounts[i])
amounts[i + 1] = outputAmount tokenAmounts[i + 1] = outputAmount
nextPairs[i] = nextPair
} }
this.inputAmount = CurrencyAmount.fromFractionalAmount(route.input, amount.numerator, amount.denominator)
this.outputAmount = CurrencyAmount.fromFractionalAmount(
route.output,
tokenAmounts[tokenAmounts.length - 1].numerator,
tokenAmounts[tokenAmounts.length - 1].denominator
)
} else { } else {
invariant(currencyEquals(amount.currency, route.output), 'OUTPUT') invariant(currencyEquals(amount.currency, route.output), 'OUTPUT')
amounts[amounts.length - 1] = wrappedAmount(amount, route.chainId) tokenAmounts[tokenAmounts.length - 1] = wrappedCurrencyAmount(amount, route.chainId)
for (let i = route.path.length - 1; i > 0; i--) { for (let i = route.path.length - 1; i > 0; i--) {
const pair = route.pairs[i - 1] const pair = route.pairs[i - 1]
const [inputAmount, nextPair] = pair.getInputAmount(amounts[i]) const [inputAmount] = pair.getInputAmount(tokenAmounts[i])
amounts[i - 1] = inputAmount tokenAmounts[i - 1] = inputAmount
nextPairs[i - 1] = nextPair
} }
this.inputAmount = CurrencyAmount.fromFractionalAmount(
route.input,
tokenAmounts[0].numerator,
tokenAmounts[0].denominator
)
this.outputAmount = CurrencyAmount.fromFractionalAmount(route.output, amount.numerator, amount.denominator)
} }
this.route = route
this.tradeType = tradeType
this.inputAmount =
tradeType === TradeType.EXACT_INPUT
? amount
: route.input === ETHER
? CurrencyAmount.ether(amounts[0].raw)
: amounts[0]
this.outputAmount =
tradeType === TradeType.EXACT_OUTPUT
? amount
: route.output === ETHER
? CurrencyAmount.ether(amounts[amounts.length - 1].raw)
: amounts[amounts.length - 1]
this.executionPrice = new Price( this.executionPrice = new Price(
this.inputAmount.currency, this.inputAmount.currency,
this.outputAmount.currency, this.outputAmount.currency,
this.inputAmount.raw, this.inputAmount.quotient,
this.outputAmount.raw this.outputAmount.quotient
) )
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)
} }
...@@ -208,7 +186,7 @@ export class Trade { ...@@ -208,7 +186,7 @@ export class Trade {
* Get the minimum amount that must be received from this trade for the given slippage tolerance * Get the minimum amount that must be received from this trade for the given slippage tolerance
* @param slippageTolerance tolerance of unfavorable slippage from the execution price of this trade * @param slippageTolerance tolerance of unfavorable slippage from the execution price of this trade
*/ */
public minimumAmountOut(slippageTolerance: Percent): CurrencyAmount { public minimumAmountOut(slippageTolerance: Percent): CurrencyAmount<TOutput> {
invariant(!slippageTolerance.lessThan(ZERO), 'SLIPPAGE_TOLERANCE') invariant(!slippageTolerance.lessThan(ZERO), 'SLIPPAGE_TOLERANCE')
if (this.tradeType === TradeType.EXACT_OUTPUT) { if (this.tradeType === TradeType.EXACT_OUTPUT) {
return this.outputAmount return this.outputAmount
...@@ -216,8 +194,8 @@ export class Trade { ...@@ -216,8 +194,8 @@ export class Trade {
const slippageAdjustedAmountOut = new Fraction(ONE) const slippageAdjustedAmountOut = new Fraction(ONE)
.add(slippageTolerance) .add(slippageTolerance)
.invert() .invert()
.multiply(this.outputAmount.raw).quotient .multiply(this.outputAmount.quotient).quotient
return new CurrencyAmount(this.outputAmount.currency, slippageAdjustedAmountOut) return CurrencyAmount.fromRawAmount(this.outputAmount.currency, slippageAdjustedAmountOut)
} }
} }
...@@ -225,13 +203,14 @@ export class Trade { ...@@ -225,13 +203,14 @@ export class Trade {
* Get the maximum amount in that can be spent via this trade for the given slippage tolerance * Get the maximum amount in that can be spent via this trade for the given slippage tolerance
* @param slippageTolerance tolerance of unfavorable slippage from the execution price of this trade * @param slippageTolerance tolerance of unfavorable slippage from the execution price of this trade
*/ */
public maximumAmountIn(slippageTolerance: Percent): CurrencyAmount { public maximumAmountIn(slippageTolerance: Percent): CurrencyAmount<TInput> {
invariant(!slippageTolerance.lessThan(ZERO), 'SLIPPAGE_TOLERANCE') invariant(!slippageTolerance.lessThan(ZERO), 'SLIPPAGE_TOLERANCE')
if (this.tradeType === TradeType.EXACT_INPUT) { if (this.tradeType === TradeType.EXACT_INPUT) {
return this.inputAmount return this.inputAmount
} else { } else {
const slippageAdjustedAmountIn = new Fraction(ONE).add(slippageTolerance).multiply(this.inputAmount.raw).quotient const slippageAdjustedAmountIn = new Fraction(ONE).add(slippageTolerance).multiply(this.inputAmount.quotient)
return new CurrencyAmount(this.inputAmount.currency, slippageAdjustedAmountIn) .quotient
return CurrencyAmount.fromRawAmount(this.inputAmount.currency, slippageAdjustedAmountIn)
} }
} }
...@@ -241,35 +220,36 @@ export class Trade { ...@@ -241,35 +220,36 @@ export class Trade {
* Note this does not consider aggregation, as routes are linear. It's possible a better route exists by splitting * Note this does not consider aggregation, as routes are linear. It's possible a better route exists by splitting
* the amount in among multiple routes. * the amount in among multiple routes.
* @param pairs the pairs to consider in finding the best trade * @param pairs the pairs to consider in finding the best trade
* @param currencyAmountIn exact amount of input currency to spend * @param nextAmountIn exact amount of input currency to spend
* @param currencyOut the desired currency out * @param currencyOut the desired currency out
* @param maxNumResults maximum number of results to return * @param maxNumResults maximum number of results to return
* @param maxHops maximum number of hops a returned trade can make, e.g. 1 hop goes through a single pair * @param maxHops maximum number of hops a returned trade can make, e.g. 1 hop goes through a single pair
* @param currentPairs used in recursion; the current list of pairs * @param currentPairs used in recursion; the current list of pairs
* @param originalAmountIn used in recursion; the original value of the currencyAmountIn parameter * @param currencyAmountIn used in recursion; the original value of the currencyAmountIn parameter
* @param bestTrades used in recursion; the current list of best trades * @param bestTrades used in recursion; the current list of best trades
*/ */
public static bestTradeExactIn( public static bestTradeExactIn<TInput extends Currency, TOutput extends Currency>(
pairs: Pair[], pairs: Pair[],
currencyAmountIn: CurrencyAmount, currencyAmountIn: CurrencyAmount<TInput>,
currencyOut: Currency, currencyOut: TOutput,
{ maxNumResults = 3, maxHops = 3 }: BestTradeOptions = {}, { maxNumResults = 3, maxHops = 3 }: BestTradeOptions = {},
// used in recursion. // used in recursion.
currentPairs: Pair[] = [], currentPairs: Pair[] = [],
originalAmountIn: CurrencyAmount = currencyAmountIn, nextAmountIn: CurrencyAmount<Currency> = currencyAmountIn,
bestTrades: Trade[] = [] bestTrades: Trade<TInput, TOutput, TradeType.EXACT_INPUT>[] = []
): Trade[] { ): Trade<TInput, TOutput, TradeType.EXACT_INPUT>[] {
invariant(pairs.length > 0, 'PAIRS') invariant(pairs.length > 0, 'PAIRS')
invariant(maxHops > 0, 'MAX_HOPS') invariant(maxHops > 0, 'MAX_HOPS')
invariant(originalAmountIn === currencyAmountIn || currentPairs.length > 0, 'INVALID_RECURSION') invariant(currencyAmountIn === nextAmountIn || currentPairs.length > 0, 'INVALID_RECURSION')
const chainId: ChainId | undefined = currencyAmountIn.currency.isToken const chainId: ChainId | undefined = nextAmountIn.currency.isToken
? currencyAmountIn.currency.chainId ? nextAmountIn.currency.chainId
: currencyOut.isToken : currencyOut.isToken
? currencyOut.chainId ? (currencyOut as Token).chainId
: undefined : undefined
invariant(chainId !== undefined, 'CHAIN_ID') invariant(chainId !== undefined, 'CHAIN_ID')
const amountIn = wrappedAmount(currencyAmountIn, chainId) const amountIn = wrappedCurrencyAmount(nextAmountIn, chainId)
const tokenOut = wrappedCurrency(currencyOut, chainId) const tokenOut = wrappedCurrency(currencyOut, chainId)
for (let i = 0; i < pairs.length; i++) { for (let i = 0; i < pairs.length; i++) {
const pair = pairs[i] const pair = pairs[i]
...@@ -277,7 +257,7 @@ export class Trade { ...@@ -277,7 +257,7 @@ export class Trade {
if (!currencyEquals(pair.token0, amountIn.currency) && !currencyEquals(pair.token1, amountIn.currency)) continue if (!currencyEquals(pair.token0, amountIn.currency) && !currencyEquals(pair.token1, amountIn.currency)) continue
if (pair.reserve0.equalTo(ZERO) || pair.reserve1.equalTo(ZERO)) continue if (pair.reserve0.equalTo(ZERO) || pair.reserve1.equalTo(ZERO)) continue
let amountOut: CurrencyAmount let amountOut: CurrencyAmount<Token>
try { try {
;[amountOut] = pair.getOutputAmount(amountIn) ;[amountOut] = pair.getOutputAmount(amountIn)
} catch (error) { } catch (error) {
...@@ -292,8 +272,8 @@ export class Trade { ...@@ -292,8 +272,8 @@ export class Trade {
sortedInsert( sortedInsert(
bestTrades, bestTrades,
new Trade( new Trade(
new Route([...currentPairs, pair], originalAmountIn.currency, currencyOut), new Route([...currentPairs, pair], currencyAmountIn.currency, currencyOut),
originalAmountIn, currencyAmountIn,
TradeType.EXACT_INPUT TradeType.EXACT_INPUT
), ),
maxNumResults, maxNumResults,
...@@ -305,14 +285,14 @@ export class Trade { ...@@ -305,14 +285,14 @@ export class Trade {
// otherwise, consider all the other paths that lead from this token as long as we have not exceeded maxHops // otherwise, consider all the other paths that lead from this token as long as we have not exceeded maxHops
Trade.bestTradeExactIn( Trade.bestTradeExactIn(
pairsExcludingThisPair, pairsExcludingThisPair,
amountOut, currencyAmountIn,
currencyOut, currencyOut,
{ {
maxNumResults, maxNumResults,
maxHops: maxHops - 1 maxHops: maxHops - 1
}, },
[...currentPairs, pair], [...currentPairs, pair],
originalAmountIn, amountOut,
bestTrades bestTrades
) )
} }
...@@ -325,12 +305,12 @@ export class Trade { ...@@ -325,12 +305,12 @@ export class Trade {
* Return the execution price after accounting for slippage tolerance * Return the execution price after accounting for slippage tolerance
* @param slippageTolerance the allowed tolerated slippage * @param slippageTolerance the allowed tolerated slippage
*/ */
public worstExecutionPrice(slippageTolerance: Percent): Price { public worstExecutionPrice(slippageTolerance: Percent): Price<TInput, TOutput> {
return new Price( return new Price(
this.inputAmount.currency, this.inputAmount.currency,
this.outputAmount.currency, this.outputAmount.currency,
this.maximumAmountIn(slippageTolerance).raw, this.maximumAmountIn(slippageTolerance).quotient,
this.minimumAmountOut(slippageTolerance).raw this.minimumAmountOut(slippageTolerance).quotient
) )
} }
...@@ -342,34 +322,34 @@ export class Trade { ...@@ -342,34 +322,34 @@ export class Trade {
* the amount in among multiple routes. * the amount in among multiple routes.
* @param pairs the pairs to consider in finding the best trade * @param pairs the pairs to consider in finding the best trade
* @param currencyIn the currency to spend * @param currencyIn the currency to spend
* @param currencyAmountOut the exact amount of currency out * @param nextAmountOut the exact amount of currency out
* @param maxNumResults maximum number of results to return * @param maxNumResults maximum number of results to return
* @param maxHops maximum number of hops a returned trade can make, e.g. 1 hop goes through a single pair * @param maxHops maximum number of hops a returned trade can make, e.g. 1 hop goes through a single pair
* @param currentPairs used in recursion; the current list of pairs * @param currentPairs used in recursion; the current list of pairs
* @param originalAmountOut used in recursion; the original value of the currencyAmountOut parameter * @param currencyAmountOut used in recursion; the original value of the currencyAmountOut parameter
* @param bestTrades used in recursion; the current list of best trades * @param bestTrades used in recursion; the current list of best trades
*/ */
public static bestTradeExactOut( public static bestTradeExactOut<TInput extends Currency, TOutput extends Currency>(
pairs: Pair[], pairs: Pair[],
currencyIn: Currency, currencyIn: TInput,
currencyAmountOut: CurrencyAmount, currencyAmountOut: CurrencyAmount<TOutput>,
{ maxNumResults = 3, maxHops = 3 }: BestTradeOptions = {}, { maxNumResults = 3, maxHops = 3 }: BestTradeOptions = {},
// used in recursion. // used in recursion.
currentPairs: Pair[] = [], currentPairs: Pair[] = [],
originalAmountOut: CurrencyAmount = currencyAmountOut, nextAmountOut: CurrencyAmount<Currency> = currencyAmountOut,
bestTrades: Trade[] = [] bestTrades: Trade<TInput, TOutput, TradeType.EXACT_OUTPUT>[] = []
): Trade[] { ): Trade<TInput, TOutput, TradeType.EXACT_OUTPUT>[] {
invariant(pairs.length > 0, 'PAIRS') invariant(pairs.length > 0, 'PAIRS')
invariant(maxHops > 0, 'MAX_HOPS') invariant(maxHops > 0, 'MAX_HOPS')
invariant(originalAmountOut === currencyAmountOut || currentPairs.length > 0, 'INVALID_RECURSION') invariant(currencyAmountOut === nextAmountOut || currentPairs.length > 0, 'INVALID_RECURSION')
const chainId: ChainId | undefined = currencyAmountOut.currency.isToken const chainId: ChainId | undefined = nextAmountOut.currency.isToken
? currencyAmountOut.currency.chainId ? nextAmountOut.currency.chainId
: currencyIn.isToken : currencyIn.isToken
? currencyIn.chainId ? (currencyIn as Token).chainId
: undefined : undefined
invariant(chainId !== undefined, 'CHAIN_ID') invariant(chainId !== undefined, 'CHAIN_ID')
const amountOut = wrappedAmount(currencyAmountOut, chainId) const amountOut = wrappedCurrencyAmount(nextAmountOut, chainId)
const tokenIn = wrappedCurrency(currencyIn, chainId) const tokenIn = wrappedCurrency(currencyIn, chainId)
for (let i = 0; i < pairs.length; i++) { for (let i = 0; i < pairs.length; i++) {
const pair = pairs[i] const pair = pairs[i]
...@@ -377,7 +357,7 @@ export class Trade { ...@@ -377,7 +357,7 @@ export class Trade {
if (!currencyEquals(pair.token0, amountOut.currency) && !currencyEquals(pair.token1, amountOut.currency)) continue if (!currencyEquals(pair.token0, amountOut.currency) && !currencyEquals(pair.token1, amountOut.currency)) continue
if (pair.reserve0.equalTo(ZERO) || pair.reserve1.equalTo(ZERO)) continue if (pair.reserve0.equalTo(ZERO) || pair.reserve1.equalTo(ZERO)) continue
let amountIn: CurrencyAmount let amountIn: CurrencyAmount<Token>
try { try {
;[amountIn] = pair.getInputAmount(amountOut) ;[amountIn] = pair.getInputAmount(amountOut)
} catch (error) { } catch (error) {
...@@ -392,8 +372,8 @@ export class Trade { ...@@ -392,8 +372,8 @@ export class Trade {
sortedInsert( sortedInsert(
bestTrades, bestTrades,
new Trade( new Trade(
new Route([pair, ...currentPairs], currencyIn, originalAmountOut.currency), new Route([pair, ...currentPairs], currencyIn, currencyAmountOut.currency),
originalAmountOut, currencyAmountOut,
TradeType.EXACT_OUTPUT TradeType.EXACT_OUTPUT
), ),
maxNumResults, maxNumResults,
...@@ -406,13 +386,13 @@ export class Trade { ...@@ -406,13 +386,13 @@ export class Trade {
Trade.bestTradeExactOut( Trade.bestTradeExactOut(
pairsExcludingThisPair, pairsExcludingThisPair,
currencyIn, currencyIn,
amountIn, currencyAmountOut,
{ {
maxNumResults, maxNumResults,
maxHops: maxHops - 1 maxHops: maxHops - 1
}, },
[pair, ...currentPairs], [pair, ...currentPairs],
originalAmountOut, amountIn,
bestTrades bestTrades
) )
} }
......
import JSBI from 'jsbi'
export { JSBI }
export { FACTORY_ADDRESS, INIT_CODE_HASH, MINIMUM_LIQUIDITY } from './constants' export { FACTORY_ADDRESS, INIT_CODE_HASH, MINIMUM_LIQUIDITY } from './constants'
export * from './errors' export * from './errors'
......
...@@ -16,15 +16,18 @@ describe('Router', () => { ...@@ -16,15 +16,18 @@ describe('Router', () => {
const token1 = new Token(ChainId.MAINNET, '0x0000000000000000000000000000000000000002', 18, 't1') const token1 = new Token(ChainId.MAINNET, '0x0000000000000000000000000000000000000002', 18, 't1')
const pair_0_1 = new Pair( const pair_0_1 = new Pair(
new CurrencyAmount(token0, JSBI.BigInt(1000)), CurrencyAmount.fromRawAmount(token0, JSBI.BigInt(1000)),
new CurrencyAmount(token1, JSBI.BigInt(1000)) CurrencyAmount.fromRawAmount(token1, JSBI.BigInt(1000))
) )
const pair_weth_0 = new Pair(new CurrencyAmount(WETH9[ChainId.MAINNET], '1000'), new CurrencyAmount(token0, '1000')) const pair_weth_0 = new Pair(
CurrencyAmount.fromRawAmount(WETH9[ChainId.MAINNET], '1000'),
CurrencyAmount.fromRawAmount(token0, '1000')
)
describe('#swapCallParameters', () => { describe('#swapCallParameters', () => {
describe('exact in', () => { describe('exact in', () => {
it('ether to token1', () => { it.only('ether to token1', () => {
const result = Router.swapCallParameters( const result = Router.swapCallParameters(
Trade.exactIn(new Route([pair_weth_0, pair_0_1], ETHER, token1), CurrencyAmount.ether(JSBI.BigInt(100))), Trade.exactIn(new Route([pair_weth_0, pair_0_1], ETHER, token1), CurrencyAmount.ether(JSBI.BigInt(100))),
{ ttl: 50, recipient: '0x0000000000000000000000000000000000000004', allowedSlippage: new Percent('1', '100') } { ttl: 50, recipient: '0x0000000000000000000000000000000000000004', allowedSlippage: new Percent('1', '100') }
...@@ -62,7 +65,7 @@ describe('Router', () => { ...@@ -62,7 +65,7 @@ describe('Router', () => {
const result = Router.swapCallParameters( const result = Router.swapCallParameters(
Trade.exactIn( Trade.exactIn(
new Route([pair_0_1, pair_weth_0], token1, ETHER), new Route([pair_0_1, pair_weth_0], token1, ETHER),
new CurrencyAmount(token1, JSBI.BigInt(100)) CurrencyAmount.fromRawAmount(token1, JSBI.BigInt(100))
), ),
{ ttl: 50, recipient: '0x0000000000000000000000000000000000000004', allowedSlippage: new Percent('1', '100') } { ttl: 50, recipient: '0x0000000000000000000000000000000000000004', allowedSlippage: new Percent('1', '100') }
) )
...@@ -78,7 +81,7 @@ describe('Router', () => { ...@@ -78,7 +81,7 @@ describe('Router', () => {
}) })
it('token0 to token1', () => { it('token0 to token1', () => {
const result = Router.swapCallParameters( const result = Router.swapCallParameters(
Trade.exactIn(new Route([pair_0_1], token0, token1), new CurrencyAmount(token0, JSBI.BigInt(100))), Trade.exactIn(new Route([pair_0_1], token0, token1), CurrencyAmount.fromRawAmount(token0, JSBI.BigInt(100))),
{ ttl: 50, recipient: '0x0000000000000000000000000000000000000004', allowedSlippage: new Percent('1', '100') } { ttl: 50, recipient: '0x0000000000000000000000000000000000000004', allowedSlippage: new Percent('1', '100') }
) )
expect(result.methodName).toEqual('swapExactTokensForTokens') expect(result.methodName).toEqual('swapExactTokensForTokens')
...@@ -97,7 +100,7 @@ describe('Router', () => { ...@@ -97,7 +100,7 @@ describe('Router', () => {
const result = Router.swapCallParameters( const result = Router.swapCallParameters(
Trade.exactOut( Trade.exactOut(
new Route([pair_weth_0, pair_0_1], ETHER, token1), new Route([pair_weth_0, pair_0_1], ETHER, token1),
new CurrencyAmount(token1, JSBI.BigInt(100)) CurrencyAmount.fromRawAmount(token1, JSBI.BigInt(100))
), ),
{ ttl: 50, recipient: '0x0000000000000000000000000000000000000004', allowedSlippage: new Percent('1', '100') } { ttl: 50, recipient: '0x0000000000000000000000000000000000000004', allowedSlippage: new Percent('1', '100') }
) )
...@@ -127,7 +130,7 @@ describe('Router', () => { ...@@ -127,7 +130,7 @@ describe('Router', () => {
}) })
it('token0 to token1', () => { it('token0 to token1', () => {
const result = Router.swapCallParameters( const result = Router.swapCallParameters(
Trade.exactOut(new Route([pair_0_1], token0, token1), new CurrencyAmount(token1, JSBI.BigInt(100))), Trade.exactOut(new Route([pair_0_1], token0, token1), CurrencyAmount.fromRawAmount(token1, JSBI.BigInt(100))),
{ ttl: 50, recipient: '0x0000000000000000000000000000000000000004', allowedSlippage: new Percent('1', '100') } { ttl: 50, recipient: '0x0000000000000000000000000000000000000004', allowedSlippage: new Percent('1', '100') }
) )
expect(result.methodName).toEqual('swapTokensForExactTokens') expect(result.methodName).toEqual('swapTokensForExactTokens')
...@@ -166,7 +169,7 @@ describe('Router', () => { ...@@ -166,7 +169,7 @@ describe('Router', () => {
const result = Router.swapCallParameters( const result = Router.swapCallParameters(
Trade.exactIn( Trade.exactIn(
new Route([pair_0_1, pair_weth_0], token1, ETHER), new Route([pair_0_1, pair_weth_0], token1, ETHER),
new CurrencyAmount(token1, JSBI.BigInt(100)) CurrencyAmount.fromRawAmount(token1, JSBI.BigInt(100))
), ),
{ {
ttl: 50, ttl: 50,
...@@ -187,7 +190,10 @@ describe('Router', () => { ...@@ -187,7 +190,10 @@ describe('Router', () => {
}) })
it('token0 to token1', () => { it('token0 to token1', () => {
const result = Router.swapCallParameters( const result = Router.swapCallParameters(
Trade.exactIn(new Route([pair_0_1], token0, token1), new CurrencyAmount(token0, JSBI.BigInt(100))), Trade.exactIn(
new Route([pair_0_1], token0, token1),
CurrencyAmount.fromRawAmount(token0, JSBI.BigInt(100))
),
{ {
ttl: 50, ttl: 50,
recipient: '0x0000000000000000000000000000000000000004', recipient: '0x0000000000000000000000000000000000000004',
...@@ -212,7 +218,7 @@ describe('Router', () => { ...@@ -212,7 +218,7 @@ describe('Router', () => {
Router.swapCallParameters( Router.swapCallParameters(
Trade.exactOut( Trade.exactOut(
new Route([pair_weth_0, pair_0_1], ETHER, token1), new Route([pair_weth_0, pair_0_1], ETHER, token1),
new CurrencyAmount(token1, JSBI.BigInt(100)) CurrencyAmount.fromRawAmount(token1, JSBI.BigInt(100))
), ),
{ {
ttl: 50, ttl: 50,
...@@ -239,7 +245,10 @@ describe('Router', () => { ...@@ -239,7 +245,10 @@ describe('Router', () => {
it('token0 to token1', () => { it('token0 to token1', () => {
expect(() => expect(() =>
Router.swapCallParameters( Router.swapCallParameters(
Trade.exactOut(new Route([pair_0_1], token0, token1), new CurrencyAmount(token1, JSBI.BigInt(100))), Trade.exactOut(
new Route([pair_0_1], token0, token1),
CurrencyAmount.fromRawAmount(token1, JSBI.BigInt(100))
),
{ {
ttl: 50, ttl: 50,
recipient: '0x0000000000000000000000000000000000000004', recipient: '0x0000000000000000000000000000000000000004',
......
import { CurrencyAmount, ETHER, Percent, TradeType, validateAndParseAddress } from '@uniswap/sdk-core' import { Currency, CurrencyAmount, Percent, TradeType, validateAndParseAddress } from '@uniswap/sdk-core'
import { Trade } from 'entities' import { Trade } from 'entities'
import invariant from 'tiny-invariant' import invariant from 'tiny-invariant'
import { Token } from '../../../sdk-core'
/** /**
* Options for producing the arguments to send call to the router. * Options for producing the arguments to send call to the router.
...@@ -53,8 +54,8 @@ export interface SwapParameters { ...@@ -53,8 +54,8 @@ export interface SwapParameters {
value: string value: string
} }
function toHex(currencyAmount: CurrencyAmount) { function toHex(currencyAmount: CurrencyAmount<Currency>) {
return `0x${currencyAmount.raw.toString(16)}` return `0x${currencyAmount.quotient.toString(16)}`
} }
const ZERO_HEX = '0x0' const ZERO_HEX = '0x0'
...@@ -72,9 +73,12 @@ export abstract class Router { ...@@ -72,9 +73,12 @@ export abstract class Router {
* @param trade to produce call parameters for * @param trade to produce call parameters for
* @param options options for the call parameters * @param options options for the call parameters
*/ */
public static swapCallParameters(trade: Trade, options: TradeOptions | TradeOptionsDeadline): SwapParameters { public static swapCallParameters(
const etherIn = trade.inputAmount.currency === ETHER trade: Trade<Currency, Currency, TradeType>,
const etherOut = trade.outputAmount.currency === ETHER options: TradeOptions | TradeOptionsDeadline
): SwapParameters {
const etherIn = trade.inputAmount.currency.isEther
const etherOut = trade.outputAmount.currency.isEther
// the router does not support both ether in and out // the router does not support both ether in and out
invariant(!(etherIn && etherOut), 'ETHER_IN_OUT') invariant(!(etherIn && etherOut), 'ETHER_IN_OUT')
invariant(!('ttl' in options) || options.ttl > 0, 'TTL') invariant(!('ttl' in options) || options.ttl > 0, 'TTL')
...@@ -82,7 +86,7 @@ export abstract class Router { ...@@ -82,7 +86,7 @@ export abstract class Router {
const to: string = validateAndParseAddress(options.recipient) const to: string = validateAndParseAddress(options.recipient)
const amountIn: string = toHex(trade.maximumAmountIn(options.allowedSlippage)) const amountIn: string = toHex(trade.maximumAmountIn(options.allowedSlippage))
const amountOut: string = toHex(trade.minimumAmountOut(options.allowedSlippage)) const amountOut: string = toHex(trade.minimumAmountOut(options.allowedSlippage))
const path: string[] = trade.route.path.map(token => token.address) const path: string[] = trade.route.path.map((token: Token) => token.address)
const deadline = const deadline =
'ttl' in options 'ttl' in options
? `0x${(Math.floor(new Date().getTime() / 1000) + options.ttl).toString(16)}` ? `0x${(Math.floor(new Date().getTime() / 1000) + options.ttl).toString(16)}`
......
...@@ -1706,10 +1706,10 @@ ...@@ -1706,10 +1706,10 @@
semver "^7.3.2" semver "^7.3.2"
tsutils "^3.17.1" tsutils "^3.17.1"
"@uniswap/sdk-core@^2.0.2": "@uniswap/sdk-core@^3.0.0-alpha.0":
version "2.0.2" version "3.0.0-alpha.0"
resolved "https://registry.yarnpkg.com/@uniswap/sdk-core/-/sdk-core-2.0.2.tgz#748d1d189503d20d3027ef69927ad13cbf2d224a" resolved "https://registry.yarnpkg.com/@uniswap/sdk-core/-/sdk-core-3.0.0-alpha.0.tgz#03f3515bc4b735c4d04b4a49e9518b2ea023a707"
integrity sha512-Cx6epJgXE/b9ZP8GAes3LiYFXuxfd7UDZtn8Wvxr6xEMh8T21HHbsoEs9sB8iCBYfYuw2PT+Pza4GJqQNvnddg== integrity sha512-42K3bYBYVdf45E5ek4GcaIkbW3eTkcpGYAufN7rw+LRsvnb5HlbF3FF0NxUP2WwLcyTe1DImHupIROUmCrC61A==
dependencies: dependencies:
"@ethersproject/address" "^5.0.2" "@ethersproject/address" "^5.0.2"
big.js "^5.2.2" big.js "^5.2.2"
......
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