diff --git a/src/constants/index.ts b/src/constants/index.ts index c24b5a07ae..d3eab50bbf 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -21,11 +21,6 @@ export const COMMON_BASES = { [ChainId.KOVAN]: [WETH[ChainId.KOVAN]] } -export const SUPPORTED_THEMES = { - DARK: 'DARK', - LIGHT: 'LIGHT' -} - const MAINNET_WALLETS = { INJECTED: { connector: injected, diff --git a/src/theme/index.tsx b/src/theme/index.tsx index 70eb87a064..32fe254fe0 100644 --- a/src/theme/index.tsx +++ b/src/theme/index.tsx @@ -8,8 +8,7 @@ import styled, { } from 'styled-components' import { AppDispatch, AppState } from '../state' import { updateUserDarkMode } from '../state/user/actions' -import { getQueryParam, checkSupportedTheme } from '../utils' -import { SUPPORTED_THEMES } from '../constants' +import { getQueryParam } from '../utils' import { useIsDarkMode } from '../state/user/hooks' import { Text, TextProps } from 'rebass' import { Colors } from './styled' @@ -122,11 +121,11 @@ export default function ThemeProvider({ children }: { children: React.ReactNode const userDarkMode = useSelector(state => state.user.userDarkMode) const darkMode = useIsDarkMode() - const themeURL = checkSupportedTheme(getQueryParam(window.location, 'theme')) + const themeURL = getQueryParam(window.location, 'theme') const urlContainsDarkMode: boolean | null = themeURL - ? themeURL.toUpperCase() === SUPPORTED_THEMES.DARK + ? themeURL.toUpperCase() === 'DARK' ? true - : themeURL.toUpperCase() === SUPPORTED_THEMES.LIGHT + : themeURL.toUpperCase() === 'LIGHT' ? false : null : null diff --git a/src/utils/index.test.ts b/src/utils/index.test.ts index f959461e99..f8a13679d6 100644 --- a/src/utils/index.test.ts +++ b/src/utils/index.test.ts @@ -1,7 +1,15 @@ +import { BigNumber } from '@ethersproject/bignumber' import { AddressZero } from '@ethersproject/constants' -import { TokenAmount, Token, ChainId } from '@uniswap/sdk' +import { TokenAmount, Token, ChainId, Percent, JSBI } from '@uniswap/sdk' -import { getEtherscanLink, calculateSlippageAmount } from '.' +import { + getEtherscanLink, + calculateSlippageAmount, + isAddress, + shortenAddress, + calculateGasMargin, + basisPointsToPercent +} from '.' describe('utils', () => { describe('#getEtherscanLink', () => { @@ -11,6 +19,15 @@ describe('utils', () => { it('correct for address', () => { expect(getEtherscanLink(1, 'abc', 'address')).toEqual('https://etherscan.io/address/abc') }) + it('unrecognized chain id defaults to mainnet', () => { + expect(getEtherscanLink(2, 'abc', 'address')).toEqual('https://etherscan.io/address/abc') + }) + it('ropsten', () => { + expect(getEtherscanLink(3, 'abc', 'address')).toEqual('https://ropsten.etherscan.io/address/abc') + }) + it('enum', () => { + expect(getEtherscanLink(ChainId.RINKEBY, 'abc', 'address')).toEqual('https://rinkeby.etherscan.io/address/abc') + }) }) describe('#calculateSlippageAmount', () => { @@ -24,4 +41,59 @@ describe('utils', () => { expect(() => calculateSlippageAmount(tokenAmount, 10001)).toThrow() }) }) + + describe('#isAddress', () => { + it('returns false if not', () => { + expect(isAddress('')).toBe(false) + expect(isAddress('0x0000')).toBe(false) + expect(isAddress(1)).toBe(false) + expect(isAddress({})).toBe(false) + expect(isAddress(undefined)).toBe(false) + }) + + it('returns the checksummed address', () => { + expect(isAddress('0xf164fc0ec4e93095b804a4795bbe1e041497b92a')).toBe('0xf164fC0Ec4E93095b804a4795bBe1e041497b92a') + expect(isAddress('0xf164fC0Ec4E93095b804a4795bBe1e041497b92a')).toBe('0xf164fC0Ec4E93095b804a4795bBe1e041497b92a') + }) + + it('succeeds even without prefix', () => { + expect(isAddress('f164fc0ec4e93095b804a4795bbe1e041497b92a')).toBe('0xf164fC0Ec4E93095b804a4795bBe1e041497b92a') + }) + it('fails if too long', () => { + expect(isAddress('f164fc0ec4e93095b804a4795bbe1e041497b92a0')).toBe(false) + }) + }) + + describe('#shortenAddress', () => { + it('throws on invalid address', () => { + expect(() => shortenAddress('abc')).toThrow("Invalid 'address'") + }) + + it('truncates middle characters', () => { + expect(shortenAddress('0xf164fc0ec4e93095b804a4795bbe1e041497b92a')).toBe('0xf164...b92a') + }) + + it('truncates middle characters even without prefix', () => { + expect(shortenAddress('f164fc0ec4e93095b804a4795bbe1e041497b92a')).toBe('0xf164...b92a') + }) + + it('renders checksummed address', () => { + expect(shortenAddress('0x2E1b342132A67Ea578e4E3B814bae2107dc254CC'.toLowerCase())).toBe('0x2E1b...54CC') + }) + }) + + describe('#calculateGasMargin', () => { + it('adds 10%', () => { + expect(calculateGasMargin(BigNumber.from(1000)).toString()).toEqual('1100') + expect(calculateGasMargin(BigNumber.from(50)).toString()).toEqual('55') + }) + }) + + describe('#basisPointsToPercent', () => { + it('converts basis points numbers to percents', () => { + expect(basisPointsToPercent(100).equalTo(new Percent(JSBI.BigInt(1), JSBI.BigInt(100)))).toBeTruthy() + expect(basisPointsToPercent(500).equalTo(new Percent(JSBI.BigInt(5), JSBI.BigInt(100)))).toBeTruthy() + expect(basisPointsToPercent(50).equalTo(new Percent(JSBI.BigInt(5), JSBI.BigInt(1000)))).toBeTruthy() + }) + }) }) diff --git a/src/utils/index.ts b/src/utils/index.ts index 93fcadbe31..74030cfcd6 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -7,15 +7,16 @@ import { BigNumber } from '@ethersproject/bignumber' import { abi as IUniswapV2PairABI } from '@uniswap/v2-core/build/IUniswapV2Pair.json' import { abi as IUniswapV2Router01ABI } from '@uniswap/v2-periphery/build/IUniswapV2Router01.json' -import { ROUTER_ADDRESS, SUPPORTED_THEMES } from '../constants' +import { ROUTER_ADDRESS } from '../constants' import ERC20_ABI from '../constants/abis/erc20.json' import ERC20_BYTES32_ABI from '../constants/abis/erc20_bytes32.json' -import { JSBI, Percent, TokenAmount } from '@uniswap/sdk' +import { ChainId, JSBI, Percent, TokenAmount } from '@uniswap/sdk' +// returns the checksummed address if the address is valid, otherwise returns false export function isAddress(value: any): string | false { try { - return getAddress(value.toLowerCase()) + return getAddress(value) } catch { return false } @@ -26,7 +27,7 @@ export enum ERROR_CODES { TOKEN_DECIMALS = 2 } -const ETHERSCAN_PREFIXES = { +const ETHERSCAN_PREFIXES: { [chainId in ChainId]: string } = { 1: '', 3: 'ropsten.', 4: 'rinkeby.', @@ -34,12 +35,8 @@ const ETHERSCAN_PREFIXES = { 42: 'kovan.' } -export function getEtherscanLink( - networkId: 1 | 3 | 4 | 5 | 42 | string | number, - data: string, - type: 'transaction' | 'address' -) { - const prefix = `https://${ETHERSCAN_PREFIXES[networkId] || ETHERSCAN_PREFIXES[1]}etherscan.io` +export function getEtherscanLink(chainId: ChainId, data: string, type: 'transaction' | 'address'): string { + const prefix = `https://${ETHERSCAN_PREFIXES[chainId] || ETHERSCAN_PREFIXES[1]}etherscan.io` switch (type) { case 'transaction': { @@ -89,22 +86,18 @@ export function getAllQueryParams(): QueryParams { } } -export function checkSupportedTheme(themeName: string) { - if (themeName && themeName.toUpperCase() in SUPPORTED_THEMES) { - return themeName.toUpperCase() - } - return null -} - +// shorten the checksummed version of the input address to have 0x + 4 characters at start and end export function shortenAddress(address: string, chars = 4): string { - if (!isAddress(address)) { + const parsed = isAddress(address) + if (!parsed) { throw Error(`Invalid 'address' parameter '${address}'.`) } - return `${address.substring(0, chars + 2)}...${address.substring(42 - chars)}` + return `${parsed.substring(0, chars + 2)}...${parsed.substring(42 - chars)}` } +// add 10% export function calculateGasMargin(value: BigNumber): BigNumber { - return value.mul(BigNumber.from(10000).add(BigNumber.from(1000))).div(BigNumber.from(10000)) // add 10% + return value.mul(BigNumber.from(10000).add(BigNumber.from(1000))).div(BigNumber.from(10000)) } // converts a basis points value to a sdk percent diff --git a/yarn.lock b/yarn.lock index 5032c9ea2b..dd85b22dc2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7205,7 +7205,7 @@ escape-html@~1.0.3: resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= -escape-string-regexp@2.0.0, escape-string-regexp@^2.0.0: +escape-string-regexp@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==