Commit 7038e3ab authored by Noah Zinsmeister's avatar Noah Zinsmeister

setup v2

parent 57c65849
{
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "./tsconfig.json"
},
"plugins": ["@typescript-eslint"],
"extends": ["plugin:@typescript-eslint/recommended", "prettier", "prettier/@typescript-eslint"],
"rules": {
"@typescript-eslint/class-name-casing": "off"
}
}
name: CI
on:
pull_request:
branches:
- v2
push:
branches:
- v2
jobs:
test:
strategy:
matrix:
node: ['10.x', '12.x']
os: [ubuntu-latest, macOS-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v1
- uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node }}
- run: npm install -g yarn
- id: yarn-cache
run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/cache@v1
with:
path: ${{ steps.yarn-cache.outputs.dir }}
key: ${{ matrix.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ matrix.os }}-yarn-
- run: yarn
- run: yarn lint
- run: yarn build
- run: yarn test
dist/
\ No newline at end of file
dist
\ No newline at end of file
src/
.eslintrc
.gitignore
.prettierrc
.travis.yml
jest.config.js
*.md
tsconfig.json
yarn.lock
\ No newline at end of file
{
"semi": false,
"singleQuote": true,
"printWidth": 120
}
language: node_js
node_js:
- '10'
cache:
directories:
- node_modules
install: yarn
script:
- yarn check:all
- yarn test
- yarn build
after_script:
- yarn coverage
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
......@@ -645,7 +645,7 @@ the "copyright" line and a pointer to where the full notice is found.
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
......@@ -664,11 +664,11 @@ might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
<https://www.gnu.org/licenses/why-not-lgpl.html>.
\ No newline at end of file
# Uniswap SDK
[![npm (scoped)](https://img.shields.io/npm/v/@uniswap/sdk.svg)](https://www.npmjs.com/package/@uniswap/sdk)
[![Build Status](https://travis-ci.org/Uniswap/uniswap-sdk.svg?branch=master)](https://travis-ci.org/Uniswap/uniswap-sdk)
[![Coverage Status](https://coveralls.io/repos/github/Uniswap/uniswap-sdk/badge.svg?branch=master)](https://coveralls.io/github/Uniswap/uniswap-sdk?branch=master)
[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)
## Documentation is available at [docs.uniswap.io](https://docs.uniswap.io/sdk-documentation/get-started)
[![Actions Status](https://github.com/Uniswap/uniswap-sdk/workflows/CI/badge.svg)](https://github.com/Uniswap/uniswap-sdk)
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node'
}
{
"name": "@uniswap/sdk",
"version": "1.0.0-beta.4",
"description": "An SDK for building applications on top of the Uniswap exchange protocol.",
"license": "GPL-3.0-or-later",
"version": "2.0.0",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"files": [
"dist"
],
"repository": "https://github.com/Uniswap/uniswap-sdk.git",
"keywords": [
"Uniswap",
"Ethereum",
"SDK"
"uniswap",
"ethereum"
],
"main": "./dist/index.js",
"module": "dist/sdk.esm.js",
"scripts": {
"lint:base": "yarn eslint './src/**/*.ts'",
"format:base": "yarn prettier './src/**/*.ts'",
"fix:lint": "yarn lint:base --fix",
"fix:format": "yarn format:base --write",
"fix:all": "yarn fix:lint && yarn fix:format",
"check:lint": "yarn lint:base",
"check:format": "yarn format:base --check",
"check:all": "yarn check:lint && yarn check:format",
"prebuild": "rm -rf dist/",
"build": "tsc",
"watch": "yarn prebuild && tsc --watch",
"test": "jest",
"coverage": "jest --coverage --coverageReporters=text-lcov | coveralls"
"lint": "tsdx lint",
"build": "tsdx build",
"start": "tsdx watch",
"test": "tsdx test"
},
"repository": {
"type": "git",
"url": "git+https://github.com/Uniswap/uniswap-sdk.git"
},
"author": "Noah Zinsmeister <noahwz@gmail.com>",
"license": "GPL-3.0-or-later",
"devDependencies": {
"@types/jest": "^24.0.13",
"@types/lodash.clonedeepwith": "^4.5.6",
"@typescript-eslint/eslint-plugin": "^1.9.0",
"@typescript-eslint/parser": "^1.9.0",
"coveralls": "^3.0.3",
"eslint": "^5.16.0",
"eslint-config-prettier": "^4.3.0",
"jest": "^24.8.0",
"prettier": "^1.17.1",
"ts-jest": "^24.0.2",
"typescript": "^3.4.5"
"@types/jest": "^24.0.25",
"tsdx": "^0.12.1",
"tslib": "^1.10.0",
"typescript": "^3.7.4"
},
"dependencies": {
"bignumber.js": "^9.0.0",
"ethers": "^4.0.28",
"lodash.clonedeepwith": "^4.5.0"
"prettier": {
"printWidth": 120,
"semi": false,
"singleQuote": true
}
}
import BigNumber from 'bignumber.js'
import { ethers } from 'ethers'
import { BigNumberish } from '../types'
import { ETH } from '../constants'
import { normalizeBigNumberish, getEthToken } from '../_utils'
interface TestCase {
input: BigNumberish
expectedOutput: BigNumber
}
function constructTestCase(input: BigNumberish, expectedOutput: BigNumber): TestCase {
return { input, expectedOutput }
}
function testSuccesses(expectedSuccesses: TestCase[]): void {
test('failures', (): void => {
expectedSuccesses.forEach(
({ input, expectedOutput }: TestCase): void => {
const output: BigNumber = normalizeBigNumberish(input)
expect(output.isEqualTo(expectedOutput)).toBe(true)
}
)
})
}
function testFailures(expectedFailures: BigNumberish[]): void {
test('failures', (): void => {
expectedFailures.forEach(
(expectedFailure: BigNumberish): void => {
expect(
(): void => {
normalizeBigNumberish(expectedFailure)
}
).toThrow()
}
)
})
}
describe('normalizeBigNumberish', (): void => {
describe('string', (): void => {
const expectedSuccesses: TestCase[] = [
constructTestCase('0', new BigNumber('0')),
constructTestCase('1', new BigNumber('1')),
constructTestCase('1.234', new BigNumber('1.234'))
]
const expectedFailures: string[] = ['.', ',', 'a', '0.0.']
testSuccesses(expectedSuccesses)
testFailures(expectedFailures)
})
describe('number', (): void => {
const expectedSuccesses: TestCase[] = [
constructTestCase(0, new BigNumber('0')),
constructTestCase(1, new BigNumber('1')),
constructTestCase(1.234, new BigNumber('1.234'))
]
const expectedFailures: number[] = [NaN, Infinity]
testSuccesses(expectedSuccesses)
testFailures(expectedFailures)
})
describe('BigNumber', (): void => {
const expectedSuccesses: TestCase[] = [
constructTestCase(new BigNumber(0), new BigNumber('0')),
constructTestCase(new BigNumber(1), new BigNumber('1')),
constructTestCase(new BigNumber('1.234'), new BigNumber('1.234'))
]
const expectedFailures: BigNumber[] = [new BigNumber(NaN)]
testSuccesses(expectedSuccesses)
testFailures(expectedFailures)
})
describe('ethers.utils.BigNumber', (): void => {
const expectedSuccesses: TestCase[] = [
constructTestCase(ethers.constants.Zero, new BigNumber('0')),
constructTestCase(ethers.constants.One, new BigNumber('1')),
constructTestCase(ethers.utils.bigNumberify('1234'), new BigNumber('1234')),
constructTestCase(ethers.utils.parseUnits('1.234', 3), new BigNumber('1234'))
]
const expectedFailures: ethers.utils.BigNumber[] = []
testSuccesses(expectedSuccesses)
testFailures(expectedFailures)
})
})
test('getEthToken', (): void => {
expect(getEthToken(1).address).toEqual(ETH)
})
import { BigNumberish, TokenReserves, MarketDetails, OptionalReserves, TradeDetails } from '../types'
import { TRADE_EXACT } from '../constants'
import { getMarketDetails, getTradeDetails } from '../computation'
function constructTokenReserves(
decimals: number,
ethReserveAmount: BigNumberish,
tokenReserveAmount: BigNumberish
): TokenReserves {
return {
token: { decimals },
ethReserve: { token: { decimals: 18 }, amount: ethReserveAmount },
tokenReserve: { token: { decimals }, amount: tokenReserveAmount }
}
}
const DAIReserves: TokenReserves = constructTokenReserves(18, '4039700561005906883487', '1094055210563660633471343')
const USDCReserves: TokenReserves = constructTokenReserves(6, '1076592291503763426634', '292657693901')
function testMarketRates(
inputTokenReserves: OptionalReserves,
outputTokenReserves: OptionalReserves,
expectedMarketRate: string,
expectedMarketRateInverted: string
): void {
test('normal', (): void => {
const marketDetails: MarketDetails = getMarketDetails(inputTokenReserves, outputTokenReserves)
expect(marketDetails.marketRate.rate.toFixed(18)).toBe(expectedMarketRate)
expect(marketDetails.marketRate.rateInverted.toFixed(18)).toBe(expectedMarketRateInverted)
})
test('inverted', (): void => {
const marketDetails: MarketDetails = getMarketDetails(outputTokenReserves, inputTokenReserves)
expect(marketDetails.marketRate.rate.toFixed(18)).toBe(expectedMarketRateInverted)
expect(marketDetails.marketRate.rateInverted.toFixed(18)).toBe(expectedMarketRate)
})
}
describe('getMarketDetails', (): void => {
describe('DAI', (): void => {
const expectedMarketRate = '270.825818409480102284'
const expectedMarketRateInverted = '0.003692410147130181'
testMarketRates(undefined, DAIReserves, expectedMarketRate, expectedMarketRateInverted)
})
describe('USDC', (): void => {
const expectedMarketRate = '0.003678674143683891'
const expectedMarketRateInverted = '271.837069808684359442'
testMarketRates(USDCReserves, undefined, expectedMarketRate, expectedMarketRateInverted)
})
describe('DAI and USDC', (): void => {
const expectedMarketRate = '1.003733954927721392'
const expectedMarketRateInverted = '0.996279935624983143'
testMarketRates(DAIReserves, USDCReserves, expectedMarketRate, expectedMarketRateInverted)
})
})
function testTradeDetails(
tradeExact: TRADE_EXACT,
tradeAmount: BigNumberish,
marketDetails: MarketDetails,
expectedInputValue: string,
expectedOutputValue: string,
expectedExecutionRate: string,
expectedExecutionRateInverted: string,
expectedMarketRateSlippage: string,
expectedExecutionRateSlippage: string
): void {
test('test trade', (): void => {
const tradeDetails: TradeDetails = getTradeDetails(tradeExact, tradeAmount, marketDetails)
expect(tradeDetails.inputAmount.amount.toFixed(0)).toBe(expectedInputValue)
expect(tradeDetails.outputAmount.amount.toFixed(0)).toBe(expectedOutputValue)
expect(tradeDetails.executionRate.rate.toFixed(18)).toBe(expectedExecutionRate)
expect(tradeDetails.executionRate.rateInverted.toFixed(18)).toBe(expectedExecutionRateInverted)
expect(tradeDetails.marketRateSlippage.toFixed(18)).toBe(expectedMarketRateSlippage)
expect(tradeDetails.executionRateSlippage.toFixed(18)).toBe(expectedExecutionRateSlippage)
})
}
describe('getTradeDetails', (): void => {
describe('ETH for DAI exact output', (): void => {
const marketDetails: MarketDetails = getMarketDetails(undefined, DAIReserves)
const expectedInputValue = '370385925334764803'
const expectedOutputValue = '100000000000000000000'
const expectedExecutionRate = '269.988660907180258319'
const expectedExecutionRateInverted = '0.003703859253347648'
const expectedMarketRateSlippage = '1.830727602963479922'
const expectedExecutionRateSlippage = '30.911288562381013644'
testTradeDetails(
TRADE_EXACT.OUTPUT,
expectedOutputValue,
marketDetails,
expectedInputValue,
expectedOutputValue,
expectedExecutionRate,
expectedExecutionRateInverted,
expectedMarketRateSlippage,
expectedExecutionRateSlippage
)
})
describe('DAI for ETH exact output', (): void => {
const marketDetails: MarketDetails = getMarketDetails(DAIReserves, undefined)
const expectedInputValue = '271708000072010674989'
const expectedOutputValue = '1000000000000000000'
const expectedExecutionRate = '0.003680421628126409'
const expectedExecutionRateInverted = '271.708000072010674989'
const expectedMarketRateSlippage = '4.957694170529155578'
const expectedExecutionRateSlippage = '32.468004707141566275'
testTradeDetails(
TRADE_EXACT.OUTPUT,
expectedOutputValue,
marketDetails,
expectedInputValue,
expectedOutputValue,
expectedExecutionRate,
expectedExecutionRateInverted,
expectedMarketRateSlippage,
expectedExecutionRateSlippage
)
})
describe('DAI for USDC exact input', (): void => {
const marketDetails: MarketDetails = getMarketDetails(DAIReserves, USDCReserves)
const expectedInputValue = '1000000000000000000'
const expectedOutputValue = '997716'
const expectedExecutionRate = '0.997716000000000000'
const expectedExecutionRateInverted = '1.002289228598118102'
const expectedMarketRateSlippage = '0.086538655703617094'
const expectedExecutionRateSlippage = '59.955677479843185050'
testTradeDetails(
TRADE_EXACT.INPUT,
expectedInputValue,
marketDetails,
expectedInputValue,
expectedOutputValue,
expectedExecutionRate,
expectedExecutionRateInverted,
expectedMarketRateSlippage,
expectedExecutionRateSlippage
)
})
})
import { Token, TokenReservesNormalized } from '../types'
import { ETH as _ETH } from '../constants'
import { getTokenReserves } from '../data'
import { ethers } from 'ethers'
const ETH: Token = {
chainId: 1,
address: _ETH,
decimals: 18
}
const DAI: Token = {
chainId: 1,
address: '0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359',
decimals: 18
}
const DAI_EXCHANGE: Token = {
chainId: 1,
address: '0x09cabEC1eAd1c0Ba254B09efb3EE13841712bE14',
decimals: 18
}
describe('getTokenReserves', (): void => {
jest.setTimeout(10000) // 10 seconds
test('DAI', async (done: jest.DoneCallback): Promise<void> => {
const tokenReserves: TokenReservesNormalized = await getTokenReserves(DAI.address as string)
const tokenReservesWithProvider: TokenReservesNormalized = await getTokenReserves(
DAI.address as string,
ethers.getDefaultProvider()
)
expect(tokenReserves.token).toEqual(tokenReservesWithProvider.token)
expect(tokenReserves.token).toEqual(DAI)
expect(tokenReserves.exchange).toEqual(DAI_EXCHANGE)
expect(tokenReserves.ethReserve.token).toEqual(ETH)
expect(tokenReserves.ethReserve.amount).toBeTruthy()
expect(tokenReserves.tokenReserve.token).toEqual(DAI)
expect(tokenReserves.tokenReserve.amount).toBeTruthy()
done()
})
})
import BigInteger from 'bignumber.js'
import { FlexibleFormat, FormatSignificantOptions, FormatFixedOptions } from '../types'
import { formatSignificant, formatSignificantDecimals, formatFixed, formatFixedDecimals } from '../format'
import { FIXED_UNDERFLOW_BEHAVIOR, _ROUNDING_MODE } from '../constants'
function constructFormatSignificantOptions(
significantDigits: number,
roundingMode: BigInteger.RoundingMode = _ROUNDING_MODE,
forceIntegerSignificance: boolean = false,
format: FlexibleFormat = false
): FormatSignificantOptions {
return {
significantDigits,
roundingMode,
forceIntegerSignificance,
format
}
}
function constructFormatFixedOptions(
decimalPlaces: number,
roundingMode: BigInteger.RoundingMode = _ROUNDING_MODE,
dropTrailingZeros: boolean = true,
underflowBehavior: FIXED_UNDERFLOW_BEHAVIOR = FIXED_UNDERFLOW_BEHAVIOR.ONE_DIGIT,
format: FlexibleFormat = false
): FormatFixedOptions {
return {
decimalPlaces,
roundingMode,
dropTrailingZeros,
underflowBehavior,
format
}
}
describe('formatSignificant', (): void => {
test('regular', (): void => {
const formatted = formatSignificant('1.234', constructFormatSignificantOptions(2))
expect(formatted).toBe('1.2')
})
test('decimal', (): void => {
const formatted = formatSignificantDecimals('1234', 3, constructFormatSignificantOptions(2))
expect(formatted).toBe('1.2')
})
})
describe('formatFixed', (): void => {
test('regular', (): void => {
const formatted = formatFixed('1.234', constructFormatFixedOptions(1))
expect(formatted).toBe('1.2')
})
test('regular', (): void => {
const formatted = formatFixedDecimals('1234', 3, constructFormatFixedOptions(1))
expect(formatted).toBe('1.2')
})
})
import { TradeDetails } from '../types'
import { tradeExactEthForTokens } from '../orchestration'
import { TRADE_TYPE, TRADE_EXACT } from '../constants'
describe('tradeExactEthForTokens', (): void => {
jest.setTimeout(10000) // 10 seconds
test('DAI', async (done: jest.DoneCallback): Promise<void> => {
const tradeDetails: TradeDetails = await tradeExactEthForTokens(
'0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359',
'1000000000000000000'
)
expect(tradeDetails.tradeType).toEqual(TRADE_TYPE.ETH_TO_TOKEN)
expect(tradeDetails.tradeExact).toEqual(TRADE_EXACT.INPUT)
expect(tradeDetails.inputAmount.amount.toFixed()).toEqual('1000000000000000000')
expect(tradeDetails.outputAmount.amount).toBeTruthy()
expect(tradeDetails.executionRate.rate).toBeTruthy()
expect(tradeDetails.executionRate.rateInverted).toBeTruthy()
expect(tradeDetails.marketRateSlippage).toBeTruthy()
expect(tradeDetails.executionRateSlippage).toBeTruthy()
done()
})
})
import { TradeDetails, ExecutionDetails } from '../types'
import { TRADE_METHODS, TRADE_METHOD_IDS } from '../constants'
import { tradeExactEthForTokens } from '../orchestration'
import { getExecutionDetails } from '../transact'
describe('tradeExactEthForTokens', (): void => {
jest.setTimeout(10000) // 10 seconds
test('DAI', async (done: jest.DoneCallback): Promise<void> => {
const tradeDetails: TradeDetails = await tradeExactEthForTokens(
'0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359',
'1000000000000000000'
)
const executionDetails: ExecutionDetails = getExecutionDetails(tradeDetails)
expect(executionDetails.exchangeAddress).toEqual('0x09cabEC1eAd1c0Ba254B09efb3EE13841712bE14')
expect(executionDetails.methodName).toEqual(TRADE_METHODS.ethToTokenSwapInput)
expect(executionDetails.methodId).toEqual(TRADE_METHOD_IDS[TRADE_METHODS.ethToTokenSwapInput])
expect(executionDetails.value.toFixed()).toEqual('1000000000000000000')
done()
})
})
import BigNumber from 'bignumber.js'
import { ethers } from 'ethers'
import { BigNumberish, Token } from '../types'
import { _0, _MAX_UINT8, _MAX_UINT256, ETH } from '../constants'
function ensureUInt8(number: number): void {
if (!Number.isInteger(number) || number < 0 || number > _MAX_UINT8) {
throw Error(`Passed number '${number}' is not a valid uint8.`)
}
}
export function ensureAllUInt8(numbers: number[]): void {
numbers.forEach(ensureUInt8)
}
function ensureUInt256(bigNumber: BigNumber): void {
if (!bigNumber.isInteger() || bigNumber.isLessThan(_0) || bigNumber.isGreaterThan(_MAX_UINT256)) {
throw Error(`Passed bigNumber '${bigNumber}' is not a valid uint256.`)
}
}
export function ensureAllUInt256(bigNumbers: BigNumber[]): void {
bigNumbers.forEach(ensureUInt256)
}
export function ensureBoundedInteger(number: number, bounds: number | number[]): void {
const [minimum, maximum]: [number, number] = typeof bounds === 'number' ? [0, bounds] : [bounds[0], bounds[1]]
if (!Number.isInteger(number) || number < minimum || number > maximum) {
throw Error(`Passed number '${number}' is not an integer between '${minimum}' and '${maximum}' (inclusive).`)
}
}
export function normalizeBigNumberish(bigNumberish: BigNumberish): BigNumber {
const bigNumber: BigNumber = BigNumber.isBigNumber(bigNumberish)
? bigNumberish
: new BigNumber(bigNumberish.toString())
if (!bigNumber.isFinite()) {
throw Error(`Passed bigNumberish '${bigNumberish}' of type '${typeof bigNumberish}' is not valid.`)
}
return bigNumber
}
export function normalizeAddress(address: string): string {
return ethers.utils.getAddress(address.toLowerCase())
}
export function getEthToken(chainId?: number): Token {
return {
...(chainId ? { chainId } : {}),
address: ETH,
decimals: 18
}
}
import BigNumber from 'bignumber.js'
import { TokenAmountNormalized, _DecimalRate, _AnyRate } from '../types'
import { _1, _10 } from '../constants'
export function calculateDecimalRate(
numerator: TokenAmountNormalized,
denominator: TokenAmountNormalized,
keepAsDecimal: boolean = false
): _AnyRate {
const largerScalar: BigNumber = _10.exponentiatedBy(
new BigNumber(Math.abs(numerator.token.decimals - denominator.token.decimals))
)
// since exponentiating with negative numbers rounds, we have to manually calculate the smaller of the scalars
const smallerScalar: BigNumber = largerScalar.isEqualTo(_1)
? _1
: new BigNumber(`0.${'0'.repeat(largerScalar.toFixed().length - 2)}1`)
const invertedIsLarger: boolean = numerator.token.decimals - denominator.token.decimals > 0
const decimalRate: _DecimalRate = {
numerator: numerator.amount,
denominator: denominator.amount,
decimalScalar: invertedIsLarger ? smallerScalar : largerScalar,
decimalScalarInverted: invertedIsLarger ? largerScalar : smallerScalar
}
return keepAsDecimal
? decimalRate
: {
rate: decimalRate.numerator.multipliedBy(decimalRate.decimalScalar).dividedBy(decimalRate.denominator),
rateInverted: decimalRate.denominator
.multipliedBy(decimalRate.decimalScalarInverted)
.dividedBy(decimalRate.numerator)
}
}
export * from './market'
export * from './trade'
import BigNumber from 'bignumber.js'
import {
TokenAmount,
TokenAmountNormalized,
TokenReserves,
TokenReservesNormalized,
areTokenReservesNormalized,
areETHReserves,
areTokenReserves,
OptionalReserves,
NormalizedReserves,
Rate,
MarketDetails,
_ParsedOptionalReserves,
_DecimalRate,
_AnyRate
} from '../types'
import { TRADE_TYPE } from '../constants'
import { normalizeBigNumberish, ensureAllUInt8, ensureAllUInt256, getEthToken } from '../_utils'
import { calculateDecimalRate } from './_utils'
function normalizeTokenAmount(tokenAmount: TokenAmount): TokenAmountNormalized {
ensureAllUInt8([tokenAmount.token.decimals])
const normalizedAmount: BigNumber = normalizeBigNumberish(tokenAmount.amount)
ensureAllUInt256([normalizedAmount])
return {
token: { ...tokenAmount.token },
amount: normalizedAmount
}
}
function normalizeTokenReserves(tokenReserves: TokenReserves): TokenReservesNormalized {
ensureAllUInt8([tokenReserves.token.decimals])
return {
token: { ...tokenReserves.token },
...(tokenReserves.exchange ? { exchange: { ...tokenReserves.exchange } } : {}),
ethReserve: normalizeTokenAmount(tokenReserves.ethReserve),
tokenReserve: normalizeTokenAmount(tokenReserves.tokenReserve)
}
}
function parseOptionalReserves(
optionalReservesInput: OptionalReserves,
optionalReservesOutput: OptionalReserves
): _ParsedOptionalReserves {
if (areTokenReserves(optionalReservesInput) && areTokenReserves(optionalReservesOutput)) {
return {
tradeType: TRADE_TYPE.TOKEN_TO_TOKEN,
inputReserves: normalizeTokenReserves(optionalReservesInput),
outputReserves: normalizeTokenReserves(optionalReservesOutput)
}
} else if (areTokenReserves(optionalReservesInput) && !areTokenReserves(optionalReservesOutput)) {
return {
tradeType: TRADE_TYPE.TOKEN_TO_ETH,
inputReserves: normalizeTokenReserves(optionalReservesInput),
outputReserves: areETHReserves(optionalReservesOutput)
? optionalReservesOutput
: {
token: getEthToken(optionalReservesInput.token.chainId)
}
}
} else if (!areTokenReserves(optionalReservesInput) && areTokenReserves(optionalReservesOutput)) {
return {
tradeType: TRADE_TYPE.ETH_TO_TOKEN,
inputReserves: areETHReserves(optionalReservesInput)
? optionalReservesInput
: {
token: getEthToken(optionalReservesOutput.token.chainId)
},
outputReserves: normalizeTokenReserves(optionalReservesOutput)
}
} else {
throw Error('optionalReservesInput, optionalReservesOutput, or both must be defined.')
}
}
// calculates the market rate for ETH_TO_TOKEN or TOKEN_TO_ETH trades
function getMarketRate(tradeType: TRADE_TYPE, reserves: NormalizedReserves, keepAsDecimal?: boolean): _AnyRate {
if (!areTokenReservesNormalized(reserves)) {
throw Error
}
const numerator: TokenAmountNormalized =
tradeType === TRADE_TYPE.ETH_TO_TOKEN ? reserves.tokenReserve : reserves.ethReserve
const denominator: TokenAmountNormalized =
tradeType === TRADE_TYPE.ETH_TO_TOKEN ? reserves.ethReserve : reserves.tokenReserve
return calculateDecimalRate(numerator, denominator, keepAsDecimal)
}
// note: rounds rates to 18 decimal places
export function getMarketDetails(
optionalReservesInput: OptionalReserves,
optionalReservesOutput: OptionalReserves
): MarketDetails {
const { tradeType, inputReserves, outputReserves }: _ParsedOptionalReserves = parseOptionalReserves(
optionalReservesInput,
optionalReservesOutput
)
if (tradeType === TRADE_TYPE.TOKEN_TO_TOKEN) {
const {
numerator: numeratorInput,
denominator: denominatorInput,
decimalScalar: decimalScalarInput,
decimalScalarInverted: decimalScalarInvertedInput
}: _DecimalRate = getMarketRate(TRADE_TYPE.TOKEN_TO_ETH, inputReserves, true) as _DecimalRate
const {
numerator: numeratorOutput,
denominator: denominatorOutput,
decimalScalar: decimalScalarOutput,
decimalScalarInverted: decimalScalarInvertedOutput
}: _DecimalRate = getMarketRate(TRADE_TYPE.ETH_TO_TOKEN, outputReserves, true) as _DecimalRate
const marketRate: BigNumber = numeratorInput
.multipliedBy(decimalScalarInput)
.multipliedBy(numeratorOutput)
.multipliedBy(decimalScalarOutput)
.dividedBy(denominatorInput.multipliedBy(denominatorOutput))
const marketRateInverted: BigNumber = denominatorInput
.multipliedBy(decimalScalarInvertedInput)
.multipliedBy(denominatorOutput)
.multipliedBy(decimalScalarInvertedOutput)
.dividedBy(numeratorInput.multipliedBy(numeratorOutput))
return {
tradeType,
inputReserves,
outputReserves,
marketRate: { rate: marketRate, rateInverted: marketRateInverted }
}
} else {
const reserves: TokenReservesNormalized = (tradeType === TRADE_TYPE.ETH_TO_TOKEN
? outputReserves
: inputReserves) as TokenReservesNormalized
return {
tradeType,
inputReserves,
outputReserves,
marketRate: getMarketRate(tradeType, reserves) as Rate
}
}
}
import BigNumber from 'bignumber.js'
import cloneDeepWith from 'lodash.clonedeepwith'
import {
BigNumberish,
TokenAmountNormalized,
areTokenReservesNormalized,
NormalizedReserves,
Rate,
MarketDetails,
TradeDetails,
_PartialTradeDetails
} from '../types'
import { _0, _1, _997, _1000, TRADE_TYPE, TRADE_EXACT, _10000 } from '../constants'
import { normalizeBigNumberish, ensureAllUInt256 } from '../_utils'
import { calculateDecimalRate } from './_utils'
import { getMarketDetails } from './market'
// emulates the uniswap smart contract logic
function getInputPrice(inputAmount: BigNumber, inputReserve: BigNumber, outputReserve: BigNumber): BigNumber {
ensureAllUInt256([inputAmount, inputReserve, outputReserve])
if (inputReserve.isLessThanOrEqualTo(_0) || outputReserve.isLessThanOrEqualTo(_0)) {
throw Error(`Both inputReserve '${inputReserve}' and outputReserve '${outputReserve}' must be non-zero.`)
}
const inputAmountWithFee: BigNumber = inputAmount.multipliedBy(_997)
const numerator: BigNumber = inputAmountWithFee.multipliedBy(outputReserve)
const denominator: BigNumber = inputReserve.multipliedBy(_1000).plus(inputAmountWithFee)
const outputAmount = numerator.dividedToIntegerBy(denominator)
ensureAllUInt256([inputAmountWithFee, numerator, denominator, outputAmount])
return outputAmount
}
// emulates the uniswap smart contract logic
function getOutputPrice(outputAmount: BigNumber, inputReserve: BigNumber, outputReserve: BigNumber): BigNumber {
ensureAllUInt256([outputAmount, inputReserve, outputReserve])
if (inputReserve.isLessThanOrEqualTo(_0) || outputReserve.isLessThanOrEqualTo(_0)) {
throw Error(`Both inputReserve '${inputReserve}' and outputReserve '${outputReserve}' must be non-zero.`)
}
const numerator: BigNumber = inputReserve.multipliedBy(outputAmount).multipliedBy(_1000)
const denominator: BigNumber = outputReserve.minus(outputAmount).multipliedBy(_997)
const inputAmount: BigNumber = numerator.dividedToIntegerBy(denominator).plus(_1)
ensureAllUInt256([numerator, denominator, inputAmount])
return inputAmount
}
function getSingleTradeTransput(
tradeType: TRADE_TYPE,
tradeExact: TRADE_EXACT,
tradeAmount: BigNumber,
reserves: NormalizedReserves
): BigNumber {
if (!areTokenReservesNormalized(reserves)) {
throw Error
}
const inputReserve: BigNumber =
tradeType === TRADE_TYPE.ETH_TO_TOKEN ? reserves.ethReserve.amount : reserves.tokenReserve.amount
const outputReserve: BigNumber =
tradeType === TRADE_TYPE.ETH_TO_TOKEN ? reserves.tokenReserve.amount : reserves.ethReserve.amount
const calculatedAmount: BigNumber =
tradeExact === TRADE_EXACT.INPUT
? getInputPrice(tradeAmount, inputReserve, outputReserve)
: getOutputPrice(tradeAmount, inputReserve, outputReserve)
return calculatedAmount
}
function customizer(value: BigNumber): BigNumber | void {
if (BigNumber.isBigNumber(value)) {
return new BigNumber(value)
}
}
// gets the corresponding input/output amount for the passed output/input amount
function getTradeTransput(
tradeType: TRADE_TYPE,
tradeExact: TRADE_EXACT,
tradeAmount: BigNumber,
inputReserves: NormalizedReserves,
outputReserves: NormalizedReserves
): _PartialTradeDetails {
const inputReservesPost: NormalizedReserves = cloneDeepWith(inputReserves, customizer)
const outputReservesPost: NormalizedReserves = cloneDeepWith(outputReserves, customizer)
if (tradeType === TRADE_TYPE.TOKEN_TO_TOKEN) {
if (!areTokenReservesNormalized(inputReservesPost) || !areTokenReservesNormalized(outputReservesPost)) {
throw Error
}
if (tradeExact === TRADE_EXACT.INPUT) {
const intermediateTransput: BigNumber = getSingleTradeTransput(
TRADE_TYPE.TOKEN_TO_ETH,
TRADE_EXACT.INPUT,
tradeAmount,
inputReserves
)
const finalTransput: BigNumber = getSingleTradeTransput(
TRADE_TYPE.ETH_TO_TOKEN,
TRADE_EXACT.INPUT,
intermediateTransput,
outputReserves
)
inputReservesPost.ethReserve.amount = inputReservesPost.ethReserve.amount.minus(intermediateTransput)
inputReservesPost.tokenReserve.amount = inputReservesPost.tokenReserve.amount.plus(tradeAmount)
outputReservesPost.ethReserve.amount = outputReservesPost.ethReserve.amount.plus(intermediateTransput)
outputReservesPost.tokenReserve.amount = outputReservesPost.tokenReserve.amount.minus(finalTransput)
return {
transput: finalTransput,
inputReservesPost,
outputReservesPost
}
} else {
const intermediateTransput: BigNumber = getSingleTradeTransput(
TRADE_TYPE.ETH_TO_TOKEN,
TRADE_EXACT.OUTPUT,
tradeAmount,
outputReserves
)
const finalTransput: BigNumber = getSingleTradeTransput(
TRADE_TYPE.TOKEN_TO_ETH,
TRADE_EXACT.OUTPUT,
intermediateTransput,
inputReserves
)
inputReservesPost.ethReserve.amount = inputReservesPost.ethReserve.amount.minus(intermediateTransput)
inputReservesPost.tokenReserve.amount = inputReservesPost.tokenReserve.amount.plus(finalTransput)
outputReservesPost.ethReserve.amount = outputReservesPost.ethReserve.amount.plus(intermediateTransput)
outputReservesPost.tokenReserve.amount = outputReservesPost.tokenReserve.amount.minus(tradeAmount)
return {
transput: finalTransput,
inputReservesPost,
outputReservesPost
}
}
} else {
const reserves: NormalizedReserves = tradeType === TRADE_TYPE.ETH_TO_TOKEN ? outputReserves : inputReserves
const finalTransput: BigNumber = getSingleTradeTransput(tradeType, tradeExact, tradeAmount, reserves)
if (tradeType === TRADE_TYPE.ETH_TO_TOKEN) {
if (!areTokenReservesNormalized(outputReservesPost)) {
throw Error
}
outputReservesPost.ethReserve.amount = outputReservesPost.ethReserve.amount.plus(
tradeExact === TRADE_EXACT.INPUT ? tradeAmount : finalTransput
)
outputReservesPost.tokenReserve.amount = outputReservesPost.tokenReserve.amount.minus(
tradeExact === TRADE_EXACT.INPUT ? finalTransput : tradeAmount
)
} else {
if (!areTokenReservesNormalized(inputReservesPost)) {
throw Error
}
inputReservesPost.ethReserve.amount = inputReservesPost.ethReserve.amount.minus(
tradeExact === TRADE_EXACT.INPUT ? finalTransput : tradeAmount
)
inputReservesPost.tokenReserve.amount = inputReservesPost.tokenReserve.amount.plus(
tradeExact === TRADE_EXACT.INPUT ? tradeAmount : finalTransput
)
}
return {
transput: finalTransput,
inputReservesPost,
outputReservesPost
}
}
}
// slippage in basis points, to 18 decimals
function calculateSlippage(baseRate: BigNumber, newRate: BigNumber): BigNumber {
const difference: BigNumber = baseRate.minus(newRate).absoluteValue()
return difference.multipliedBy(_10000).dividedBy(baseRate)
}
export function getTradeDetails(
tradeExact: TRADE_EXACT,
_tradeAmount: BigNumberish,
marketDetails: MarketDetails
): TradeDetails {
const tradeAmount: BigNumber = normalizeBigNumberish(_tradeAmount)
// get other input/output amount
const { transput, inputReservesPost, outputReservesPost }: _PartialTradeDetails = getTradeTransput(
marketDetails.tradeType,
tradeExact,
tradeAmount,
marketDetails.inputReserves,
marketDetails.outputReserves
)
// get input and output amounts
const inputAmount: TokenAmountNormalized = {
token: marketDetails.inputReserves.token,
amount: tradeExact === TRADE_EXACT.INPUT ? tradeAmount : transput
}
const outputAmount: TokenAmountNormalized = {
token: marketDetails.outputReserves.token,
amount: tradeExact === TRADE_EXACT.INPUT ? transput : tradeAmount
}
const marketDetailsPost: MarketDetails = getMarketDetails(inputReservesPost, outputReservesPost)
const executionRate: Rate = calculateDecimalRate(outputAmount, inputAmount) as Rate
const marketRateSlippage: BigNumber = calculateSlippage(
marketDetails.marketRate.rate,
marketDetailsPost.marketRate.rate
)
const executionRateSlippage: BigNumber = calculateSlippage(marketDetails.marketRate.rate, executionRate.rate)
return {
marketDetailsPre: marketDetails,
marketDetailsPost,
tradeType: marketDetails.tradeType,
tradeExact,
inputAmount,
outputAmount,
executionRate,
marketRateSlippage,
executionRateSlippage
}
}
[
{
"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"
}
]
This diff is collapsed.
[
{
"name": "NewExchange",
"inputs": [
{ "type": "address", "name": "token", "indexed": true },
{ "type": "address", "name": "exchange", "indexed": true }
],
"anonymous": false,
"type": "event"
},
{
"name": "initializeFactory",
"outputs": [],
"inputs": [{ "type": "address", "name": "template" }],
"constant": false,
"payable": false,
"type": "function",
"gas": 35725
},
{
"name": "createExchange",
"outputs": [{ "type": "address", "name": "out" }],
"inputs": [{ "type": "address", "name": "token" }],
"constant": false,
"payable": false,
"type": "function",
"gas": 187911
},
{
"name": "getExchange",
"outputs": [{ "type": "address", "name": "out" }],
"inputs": [{ "type": "address", "name": "token" }],
"constant": true,
"payable": false,
"type": "function",
"gas": 715
},
{
"name": "getToken",
"outputs": [{ "type": "address", "name": "out" }],
"inputs": [{ "type": "address", "name": "exchange" }],
"constant": true,
"payable": false,
"type": "function",
"gas": 745
},
{
"name": "getTokenWithId",
"outputs": [{ "type": "address", "name": "out" }],
"inputs": [{ "type": "uint256", "name": "token_id" }],
"constant": true,
"payable": false,
"type": "function",
"gas": 736
},
{
"name": "exchangeTemplate",
"outputs": [{ "type": "address", "name": "out" }],
"inputs": [],
"constant": true,
"payable": false,
"type": "function",
"gas": 633
},
{
"name": "tokenCount",
"outputs": [{ "type": "uint256", "name": "out" }],
"inputs": [],
"constant": true,
"payable": false,
"type": "function",
"gas": 663
}
]
import BigNumber from 'bignumber.js'
import ERC20 from './abis/ERC20.json'
import FACTORY from './abis/FACTORY.json'
import EXCHANGE from './abis/EXCHANGE.json'
//// constants for internal and external use
export const ETH = 'ETH'
export enum SUPPORTED_CHAIN_ID {
Mainnet = 1,
Ropsten = 3,
Rinkeby = 4,
Kovan = 42
}
export const FACTORY_ADDRESS: { [key: number]: string } = {
[SUPPORTED_CHAIN_ID.Mainnet]: '0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95',
[SUPPORTED_CHAIN_ID.Ropsten]: '0x9c83dCE8CA20E9aAF9D3efc003b2ea62aBC08351',
[SUPPORTED_CHAIN_ID.Rinkeby]: '0xf5D915570BC477f9B8D6C0E980aA81757A3AaC36',
[SUPPORTED_CHAIN_ID.Kovan]: '0xD3E51Ef092B2845f10401a0159B2B96e8B6c3D30'
}
export const FACTORY_ABI: string = JSON.stringify(FACTORY)
export const EXCHANGE_ABI: string = JSON.stringify(EXCHANGE)
export enum TRADE_TYPE {
ETH_TO_TOKEN = 'ETH_TO_TOKEN',
TOKEN_TO_ETH = 'TOKEN_TO_ETH',
TOKEN_TO_TOKEN = 'TOKEN_TO_TOKEN'
}
export enum TRADE_EXACT {
INPUT = 'INPUT',
OUTPUT = 'OUTPUT'
}
export enum TRADE_METHODS {
ethToTokenSwapInput = 'ethToTokenSwapInput',
ethToTokenTransferInput = 'ethToTokenTransferInput',
ethToTokenSwapOutput = 'ethToTokenSwapOutput',
ethToTokenTransferOutput = 'ethToTokenTransferOutput',
tokenToEthSwapInput = 'tokenToEthSwapInput',
tokenToEthTransferInput = 'tokenToEthTransferInput',
tokenToEthSwapOutput = 'tokenToEthSwapOutput',
tokenToEthTransferOutput = 'tokenToEthTransferOutput',
tokenToTokenSwapInput = 'tokenToTokenSwapInput',
tokenToTokenTransferInput = 'tokenToTokenTransferInput',
tokenToTokenSwapOutput = 'tokenToTokenSwapOutput',
tokenToTokenTransferOutput = 'tokenToTokenTransferOutput'
}
export const TRADE_METHOD_IDS: { [key: string]: string } = {
[TRADE_METHODS.ethToTokenSwapInput]: '0xf39b5b9b',
[TRADE_METHODS.ethToTokenTransferInput]: '0xad65d76d',
[TRADE_METHODS.ethToTokenSwapOutput]: '0x6b1d4db7',
[TRADE_METHODS.ethToTokenTransferOutput]: '0x0b573638',
[TRADE_METHODS.tokenToEthSwapInput]: '0x95e3c50b',
[TRADE_METHODS.tokenToEthTransferInput]: '0x7237e031',
[TRADE_METHODS.tokenToEthSwapOutput]: '0x013efd8b',
[TRADE_METHODS.tokenToEthTransferOutput]: '0xd4e4841d',
[TRADE_METHODS.tokenToTokenSwapInput]: '0xddf7e1a7',
[TRADE_METHODS.tokenToTokenTransferInput]: '0xf552d91b',
[TRADE_METHODS.tokenToTokenSwapOutput]: '0xb040d545',
[TRADE_METHODS.tokenToTokenTransferOutput]: '0xf3c0efe9'
}
export enum FIXED_UNDERFLOW_BEHAVIOR {
ZERO = 'ZERO',
LESS_THAN = 'LESS_THAN',
ONE_DIGIT = 'ONE_DIGIT'
}
//// constants for internal use
export const _MAX_DECIMAL_PLACES = 18
export const _ROUNDING_MODE = BigNumber.ROUND_HALF_UP
BigNumber.set({ DECIMAL_PLACES: _MAX_DECIMAL_PLACES, ROUNDING_MODE: _ROUNDING_MODE })
export const _0: BigNumber = new BigNumber('0')
export const _1: BigNumber = new BigNumber('1')
export const _10: BigNumber = new BigNumber('10')
export const _997: BigNumber = new BigNumber('997')
export const _1000: BigNumber = new BigNumber('1000')
export const _10000: BigNumber = new BigNumber('10000')
export const _MAX_UINT8 = 255
export const _MAX_UINT256: BigNumber = new BigNumber(
'0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
)
export const _CHAIN_ID_NAME: { [key: number]: string } = {
[SUPPORTED_CHAIN_ID.Mainnet]: 'homestead',
[SUPPORTED_CHAIN_ID.Ropsten]: 'ropsten',
[SUPPORTED_CHAIN_ID.Rinkeby]: 'rinkeby',
[SUPPORTED_CHAIN_ID.Kovan]: 'kovan'
}
export const _ERC20_ABI: string = JSON.stringify(ERC20)
import { ethers } from 'ethers'
import {
ChainIdOrProvider,
isChainId,
isLowLevelProvider,
Token,
TokenReservesNormalized,
_ChainIdAndProvider
} from '../types'
import { ETH, SUPPORTED_CHAIN_ID, FACTORY_ABI, FACTORY_ADDRESS, _CHAIN_ID_NAME, _ERC20_ABI } from '../constants'
import { normalizeBigNumberish, normalizeAddress, getEthToken } from '../_utils'
function getContract(address: string, ABI: string, provider: ethers.providers.Provider): ethers.Contract {
return new ethers.Contract(address, ABI, provider)
}
async function getChainIdAndProvider(chainIdOrProvider: ChainIdOrProvider): Promise<_ChainIdAndProvider> {
// if a chainId is provided, get a default provider for it
if (isChainId(chainIdOrProvider)) {
return {
chainId: chainIdOrProvider,
provider: ethers.getDefaultProvider(_CHAIN_ID_NAME[chainIdOrProvider])
}
}
// if a provider is provided, fetch the chainId from it
else {
const provider: ethers.providers.Provider = isLowLevelProvider(chainIdOrProvider)
? new ethers.providers.Web3Provider(chainIdOrProvider)
: chainIdOrProvider
const { chainId }: ethers.utils.Network = await provider.getNetwork()
if (!(chainId in SUPPORTED_CHAIN_ID)) {
throw Error(`chainId ${chainId} is not valid.`)
}
return {
chainId,
provider
}
}
}
async function getToken(tokenAddress: string, chainIdAndProvider: _ChainIdAndProvider): Promise<Token> {
if (tokenAddress === ETH) {
return getEthToken(chainIdAndProvider.chainId)
} else {
const ERC20Contract: ethers.Contract = getContract(tokenAddress, _ERC20_ABI, chainIdAndProvider.provider)
const decimals: number = await ERC20Contract.decimals()
return {
chainId: chainIdAndProvider.chainId,
address: ERC20Contract.address,
decimals
}
}
}
export async function getTokenReserves(
tokenAddress: string,
chainIdOrProvider: ChainIdOrProvider = 1
): Promise<TokenReservesNormalized> {
// validate input arguments
const normalizedTokenAddress: string = normalizeAddress(tokenAddress)
const chainIdAndProvider: _ChainIdAndProvider = await getChainIdAndProvider(chainIdOrProvider)
// fetch tokens (async)
const ethTokenPromise: Promise<Token> = getToken(ETH, chainIdAndProvider)
const tokenPromise: Promise<Token> = getToken(normalizedTokenAddress, chainIdAndProvider)
// get contracts
const factoryContract: ethers.Contract = getContract(
FACTORY_ADDRESS[chainIdAndProvider.chainId],
FACTORY_ABI,
chainIdAndProvider.provider
)
const tokenContract: ethers.Contract = getContract(normalizedTokenAddress, _ERC20_ABI, chainIdAndProvider.provider)
// fetch exchange adddress (blocking async)
const exchangeAddress: string = await factoryContract.getExchange(normalizedTokenAddress)
// fetch exchange token and eth/token balances (async)
const exchangeTokenPromise: Promise<Token> = getToken(exchangeAddress, chainIdAndProvider)
const ethBalancePromise: Promise<ethers.utils.BigNumber> = chainIdAndProvider.provider.getBalance(exchangeAddress)
const tokenBalancePromise: Promise<ethers.utils.BigNumber> = tokenContract.balanceOf(exchangeAddress)
const [ethToken, token, exchangeToken, ethBalance, tokenBalance]: [
Token,
Token,
Token,
ethers.utils.BigNumber,
ethers.utils.BigNumber
] = await Promise.all([ethTokenPromise, tokenPromise, exchangeTokenPromise, ethBalancePromise, tokenBalancePromise])
const ethReserve = { token: ethToken, amount: normalizeBigNumberish(ethBalance) }
const tokenReserve = { token, amount: normalizeBigNumberish(tokenBalance) }
return { token, exchange: exchangeToken, ethReserve, tokenReserve }
}
import BigNumber from 'bignumber.js'
import { BigNumberish, FlexibleFormat, isFormat, FormatSignificantOptions, FormatFixedOptions } from '../types'
import { _0, _10, _MAX_DECIMAL_PLACES, _ROUNDING_MODE, FIXED_UNDERFLOW_BEHAVIOR } from '../constants'
import { normalizeBigNumberish, ensureBoundedInteger, ensureAllUInt256, ensureAllUInt8 } from '../_utils'
function _format(
bigNumber: BigNumber,
decimalPlaces: number,
roundingMode: BigNumber.RoundingMode = _ROUNDING_MODE,
format: FlexibleFormat
): string {
return isFormat(format) || format
? bigNumber.toFormat(decimalPlaces, roundingMode, isFormat(format) ? format : undefined)
: bigNumber.toFixed(decimalPlaces, roundingMode)
}
// bignumberish is converted to significantDigits, then cast back as a bignumber and formatted, dropping trailing 0s
export function formatSignificant(bigNumberish: BigNumberish, options?: FormatSignificantOptions): string {
const { significantDigits = 6, roundingMode = _ROUNDING_MODE, forceIntegerSignificance = true, format = false } =
options || {}
const bigNumber: BigNumber = normalizeBigNumberish(bigNumberish)
ensureBoundedInteger(significantDigits, [1, _MAX_DECIMAL_PLACES])
const minimumSignificantDigits: number = forceIntegerSignificance ? bigNumber.integerValue().toFixed().length : 0
const preciseBigNumber: BigNumber = new BigNumber(
bigNumber.toPrecision(Math.max(minimumSignificantDigits, significantDigits))
)
return _format(preciseBigNumber, preciseBigNumber.decimalPlaces(), roundingMode, format)
}
export function formatFixed(bigNumberish: BigNumberish, options?: FormatFixedOptions): string {
const {
decimalPlaces = 4,
roundingMode = _ROUNDING_MODE,
dropTrailingZeros = true,
underflowBehavior = FIXED_UNDERFLOW_BEHAVIOR.ONE_DIGIT,
format = false
} = options || {}
const bigNumber: BigNumber = normalizeBigNumberish(bigNumberish)
ensureBoundedInteger(decimalPlaces, _MAX_DECIMAL_PLACES)
const minimumNonZeroValue: BigNumber = new BigNumber(decimalPlaces === 0 ? '0.5' : `0.${'0'.repeat(decimalPlaces)}5`)
if (bigNumber.isLessThan(minimumNonZeroValue)) {
switch (underflowBehavior) {
case FIXED_UNDERFLOW_BEHAVIOR.ZERO: {
return _format(_0, dropTrailingZeros ? 0 : decimalPlaces, undefined, format)
}
case FIXED_UNDERFLOW_BEHAVIOR.LESS_THAN: {
return `<${_format(minimumNonZeroValue, minimumNonZeroValue.decimalPlaces(), undefined, format)}`
}
case FIXED_UNDERFLOW_BEHAVIOR.ONE_DIGIT: {
const newBigNumber = new BigNumber(bigNumber.toPrecision(1))
return _format(newBigNumber, newBigNumber.decimalPlaces(), undefined, format)
}
default: {
throw Error(`Passed FIXED_UNDERFLOW_BEHAVIOR ${underflowBehavior} is not valid.`)
}
}
} else {
const newDecimalPlaces = dropTrailingZeros
? new BigNumber(bigNumber.toFixed(decimalPlaces, roundingMode)).decimalPlaces()
: decimalPlaces
return _format(bigNumber, newDecimalPlaces, roundingMode, format)
}
}
function decimalize(bigNumberish: BigNumberish, decimals: number): BigNumber {
const bigNumber: BigNumber = normalizeBigNumberish(bigNumberish)
ensureAllUInt256([bigNumber])
ensureAllUInt8([decimals])
if (decimals > _MAX_DECIMAL_PLACES) {
throw Error(`This function does not support decimals greater than ${_MAX_DECIMAL_PLACES}.`)
}
return bigNumber.dividedBy(_10.exponentiatedBy(decimals))
}
export function formatSignificantDecimals(
bigNumberish: BigNumberish,
decimals: number,
options?: FormatSignificantOptions
): string {
return formatSignificant(decimalize(bigNumberish, decimals), options)
}
export function formatFixedDecimals(
bigNumberish: BigNumberish,
decimals: number,
options?: FormatFixedOptions
): string {
return formatFixed(decimalize(bigNumberish, decimals), options)
}
import BigNumber from 'bignumber.js'
export { BigNumber }
export {
ETH,
SUPPORTED_CHAIN_ID,
FACTORY_ADDRESS,
FACTORY_ABI,
EXCHANGE_ABI,
TRADE_TYPE,
TRADE_EXACT,
TRADE_METHODS,
TRADE_METHOD_IDS,
FIXED_UNDERFLOW_BEHAVIOR
} from './constants'
export * from './data'
export * from './computation'
export * from './format'
export * from './orchestration'
export * from './transact'
export const ONE = BigInt(1)
import {
BigNumberish,
ChainIdOrProvider,
TokenReservesNormalized,
OptionalReserves,
MarketDetails,
TradeDetails
} from '../types'
import { TRADE_EXACT } from '../constants'
import { getTokenReserves } from '../data'
import { getMarketDetails, getTradeDetails } from '../computation'
//// eth for tokens
export function tradeExactEthForTokensWithData(reserves: OptionalReserves, ethAmount: BigNumberish): TradeDetails {
const marketDetails: MarketDetails = getMarketDetails(undefined, reserves)
return getTradeDetails(TRADE_EXACT.INPUT, ethAmount, marketDetails)
}
export async function tradeExactEthForTokens(
tokenAddress: string,
ethAmount: BigNumberish,
chainIdOrProvider?: ChainIdOrProvider
): Promise<TradeDetails> {
const tokenReserves: TokenReservesNormalized = await getTokenReserves(tokenAddress, chainIdOrProvider)
return tradeExactEthForTokensWithData(tokenReserves, ethAmount)
}
export function tradeEthForExactTokensWithData(reserves: OptionalReserves, tokenAmount: BigNumberish): TradeDetails {
const marketDetails: MarketDetails = getMarketDetails(undefined, reserves)
return getTradeDetails(TRADE_EXACT.OUTPUT, tokenAmount, marketDetails)
}
export async function tradeEthForExactTokens(
tokenAddress: string,
tokenAmount: BigNumberish,
chainIdOrProvider?: ChainIdOrProvider
): Promise<TradeDetails> {
const tokenReserves: TokenReservesNormalized = await getTokenReserves(tokenAddress, chainIdOrProvider)
return tradeEthForExactTokensWithData(tokenReserves, tokenAmount)
}
//// tokens to eth
export function tradeExactTokensForEthWithData(reserves: OptionalReserves, tokenAmount: BigNumberish): TradeDetails {
const marketDetails: MarketDetails = getMarketDetails(reserves, undefined)
return getTradeDetails(TRADE_EXACT.INPUT, tokenAmount, marketDetails)
}
export async function tradeExactTokensForEth(
tokenAddress: string,
tokenAmount: BigNumberish,
chainIdOrProvider?: ChainIdOrProvider
): Promise<TradeDetails> {
const tokenReserves: TokenReservesNormalized = await getTokenReserves(tokenAddress, chainIdOrProvider)
return tradeExactTokensForEthWithData(tokenReserves, tokenAmount)
}
export function tradeTokensForExactEthWithData(reserves: OptionalReserves, ethAmount: BigNumberish): TradeDetails {
const marketDetails: MarketDetails = getMarketDetails(reserves, undefined)
return getTradeDetails(TRADE_EXACT.OUTPUT, ethAmount, marketDetails)
}
export async function tradeTokensForExactEth(
tokenAddress: string,
ethAmount: BigNumberish,
chainIdOrProvider?: ChainIdOrProvider
): Promise<TradeDetails> {
const tokenReserves: TokenReservesNormalized = await getTokenReserves(tokenAddress, chainIdOrProvider)
return tradeTokensForExactEthWithData(tokenReserves, ethAmount)
}
//// tokens for tokens
export function tradeExactTokensForTokensWithData(
reservesInput: OptionalReserves,
reservesOutput: OptionalReserves,
tokenAmount: BigNumberish
): TradeDetails {
const marketDetails: MarketDetails = getMarketDetails(reservesInput, reservesOutput)
return getTradeDetails(TRADE_EXACT.INPUT, tokenAmount, marketDetails)
}
export async function tradeExactTokensForTokens(
tokenAddressInput: string,
tokenAddressOutput: string,
tokenAmount: BigNumberish,
chainIdOrProvider?: ChainIdOrProvider
): Promise<TradeDetails> {
const tokenReservesInput: TokenReservesNormalized = await getTokenReserves(tokenAddressInput, chainIdOrProvider)
const tokenReservesOutput: TokenReservesNormalized = await getTokenReserves(tokenAddressOutput, chainIdOrProvider)
return tradeExactTokensForTokensWithData(tokenReservesInput, tokenReservesOutput, tokenAmount)
}
export function tradeTokensForExactTokensWithData(
reservesInput: OptionalReserves,
reservesOutput: OptionalReserves,
tokenAmount: BigNumberish
): TradeDetails {
const marketDetails: MarketDetails = getMarketDetails(reservesInput, reservesOutput)
return getTradeDetails(TRADE_EXACT.OUTPUT, tokenAmount, marketDetails)
}
export async function tradeTokensForExactTokens(
tokenAddressInput: string,
tokenAddressOutput: string,
tokenAmount: BigNumberish,
chainIdOrProvider?: ChainIdOrProvider
): Promise<TradeDetails> {
const tokenReservesInput: TokenReservesNormalized = await getTokenReserves(tokenAddressInput, chainIdOrProvider)
const tokenReservesOutput: TokenReservesNormalized = await getTokenReserves(tokenAddressOutput, chainIdOrProvider)
return tradeTokensForExactTokensWithData(tokenReservesInput, tokenReservesOutput, tokenAmount)
}
import BigNumber from 'bignumber.js'
import {
TokenReservesNormalized,
TradeDetails,
MethodArgument,
ExecutionDetails,
_SlippageBounds,
_PartialExecutionDetails
} from '../types'
import { _0, _1, _10000, _MAX_UINT256, TRADE_TYPE, TRADE_EXACT, TRADE_METHODS, TRADE_METHOD_IDS } from '../constants'
import { normalizeAddress } from '../_utils'
function removeUndefined(methodArguments: (MethodArgument | undefined)[]): MethodArgument[] {
return methodArguments.filter((a: MethodArgument | undefined): boolean => a !== undefined) as MethodArgument[]
}
function calculateSlippage(value: BigNumber, maxSlippage: number): _SlippageBounds {
const offset: BigNumber = value.multipliedBy(maxSlippage).dividedBy(_10000)
const minimum: BigNumber = value.minus(offset).integerValue(BigNumber.ROUND_FLOOR)
const maximum: BigNumber = value.plus(offset).integerValue(BigNumber.ROUND_CEIL)
return {
minimum: minimum.isLessThan(_0) ? _0 : minimum,
maximum: maximum.isGreaterThan(_MAX_UINT256) ? _MAX_UINT256 : maximum
}
}
function getReserves(trade: TradeDetails): TokenReservesNormalized {
switch (trade.tradeType) {
case TRADE_TYPE.ETH_TO_TOKEN: {
return trade.marketDetailsPre.outputReserves as TokenReservesNormalized
}
case TRADE_TYPE.TOKEN_TO_ETH: {
return trade.marketDetailsPre.inputReserves as TokenReservesNormalized
}
case TRADE_TYPE.TOKEN_TO_TOKEN: {
return trade.marketDetailsPre.inputReserves as TokenReservesNormalized
}
default: {
throw Error(`tradeType ${trade.tradeType} is invalid.`)
}
}
}
function getMethodName(trade: TradeDetails, transfer: boolean = false): string {
switch (trade.tradeType) {
case TRADE_TYPE.ETH_TO_TOKEN: {
if (trade.tradeExact === TRADE_EXACT.INPUT && !transfer) {
return TRADE_METHODS.ethToTokenSwapInput
} else if (trade.tradeExact === TRADE_EXACT.INPUT && transfer) {
return TRADE_METHODS.ethToTokenTransferInput
} else if (trade.tradeExact === TRADE_EXACT.OUTPUT && !transfer) {
return TRADE_METHODS.ethToTokenSwapOutput
} else {
return TRADE_METHODS.ethToTokenTransferOutput
}
}
case TRADE_TYPE.TOKEN_TO_ETH: {
if (trade.tradeExact === TRADE_EXACT.INPUT && !transfer) {
return TRADE_METHODS.tokenToEthSwapInput
} else if (trade.tradeExact === TRADE_EXACT.INPUT && transfer) {
return TRADE_METHODS.tokenToEthTransferInput
} else if (trade.tradeExact === TRADE_EXACT.OUTPUT && !transfer) {
return TRADE_METHODS.tokenToEthSwapOutput
} else {
return TRADE_METHODS.tokenToEthTransferOutput
}
}
case TRADE_TYPE.TOKEN_TO_TOKEN: {
if (trade.tradeExact === TRADE_EXACT.INPUT && !transfer) {
return TRADE_METHODS.tokenToTokenSwapInput
} else if (trade.tradeExact === TRADE_EXACT.INPUT && transfer) {
return TRADE_METHODS.tokenToTokenTransferInput
} else if (trade.tradeExact === TRADE_EXACT.OUTPUT && !transfer) {
return TRADE_METHODS.tokenToTokenSwapOutput
} else {
return TRADE_METHODS.tokenToTokenTransferOutput
}
}
default: {
throw Error(`tradeType ${trade.tradeType} is invalid.`)
}
}
}
function getValueAndMethodArguments(
trade: TradeDetails,
methodName: string,
maxSlippage: number,
deadline: number,
recipient?: string
): _PartialExecutionDetails {
switch (methodName) {
case TRADE_METHODS.ethToTokenSwapInput:
case TRADE_METHODS.ethToTokenTransferInput: {
return {
value: trade.inputAmount.amount,
methodArguments: removeUndefined([
calculateSlippage(trade.outputAmount.amount, maxSlippage).minimum,
deadline,
recipient
])
}
}
case TRADE_METHODS.ethToTokenSwapOutput:
case TRADE_METHODS.ethToTokenTransferOutput: {
return {
value: calculateSlippage(trade.inputAmount.amount, maxSlippage).maximum,
methodArguments: removeUndefined([trade.outputAmount.amount, deadline, recipient])
}
}
case TRADE_METHODS.tokenToEthSwapInput:
case TRADE_METHODS.tokenToEthTransferInput: {
return {
value: _0,
methodArguments: removeUndefined([
trade.inputAmount.amount,
calculateSlippage(trade.outputAmount.amount, maxSlippage).minimum,
deadline,
recipient
])
}
}
case TRADE_METHODS.tokenToEthSwapOutput:
case TRADE_METHODS.tokenToEthTransferOutput: {
return {
value: _0,
methodArguments: removeUndefined([
trade.outputAmount.amount,
calculateSlippage(trade.inputAmount.amount, maxSlippage).maximum,
deadline,
recipient
])
}
}
case TRADE_METHODS.tokenToTokenSwapInput:
case TRADE_METHODS.tokenToTokenTransferInput: {
if (!trade.outputAmount.token.address) {
throw Error('trade does not include output token address.')
}
return {
value: _0,
methodArguments: removeUndefined([
trade.inputAmount.amount,
calculateSlippage(trade.outputAmount.amount, maxSlippage).minimum,
_1,
deadline,
recipient,
trade.outputAmount.token.address
])
}
}
case TRADE_METHODS.tokenToTokenSwapOutput:
case TRADE_METHODS.tokenToTokenTransferOutput: {
if (!trade.outputAmount.token.address) {
throw Error('trade does not include output token address.')
}
return {
value: _0,
methodArguments: removeUndefined([
trade.outputAmount.amount,
calculateSlippage(trade.inputAmount.amount, maxSlippage).maximum,
_MAX_UINT256,
deadline,
recipient,
trade.outputAmount.token.address
])
}
}
default: {
throw Error(`methodName ${methodName} is invalid.`)
}
}
}
export function getExecutionDetails(
trade: TradeDetails,
maxSlippage?: number,
deadline?: number,
recipient?: string
): ExecutionDetails {
const reserves: TokenReservesNormalized = getReserves(trade)
if (!reserves.exchange || !reserves.exchange.address) {
throw Error('trade does not include exchange address.')
}
const methodName: string = getMethodName(trade, !!recipient)
const methodId: string = TRADE_METHOD_IDS[methodName]
const { value, methodArguments }: _PartialExecutionDetails = getValueAndMethodArguments(
trade,
methodName,
maxSlippage || 200,
deadline || Math.round(Date.now() / 1000 + 60 * 10),
recipient && normalizeAddress(recipient)
)
return {
exchangeAddress: reserves.exchange.address,
methodName,
methodId,
value,
methodArguments: methodArguments
}
}
import BigNumber from 'bignumber.js'
import { ethers } from 'ethers'
import { SUPPORTED_CHAIN_ID, TRADE_TYPE, TRADE_EXACT, FIXED_UNDERFLOW_BEHAVIOR } from './constants'
export type BigNumberish = BigNumber | ethers.utils.BigNumber | string | number
//// types for on-chain, submitted, and normalized data
export type ChainIdOrProvider = SUPPORTED_CHAIN_ID | ethers.providers.AsyncSendable | ethers.providers.Provider
// type guard for ChainIdOrProvider
export function isChainId(chainIdOrProvider: ChainIdOrProvider): chainIdOrProvider is SUPPORTED_CHAIN_ID {
const chainId: SUPPORTED_CHAIN_ID = chainIdOrProvider as SUPPORTED_CHAIN_ID
return typeof chainId === 'number'
}
// type guard for ChainIdOrProvider
export function isLowLevelProvider(
chainIdOrProvider: ChainIdOrProvider
): chainIdOrProvider is ethers.providers.AsyncSendable {
if (isChainId(chainIdOrProvider)) {
return false
} else {
const provider: ethers.providers.Provider = chainIdOrProvider as ethers.providers.Provider
return !ethers.providers.Provider.isProvider(provider)
}
}
export interface Token {
chainId?: SUPPORTED_CHAIN_ID
address?: string
decimals: number
}
export interface TokenAmount {
token: Token
amount: BigNumberish
}
export interface TokenAmountNormalized {
token: Token
amount: BigNumber
}
export interface TokenReserves {
token: Token
exchange?: Token
ethReserve: TokenAmount
tokenReserve: TokenAmount
}
export interface TokenReservesNormalized {
token: Token
exchange?: Token
ethReserve: TokenAmountNormalized
tokenReserve: TokenAmountNormalized
}
export interface EthReserves {
token: Token
}
// type for input data
export type OptionalReserves = TokenReserves | EthReserves | undefined
// type guard for OptionalReserves
export function areTokenReserves(reserves: OptionalReserves): reserves is TokenReserves {
const tokenReserves: TokenReserves = reserves as TokenReserves
return (
tokenReserves !== undefined && tokenReserves.ethReserve !== undefined && tokenReserves.tokenReserve !== undefined
)
}
// type guard for OptionalReserves
export function areETHReserves(reserves: OptionalReserves): reserves is EthReserves {
const tokenReserves: TokenReserves = reserves as TokenReserves
return (
tokenReserves !== undefined && tokenReserves.ethReserve === undefined && tokenReserves.tokenReserve === undefined
)
}
// type for output data
export type NormalizedReserves = TokenReservesNormalized | EthReserves
// type guard for NormalizedReserves
export function areTokenReservesNormalized(reserves: NormalizedReserves): reserves is TokenReservesNormalized {
const tokenReservesNormalized: TokenReservesNormalized = reserves as TokenReservesNormalized
return tokenReservesNormalized.ethReserve !== undefined && tokenReservesNormalized.tokenReserve !== undefined
}
//// types for computed data
export interface Rate {
rate: BigNumber
rateInverted: BigNumber
}
export interface MarketDetails {
tradeType: TRADE_TYPE
inputReserves: NormalizedReserves
outputReserves: NormalizedReserves
marketRate: Rate
}
export interface TradeDetails {
marketDetailsPre: MarketDetails
marketDetailsPost: MarketDetails
tradeType: TRADE_TYPE
tradeExact: TRADE_EXACT
inputAmount: TokenAmountNormalized
outputAmount: TokenAmountNormalized
executionRate: Rate
marketRateSlippage: BigNumber
executionRateSlippage: BigNumber
}
export type MethodArgument = BigNumber | number | string
export interface ExecutionDetails {
exchangeAddress: string
methodName: string
methodId: string
value: BigNumber
methodArguments: MethodArgument[]
}
//// types for formatting data
export type FlexibleFormat = BigNumber.Format | boolean
// type guard for FlexibleFormat
export function isFormat(flexibleFormat: FlexibleFormat): flexibleFormat is BigNumber.Format {
const format: BigNumber.Format = flexibleFormat as BigNumber.Format
return typeof format !== 'boolean'
}
export interface FormatSignificantOptions {
significantDigits: number
roundingMode: BigNumber.RoundingMode
forceIntegerSignificance: boolean
format: FlexibleFormat
}
export interface FormatFixedOptions {
decimalPlaces: number
roundingMode: BigNumber.RoundingMode
dropTrailingZeros: boolean
underflowBehavior: FIXED_UNDERFLOW_BEHAVIOR
format: FlexibleFormat
}
//// internal-only interfaces
export interface _ChainIdAndProvider {
chainId: number
provider: ethers.providers.Provider
}
export interface _ParsedOptionalReserves {
tradeType: TRADE_TYPE
inputReserves: NormalizedReserves
outputReserves: NormalizedReserves
}
export interface _DecimalRate {
numerator: BigNumber
denominator: BigNumber
decimalScalar: BigNumber
decimalScalarInverted: BigNumber
}
export type _AnyRate = _DecimalRate | Rate
export interface _PartialTradeDetails {
transput: BigNumber
inputReservesPost: NormalizedReserves
outputReservesPost: NormalizedReserves
}
export interface _SlippageBounds {
minimum: BigNumber
maximum: BigNumber
}
export interface _PartialExecutionDetails {
value: BigNumber
methodArguments: MethodArgument[]
}
import { ONE } from '../src'
describe('blah', () => {
it('works', () => {
expect(ONE.toString()).toEqual('1')
})
})
{
"include": ["src", "types", "test"],
"compilerOptions": {
/* Basic Options */
"target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */,
"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,
// "lib": [], /* Specify library files to be included in the compilation. */
"allowJs": false /* Allow javascript files to be compiled. */,
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
"declaration": true /* Generates corresponding '.d.ts' file. */,
// "declarationMap": true /* Generates a sourcemap for each corresponding '.d.ts' file. */
"sourceMap": true /* Generates corresponding '.map' file. */,
// "outFile": "./", /* Concatenate and emit output to single file. */
"outDir": "./dist" /* Redirect output structure to the directory. */,
"rootDir": "./src" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */,
// "composite": true, /* Enable project compilation */
// "incremental": true, /* Enable incremental compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
"removeComments": true /* Do not emit comments to output. */,
// "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true /* Enable all strict type-checking options. */,
"noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */,
"strictNullChecks": true /* Enable strict null checks. */,
"strictFunctionTypes": true /* Enable strict checking of function types. */,
"strictBindCallApply": true /* Enable strict 'bind', 'call', and 'apply' methods on functions. */,
"strictPropertyInitialization": true /* Enable strict checking of property initialization in classes. */,
"noImplicitThis": true /* Raise error on 'this' expressions with an implied 'any' type. */,
"alwaysStrict": true /* Parse in strict mode and emit "use strict" for each source file. */,
/* Additional Checks */
"noUnusedLocals": true /* Report errors on unused locals. */,
"noUnusedParameters": true /* Report errors on unused parameters. */,
"noImplicitReturns": true /* Report error when not all code paths in function return a value. */,
"noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */,
/* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
/* Source Map Options */
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
/* Manually Added Options */
"resolveJsonModule": true
},
"include": ["./src/**/*"],
"exclude": ["./src/__tests__/**/*"]
"target": "es5",
"module": "esnext",
"lib": ["dom", "esnext"],
"importHelpers": true,
"declaration": true,
"sourceMap": true,
"rootDir": "./",
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictPropertyInitialization": true,
"noImplicitThis": true,
"alwaysStrict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"moduleResolution": "node",
"baseUrl": "./",
"paths": {
"*": ["src/*", "node_modules/*"]
},
"jsx": "react",
"esModuleInterop": true
}
}
This diff is collapsed.
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