+ [state, { dismissBetaMessage, updateBlockNumber }], [
+ state,
+ dismissBetaMessage,
+ updateBlockNumber
+ ])}
+ >
{children}
)
diff --git a/src/contexts/Balances.js b/src/contexts/Balances.js
index 3bbf8619f3..b5193e94a8 100644
--- a/src/contexts/Balances.js
+++ b/src/contexts/Balances.js
@@ -1,4 +1,4 @@
-import React, { createContext, useContext, useReducer, useCallback, useEffect } from 'react'
+import React, { createContext, useContext, useReducer, useMemo, useCallback, useEffect } from 'react'
import { useWeb3Context } from 'web3-react'
import { safeAccess, isAddress, getEtherBalance, getTokenBalance } from '../utils'
@@ -44,7 +44,11 @@ export default function Provider({ children }) {
dispatch({ type: UPDATE, payload: { networkId, address, tokenAddress, value, blockNumber } })
}, [])
- return {children}
+ return (
+ [state, { update }], [state, update])}>
+ {children}
+
+ )
}
export function useAddressBalance(address, tokenAddress) {
diff --git a/src/contexts/Tokens.js b/src/contexts/Tokens.js
index c23d91f7af..6095db3795 100644
--- a/src/contexts/Tokens.js
+++ b/src/contexts/Tokens.js
@@ -1,4 +1,4 @@
-import React, { createContext, useContext, useReducer, useCallback, useEffect } from 'react'
+import React, { createContext, useContext, useReducer, useMemo, useCallback, useEffect } from 'react'
import { useWeb3Context } from 'web3-react'
import { ethers } from 'ethers'
@@ -59,6 +59,12 @@ const INITIAL_TOKENS_CONTEXT = {
[DECIMALS]: 18,
[EXCHANGE_ADDRESS]: '0xF7B5A4b934658025390ff69dB302BC7F2AC4a542'
},
+ '0xF5DCe57282A584D2746FaF1593d3121Fcac444dC': {
+ [NAME]: 'Compound Dai',
+ [SYMBOL]: 'cDAI',
+ [DECIMALS]: 8,
+ [EXCHANGE_ADDRESS]: '0x45A2FDfED7F7a2c791fb1bdF6075b83faD821ddE'
+ },
'0x41e5560054824eA6B0732E656E3Ad64E20e94E45': {
[NAME]: 'Civic',
[SYMBOL]: 'CVC',
@@ -101,12 +107,6 @@ const INITIAL_TOKENS_CONTEXT = {
[DECIMALS]: 12,
[EXCHANGE_ADDRESS]: '0x4B17685b330307C751B47f33890c8398dF4Fe407'
},
- '0x056Fd409E1d7A124BD7017459dFEa2F387b6d5Cd': {
- [NAME]: 'Gemini dollar',
- [SYMBOL]: 'GUSD',
- [DECIMALS]: 2,
- [EXCHANGE_ADDRESS]: '0xD883264737Ed969d2696eE4B4cAF529c2Fc2A141'
- },
'0x818Fc6C2Ec5986bc6E2CBf00939d90556aB12ce5': {
[NAME]: 'Kin',
[SYMBOL]: 'KIN',
@@ -125,6 +125,12 @@ const INITIAL_TOKENS_CONTEXT = {
[DECIMALS]: 18,
[EXCHANGE_ADDRESS]: '0xF173214C720f58E03e194085B1DB28B50aCDeeaD'
},
+ '0xBBbbCA6A901c926F240b89EacB641d8Aec7AEafD': {
+ [NAME]: 'LoopringCoin V2',
+ [SYMBOL]: 'LRC',
+ [DECIMALS]: 18,
+ [EXCHANGE_ADDRESS]: '0xA539BAaa3aCA455c986bB1E25301CEF936CE1B65'
+ },
'0x6c6EE5e31d828De241282B9606C8e98Ea48526E2': {
[NAME]: 'HoloToken',
[SYMBOL]: 'HOT',
@@ -233,12 +239,6 @@ const INITIAL_TOKENS_CONTEXT = {
[DECIMALS]: 18,
[EXCHANGE_ADDRESS]: '0x1aEC8F11A7E78dC22477e91Ed924Fab46e3A88Fd'
},
- '0xEf8a2c1BC94e630463293F71bF5414d13e80F62D': {
- [NAME]: 'Synthetix Network Token',
- [SYMBOL]: 'SNX',
- [DECIMALS]: 18,
- [EXCHANGE_ADDRESS]: '0xd9025Ed64BAA7B9046E37fe94670C79fcCB2b5C8'
- },
'0x42d6622deCe394b54999Fbd73D108123806f6a18': {
[NAME]: 'SPANK',
[SYMBOL]: 'SPANK',
@@ -293,12 +293,6 @@ const INITIAL_TOKENS_CONTEXT = {
[DECIMALS]: 18,
[EXCHANGE_ADDRESS]: '0x8dE0d002DC83478f479dC31F76cB0a8aa7CcEa17'
},
- '0x05f4a42e251f2d52b8ed15E9FEdAacFcEF1FAD27': {
- [NAME]: 'Zilliqa',
- [SYMBOL]: 'ZIL',
- [DECIMALS]: 12,
- [EXCHANGE_ADDRESS]: '0x7dc095A5CF7D6208CC680fA9866F80a53911041a'
- },
'0xE41d2489571d322189246DaFA5ebDe1F4699F498': {
[NAME]: '0x Protocol Token',
[SYMBOL]: 'ZRX',
@@ -344,7 +338,11 @@ export default function Provider({ children }) {
dispatch({ type: UPDATE, payload: { networkId, tokenAddress, name, symbol, decimals, exchangeAddress } })
}, [])
- return {children}
+ return (
+ [state, { update }], [state, update])}>
+ {children}
+
+ )
}
export function useTokenDetails(tokenAddress) {
diff --git a/src/contexts/Transactions.js b/src/contexts/Transactions.js
index 2cf65a8bde..2d144db98e 100644
--- a/src/contexts/Transactions.js
+++ b/src/contexts/Transactions.js
@@ -1,11 +1,11 @@
-import React, { createContext, useContext, useReducer, useCallback, useEffect } from 'react'
+import React, { createContext, useContext, useReducer, useMemo, useCallback, useEffect } from 'react'
import { useWeb3Context } from 'web3-react'
-import { ethers } from 'ethers'
import { safeAccess } from '../utils'
import { useBlockNumber } from './Application'
const RESPONSE = 'response'
+const CUSTOM_DATA = 'CUSTOM_DATA'
const BLOCK_NUMBER_CHECKED = 'BLOCK_NUMBER_CHECKED'
const RECEIPT = 'receipt'
@@ -94,7 +94,11 @@ export default function Provider({ children }) {
}, [])
return (
- {children}
+ [state, { add, check, finalize }], [state, add, check, finalize])}
+ >
+ {children}
+
)
}
@@ -145,7 +149,7 @@ export function useTransactionAdder() {
const [, { add }] = useTransactionsContext()
return useCallback(
- response => {
+ (response, customData = {}) => {
if (!(networkId || networkId === 0)) {
throw Error(`Invalid networkId '${networkId}`)
}
@@ -155,7 +159,7 @@ export function useTransactionAdder() {
if (!hash) {
throw Error('No transaction hash found.')
}
- add(networkId, hash, response)
+ add(networkId, hash, { ...response, [CUSTOM_DATA]: customData })
},
[networkId, add]
)
@@ -178,12 +182,7 @@ export function usePendingApproval(tokenAddress) {
return false
} else if (!allTransactions[hash][RESPONSE]) {
return false
- } else if (allTransactions[hash][RESPONSE].to !== tokenAddress) {
- return false
- } else if (
- allTransactions[hash][RESPONSE].data.substring(0, 10) !==
- ethers.utils.id('approve(address,uint256)').substring(0, 10)
- ) {
+ } else if (allTransactions[hash][RESPONSE][CUSTOM_DATA].approval !== tokenAddress) {
return false
} else {
return true
diff --git a/src/pages/Pool/AddLiquidity.js b/src/pages/Pool/AddLiquidity.js
index fb1039124c..ad7a6be8d3 100644
--- a/src/pages/Pool/AddLiquidity.js
+++ b/src/pages/Pool/AddLiquidity.js
@@ -305,7 +305,7 @@ export default function AddLiquidity() {
<>
{t('youAreAdding')} {b(`${amountFormatter(inputValueParsed, 18, 4)} ETH`)} {t('and')} {'at most'}{' '}
- {b(`${amountFormatter(outputValueMax, 18, 4)} ${symbol}`)} {t('intoPool')}
+ {b(`${amountFormatter(outputValueMax, decimals, 4)} ${symbol}`)} {t('intoPool')}
{t('youWillMint')} {b(amountFormatter(liquidityMinted, 18, 4))} {t('liquidityTokens')}
@@ -405,7 +405,7 @@ export default function AddLiquidity() {
// parse input value
useEffect(() => {
- if (isNewExchange === false && inputValue && marketRate && lastEditedField === INPUT) {
+ if (isNewExchange === false && inputValue && marketRate && lastEditedField === INPUT && decimals) {
try {
const parsedValue = ethers.utils.parseUnits(inputValue, 18)
@@ -418,10 +418,12 @@ export default function AddLiquidity() {
const currencyAmount = marketRate
.mul(parsedValue)
.div(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(18)))
+ .div(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(18 - decimals)))
+
setOutputValueParsed(currencyAmount)
dispatchAddLiquidityState({
type: 'UPDATE_DEPENDENT_VALUE',
- payload: { field: OUTPUT, value: amountFormatter(currencyAmount, 18, 4, false) }
+ payload: { field: OUTPUT, value: amountFormatter(currencyAmount, decimals, 4, false) }
})
return () => {
@@ -437,13 +439,13 @@ export default function AddLiquidity() {
setOutputError(t('inputNotValid'))
}
}
- }, [inputValue, isNewExchange, lastEditedField, marketRate, t])
+ }, [inputValue, isNewExchange, lastEditedField, marketRate, decimals, t])
// parse output value
useEffect(() => {
- if (isNewExchange === false && outputValue && marketRateInverted && lastEditedField === OUTPUT) {
+ if (isNewExchange === false && outputValue && marketRateInverted && lastEditedField === OUTPUT && decimals) {
try {
- const parsedValue = ethers.utils.parseUnits(outputValue, 18)
+ const parsedValue = ethers.utils.parseUnits(outputValue, decimals)
if (parsedValue.lte(ethers.constants.Zero) || parsedValue.gte(ethers.constants.MaxUint256)) {
throw Error()
@@ -453,7 +455,8 @@ export default function AddLiquidity() {
const currencyAmount = marketRateInverted
.mul(parsedValue)
- .div(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(18)))
+ .div(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(decimals)))
+
setInputValueParsed(currencyAmount)
dispatchAddLiquidityState({
type: 'UPDATE_DEPENDENT_VALUE',
@@ -473,7 +476,7 @@ export default function AddLiquidity() {
setInputError(t('inputNotValid'))
}
}
- }, [outputValue, isNewExchange, lastEditedField, marketRateInverted, t])
+ }, [outputValue, isNewExchange, lastEditedField, marketRateInverted, decimals, t])
// input validation
useEffect(() => {
@@ -485,14 +488,14 @@ export default function AddLiquidity() {
}
}
- if (outputValueParsed && outputBalance) {
- if (outputValueParsed.gt(outputBalance)) {
+ if (outputValueMax && outputBalance) {
+ if (outputValueMax.gt(outputBalance)) {
setOutputError(t('insufficientBalance'))
} else {
setOutputError(null)
}
}
- }, [inputValueParsed, inputBalance, outputValueParsed, outputBalance, t])
+ }, [inputValueParsed, inputBalance, outputValueMax, outputBalance, t])
const allowance = useAddressAllowance(account, outputCurrency, exchangeAddress)
const [showUnlock, setShowUnlock] = useState(false)
@@ -590,7 +593,7 @@ export default function AddLiquidity() {
{renderSummary()}
-
diff --git a/src/pages/Pool/CreateExchange.js b/src/pages/Pool/CreateExchange.js
index 262edf17b6..903e664a80 100644
--- a/src/pages/Pool/CreateExchange.js
+++ b/src/pages/Pool/CreateExchange.js
@@ -142,7 +142,7 @@ function CreateExchange({ history, location }) {
{errorMessage ? errorMessage : t('enterTokenCont')}
-
+
{t('createExchange')}
diff --git a/src/pages/Pool/RemoveLiquidity.js b/src/pages/Pool/RemoveLiquidity.js
index fee5d499fd..f06291ecf1 100644
--- a/src/pages/Pool/RemoveLiquidity.js
+++ b/src/pages/Pool/RemoveLiquidity.js
@@ -206,13 +206,13 @@ export default function RemoveLiquidity() {
: undefined
const ethWithdrawn =
- ETHPer &&
- valueParsed &&
- ETHPer.mul(valueParsed).div(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(18)))
+ ETHPer && valueParsed
+ ? ETHPer.mul(valueParsed).div(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(18)))
+ : undefined
const tokenWithdrawn =
- tokenPer &&
- valueParsed &&
- tokenPer.mul(valueParsed).div(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(18)))
+ tokenPer && valueParsed
+ ? tokenPer.mul(valueParsed).div(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(18)))
+ : undefined
const ethWithdrawnMin = ethWithdrawn ? calculateSlippageBounds(ethWithdrawn).minimum : undefined
const tokenWithdrawnMin = tokenWithdrawn ? calculateSlippageBounds(tokenWithdrawn).minimum : undefined
@@ -348,17 +348,17 @@ export default function RemoveLiquidity() {
- ethWithdrawn && tokenWithdrawn ? (
+ ethWithdrawnMin && tokenWithdrawnMin ? (
- {`${amountFormatter(ethWithdrawn, 18, 4, false)} ETH`}
+ {`${amountFormatter(ethWithdrawnMin, 18, 4, false)} ETH`}
+
- {`${amountFormatter(tokenWithdrawn, decimals, Math.min(4, decimals))} ${symbol}`}
+ {`${amountFormatter(tokenWithdrawnMin, decimals, Math.min(4, decimals))} ${symbol}`}
) : (
@@ -406,7 +406,7 @@ export default function RemoveLiquidity() {
{renderSummary()}
-
+
{t('removeLiquidity')}
diff --git a/src/pages/Send/index.js b/src/pages/Send/index.js
index 73345f99ab..22280164d2 100644
--- a/src/pages/Send/index.js
+++ b/src/pages/Send/index.js
@@ -486,7 +486,11 @@ export default function Swap() {
.sub(ethers.utils.bigNumberify(3).mul(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(15))))
: undefined
const percentSlippageFormatted = percentSlippage && amountFormatter(percentSlippage, 16, 2)
- const slippageWarning = percentSlippage && percentSlippage.gte(ethers.utils.parseEther('.1')) // 10%
+ const slippageWarning =
+ percentSlippage &&
+ percentSlippage.gte(ethers.utils.parseEther('.05')) &&
+ percentSlippage.lt(ethers.utils.parseEther('.2')) // [5% - 20%)
+ const highSlippageWarning = percentSlippage && percentSlippage.gte(ethers.utils.parseEther('.2')) // [20+%
const isValid = exchangeRate && inputError === null && independentError === null && recipientError === null
@@ -529,6 +533,11 @@ export default function Swap() {
{t('orTransFail')}
+ {(slippageWarning || highSlippageWarning) && (
+
+ ⚠️
+
+ )}
{t('priceChange')} {b(`${percentSlippageFormatted}%`)}.
@@ -586,13 +595,21 @@ export default function Swap() {
isError = true
}
+ const slippageWarningText = highSlippageWarning
+ ? t('highSlippageWarning')
+ : slippageWarning
+ ? t('slippageWarning')
+ : ''
+
return (
)
@@ -755,8 +772,8 @@ export default function Swap() {
{renderSummary()}
-
- {t('swap')}
+
+ {highSlippageWarning ? t('sendAnyway') : t('send')}
>
diff --git a/src/pages/Swap/index.js b/src/pages/Swap/index.js
index 1c2336b40a..7719637898 100644
--- a/src/pages/Swap/index.js
+++ b/src/pages/Swap/index.js
@@ -481,7 +481,11 @@ export default function Swap() {
.sub(ethers.utils.bigNumberify(3).mul(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(15))))
: undefined
const percentSlippageFormatted = percentSlippage && amountFormatter(percentSlippage, 16, 2)
- const slippageWarning = percentSlippage && percentSlippage.gte(ethers.utils.parseEther('.1')) // 10%
+ const slippageWarning =
+ percentSlippage &&
+ percentSlippage.gte(ethers.utils.parseEther('.05')) &&
+ percentSlippage.lt(ethers.utils.parseEther('.2')) // [5% - 20%)
+ const highSlippageWarning = percentSlippage && percentSlippage.gte(ethers.utils.parseEther('.2')) // [20+%
const isValid = exchangeRate && inputError === null && independentError === null
@@ -524,6 +528,11 @@ export default function Swap() {
{t('orTransFail')}
+ {(slippageWarning || highSlippageWarning) && (
+
+ ⚠️
+
+ )}
{t('priceChange')} {b(`${percentSlippageFormatted}%`)}.
@@ -577,13 +586,21 @@ export default function Swap() {
isError = true
}
+ const slippageWarningText = highSlippageWarning
+ ? t('highSlippageWarning')
+ : slippageWarning
+ ? t('slippageWarning')
+ : ''
+
return (
)
@@ -726,8 +743,8 @@ export default function Swap() {
{renderSummary()}
-
- {t('swap')}
+
+ {highSlippageWarning ? t('swapAnyway') : t('swap')}
>
diff --git a/src/theme/components.js b/src/theme/components.js
index b5ba861b16..a043f4d07a 100644
--- a/src/theme/components.js
+++ b/src/theme/components.js
@@ -1,7 +1,9 @@
import styled from 'styled-components'
import { lighten, darken } from 'polished'
-export const Button = styled.button`
+export const Button = styled.button.attrs({
+ backgroundColor: ({ warning, theme }) => (warning ? theme.salmonRed : theme.royalBlue)
+})`
padding: 1rem 2rem 1rem 2rem;
border-radius: 3rem;
cursor: pointer;
@@ -9,18 +11,18 @@ export const Button = styled.button`
font-size: 1rem;
border: none;
outline: none;
- background-color: ${({ theme }) => theme.royalBlue};
+ background-color: ${({ backgroundColor }) => backgroundColor};
color: ${({ theme }) => theme.white};
transition: background-color 125ms ease-in-out;
width: 100%;
:hover,
:focus {
- background-color: ${({ theme }) => lighten(0.05, theme.royalBlue)};
+ background-color: ${({ backgroundColor }) => lighten(0.05, backgroundColor)};
}
:active {
- background-color: ${({ theme }) => darken(0.05, theme.royalBlue)};
+ background-color: ${({ backgroundColor }) => darken(0.05, backgroundColor)};
}
:disabled {
diff --git a/src/theme/index.js b/src/theme/index.js
index 57a3f6e964..04d0e0622d 100644
--- a/src/theme/index.js
+++ b/src/theme/index.js
@@ -57,6 +57,8 @@ const theme = {
salmonRed: '#FF6871',
// orange
pizazzOrange: '#FF8F05',
+ // yellows
+ warningYellow: '#FFE270',
// pink
uniswapPink: '#DC6BE5',
connectedGreen: '#27AE60',
diff --git a/src/utils/index.js b/src/utils/index.js
index 3d39a0ee7b..13a5d80e85 100644
--- a/src/utils/index.js
+++ b/src/utils/index.js
@@ -6,6 +6,8 @@ import ERC20_ABI from '../constants/abis/erc20'
import ERC20_BYTES32_ABI from '../constants/abis/erc20_bytes32'
import { FACTORY_ADDRESSES } from '../constants'
+import UncheckedJsonRpcSigner from './signer'
+
export const ERROR_CODES = ['TOKEN_NAME', 'TOKEN_SYMBOL', 'TOKEN_DECIMALS'].reduce(
(accumulator, currentValue, currentIndex) => {
accumulator[currentValue] = currentIndex
@@ -93,7 +95,7 @@ export function calculateGasMargin(value, margin) {
// account is optional
export function getProviderOrSigner(library, account) {
- return account ? library.getSigner(account) : library
+ return account ? new UncheckedJsonRpcSigner(library.getSigner(account)) : library
}
// account is optional
diff --git a/src/utils/signer.js b/src/utils/signer.js
new file mode 100644
index 0000000000..738b6e797c
--- /dev/null
+++ b/src/utils/signer.js
@@ -0,0 +1,36 @@
+import * as ethers from 'ethers'
+
+export default class UncheckedJsonRpcSigner extends ethers.Signer {
+ constructor(signer) {
+ super()
+ ethers.utils.defineReadOnly(this, 'signer', signer)
+ ethers.utils.defineReadOnly(this, 'provider', signer.provider)
+ }
+
+ getAddress() {
+ return this.signer.getAddress()
+ }
+
+ sendTransaction(transaction) {
+ return this.signer.sendUncheckedTransaction(transaction).then(hash => {
+ return {
+ hash: hash,
+ nonce: null,
+ gasLimit: null,
+ gasPrice: null,
+ data: null,
+ value: null,
+ chainId: null,
+ confirmations: 0,
+ from: null,
+ wait: confirmations => {
+ return this.signer.provider.waitForTransaction(hash, confirmations)
+ }
+ }
+ })
+ }
+
+ signMessage(message) {
+ return this.signer.signMessage(message)
+ }
+}
diff --git a/yarn.lock b/yarn.lock
index 21b937dd1e..3a17f4cfc6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4715,7 +4715,23 @@ ethereumjs-wallet@0.6.2:
utf8 "^3.0.0"
uuid "^3.3.2"
-ethers@^4.0.27, ethers@~4.0.4:
+ethers@^4.0.28:
+ version "4.0.28"
+ resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.28.tgz#74d9acb57f4ede3337c8d60476b38d0fe646af01"
+ integrity sha512-5JTHrPoFLqf+xCAI3pKwXSOgWBSJJoAUdPtPLr1ZlKbSKiIFMkPlRNovmZS3jhIw5sHW1YoVWOaJ6ZR2gKRbwg==
+ dependencies:
+ "@types/node" "^10.3.2"
+ aes-js "3.0.0"
+ bn.js "^4.4.0"
+ elliptic "6.3.3"
+ hash.js "1.1.3"
+ js-sha3 "0.5.7"
+ scrypt-js "2.0.4"
+ setimmediate "1.0.4"
+ uuid "2.0.1"
+ xmlhttprequest "1.8.0"
+
+ethers@~4.0.4:
version "4.0.27"
resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.27.tgz#e570b0da9d805ad65c83d81919abe02b2264c6bf"
dependencies: