Skip to content

Commit

Permalink
big refactor, more abstraction
Browse files Browse the repository at this point in the history
add data fetching
  • Loading branch information
NoahZinsmeister committed Feb 11, 2020
1 parent 0badfed commit 0e80185
Show file tree
Hide file tree
Showing 27 changed files with 995 additions and 810 deletions.
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,20 @@
},
"dependencies": {
"@ethersproject/address": "^5.0.0-beta.134",
"@ethersproject/contracts": "^5.0.0-beta.143",
"@ethersproject/networks": "^5.0.0-beta.134",
"@ethersproject/providers": "^5.0.0-beta.151",
"big.js": "^5.2.2",
"decimal.js-light": "^2.5.0",
"jsbi": "^3.1.1",
"tiny-invariant": "^1.0.6",
"tiny-invariant": "^1.1.0",
"toformat": "^2.0.0"
},
"devDependencies": {
"@types/big.js": "^4.0.5",
"@types/jest": "^24.0.25",
"babel-plugin-transform-jsbi-to-bigint": "^1.3.1",
"tiny-warning": "^1.0.3",
"tsdx": "^0.12.3"
},
"engines": {
Expand Down
30 changes: 30 additions & 0 deletions src/abis/ERC20.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[
{
"constant": true,
"inputs": [],
"name": "decimals",
"outputs": [{ "name": "", "type": "uint8" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "",
"type": "address"
}
],
"name": "balanceOf",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
}
]
25 changes: 25 additions & 0 deletions src/abis/UniswapV2Factory.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[
{
"constant": true,
"inputs": [
{
"name": "tokenA",
"type": "address"
},
{
"name": "tokenB",
"type": "address"
}
],
"name": "getExchange",
"outputs": [
{
"name": "exchange",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
}
]
22 changes: 13 additions & 9 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@ import JSBI from 'jsbi'

// exports for external consumption
export enum ChainId {
RINKEBY = 4
MAINNET = 1,
ROPSTEN = 3,
RINKEBY = 4,
GÖRLI = 5,
KOVAN = 42
}

export const WETH = {
[ChainId.RINKEBY]: {
chainId: ChainId.RINKEBY,
address: '0xc778417E063141139Fce010982780140Aa0cD5Ab',
decimals: 18
}
export const FACTORY_ADDRESS = {
[ChainId.MAINNET]: '',
[ChainId.ROPSTEN]: '',
[ChainId.RINKEBY]: '0xbe52bB8cCa36fcD6061C307f14bAB48F09A760f9',
[ChainId.GÖRLI]: '',
[ChainId.KOVAN]: ''
}

export enum TradeType {
Expand All @@ -27,6 +31,6 @@ export const _997 = JSBI.BigInt(997)
export const _1000 = JSBI.BigInt(1000)

export enum SolidityType {
uint8,
uint256
uint8 = 'uint8',
uint256 = 'uint256'
}
109 changes: 87 additions & 22 deletions src/entities/exchange.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,100 @@
import invariant from 'tiny-invariant'
import JSBI from 'jsbi'
import { getNetwork } from '@ethersproject/networks'
import { getDefaultProvider } from '@ethersproject/providers'
import { Contract } from '@ethersproject/contracts'

import { SolidityType } from '../constants'
import { BigintIsh } from '../types'
import { parseBigintIsh } from '../utils/parseInputs'
import { validateSolidityTypeInstance } from '../utils/validateInputs'
import { FACTORY_ADDRESS, ZERO, ONE, _997, _1000 } from '../constants'
import UniswapV2Factory from '../abis/UniswapV2Factory.json'
import ERC20 from '../abis/ERC20.json'
import { validateAndParseAddress } from '../utils'
import { Token } from './token'
import { TokenAmount } from './fractions/tokenAmount'

export class Exchange {
public readonly pair: [Token, Token]
public readonly balances: [JSBI, JSBI]
private readonly tokenAmounts: [TokenAmount, TokenAmount]
public readonly address?: string

static validate(pair: [Token, Token], balances: [JSBI, JSBI]) {
// validate components of an Exchange
balances.forEach(balance => validateSolidityTypeInstance(balance, SolidityType.uint256))
static async fetchData(
tokenA: Token,
tokenB: Token,
provider = getDefaultProvider(getNetwork(tokenA.chainId)),
address?: string
): Promise<Exchange> {
const parsedAddress =
typeof address === 'string'
? address
: await new Contract(FACTORY_ADDRESS[tokenA.chainId], UniswapV2Factory, provider).getExchange(
tokenA.address,
tokenB.address
)
const balances = await Promise.all([
new Contract(tokenA.address, ERC20, provider).balanceOf(parsedAddress),
new Contract(tokenB.address, ERC20, provider).balanceOf(parsedAddress)
])
return new Exchange(new TokenAmount(tokenA, balances[0]), new TokenAmount(tokenB, balances[1]), parsedAddress)
}

constructor(tokenAmountA: TokenAmount, tokenAmountB: TokenAmount, address?: string) {
invariant(tokenAmountA.token.chainId === tokenAmountB.token.chainId, 'CHAIN_IDS')
const tokenAmounts: [TokenAmount, TokenAmount] =
tokenAmountA.token.address < tokenAmountB.token.address
? [tokenAmountA, tokenAmountB]
: [tokenAmountB, tokenAmountA]
invariant(tokenAmounts[0].token.address < tokenAmounts[1].token.address, 'ADDRESSES')

this.tokenAmounts = tokenAmounts
if (typeof address === 'string') this.address = validateAndParseAddress(address)
}

public get reserve0(): TokenAmount {
return this.tokenAmounts[0]
}

public get reserve1(): TokenAmount {
return this.tokenAmounts[1]
}

public get token0(): Token {
return this.tokenAmounts[0].token
}

public get token1(): Token {
return this.tokenAmounts[1].token
}

public getOutputAmount(inputAmount: TokenAmount): [TokenAmount, Exchange] {
invariant(inputAmount.token.equals(this.token0) || inputAmount.token.equals(this.token1), 'TOKEN')
invariant(JSBI.greaterThan(inputAmount.raw, ZERO), 'ZERO')
invariant(JSBI.greaterThan(this.reserve0.raw, ZERO), 'ZERO')
invariant(JSBI.greaterThan(this.reserve1.raw, ZERO), 'ZERO')

// validate conditions that must be true of an Exchange
const chainIds = pair.map(token => token.chainId)
invariant(chainIds[0] === chainIds[1], `${chainIds} are not equal.`)
const addresses = pair.map(token => token.address)
invariant(addresses[0] < addresses[1], `${addresses} are not ordered.`)
const inputReserve = inputAmount.token.equals(this.reserve0.token) ? this.reserve0 : this.reserve1
const outputReserve = inputAmount.token.equals(this.reserve0.token) ? this.reserve1 : this.reserve0
const inputAmountWithFee = JSBI.multiply(inputAmount.raw, _997)
const numerator = JSBI.multiply(inputAmountWithFee, outputReserve.raw)
const denominator = JSBI.add(JSBI.multiply(inputReserve.raw, _1000), inputAmountWithFee)
const output = new TokenAmount(
inputAmount.token.equals(this.token0) ? this.token1 : this.token0,
JSBI.divide(numerator, denominator)
)
return [output, new Exchange(inputReserve.add(inputAmount), outputReserve.subtract(output), this.address)]
}

constructor(pair: [Token, Token], balances: [BigintIsh, BigintIsh]) {
const balancesParsed = balances.map(balance => parseBigintIsh(balance))
const inOrder = pair[0].address < pair[1].address
const orderedPair = (inOrder ? pair : pair.slice().reverse()) as [Token, Token]
const orderedBalances = (inOrder ? balancesParsed : balancesParsed.slice().reverse()) as [JSBI, JSBI]
Exchange.validate(orderedPair, orderedBalances)
public getInputAmount(outputAmount: TokenAmount): [TokenAmount, Exchange] {
invariant(outputAmount.token.equals(this.token0) || outputAmount.token.equals(this.token1), 'TOKEN')
invariant(JSBI.greaterThan(outputAmount.raw, ZERO), 'ZERO')
invariant(JSBI.greaterThan(this.reserve0.raw, ZERO), 'ZERO')
invariant(JSBI.greaterThan(this.reserve1.raw, ZERO), 'ZERO')

this.pair = orderedPair
this.balances = orderedBalances
const inputReserve = outputAmount.token.equals(this.reserve0.token) ? this.reserve1 : this.reserve0
const outputReserve = outputAmount.token.equals(this.reserve0.token) ? this.reserve0 : this.reserve1
const numerator = JSBI.multiply(JSBI.multiply(inputReserve.raw, outputAmount.raw), _1000)
const denominator = JSBI.multiply(JSBI.subtract(outputReserve.raw, outputAmount.raw), _997)
const input = new TokenAmount(
outputAmount.token.equals(this.token0) ? this.token1 : this.token0,
JSBI.add(JSBI.divide(numerator, denominator), ONE)
)
return [input, new Exchange(inputReserve.add(input), outputReserve.subtract(outputAmount), this.address)]
}
}
111 changes: 0 additions & 111 deletions src/entities/fractions.ts

This file was deleted.

Loading

0 comments on commit 0e80185

Please sign in to comment.