Commit 78cdf11b authored by Noah Zinsmeister's avatar Noah Zinsmeister Committed by GitHub

Liquidity migration (#279)

* finalize initial migration

* remove redux

* fix token to token rate bug

* finalize initial migration

* fix test
parent eb394ee8
......@@ -47,7 +47,7 @@
"noZero": "Amount cannot be zero.",
"mustBeETH": "One of the input must be ETH.",
"enterCurrencyOrLabelCont": "Enter a {{ inputCurrency }} or {{ label }} value to continue.",
"youAreAdding": "You are adding between",
"youAreAdding": "You are adding",
"and": "and",
"intoPool": "into the liquidity pool.",
"outPool": "from the liquidity pool.",
......
......@@ -26,7 +26,7 @@ export default function CurrencyInputPanel({
title,
description,
extraText,
extraTextClickHander,
extraTextClickHander = () => {},
errorMessage,
disableUnlock,
disableTokenSelect,
......
import React, { useCallback } from 'react'
import { withRouter, NavLink } from 'react-router-dom'
import { connect } from 'react-redux'
import { useTranslation } from 'react-i18next'
import { dismissBetaMessage } from '../../ducks/app'
import { useBodyKeyDown } from '../../hooks'
import './navigation-tabs.scss'
import { useApplicationContext } from '../../contexts/Application'
const tabOrder = [
{
......@@ -26,9 +25,11 @@ const tabOrder = [
}
]
function NavigationTabs({ location: { pathname }, history, dismissBetaMessage, showBetaMessage }) {
function NavigationTabs({ location: { pathname }, history }) {
const { t } = useTranslation()
const { showBetaMessage, dismissBetaMessage } = useApplicationContext()
const navigate = useCallback(
direction => {
const tabIndex = tabOrder.findIndex(({ regex }) => pathname.match(regex))
......@@ -73,13 +74,4 @@ function NavigationTabs({ location: { pathname }, history, dismissBetaMessage, s
)
}
export default withRouter(
connect(
state => ({
showBetaMessage: state.app.showBetaMessage
}),
dispatch => ({
dismissBetaMessage: () => dispatch(dismissBetaMessage())
})
)(NavigationTabs)
)
export default withRouter(NavigationTabs)
// string literals for actions
// set global web3 object
export const INITIALIZE_GLOBAL_WEB3 = 'INITIALIZE_GLOBAL_WEB3'
// web3 actions, all set from action creator to reducer to app
export const SET_WEB3_CONNECTION_STATUS = 'WEB3_CONNECTION_STATUS'
export const CHECK_WEB3_CONNECTION = 'CHECK_WEB3_CONNECTION'
export const SET_CURRENT_MASK_ADDRESS = 'SET_CURRENT_MASK_ADDRESS'
export const SET_INTERACTION_STATE = 'SET_INTERACTION_STATE'
export const SET_NETWORK_MESSAGE = 'SET_NETWORK_MESSAGE'
export const SET_BLOCK_TIMESTAMP = 'SET_BLOCK_TIMESTAMP'
export const SET_EXCHANGE_TYPE = 'SET_EXCHANGE_TYPE'
// actions to toggle divs
export const TOGGLE_ABOUT = 'TOGGLE_ABOUT'
export const TOGGLE_INVEST = 'TOGGLE_INVEST'
// CONTRACT actions in actions, action creator, reducer
export const FACTORY_CONTRACT_READY = 'FACTORY_CONTRACT_READY'
export const EXCHANGE_CONTRACT_READY = 'EXCHANGE_CONTRACT_READY'
export const TOKEN_CONTRACT_READY = 'TOKEN_CONTRACT_READY'
// actions for the exchange
export const SET_INPUT_BALANCE = 'SET_INPUT_BALANCE'
export const SET_OUTPUT_BALANCE = 'SET_OUTPUT_BALANCE'
export const SET_INPUT_TOKEN = 'SET_INPUT_TOKEN'
export const SET_OUTPUT_TOKEN = 'SET_OUTPUT_TOKEN'
export const SET_ETH_POOL_1 = 'SET_ETH_POOL_1'
export const SET_ETH_POOL_2 = 'SET_ETH_POOL_2'
export const SET_TOKEN_POOL_1 = 'SET_TOKEN_POOL_1'
export const SET_TOKEN_POOL_2 = 'SET_TOKEN_POOL_2'
export const SET_ALLOWANCE_APPROVAL_STATE = 'SET_ALLOWANCE_APPROVAL_STATE'
export const SET_EXCHANGE_INPUT_VALUE = 'SET_EXCHANGE_INPUT_VALUE'
export const SET_EXCHANGE_OUTPUT_VALUE = 'SET_EXCHANGE_OUTPUT_VALUE'
export const SET_EXCHANGE_RATE = 'SET_EXCHANGE_RATE'
export const SET_EXCHANGE_FEE = 'SET_EXCHANGE_FEE'
export const SET_INVEST_TOKEN = 'SET_INVEST_TOKEN'
export const SET_INVEST_ETH_POOL = 'SET_INVEST_ETH'
export const SET_INVEST_TOKEN_POOL = 'SET_INVEST_TOKENS'
export const SET_INVEST_TOKEN_ALLOWANCE = 'SET_INVEST_TOKEN_ALLOWANCE'
export const SET_INVEST_SHARES = 'SET_INVEST_SHARES'
export const SET_USER_SHARES = 'SET_USER_SHARES'
export const SET_INVEST_TOKEN_BALANCE = 'SET_INVEST_TOKEN_BALANCE'
export const SET_INVEST_ETH_BALANCE = 'SET_INVEST_ETH_BALANCE'
export const SET_INVEST_SHARES_INPUT = 'SET_INVEST_SHARES_INPUT'
export const SET_INVEST_ETH_REQUIRED = 'SET_INVEST_ETH_REQUIRED'
export const SET_INVEST_TOKENS_REQUIRED = 'SET_INVEST_TOKENS_REQUIRED'
export const SET_INVEST_CHECKED = 'SET_INVEST_CHECKED'
export const INSUFFICIENT_BALANCE = 'Insufficient balance'
......@@ -116,6 +116,12 @@ const TOKEN_DETAILS_MAINNET = {
[DECIMALS]: 18,
[EXCHANGE_ADDRESS]: '0xF173214C720f58E03e194085B1DB28B50aCDeeaD'
},
'0x6c6EE5e31d828De241282B9606C8e98Ea48526E2': {
[NAME]: 'HoloToken',
[SYMBOL]: 'HOT',
[DECIMALS]: 18,
[EXCHANGE_ADDRESS]: '0xd4777E164c6C683E10593E08760B803D58529a8E'
},
'0xA4e8C3Ec456107eA67d3075bF9e3DF3A75823DB0': {
[NAME]: 'LoomToken',
[SYMBOL]: 'LOOM',
......
const RINKEBY = {
factoryAddress: '0xf5D915570BC477f9B8D6C0E980aA81757A3AaC36',
exchangeAddresses: {
addresses: [
['BAT', '0x9B913956036a3462330B0642B20D3879ce68b450'],
['DAI', '0x77dB9C915809e7BE439D2AB21032B1b8B58F6891'],
['MKR', '0x93bB63aFe1E0180d0eF100D774B473034fd60C36'],
['OMG', '0x26C226EBb6104676E593F8A070aD6f25cDa60F8D']
// ['ZRX','0xaBD44a1D1b9Fb0F39fE1D1ee6b1e2a14916D067D'],
],
fromToken: {
'0xDA5B056Cfb861282B4b59d29c9B395bcC238D29B': '0x9B913956036a3462330B0642B20D3879ce68b450',
'0x2448eE2641d78CC42D7AD76498917359D961A783': '0x77dB9C915809e7BE439D2AB21032B1b8B58F6891',
'0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85': '0x93bB63aFe1E0180d0eF100D774B473034fd60C36',
'0x879884c3C46A24f56089f3bBbe4d5e38dB5788C0': '0x26C226EBb6104676E593F8A070aD6f25cDa60F8D'
// '0xF22e3F33768354c9805d046af3C0926f27741B43': '0xaBD44a1D1b9Fb0F39fE1D1ee6b1e2a14916D067D',
}
},
tokenAddresses: {
addresses: [
['BAT', '0xDA5B056Cfb861282B4b59d29c9B395bcC238D29B'],
['DAI', '0x2448eE2641d78CC42D7AD76498917359D961A783'],
['MKR', '0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85'],
['OMG', '0x879884c3C46A24f56089f3bBbe4d5e38dB5788C0']
// ['ZRX','0xF22e3F33768354c9805d046af3C0926f27741B43'],
]
}
}
const MAIN = {
factoryAddress: '0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95',
exchangeAddresses: {
addresses: [
['ANT', '0x077d52B047735976dfdA76feF74d4d988AC25196'],
['BAT', '0x2E642b8D59B45a1D8c5aEf716A84FF44ea665914'],
['BLT', '0x0E6A53B13688018A3df8C69f99aFB19A3068D04f'],
['BNT', '0x87d80DBD37E551F58680B4217b23aF6a752DA83F'],
['C20', '0xf7b5a4b934658025390ff69db302bc7f2ac4a542'],
['CVC', '0x1C6c712b1F4a7c263B1DBd8F97fb447c945d3b9a'],
['DAI', '0x09cabEC1eAd1c0Ba254B09efb3EE13841712bE14'],
['DGX', '0xb92dE8B30584392Af27726D5ce04Ef3c4e5c9924'],
['FOAM', '0xf79cb3BEA83BD502737586A6E8B133c378FD1fF2'],
['FUN', '0x60a87cC7Fca7E53867facB79DA73181B1bB4238B'],
['GNO', '0xe8e45431b93215566BA923a7E611B7342Ea954DF'],
['GRID', '0x4B17685b330307C751B47f33890c8398dF4Fe407'],
['GUSD', '0xD883264737Ed969d2696eE4B4cAF529c2Fc2A141'],
['KIN', '0xb7520a5F8c832c573d6BD0Df955fC5c9b72400F7'],
['KNC', '0x49c4f9bc14884f6210F28342ceD592A633801a8b'],
['LINK', '0xF173214C720f58E03e194085B1DB28B50aCDeeaD'],
['LOOM', '0x417CB32bc991fBbDCaE230C7c4771CC0D69daA6b'],
['LPT', '0xc4a1C45D5546029Fd57128483aE65b56124BFA6A'],
['MANA', '0xC6581Ce3A005e2801c1e0903281BBd318eC5B5C2'],
['MKR', '0x2C4Bd064b998838076fa341A83d007FC2FA50957'],
['NEXO', '0x069C97DBA948175D10af4b2414969e0B88d44669'],
['NMR', '0x2Bf5A5bA29E60682fC56B2Fcf9cE07Bef4F6196f'],
['PAX', '0xC040d51b07Aea5d94a89Bc21E8078B77366Fc6C7'],
['QCH', '0x755899F0540c3548b99E68C59AdB0f15d2695188'],
['RDN', '0x7D03CeCb36820b4666F45E1b4cA2538724Db271C'],
['REN', '0x43892992B0b102459E895B88601Bb2C76736942c'],
['REP', '0x48B04d2A05B6B604d8d5223Fd1984f191DED51af'],
['RLC', '0xA825CAE02B310E9901b4776806CE25db520c8642'],
['RHOC', '0x394e524b47A3AB3D3327f7fF6629dC378c1494a3'],
['SALT', '0xC0C59cDe851bfcbdddD3377EC10ea54A18Efb937'],
['SNT', '0x1aEC8F11A7E78dC22477e91Ed924Fab46e3A88Fd'],
['SNX', '0x5d8888a212d033cff5f2e0ac24ad91a5495bad62'],
['SPANK', '0x4e395304655F0796bc3bc63709DB72173b9DdF98'],
['SUSD', '0xa1ecdcca26150cf69090280ee2ee32347c238c7b'],
['TKN', '0xb6cFBf322db47D39331E306005DC7E5e6549942B'],
['TUSD', '0x4F30E682D0541eAC91748bd38A648d759261b8f3'],
['USDC', '0x97deC872013f6B5fB443861090ad931542878126'],
['VERI', '0x17e5BF07D696eaf0d14caA4B44ff8A1E17B34de3'],
['WBTC', '0x4d2f5cFbA55AE412221182D8475bC85799A5644b'],
['WETH', '0xA2881A90Bf33F03E7a3f803765Cd2ED5c8928dFb'],
['XCHF', '0x8dE0d002DC83478f479dC31F76cB0a8aa7CcEa17'],
['ZIL', '0x7dc095A5CF7D6208CC680fA9866F80a53911041a'],
['ZRX', '0xaE76c84C9262Cdb9abc0C2c8888e62Db8E22A0bF']
],
fromToken: {
'0x960b236A07cf122663c4303350609A66A7B288C0': '0x077d52B047735976dfdA76feF74d4d988AC25196',
'0x0D8775F648430679A709E98d2b0Cb6250d2887EF': '0x2E642b8D59B45a1D8c5aEf716A84FF44ea665914',
'0x107c4504cd79C5d2696Ea0030a8dD4e92601B82e': '0x0E6A53B13688018A3df8C69f99aFB19A3068D04f',
'0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C': '0x87d80DBD37E551F58680B4217b23aF6a752DA83F',
'0x26E75307Fc0C021472fEb8F727839531F112f317': '0xf7b5a4b934658025390ff69db302bc7f2ac4a542',
'0x41e5560054824eA6B0732E656E3Ad64E20e94E45': '0x1C6c712b1F4a7c263B1DBd8F97fb447c945d3b9a',
'0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359': '0x09cabEC1eAd1c0Ba254B09efb3EE13841712bE14',
'0x4f3AfEC4E5a3F2A6a1A411DEF7D7dFe50eE057bF': '0xb92dE8B30584392Af27726D5ce04Ef3c4e5c9924',
'0x4946Fcea7C692606e8908002e55A582af44AC121': '0xf79cb3BEA83BD502737586A6E8B133c378FD1fF2',
'0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b': '0x60a87cC7Fca7E53867facB79DA73181B1bB4238B',
'0x6810e776880C02933D47DB1b9fc05908e5386b96': '0xe8e45431b93215566BA923a7E611B7342Ea954DF',
'0x12B19D3e2ccc14Da04FAe33e63652ce469b3F2FD': '0x4B17685b330307C751B47f33890c8398dF4Fe407',
'0x056Fd409E1d7A124BD7017459dFEa2F387b6d5Cd': '0xD883264737Ed969d2696eE4B4cAF529c2Fc2A141',
'0x818Fc6C2Ec5986bc6E2CBf00939d90556aB12ce5': '0xb7520a5F8c832c573d6BD0Df955fC5c9b72400F7',
'0xdd974D5C2e2928deA5F71b9825b8b646686BD200': '0x49c4f9bc14884f6210F28342ceD592A633801a8b',
'0x514910771AF9Ca656af840dff83E8264EcF986CA': '0xF173214C720f58E03e194085B1DB28B50aCDeeaD',
'0xA4e8C3Ec456107eA67d3075bF9e3DF3A75823DB0': '0x417CB32bc991fBbDCaE230C7c4771CC0D69daA6b',
'0x58b6A8A3302369DAEc383334672404Ee733aB239': '0xc4a1C45D5546029Fd57128483aE65b56124BFA6A',
'0x0F5D2fB29fb7d3CFeE444a200298f468908cC942': '0xC6581Ce3A005e2801c1e0903281BBd318eC5B5C2',
'0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2': '0x2C4Bd064b998838076fa341A83d007FC2FA50957',
'0xB62132e35a6c13ee1EE0f84dC5d40bad8d815206': '0x069C97DBA948175D10af4b2414969e0B88d44669',
'0x1776e1F26f98b1A5dF9cD347953a26dd3Cb46671': '0x2Bf5A5bA29E60682fC56B2Fcf9cE07Bef4F6196f',
'0x8E870D67F660D95d5be530380D0eC0bd388289E1': '0xC040d51b07Aea5d94a89Bc21E8078B77366Fc6C7',
'0x687BfC3E73f6af55F0CccA8450114D107E781a0e': '0x755899F0540c3548b99E68C59AdB0f15d2695188',
'0x255Aa6DF07540Cb5d3d297f0D0D4D84cb52bc8e6': '0x7D03CeCb36820b4666F45E1b4cA2538724Db271C',
'0x408e41876cCCDC0F92210600ef50372656052a38': '0x43892992B0b102459E895B88601Bb2C76736942c',
'0x1985365e9f78359a9B6AD760e32412f4a445E862': '0x48B04d2A05B6B604d8d5223Fd1984f191DED51af',
'0x607F4C5BB672230e8672085532f7e901544a7375': '0xA825CAE02B310E9901b4776806CE25db520c8642',
'0x168296bb09e24A88805CB9c33356536B980D3fC5': '0x394e524b47A3AB3D3327f7fF6629dC378c1494a3',
'0x4156D3342D5c385a87D264F90653733592000581': '0xC0C59cDe851bfcbdddD3377EC10ea54A18Efb937',
'0x744d70FDBE2Ba4CF95131626614a1763DF805B9E': '0x1aEC8F11A7E78dC22477e91Ed924Fab46e3A88Fd',
'0x42d6622deCe394b54999Fbd73D108123806f6a18': '0x4e395304655F0796bc3bc63709DB72173b9DdF98',
'0xaAAf91D9b90dF800Df4F55c205fd6989c977E73a': '0xb6cFBf322db47D39331E306005DC7E5e6549942B',
'0x8dd5fbCe2F6a956C3022bA3663759011Dd51e73E': '0x4F30E682D0541eAC91748bd38A648d759261b8f3',
'0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48': '0x97deC872013f6B5fB443861090ad931542878126',
'0x8f3470A7388c05eE4e7AF3d01D8C722b0FF52374': '0x17e5BF07D696eaf0d14caA4B44ff8A1E17B34de3',
'0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599': '0x4d2f5cFbA55AE412221182D8475bC85799A5644b',
'0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2': '0xA2881A90Bf33F03E7a3f803765Cd2ED5c8928dFb',
'0xB4272071eCAdd69d933AdcD19cA99fe80664fc08': '0x8dE0d002DC83478f479dC31F76cB0a8aa7CcEa17',
'0x05f4a42e251f2d52b8ed15E9FEdAacFcEF1FAD27': '0x7dc095A5CF7D6208CC680fA9866F80a53911041a',
'0xE41d2489571d322189246DaFA5ebDe1F4699F498': '0xaE76c84C9262Cdb9abc0C2c8888e62Db8E22A0bF',
'0x3772f9716Cf6D7a09edE3587738AA2af5577483a': '0x5d8888a212d033cff5f2e0ac24ad91a5495bad62',
'0x0cbe2df57ca9191b64a7af3baa3f946fa7df2f25': '0xa1ecdcca26150cf69090280ee2ee32347c238c7b'
}
},
tokenAddresses: {
addresses: [
['ANT', '0x960b236A07cf122663c4303350609A66A7B288C0'],
['BAT', '0x0D8775F648430679A709E98d2b0Cb6250d2887EF'],
['BLT', '0x107c4504cd79C5d2696Ea0030a8dD4e92601B82e'],
['BNT', '0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C'],
['C20', '0x26E75307Fc0C021472fEb8F727839531F112f317'],
['CVC', '0x41e5560054824eA6B0732E656E3Ad64E20e94E45'],
['DAI', '0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359'],
['DGX', '0x4f3AfEC4E5a3F2A6a1A411DEF7D7dFe50eE057bF'],
['FOAM', '0x4946Fcea7C692606e8908002e55A582af44AC121'],
['FUN', '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b'],
['GNO', '0x6810e776880C02933D47DB1b9fc05908e5386b96'],
['GRID', '0x12B19D3e2ccc14Da04FAe33e63652ce469b3F2FD'],
['GUSD', '0x056Fd409E1d7A124BD7017459dFEa2F387b6d5Cd'],
['KIN', '0x818Fc6C2Ec5986bc6E2CBf00939d90556aB12ce5'],
['KNC', '0xdd974D5C2e2928deA5F71b9825b8b646686BD200'],
['LINK', '0x514910771AF9Ca656af840dff83E8264EcF986CA'],
['LOOM', '0xA4e8C3Ec456107eA67d3075bF9e3DF3A75823DB0'],
['LPT', '0x58b6A8A3302369DAEc383334672404Ee733aB239'],
['MANA', '0x0F5D2fB29fb7d3CFeE444a200298f468908cC942'],
['MKR', '0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2'],
['NEXO', '0xB62132e35a6c13ee1EE0f84dC5d40bad8d815206'],
['NMR', '0x1776e1F26f98b1A5dF9cD347953a26dd3Cb46671'],
['PAX', '0x8E870D67F660D95d5be530380D0eC0bd388289E1'],
['QCH', '0x687BfC3E73f6af55F0CccA8450114D107E781a0e'],
['RDN', '0x255Aa6DF07540Cb5d3d297f0D0D4D84cb52bc8e6'],
['REN', '0x408e41876cCCDC0F92210600ef50372656052a38'],
['REP', '0x1985365e9f78359a9B6AD760e32412f4a445E862'],
['RLC', '0x607F4C5BB672230e8672085532f7e901544a7375'],
['RHOC', '0x168296bb09e24A88805CB9c33356536B980D3fC5'],
['SALT', '0x4156D3342D5c385a87D264F90653733592000581'],
['SNT', '0x744d70FDBE2Ba4CF95131626614a1763DF805B9E'],
['SNX', '0x3772f9716Cf6D7a09edE3587738AA2af5577483a'],
['SPANK', '0x42d6622deCe394b54999Fbd73D108123806f6a18'],
['SUSD', '0x0cbe2df57ca9191b64a7af3baa3f946fa7df2f25'],
['TKN', '0xaAAf91D9b90dF800Df4F55c205fd6989c977E73a'],
['TUSD', '0x8dd5fbCe2F6a956C3022bA3663759011Dd51e73E'],
['USDC', '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'],
['VERI', '0x8f3470A7388c05eE4e7AF3d01D8C722b0FF52374'],
['WBTC', '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599'],
['WETH', '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'],
['XCHF', '0xB4272071eCAdd69d933AdcD19cA99fe80664fc08'],
['ZIL', '0x05f4a42e251f2d52b8ed15E9FEdAacFcEF1FAD27'],
['ZRX', '0xE41d2489571d322189246DaFA5ebDe1F4699F498']
]
}
}
const SET_ADDRESSES = 'app/addresses/setAddresses'
const ADD_EXCHANGE = 'app/addresses/addExchange'
const initialState = RINKEBY
export const addExchange = ({ label, exchangeAddress, tokenAddress }) => (dispatch, getState) => {
const {
addresses: { tokenAddresses, exchangeAddresses }
} = getState()
if (tokenAddresses.addresses.filter(([symbol]) => symbol === label).length) {
return
}
if (exchangeAddresses.fromToken[tokenAddresses]) {
return
}
dispatch({
type: ADD_EXCHANGE,
payload: {
label,
exchangeAddress,
tokenAddress
}
})
}
export const setAddresses = networkId => {
switch (networkId) {
// Main Net
case 1:
case '1':
return {
type: SET_ADDRESSES,
payload: MAIN
}
// Rinkeby
case 4:
case '4':
return {
type: SET_ADDRESSES,
payload: RINKEBY
}
default:
return {
type: SET_ADDRESSES,
payload: RINKEBY
}
}
}
export default (state = initialState, { type, payload }) => {
switch (type) {
case SET_ADDRESSES:
return payload
case ADD_EXCHANGE:
return handleAddExchange(state, { payload })
default:
return state
}
}
function handleAddExchange(state, { payload }) {
const { label, tokenAddress, exchangeAddress } = payload
if (!label || !tokenAddress || !exchangeAddress) {
return state
}
return {
...state,
exchangeAddresses: {
...state.exchangeAddresses,
addresses: [...state.exchangeAddresses.addresses, [label, exchangeAddress]],
fromToken: {
...state.exchangeAddresses.fromToken,
[tokenAddress]: exchangeAddress
}
},
tokenAddresses: {
...state.tokenAddresses,
addresses: [...state.tokenAddresses.addresses, [label, tokenAddress]]
}
}
}
const DISMISS_BETA_MESSAGE = 'app/app/dismissBetaMessage'
const initialState = {
showBetaMessage: true
}
export const dismissBetaMessage = () => ({ type: DISMISS_BETA_MESSAGE })
export default function appReducer(state = initialState, { type, payload }) {
switch (type) {
case DISMISS_BETA_MESSAGE:
return { ...state, showBetaMessage: false }
default:
return state
}
}
import { combineReducers } from 'redux'
import addresses from './addresses'
import app from './app'
import pending from './pending'
import web3connect from './web3connect'
export default combineReducers({
app,
addresses,
pending,
web3connect
})
const ADD_APPROVAL_TX = 'app/send/addApprovalTx'
const getInitialState = () => {
return {
approvals: {}
}
}
export const addApprovalTx = ({ tokenAddress, txId }) => ({
type: ADD_APPROVAL_TX,
payload: { tokenAddress, txId }
})
export default function sendReducer(state = getInitialState(), { type, payload }) {
switch (type) {
case ADD_APPROVAL_TX:
return {
approvals: {
...state.approvals,
[payload.tokenAddress]: payload.txId
}
}
default:
return state
}
}
import { BigNumber as BN } from 'bignumber.js'
import Web3 from 'web3'
import ERC20_ABI from '../abi/erc20'
import ERC20_WITH_BYTES_ABI from '../abi/erc20_bytes32'
export const INITIALIZE = 'web3connect/initialize'
export const INITIALIZE_WEB3 = 'web3connect/initializeWeb3'
export const UPDATE_ACCOUNT = 'web3connect/updateAccount'
export const WATCH_ETH_BALANCE = 'web3connect/watchEthBalance'
export const WATCH_TOKEN_BALANCE = 'web3connect/watchTokenBalance'
export const UPDATE_ETH_BALANCE = 'web3connect/updateEthBalance'
export const UPDATE_TOKEN_BALANCE = 'web3connect/updateTokenBalance'
export const WATCH_APPROVALS = 'web3connect/watchApprovals'
export const UPDATE_APPROVALS = 'web3connect/updateApprovals'
export const ADD_CONTRACT = 'web3connect/addContract'
export const UPDATE_NETWORK_ID = 'web3connect/updateNetworkId'
export const ADD_PENDING_TX = 'web3connect/addPendingTx'
export const REMOVE_PENDING_TX = 'web3connect/removePendingTx'
export const ADD_CONFIRMED_TX = 'web3connect/addConfirmedTx'
const initialState = {
web3: null,
networkId: 0,
initialized: false,
account: null,
balances: {
ethereum: {}
},
approvals: {
'0x0': {
TOKEN_OWNER: {
SPENDER: {}
}
}
},
transactions: {
pending: [],
confirmed: []
},
watched: {
balances: {
ethereum: []
},
approvals: {}
},
contracts: {}
}
// selectors
export const selectors = () => (dispatch, getState) => {
const state = getState().web3connect
const getTokenBalance = (tokenAddress, address) => {
const tokenBalances = state.balances[tokenAddress] || {}
const balance = tokenBalances[address]
if (!balance) {
dispatch(watchBalance({ balanceOf: address, tokenAddress }))
return Balance(0)
}
return balance
}
const getBalance = (address, tokenAddress) => {
if (!tokenAddress || tokenAddress === 'ETH') {
const balance = state.balances.ethereum[address]
if (!balance) {
dispatch(watchBalance({ balanceOf: address }))
return Balance(0, 'ETH')
}
return balance
} else if (tokenAddress) {
return getTokenBalance(tokenAddress, address)
}
return Balance(NaN)
}
const getApprovals = (tokenAddress, tokenOwner, spender) => {
const token = state.approvals[tokenAddress] || {}
const owner = token[tokenOwner] || {}
if (!owner[spender]) {
dispatch(watchApprovals({ tokenAddress, tokenOwner, spender }))
return Balance(0)
}
return owner[spender]
}
return {
getBalance,
getTokenBalance,
getApprovals
}
}
const Balance = (value, label = '', decimals = 0) => ({
value: BN(value),
label: label.toUpperCase(),
decimals: +decimals
})
export const initialize = () => async dispatch => {
await dispatch({ type: INITIALIZE })
}
export const updateNetwork = (passedProvider, networkId) => async dispatch => {
const web3 = new Web3(passedProvider)
const dispatches = [
dispatch({ type: INITIALIZE_WEB3, payload: web3 }),
dispatch({ type: UPDATE_NETWORK_ID, payload: networkId })
]
await Promise.all(dispatches)
}
export const updateAccount = account => async dispatch => {
if (account !== null) {
const dispatches = [
dispatch({ type: UPDATE_ACCOUNT, payload: account }),
dispatch(watchBalance({ balanceOf: account }))
]
await Promise.all(dispatches)
}
}
export const watchBalance = ({ balanceOf, tokenAddress }) => (dispatch, getState) => {
if (!balanceOf) {
return
}
const { web3connect } = getState()
const { watched } = web3connect
if (!tokenAddress) {
if (watched.balances.ethereum.includes(balanceOf)) {
return
}
dispatch({
type: WATCH_ETH_BALANCE,
payload: balanceOf
})
setTimeout(() => dispatch(sync()), 0)
} else if (tokenAddress) {
if (watched.balances[tokenAddress] && watched.balances[tokenAddress].includes(balanceOf)) {
return
}
dispatch({
type: WATCH_TOKEN_BALANCE,
payload: {
tokenAddress,
balanceOf
}
})
setTimeout(() => dispatch(sync()), 0)
}
}
export const watchApprovals = ({ tokenAddress, tokenOwner, spender }) => (dispatch, getState) => {
const {
web3connect: { watched }
} = getState()
const token = watched.approvals[tokenAddress] || {}
const owner = token[tokenOwner] || []
if (owner.includes(spender)) {
return
}
return dispatch({
type: WATCH_APPROVALS,
payload: {
tokenAddress,
tokenOwner,
spender
}
})
}
export const addPendingTx = txId => ({
type: ADD_PENDING_TX,
payload: txId
})
export const updateApprovals = ({ tokenAddress, tokenOwner, spender, balance }) => ({
type: UPDATE_APPROVALS,
payload: {
tokenAddress,
tokenOwner,
spender,
balance
}
})
export const sync = () => async (dispatch, getState) => {
const { getBalance, getApprovals } = dispatch(selectors())
const {
web3,
watched,
contracts,
transactions: { pending }
} = getState().web3connect
// Sync Ethereum Balances
watched.balances.ethereum.forEach(async address => {
const balance = await web3.eth.getBalance(address)
const { value } = getBalance(address)
if (value.isEqualTo(BN(balance))) {
return
}
dispatch({
type: UPDATE_ETH_BALANCE,
payload: {
balance: Balance(balance, 'ETH', 18),
balanceOf: address
}
})
})
// Sync Token Balances
Object.keys(watched.balances).forEach(tokenAddress => {
if (tokenAddress === 'ethereum') {
return
}
const contract = contracts[tokenAddress] || new web3.eth.Contract(ERC20_ABI, tokenAddress)
if (!contracts[tokenAddress]) {
dispatch({
type: ADD_CONTRACT,
payload: {
address: tokenAddress,
contract: contract
}
})
}
const watchlist = watched.balances[tokenAddress] || []
watchlist.forEach(async address => {
const tokenBalance = getBalance(address, tokenAddress)
const balance = await contract.methods.balanceOf(address).call()
const decimals = tokenBalance.decimals || (await contract.methods.decimals().call())
let symbol = tokenBalance.symbol
try {
symbol =
symbol ||
(await contract.methods
.symbol()
.call()
.catch())
} catch (e) {
try {
const contractBytes32 = new web3.eth.Contract(ERC20_WITH_BYTES_ABI, tokenAddress)
symbol =
symbol ||
web3.utils.hexToString(
await contractBytes32.methods
.symbol()
.call()
.catch()
)
} catch (err) {}
}
if (tokenBalance.value.isEqualTo(BN(balance)) && tokenBalance.label && tokenBalance.decimals) {
return
}
dispatch({
type: UPDATE_TOKEN_BALANCE,
payload: {
tokenAddress,
balanceOf: address,
balance: Balance(balance, symbol, decimals)
}
})
})
})
// Update Approvals
Object.entries(watched.approvals).forEach(([tokenAddress, token]) => {
const contract = contracts[tokenAddress] || new web3.eth.Contract(ERC20_ABI, tokenAddress)
Object.entries(token).forEach(([tokenOwnerAddress, tokenOwner]) => {
tokenOwner.forEach(async spenderAddress => {
if (tokenOwnerAddress !== null && tokenOwnerAddress !== 'null') {
const approvalBalance = getApprovals(tokenAddress, tokenOwnerAddress, spenderAddress)
const balance = await contract.methods.allowance(tokenOwnerAddress, spenderAddress).call()
const decimals = approvalBalance.decimals || (await contract.methods.decimals().call())
let symbol = approvalBalance.label
try {
symbol = symbol || (await contract.methods.symbol().call())
} catch (e) {
try {
const contractBytes32 = new web3.eth.Contract(ERC20_WITH_BYTES_ABI, tokenAddress)
symbol = symbol || web3.utils.hexToString(await contractBytes32.methods.symbol().call())
} catch (err) {}
}
if (approvalBalance.label && approvalBalance.value.isEqualTo(BN(balance))) {
return
}
dispatch(
updateApprovals({
tokenAddress,
tokenOwner: tokenOwnerAddress,
spender: spenderAddress,
balance: Balance(balance, symbol, decimals)
})
)
}
})
})
})
pending.forEach(async txId => {
try {
const data = (await web3.eth.getTransactionReceipt(txId)) || {}
// If data is an empty obj, then it's still pending.
if (!('status' in data)) {
return
}
dispatch({
type: REMOVE_PENDING_TX,
payload: txId
})
if (data.status) {
dispatch({
type: ADD_CONFIRMED_TX,
payload: txId
})
} else {
// TODO: dispatch ADD_REJECTED_TX
}
} catch (err) {
dispatch({
type: REMOVE_PENDING_TX,
payload: txId
})
// TODO: dispatch ADD_REJECTED_TX
}
})
}
export const startWatching = () => async dispatch => {
await dispatch(sync())
setTimeout(() => dispatch(startWatching()), 5000)
}
export default function web3connectReducer(state = initialState, { type, payload }) {
switch (type) {
case INITIALIZE_WEB3:
return {
...state,
web3: payload
}
case INITIALIZE:
return {
...state,
initialized: true
}
case UPDATE_NETWORK_ID:
return { ...state, networkId: payload }
case UPDATE_ACCOUNT:
return {
...state,
account: payload
}
case WATCH_ETH_BALANCE:
return {
...state,
watched: {
...state.watched,
balances: {
...state.watched.balances,
ethereum: [...state.watched.balances.ethereum, payload]
}
}
}
case WATCH_TOKEN_BALANCE:
const { watched } = state
const { balances } = watched
const watchlist = balances[payload.tokenAddress] || []
return {
...state,
watched: {
...watched,
balances: {
...balances,
[payload.tokenAddress]: [...watchlist, payload.balanceOf]
}
}
}
case UPDATE_ETH_BALANCE:
return {
...state,
balances: {
...state.balances,
ethereum: {
...state.balances.ethereum,
[payload.balanceOf]: payload.balance
}
}
}
case UPDATE_TOKEN_BALANCE:
const tokenBalances = state.balances[payload.tokenAddress] || {}
return {
...state,
balances: {
...state.balances,
[payload.tokenAddress]: {
...tokenBalances,
[payload.balanceOf]: payload.balance
}
}
}
case ADD_CONTRACT:
return {
...state,
contracts: {
...state.contracts,
[payload.address]: payload.contract
}
}
case WATCH_APPROVALS:
const token = state.watched.approvals[payload.tokenAddress] || {}
const tokenOwner = token[payload.tokenOwner] || []
return {
...state,
watched: {
...state.watched,
approvals: {
...state.watched.approvals,
[payload.tokenAddress]: {
...token,
[payload.tokenOwner]: [...tokenOwner, payload.spender]
}
}
}
}
case UPDATE_APPROVALS:
const erc20 = state.approvals[payload.tokenAddress] || {}
const erc20Owner = erc20[payload.tokenOwner] || {}
return {
...state,
approvals: {
...state.approvals,
[payload.tokenAddress]: {
...erc20,
[payload.tokenOwner]: {
...erc20Owner,
[payload.spender]: payload.balance
}
}
}
}
case ADD_PENDING_TX:
return {
...state,
transactions: {
...state.transactions,
pending: [...state.transactions.pending, payload]
}
}
case REMOVE_PENDING_TX:
return {
...state,
transactions: {
...state.transactions,
pending: state.transactions.pending.filter(id => id !== payload)
}
}
case ADD_CONFIRMED_TX:
if (state.transactions.confirmed.includes(payload)) {
return state
}
return {
...state,
transactions: {
...state.transactions,
confirmed: [...state.transactions.confirmed, payload]
}
}
default:
return state
}
}
export function retry(func, retryCount = 5) {
return new Promise((resolve, reject) => {
func().then(
(...args) => {
resolve(...args)
},
() => {
if (retryCount === 0) {
return reject()
}
setTimeout(() => retry(func, retryCount - 1).then(resolve, reject), 50)
}
)
})
}
export default function promisify(web3, methodName, ...args) {
return new Promise((resolve, reject) => {
if (!web3) {
reject(new Error('No Web3 object'))
return
}
const method = web3.eth[methodName]
if (!method) {
reject(new Error(`Cannot find web3.eth.${methodName}`))
return
}
method(...args, (error, data) => {
if (error) {
reject(error)
return
}
resolve(data)
})
})
}
import promisify from './web3-promisfy'
export function getBlockDeadline(web3, deadline) {
return new Promise(async (resolve, reject) => {
const blockNumber = await promisify(web3, 'getBlockNumber')
if (!blockNumber && blockNumber !== 0) {
return reject()
}
const block = await promisify(web3, 'getBlock', blockNumber)
if (!block) {
return reject()
}
resolve(block.timestamp + deadline)
})
}
import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import ReactGA from 'react-ga'
import Web3Provider, { Connectors } from 'web3-react'
import ApplicationContextProvider, { Updater as ApplicationContextUpdater } from './contexts/Application'
......@@ -9,7 +8,6 @@ import StaticContextProvider, { Updater as StaticContextUpdater } from './contex
import BlockContextProvider, { Updater as BlockContextUpdater } from './contexts/Block'
import App from './pages/App'
import store from './store'
import './index.scss'
import './i18n'
......@@ -52,13 +50,11 @@ function Updaters() {
}
ReactDOM.render(
<Provider store={store}>
<Web3Provider connectors={connectors} libraryName="ethers.js">
<ContextProviders>
<Updaters />
<App />
</ContextProviders>
</Web3Provider>
</Provider>,
<Web3Provider connectors={connectors} libraryName="ethers.js">
<ContextProviders>
<Updaters />
<App />
</ContextProviders>
</Web3Provider>,
document.getElementById('root')
)
import React, { useState, useEffect } from 'react'
import { connect } from 'react-redux'
import React, { useEffect } from 'react'
import { BrowserRouter, Redirect, Route, Switch } from 'react-router-dom'
import { useWeb3Context, Connectors } from 'web3-react'
import NavigationTabs from '../components/NavigationTabs'
import { updateNetwork, updateAccount, initialize, startWatching } from '../ducks/web3connect'
import { setAddresses } from '../ducks/addresses'
import Header from '../components/Header'
import Swap from './Swap'
import Send from './Send'
......@@ -15,60 +12,34 @@ import './App.scss'
const { Connector, InjectedConnector } = Connectors
function App({ initialized, setAddresses, updateNetwork, updateAccount, initialize, startWatching }) {
const context = useWeb3Context()
export default function App() {
const { setConnector, setError, error, active, connectorName } = useWeb3Context()
// start web3-react on page-load
useEffect(() => {
context.setConnector('Injected', { suppressAndThrowErrors: true }).catch(error => {
setConnector('Injected', { suppressAndThrowErrors: true }).catch(error => {
if (error.code === Connector.errorCodes.UNSUPPORTED_NETWORK) {
context.setError(error, { connectorName: 'Injected' })
setError(error, { connectorName: 'Injected' })
} else {
context.setConnector('Infura')
setConnector('Infura')
}
})
}, []) // eslint-disable-line react-hooks/exhaustive-deps
// if the metamask user logs out, set the infura provider
useEffect(() => {
if (context.error && context.error.code === InjectedConnector.errorCodes.UNLOCK_REQUIRED) {
context.setConnector('Infura')
if (error && error.code === InjectedConnector.errorCodes.UNLOCK_REQUIRED) {
setConnector('Infura')
}
}, [context, context.error, context.connectorName])
// initialize redux network
const [reduxNetworkInitialized, setReduxNetworkInitialized] = useState(false)
useEffect(() => {
if (context.active) {
setAddresses(context.networkId)
updateNetwork(context.library._web3Provider, context.networkId)
setReduxNetworkInitialized(true)
}
}, [context.active, context.networkId]) // eslint-disable-line react-hooks/exhaustive-deps
// initialize redux account
const [reduxAccountInitialized, setReduxAccountInitialized] = useState(false)
useEffect(() => {
if (context.active) {
updateAccount(context.account)
setReduxAccountInitialized(true)
}
}, [context.active, context.account]) // eslint-disable-line react-hooks/exhaustive-deps
// initialize redux
useEffect(() => {
if (reduxNetworkInitialized && reduxAccountInitialized) {
initialize().then(startWatching)
}
}, [reduxNetworkInitialized, reduxAccountInitialized]) // eslint-disable-line react-hooks/exhaustive-deps
}, [error, connectorName, setConnector])
// active state
if (initialized || context.error) {
if (active || error) {
return (
<div id="app-container">
<Header />
{/* this is an intermediate state before infura is set */}
{initialized && (!context.error || context.error.code === InjectedConnector.errorCodes.UNLOCK_REQUIRED) && (
{(!error || error.code === InjectedConnector.errorCodes.UNLOCK_REQUIRED) && (
<div className="app__wrapper">
<div className="body">
<div className="body__content">
......@@ -100,16 +71,3 @@ function App({ initialized, setAddresses, updateNetwork, updateAccount, initiali
// loading state
return null
}
export default connect(
state => ({
initialized: state.web3connect.initialized
}),
dispatch => ({
setAddresses: networkId => dispatch(setAddresses(networkId)),
updateNetwork: (passedProvider, networkId) => dispatch(updateNetwork(passedProvider, networkId)),
updateAccount: account => dispatch(updateAccount(account)),
initialize: () => dispatch(initialize()),
startWatching: () => dispatch(startWatching())
})
)(App)
import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import Web3Provider, { Connectors } from 'web3-react'
import App from './App'
import store from '../store'
// TODO, fix this hacky workaround
const { NetworkOnlyConnector } = Connectors
......@@ -16,11 +14,9 @@ export const connectors = { Injected }
it('renders without crashing', () => {
const div = document.createElement('div')
ReactDOM.render(
<Provider store={store}>
<Web3Provider connectors={connectors} libraryName="ethers.js">
<App />
</Web3Provider>
</Provider>,
<Web3Provider connectors={connectors} libraryName="ethers.js">
<App />
</Web3Provider>,
div
)
ReactDOM.unmountComponentAtNode(div)
......
import React, { Component } from 'react'
import { connect } from 'react-redux'
import PropTypes from 'prop-types'
import React, { useReducer, useState, useCallback, useEffect, useMemo } from 'react'
import classnames from 'classnames'
import { withTranslation, useTranslation } from 'react-i18next'
import { useTranslation } from 'react-i18next'
import { useWeb3Context } from 'web3-react'
import { ethers } from 'ethers'
import ReactGA from 'react-ga'
import CurrencyInputPanel from '../../components/CurrencyInputPanel'
import OversizedPanel from '../../components/OversizedPanel'
import ContextualInfo from '../../components/ContextualInfo'
import { selectors, addPendingTx } from '../../ducks/web3connect'
import PlusBlue from '../../assets/images/plus-blue.svg'
import PlusGrey from '../../assets/images/plus-grey.svg'
import { getBlockDeadline } from '../../helpers/web3-utils'
import { retry } from '../../helpers/promise-utils'
import { BigNumber as BN } from 'bignumber.js'
import EXCHANGE_ABI from '../../abi/exchange'
import { useBlockEffect, useExchangeContract } from '../../hooks'
import { amountFormatter, calculateGasMargin } from '../../utils'
import { useTokenDetails } from '../../contexts/Static'
import { useAddressBalance, useExchangeReserves, useAddressAllowance } from '../../contexts/Block'
import './pool.scss'
import { useTransactionContext } from '../../contexts/Transaction'
const INPUT = 0
const OUTPUT = 1
class AddLiquidity extends Component {
static propTypes = {
account: PropTypes.string,
selectors: PropTypes.func.isRequired,
balances: PropTypes.object.isRequired,
exchangeAddresses: PropTypes.shape({
fromToken: PropTypes.object.isRequired
}).isRequired
}
// denominated in bips
const ALLOWED_SLIPPAGE = ethers.utils.bigNumberify(200)
state = {
inputValue: '',
outputValue: '',
inputCurrency: 'ETH',
outputCurrency: '',
lastEditedField: '',
totalSupply: BN(0)
}
// denominated in seconds
const DEADLINE_FROM_NOW = 60 * 15
reset = () => {
this.setState({
inputValue: '',
outputValue: '',
lastEditedField: ''
})
}
// denominated in bips
const GAS_MARGIN = ethers.utils.bigNumberify(1000)
shouldComponentUpdate(nextProps, nextState) {
const { t, account, exchangeAddresses, balances, web3 } = this.props
const { inputValue, outputValue, inputCurrency, outputCurrency, lastEditedField } = this.state
return (
t !== nextProps.t ||
account !== nextProps.account ||
exchangeAddresses !== nextProps.exchangeAddresses ||
web3 !== nextProps.web3 ||
balances !== nextProps.balances ||
inputValue !== nextState.inputValue ||
outputValue !== nextState.outputValue ||
inputCurrency !== nextState.inputCurrency ||
outputCurrency !== nextState.outputCurrency ||
lastEditedField !== nextState.lastEditedField
)
function calculateSlippageBounds(value) {
if (value) {
const offset = value.mul(ALLOWED_SLIPPAGE).div(ethers.utils.bigNumberify(10000))
const minimum = value.sub(offset)
const maximum = value.add(offset)
return {
minimum: minimum.lt(ethers.constants.Zero) ? ethers.constants.Zero : minimum,
maximum: maximum.gt(ethers.constants.MaxUint256) ? ethers.constants.MaxUint256 : maximum
}
} else {
return {}
}
}
componentWillReceiveProps() {
this.recalcForm()
}
const initialAddLiquidityState = {
inputValue: '',
outputValue: '',
lastEditedField: INPUT,
outputCurrency: ''
}
recalcForm = async () => {
const { outputCurrency, inputValue, outputValue, lastEditedField, totalSupply: oldTotalSupply } = this.state
const {
exchangeAddresses: { fromToken },
web3
} = this.props
const exchangeAddress = fromToken[outputCurrency]
const exchangeRate = this.getExchangeRate()
const append = {}
if (!outputCurrency || this.isNewExchange() || !web3) {
return
function addLiquidityStateReducer(state, action) {
switch (action.type) {
case 'SELECT_CURRENCY': {
return {
...state,
outputCurrency: action.payload
}
}
const exchange = new web3.eth.Contract(EXCHANGE_ABI, exchangeAddress)
const totalSupply = await exchange.methods.totalSupply().call()
if (!oldTotalSupply.isEqualTo(BN(totalSupply))) {
append.totalSupply = BN(totalSupply)
case 'UPDATE_VALUE': {
const { inputValue, outputValue } = state
const { field, value } = action.payload
return {
...state,
inputValue: field === INPUT ? value : inputValue,
outputValue: field === OUTPUT ? value : outputValue,
lastEditedField: field
}
}
if (lastEditedField === INPUT) {
const newOutputValue = exchangeRate.multipliedBy(inputValue).toFixed(7)
if (newOutputValue !== outputValue) {
append.outputValue = newOutputValue
case 'UPDATE_DEPENDENT_VALUE': {
const { inputValue, outputValue } = state
const { field, value } = action.payload
return {
...state,
inputValue: field === INPUT ? value : inputValue,
outputValue: field === OUTPUT ? value : outputValue
}
}
default: {
return initialAddLiquidityState
}
}
}
if (lastEditedField === OUTPUT) {
const newInputValue = BN(outputValue)
.dividedBy(exchangeRate)
.toFixed(7)
if (newInputValue !== inputValue) {
append.inputValue = newInputValue
function getExchangeRate(inputValue, inputDecimals, outputValue, outputDecimals, invert = false) {
try {
if (inputValue && inputDecimals && outputValue && outputDecimals) {
const factor = ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(18))
if (invert) {
return inputValue
.mul(factor)
.div(outputValue)
.mul(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(outputDecimals)))
.div(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(inputDecimals)))
} else {
return outputValue
.mul(factor)
.div(inputValue)
.mul(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(inputDecimals)))
.div(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(outputDecimals)))
}
}
} catch {}
}
this.setState(append)
}
getBalance(currency) {
const { t, selectors, account } = this.props
function getMarketRate(reserveETH, reserveToken, decimals, invert = false) {
return getExchangeRate(reserveETH, 18, reserveToken, decimals, invert)
}
if (!currency) {
return ''
export default function AddLiquidity() {
const { t } = useTranslation()
const { active, account } = useWeb3Context()
const [addLiquidityState, dispatchAddLiquidityState] = useReducer(addLiquidityStateReducer, initialAddLiquidityState)
const { inputValue, outputValue, lastEditedField, outputCurrency } = addLiquidityState
const inputCurrency = 'ETH'
const [inputValueParsed, setInputValueParsed] = useState()
const [outputValueParsed, setOutputValueParsed] = useState()
const [inputError, setInputError] = useState()
const [outputError, setOutputError] = useState()
const { symbol, decimals, exchangeAddress } = useTokenDetails(outputCurrency)
const exchangeContract = useExchangeContract(exchangeAddress)
const [totalPoolTokens, setTotalPoolTokens] = useState()
const fetchPoolTokens = useCallback(() => {
if (exchangeContract) {
exchangeContract.totalSupply().then(totalSupply => {
setTotalPoolTokens(totalSupply)
})
}
}, [exchangeContract])
useEffect(() => {
fetchPoolTokens()
}, [fetchPoolTokens])
useBlockEffect(fetchPoolTokens)
const poolTokenBalance = useAddressBalance(account, exchangeAddress)
const exchangeETHBalance = useAddressBalance(exchangeAddress, 'ETH')
const exchangeTokenBalance = useAddressBalance(exchangeAddress, outputCurrency)
const { reserveETH, reserveToken } = useExchangeReserves(outputCurrency)
const isNewExchange = !!(reserveETH && reserveToken && reserveETH.isZero() && reserveToken.isZero())
// 18 decimals
const poolTokenPercentage =
poolTokenBalance && totalPoolTokens && isNewExchange === false && !totalPoolTokens.isZero()
? poolTokenBalance.mul(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(18))).div(totalPoolTokens)
: undefined
const ethShare =
exchangeETHBalance && poolTokenPercentage
? exchangeETHBalance
.mul(poolTokenPercentage)
.div(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(18)))
: undefined
const tokenShare =
exchangeTokenBalance && poolTokenPercentage
? exchangeTokenBalance
.mul(poolTokenPercentage)
.div(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(18)))
: undefined
const liquidityMinted = isNewExchange
? inputValueParsed
: totalPoolTokens && inputValueParsed && exchangeETHBalance
? exchangeETHBalance.isZero() && totalPoolTokens.mul(inputValueParsed).div(exchangeETHBalance)
: undefined
// user balances
const inputBalance = useAddressBalance(account, inputCurrency)
const outputBalance = useAddressBalance(account, outputCurrency)
const ethPerLiquidityToken =
exchangeETHBalance && totalPoolTokens && isNewExchange === false && !totalPoolTokens.isZero()
? exchangeETHBalance.mul(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(18))).div(totalPoolTokens)
: undefined
const tokenPerLiquidityToken =
exchangeTokenBalance && totalPoolTokens && isNewExchange === false && !totalPoolTokens.isZero()
? exchangeTokenBalance.mul(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(18))).div(totalPoolTokens)
: undefined
const outputValueMax = outputValueParsed && calculateSlippageBounds(outputValueParsed).maximum
const liquidityTokensMin = liquidityMinted && calculateSlippageBounds(liquidityMinted).minimum
const marketRate = useMemo(() => {
return getMarketRate(reserveETH, reserveToken, decimals)
}, [reserveETH, reserveToken, decimals])
const marketRateInverted = useMemo(() => {
return getMarketRate(reserveETH, reserveToken, decimals, true)
}, [reserveETH, reserveToken, decimals])
function renderTransactionDetails() {
ReactGA.event({
category: 'TransactionDetail',
action: 'Open'
})
const { value, decimals } = selectors().getBalance(account, currency)
if (!decimals) {
return ''
}
const b = text => <span className="swap__highlight-text">{text}</span>
const balanceInput = value.dividedBy(10 ** decimals).toFixed(4)
return t('balance', { balanceInput })
if (isNewExchange) {
return (
<div>
<div className="pool__summary-item">
{t('youAreAdding')} {b(`${inputValue} ETH`)} {t('and')} {b(`${outputValue} ${symbol}`)} {t('intoPool')}
</div>
<div className="pool__summary-item">
{t('youAreSettingExRate')}{' '}
{b(
`1 ETH = ${amountFormatter(
getMarketRate(inputValueParsed, outputValueParsed, decimals),
18,
4,
false
)} ${symbol}`
)}
.
</div>
<div className="pool__summary-item">
{t('youWillMint')} {b(`${inputValue}`)} {t('liquidityTokens')}
</div>
<div className="pool__summary-item">{t('totalSupplyIs0')}</div>
</div>
)
} else {
return (
<>
<div className="pool__summary-modal__item">
{t('youAreAdding')} {b(`${amountFormatter(inputValueParsed, 18, 4)} ETH`)} {t('and')} {'at most'}{' '}
{b(`${amountFormatter(outputValueMax, 18, 4)} ${symbol}`)} {t('intoPool')}
</div>
<div className="pool__summary-modal__item">
{t('youWillMint')} {b(amountFormatter(liquidityMinted, 18, 4))} {t('liquidityTokens')}
</div>
<div className="pool__summary-modal__item">
{t('totalSupplyIs')} {b(amountFormatter(totalPoolTokens, 18, 4))}
</div>
<div className="pool__summary-modal__item">
{t('tokenWorth')} {b(amountFormatter(ethPerLiquidityToken, 18, 4))} ETH {t('and')}{' '}
{b(amountFormatter(tokenPerLiquidityToken, decimals, Math.min(decimals, 4)))} {symbol}
</div>
</>
)
}
}
isUnapproved() {
const { account, exchangeAddresses, selectors } = this.props
const { outputCurrency, outputValue } = this.state
function renderSummary() {
let contextualInfo = ''
let isError = false
if (!outputCurrency) {
return false
if (inputError || outputError) {
contextualInfo = inputError || outputError
isError = true
} else if (!inputCurrency || !outputCurrency) {
contextualInfo = t('selectTokenCont')
} else if (!inputValue) {
contextualInfo = t('enterValueCont')
} else if (!account) {
contextualInfo = t('noWallet')
isError = true
}
const { value: allowance, label, decimals } = selectors().getApprovals(
outputCurrency,
account,
exchangeAddresses.fromToken[outputCurrency]
return (
<ContextualInfo
openDetailsText={t('transactionDetails')}
closeDetailsText={t('hideDetails')}
contextualInfo={contextualInfo}
isError={isError}
renderTransactionDetails={renderTransactionDetails}
/>
)
}
if (label && allowance.isLessThan(BN(outputValue * 10 ** decimals || 0))) {
return true
}
const { addTransaction } = useTransactionContext()
return false
}
const isActive = active && account
const isValid = inputError === null || outputError === null
onAddLiquidity = async () => {
const {
account,
web3,
exchangeAddresses: { fromToken },
selectors
} = this.props
const { inputValue, outputValue, outputCurrency } = this.state
const exchange = new web3.eth.Contract(EXCHANGE_ABI, fromToken[outputCurrency])
const ethAmount = BN(inputValue).multipliedBy(10 ** 18)
const { decimals } = selectors().getTokenBalance(outputCurrency, fromToken[outputCurrency])
const tokenAmount = BN(outputValue).multipliedBy(10 ** decimals)
const { value: ethReserve } = selectors().getBalance(fromToken[outputCurrency])
const totalLiquidity = await exchange.methods.totalSupply().call()
const liquidityMinted = BN(totalLiquidity).multipliedBy(ethAmount.dividedBy(ethReserve))
let deadline
try {
deadline = await retry(() => getBlockDeadline(web3, 600))
} catch (e) {
// TODO: Handle error.
return
}
async function onAddLiquidity() {
ReactGA.event({
category: 'Pool',
action: 'AddLiquidity'
})
const MAX_LIQUIDITY_SLIPPAGE = 0.025
const minLiquidity = this.isNewExchange() ? BN(0) : liquidityMinted.multipliedBy(1 - MAX_LIQUIDITY_SLIPPAGE)
const maxTokens = this.isNewExchange() ? tokenAmount : tokenAmount.multipliedBy(1 + MAX_LIQUIDITY_SLIPPAGE)
const deadline = Math.ceil(Date.now() / 1000) + DEADLINE_FROM_NOW
const estimatedGasLimit = await exchangeContract.estimate.addLiquidity(
isNewExchange ? inputValueParsed : liquidityTokensMin,
isNewExchange ? outputValueParsed : outputValueMax,
deadline,
{
value: inputValueParsed
}
)
try {
exchange.methods.addLiquidity(minLiquidity.toFixed(0), maxTokens.toFixed(0), deadline).send(
exchangeContract
.addLiquidity(
isNewExchange ? inputValueParsed : liquidityTokensMin,
isNewExchange ? outputValueParsed : outputValueMax,
deadline,
{
from: account,
value: ethAmount.toFixed(0)
},
(err, data) => {
this.reset()
this.props.addPendingTx(data)
if (data) {
ReactGA.event({
category: 'Pool',
action: 'AddLiquidity'
})
}
value: inputValueParsed,
gasLimit: calculateGasMargin(estimatedGasLimit, GAS_MARGIN)
}
)
} catch (err) {
console.error(err)
}
.then(response => {
addTransaction(response.hash, response)
})
}
onInputChange = value => {
const { inputCurrency, outputCurrency } = this.state
const exchangeRate = this.getExchangeRate()
let outputValue
if (inputCurrency === 'ETH' && outputCurrency && outputCurrency !== 'ETH') {
outputValue = exchangeRate.multipliedBy(value).toFixed(7)
}
if (outputCurrency === 'ETH' && inputCurrency && inputCurrency !== 'ETH') {
outputValue = BN(value)
.dividedBy(exchangeRate)
.toFixed(7)
}
const append = {
inputValue: value,
lastEditedField: INPUT
}
if (!this.isNewExchange()) {
append.outputValue = outputValue
}
this.setState(append)
function formatBalance(value) {
return `Balance: ${value}`
}
onOutputChange = value => {
const { inputCurrency, outputCurrency } = this.state
const exchangeRate = this.getExchangeRate()
let inputValue
if (inputCurrency === 'ETH' && outputCurrency && outputCurrency !== 'ETH') {
inputValue = BN(value)
.dividedBy(exchangeRate)
.toFixed(7)
}
if (outputCurrency === 'ETH' && inputCurrency && inputCurrency !== 'ETH') {
inputValue = exchangeRate.multipliedBy(value).toFixed(7)
}
const append = {
outputValue: value,
lastEditedField: INPUT
}
if (!this.isNewExchange()) {
append.inputValue = inputValue
}
this.setState(append)
}
useEffect(() => {
if (isNewExchange) {
if (inputValue) {
const parsedInputValue = ethers.utils.parseUnits(inputValue, 18)
setInputValueParsed(parsedInputValue)
}
isNewExchange() {
const {
selectors,
exchangeAddresses: { fromToken }
} = this.props
const { inputCurrency, outputCurrency } = this.state
const eth = [inputCurrency, outputCurrency].filter(currency => currency === 'ETH')[0]
const token = [inputCurrency, outputCurrency].filter(currency => currency !== 'ETH')[0]
if (!eth || !token) {
return false
if (outputValue) {
const parsedOutputValue = ethers.utils.parseUnits(outputValue, decimals)
setOutputValueParsed(parsedOutputValue)
}
}
}, [decimals, inputValue, isNewExchange, outputValue])
const { value: tokenValue, decimals } = selectors().getBalance(fromToken[token], token)
const { value: ethValue } = selectors().getBalance(fromToken[token], eth)
console.log('rendering')
// parse input value
useEffect(() => {
if (isNewExchange === false && inputValue && marketRate && lastEditedField === INPUT) {
try {
const parsedValue = ethers.utils.parseUnits(inputValue, 18)
return tokenValue.isZero() && ethValue.isZero() && decimals !== 0
}
if (parsedValue.lte(ethers.constants.Zero) || parsedValue.gte(ethers.constants.MaxUint256)) {
throw Error()
}
getExchangeRate() {
const {
selectors,
exchangeAddresses: { fromToken }
} = this.props
const { inputCurrency, outputCurrency } = this.state
const eth = [inputCurrency, outputCurrency].filter(currency => currency === 'ETH')[0]
const token = [inputCurrency, outputCurrency].filter(currency => currency !== 'ETH')[0]
if (!eth || !token) {
return
setInputValueParsed(parsedValue)
const currencyAmount = marketRate
.mul(parsedValue)
.div(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(18)))
setOutputValueParsed(currencyAmount)
dispatchAddLiquidityState({
type: 'UPDATE_DEPENDENT_VALUE',
payload: { field: OUTPUT, value: amountFormatter(currencyAmount, 18, 4, false) }
})
return () => {
setOutputError()
setInputValueParsed()
setOutputValueParsed()
dispatchAddLiquidityState({
type: 'UPDATE_DEPENDENT_VALUE',
payload: { field: OUTPUT, value: '' }
})
}
} catch {
setOutputError(t('inputNotValid'))
}
}
}, [inputValue, isNewExchange, lastEditedField, marketRate, t])
const { value: tokenValue, decimals } = selectors().getBalance(fromToken[token], token)
const { value: ethValue } = selectors().getBalance(fromToken[token], eth)
// parse output value
useEffect(() => {
if (isNewExchange === false && outputValue && marketRateInverted && lastEditedField === OUTPUT) {
try {
const parsedValue = ethers.utils.parseUnits(outputValue, 18)
return tokenValue.multipliedBy(10 ** (18 - decimals)).dividedBy(ethValue)
}
if (parsedValue.lte(ethers.constants.Zero) || parsedValue.gte(ethers.constants.MaxUint256)) {
throw Error()
}
validate() {
const { t, selectors, account } = this.props
const { inputValue, outputValue, inputCurrency, outputCurrency } = this.state
let inputError
let outputError
let isValid = true
const inputIsZero = BN(inputValue).isZero()
const outputIsZero = BN(outputValue).isZero()
if (
!inputValue ||
inputIsZero ||
!outputValue ||
outputIsZero ||
!inputCurrency ||
!outputCurrency ||
this.isUnapproved()
) {
isValid = false
setOutputValueParsed(parsedValue)
const currencyAmount = marketRateInverted
.mul(parsedValue)
.div(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(18)))
setInputValueParsed(currencyAmount)
dispatchAddLiquidityState({
type: 'UPDATE_DEPENDENT_VALUE',
payload: { field: INPUT, value: amountFormatter(currencyAmount, 18, 4, false) }
})
return () => {
setInputError()
setOutputValueParsed()
setInputValueParsed()
dispatchAddLiquidityState({
type: 'UPDATE_DEPENDENT_VALUE',
payload: { field: INPUT, value: '' }
})
}
} catch {
setInputError(t('inputNotValid'))
}
}
const { value: ethValue } = selectors().getBalance(account, inputCurrency)
const { value: tokenValue, decimals } = selectors().getBalance(account, outputCurrency)
if (ethValue.isLessThan(BN(inputValue * 10 ** 18))) {
inputError = t('insufficientBalance')
}, [outputValue, isNewExchange, lastEditedField, marketRateInverted, t])
// input validation
useEffect(() => {
if (inputValueParsed && inputBalance) {
if (inputValueParsed.gt(inputBalance.sub(ethers.utils.parseEther('.1')))) {
setInputError(t('insufficientBalance'))
} else {
setInputError(null)
}
}
if (tokenValue.isLessThan(BN(outputValue * 10 ** decimals))) {
outputError = t('insufficientBalance')
if (outputValueParsed && outputBalance) {
if (outputValueParsed.gt(outputBalance)) {
setOutputError(t('insufficientBalance'))
} else {
setOutputError(null)
}
}
return {
inputError,
outputError,
isValid: isValid && !inputError && !outputError
}, [inputValueParsed, inputBalance, outputValueParsed, outputBalance, t])
const allowance = useAddressAllowance(account, outputCurrency, exchangeAddress)
const [showUnlock, setShowUnlock] = useState(false)
useEffect(() => {
if (outputValueParsed && allowance) {
if (allowance.lt(outputValueParsed)) {
setOutputError(t('unlockTokenCont'))
setShowUnlock(true)
}
return () => {
setOutputError()
setShowUnlock(false)
}
}
}
}, [outputValueParsed, allowance, t])
renderInfo() {
const t = this.props.t
const blank = (
<div className="pool__summary-panel">
<div className="pool__exchange-rate-wrapper">
<span className="pool__exchange-rate">{t('exchangeRate')}</span>
<span> - </span>
</div>
<div className="pool__exchange-rate-wrapper">
<span className="swap__exchange-rate">{t('currentPoolSize')}</span>
<span> - </span>
return (
<>
{isNewExchange ? (
<div className="pool__new-exchange-warning">
<div className="pool__new-exchange-warning-text">
<span role="img" aria-label="first-liquidity">
🚰
</span>{' '}
{t('firstLiquidity')}
</div>
<div className="pool__new-exchange-warning-text">{t('initialExchangeRate', { symbol })}</div>
</div>
<div className="pool__exchange-rate-wrapper">
<span className="swap__exchange-rate">{t('yourPoolShare')}</span>
<span> - </span>
) : null}
<CurrencyInputPanel
title={t('deposit')}
extraText={inputBalance && formatBalance(amountFormatter(inputBalance, 18, 4))}
onValueChange={inputValue => {
dispatchAddLiquidityState({ type: 'UPDATE_VALUE', payload: { value: inputValue, field: INPUT } })
}}
selectedTokenAddress="ETH"
value={inputValue}
errorMessage={inputError}
disableTokenSelect
/>
<OversizedPanel>
<div className="swap__down-arrow-background">
<img className="swap__down-arrow" src={isActive ? PlusBlue : PlusGrey} alt="plus" />
</div>
</div>
)
const {
selectors,
exchangeAddresses: { fromToken },
account
} = this.props
const { getBalance } = selectors()
const { inputCurrency, outputCurrency, inputValue, outputValue, totalSupply } = this.state
const eth = [inputCurrency, outputCurrency].filter(currency => currency === 'ETH')[0]
const token = [inputCurrency, outputCurrency].filter(currency => currency !== 'ETH')[0]
const exchangeAddress = fromToken[token]
if (!eth || !token || !exchangeAddress) {
return blank
}
const { value: tokenValue, decimals, label } = getBalance(exchangeAddress, token)
const { value: ethValue } = getBalance(exchangeAddress)
const { value: liquidityBalance } = getBalance(account, exchangeAddress)
const ownership = liquidityBalance.dividedBy(totalSupply)
const ethPer = ethValue.dividedBy(totalSupply)
const tokenPer = tokenValue.dividedBy(totalSupply)
const ownedEth = ethPer.multipliedBy(liquidityBalance).dividedBy(10 ** 18)
const ownedToken = tokenPer.multipliedBy(liquidityBalance).dividedBy(10 ** decimals)
if (!label || !decimals) {
return blank
}
if (this.isNewExchange()) {
const rate = BN(outputValue).dividedBy(inputValue)
const rateText = rate.isNaN() ? '---' : rate.toFixed(4)
return (
</OversizedPanel>
<CurrencyInputPanel
title={t('deposit')}
description={isNewExchange ? `(${t('estimated')})` : ''}
extraText={outputBalance && formatBalance(amountFormatter(outputBalance, decimals, Math.min(decimals, 4)))}
selectedTokenAddress={outputCurrency}
onCurrencySelected={outputCurrency => {
dispatchAddLiquidityState({ type: 'SELECT_CURRENCY', payload: outputCurrency })
}}
onValueChange={outputValue => {
dispatchAddLiquidityState({ type: 'UPDATE_VALUE', payload: { value: outputValue, field: OUTPUT } })
}}
value={outputValue}
showUnlock={showUnlock}
errorMessage={outputError}
/>
<OversizedPanel hideBottom>
<div className="pool__summary-panel">
<div className="pool__exchange-rate-wrapper">
<span className="pool__exchange-rate">{t('exchangeRate')}</span>
<span>{`1 ETH = ${rateText} ${label}`}</span>
<span>{marketRate ? `1 ETH = ${amountFormatter(marketRate, 18, 4)} ${symbol}` : ' - '}</span>
</div>
<div className="pool__exchange-rate-wrapper">
<span className="swap__exchange-rate">{t('currentPoolSize')}</span>
<span>{` ${ethValue.dividedBy(10 ** 18).toFixed(2)} ${eth} + ${tokenValue
.dividedBy(10 ** decimals)
.toFixed(2)} ${label}`}</span>
<span>
{exchangeETHBalance && exchangeTokenBalance
? `${amountFormatter(exchangeETHBalance, 18, 4)} ETH + ${amountFormatter(
exchangeTokenBalance,
decimals,
Math.min(4, decimals)
)} ${symbol}`
: ' - '}
</span>
</div>
<div className="pool__exchange-rate-wrapper">
<span className="swap__exchange-rate">
{t('yourPoolShare')} ({ownership.multipliedBy(100).toFixed(2)}%)
{t('yourPoolShare')} ({exchangeETHBalance && amountFormatter(poolTokenPercentage, 16, 2)}%)
</span>
<span>
{ethShare && tokenShare
? `${amountFormatter(ethShare, 18, 4)} ETH + ${amountFormatter(
tokenShare,
decimals,
Math.min(4, decimals)
)} ${symbol}`
: ' - '}
</span>
<span>{`${ownedEth.toFixed(2)} ETH + ${ownedToken.toFixed(2)} ${label}`}</span>
</div>
</div>
)
}
if (tokenValue.dividedBy(ethValue).isNaN()) {
return blank
}
return (
<div className="pool__summary-panel">
<div className="pool__exchange-rate-wrapper">
<span className="pool__exchange-rate">{t('exchangeRate')}</span>
<span>{`1 ETH = ${tokenValue
.multipliedBy(10 ** (18 - decimals))
.dividedBy(ethValue)
.toFixed(4)} ${label}`}</span>
</div>
<div className="pool__exchange-rate-wrapper">
<span className="swap__exchange-rate">{t('currentPoolSize')}</span>
<span>{` ${ethValue.dividedBy(10 ** 18).toFixed(2)} ${eth} + ${tokenValue
.dividedBy(10 ** decimals)
.toFixed(2)} ${label}`}</span>
</div>
<div className="pool__exchange-rate-wrapper">
<span className="swap__exchange-rate">
{t('yourPoolShare')} ({ownership.multipliedBy(100).toFixed(2)}%)
</span>
<span>{`${ownedEth.toFixed(2)} ETH + ${ownedToken.toFixed(2)} ${label}`}</span>
</div>
</OversizedPanel>
{renderSummary()}
<div className="pool__cta-container">
<button
className={classnames('pool__cta-btn', {
'pool__cta-btn--inactive': !isActive
})}
disabled={!isValid}
onClick={onAddLiquidity}
>
{t('addLiquidity')}
</button>
</div>
)
}
renderSummary(inputError, outputError) {
const {
t,
account,
selectors,
exchangeAddresses: { fromToken }
} = this.props
const { inputValue, outputValue, inputCurrency, outputCurrency } = this.state
const inputIsZero = BN(inputValue).isZero()
const outputIsZero = BN(outputValue).isZero()
let contextualInfo = ''
let isError = false
const { label } = selectors().getTokenBalance(outputCurrency, fromToken[outputCurrency])
if (!account) {
contextualInfo = t('noWallet')
isError = true
} else if (inputError || outputError) {
contextualInfo = inputError || outputError
isError = true
} else if (!inputCurrency || !outputCurrency) {
contextualInfo = t('selectTokenCont')
} else if (inputCurrency === outputCurrency) {
contextualInfo = t('differentToken')
} else if (![inputCurrency, outputCurrency].includes('ETH')) {
contextualInfo = t('mustBeETH')
} else if (inputIsZero || outputIsZero) {
contextualInfo = t('noZero')
} else if (this.isUnapproved()) {
contextualInfo = t('unlockTokenCont')
} else if (!inputValue || !outputValue) {
contextualInfo = t('enterCurrencyOrLabelCont', { inputCurrency, label })
}
return (
<ContextualInfo
key="context-info"
openDetailsText={t('transactionDetails')}
closeDetailsText={t('hideDetails')}
contextualInfo={contextualInfo}
isError={isError}
renderTransactionDetails={this.renderTransactionDetails}
/>
)
}
renderTransactionDetails = () => {
const {
t,
selectors,
exchangeAddresses: { fromToken },
account
} = this.props
const { inputValue, outputValue, outputCurrency, totalSupply } = this.state
ReactGA.event({
category: 'TransactionDetail',
action: 'Open'
})
const { value: tokenReserve, label } = selectors().getTokenBalance(outputCurrency, fromToken[outputCurrency])
const { value: ethReserve } = selectors().getBalance(fromToken[outputCurrency])
const { decimals: poolTokenDecimals } = selectors().getBalance(account, fromToken[outputCurrency])
if (this.isNewExchange()) {
return (
<>
<div className="pool__summary-item">
{t('youAreAdding')} {b(`${inputValue} ETH`)} {t('and')} {b(`${outputValue} ${label}`)} {t('intoPool')}
</div>
<div className="pool__summary-item">
{t('youAreSettingExRate')}{' '}
{b(
`1 ETH = ${BN(outputValue)
.dividedBy(inputValue)
.toFixed(4)} ${label}`
)}
.
</div>
<div className="pool__summary-item">
{t('youWillMint')} {b(`${inputValue}`)} {t('liquidityTokens')}
</div>
<div className="pool__summary-item">{t('totalSupplyIs0')}</div>
</>
)
}
const SLIPPAGE = 0.025
const minOutput = BN(outputValue).multipliedBy(1 - SLIPPAGE)
const maxOutput = BN(outputValue).multipliedBy(1 + SLIPPAGE)
// const minPercentage = minOutput.dividedBy(minOutput.plus(tokenReserve)).multipliedBy(100);
// const maxPercentage = maxOutput.dividedBy(maxOutput.plus(tokenReserve)).multipliedBy(100);
const liquidityMinted = BN(inputValue).multipliedBy(totalSupply.dividedBy(ethReserve))
const adjTotalSupply = totalSupply.dividedBy(10 ** poolTokenDecimals)
return (
<>
<div className="pool__summary-modal__item">
{t('youAreAdding')} {b(`${+BN(inputValue).toFixed(7)} ETH`)} {t('and')}{' '}
{b(`${+minOutput.toFixed(7)} - ${+maxOutput.toFixed(7)} ${label}`)} {t('intoPool')}
</div>
<div className="pool__summary-modal__item">
{t('youWillMint')} {b(+liquidityMinted.toFixed(7))} {t('liquidityTokens')}
</div>
<div className="pool__summary-modal__item">
{t('totalSupplyIs')} {b(+adjTotalSupply.toFixed(7))}
</div>
<div className="pool__summary-modal__item">
{t('tokenWorth')} {b(+ethReserve.dividedBy(totalSupply).toFixed(7))} ETH {t('and')}{' '}
{b(+tokenReserve.dividedBy(totalSupply).toFixed(7))} {label}
</div>
</>
)
}
render() {
const {
t,
exchangeAddresses: { fromToken },
selectors
} = this.props
const { inputValue, outputValue, inputCurrency, outputCurrency } = this.state
const { inputError, outputError, isValid } = this.validate()
const { label } = selectors().getTokenBalance(outputCurrency, fromToken[outputCurrency])
return (
<>
{this.isNewExchange() ? (
<div className="pool__new-exchange-warning">
<div className="pool__new-exchange-warning-text">
<span role="img" aria-label="liquidity">
🚰
</span>{' '}
{t('firstLiquidity')}
</div>
<div className="pool__new-exchange-warning-text">{t('initialExchangeRate', { label })}</div>
</div>
) : null}
<CurrencyInputPanel
title={t('deposit')}
extraText={this.getBalance(inputCurrency)}
onValueChange={this.onInputChange}
selectedTokenAddress="ETH"
value={inputValue}
errorMessage={inputError}
disableTokenSelect
/>
<OversizedPanel>
<div className="swap__down-arrow-background">
<img className="swap__down-arrow" src={isValid ? PlusBlue : PlusGrey} alt="plus" />
</div>
</OversizedPanel>
<CurrencyInputPanel
title={t('deposit')}
description={this.isNewExchange() ? `(${t('estimated')})` : ''}
extraText={this.getBalance(outputCurrency)}
selectedTokenAddress={outputCurrency}
onCurrencySelected={currency => {
this.setState(
{
outputCurrency: currency
},
this.recalcForm
)
}}
onValueChange={this.onOutputChange}
value={outputValue}
errorMessage={outputError}
filteredTokens={['ETH']}
/>
<OversizedPanel hideBottom>{this.renderInfo()}</OversizedPanel>
{this.renderSummary(inputError, outputError)}
<div className="pool__cta-container">
<AddLiquidityButton callOnClick={this.onAddLiquidity} isValid={isValid} />
</div>
</>
)
}
}
function AddLiquidityButton({ callOnClick, isValid }) {
const { t } = useTranslation()
const context = useWeb3Context()
const isActive = context.active && context.account
return (
<button
className={classnames('pool__cta-btn', {
'pool__cta-btn--inactive': !isActive
})}
disabled={!isValid}
onClick={callOnClick}
>
{t('addLiquidity')}
</button>
</>
)
}
export default connect(
state => ({
account: state.web3connect.account,
balances: state.web3connect.balances,
web3: state.web3connect.web3,
exchangeAddresses: state.addresses.exchangeAddresses
}),
dispatch => ({
selectors: () => dispatch(selectors()),
addPendingTx: id => dispatch(addPendingTx(id))
})
)(withTranslation()(AddLiquidity))
function b(text) {
return <span className="swap__highlight-text">{text}</span>
}
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import React, { useState, useEffect, useCallback } from 'react'
import classnames from 'classnames'
import { connect } from 'react-redux'
import { BigNumber as BN } from 'bignumber.js'
import { withTranslation, useTranslation } from 'react-i18next'
import { useTranslation } from 'react-i18next'
import ReactGA from 'react-ga'
import { useWeb3Context } from 'web3-react'
import { ethers } from 'ethers'
import CurrencyInputPanel from '../../components/CurrencyInputPanel'
import { selectors, addPendingTx } from '../../ducks/web3connect'
import ContextualInfo from '../../components/ContextualInfo'
import OversizedPanel from '../../components/OversizedPanel'
import ArrowDownBlue from '../../assets/images/arrow-down-blue.svg'
import ArrowDownGrey from '../../assets/images/arrow-down-grey.svg'
import { getBlockDeadline } from '../../helpers/web3-utils'
import { retry } from '../../helpers/promise-utils'
import EXCHANGE_ABI from '../../abi/exchange'
class RemoveLiquidity extends Component {
static propTypes = {
account: PropTypes.string,
balances: PropTypes.object,
web3: PropTypes.object,
exchangeAddresses: PropTypes.shape({
fromToken: PropTypes.object.isRequired
}).isRequired
}
state = {
tokenAddress: '',
value: '',
totalSupply: BN(0)
}
reset() {
this.setState({
value: ''
})
}
validate() {
const { tokenAddress, value } = this.state
const {
t,
account,
selectors,
exchangeAddresses: { fromToken },
web3
} = this.props
const exchangeAddress = fromToken[tokenAddress]
if (!web3 || !exchangeAddress || !account || !value) {
return {
isValid: false
import { useExchangeContract, useBlockEffect } from '../../hooks'
import { useAddressBalance } from '../../contexts/Block'
import { useTokenDetails } from '../../contexts/Static'
import { calculateGasMargin, amountFormatter } from '../../utils'
import { useTransactionContext } from '../../contexts/Transaction'
// denominated in bips
const ALLOWED_SLIPPAGE = ethers.utils.bigNumberify(200)
// denominated in seconds
const DEADLINE_FROM_NOW = 60 * 15
// denominated in bips
const GAS_MARGIN = ethers.utils.bigNumberify(1000)
function getExchangeRate(inputValue, inputDecimals, outputValue, outputDecimals, invert = false) {
try {
if (inputValue && inputDecimals && outputValue && outputDecimals) {
const factor = ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(18))
if (invert) {
return inputValue
.mul(factor)
.div(outputValue)
.mul(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(outputDecimals)))
.div(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(inputDecimals)))
} else {
return outputValue
.mul(factor)
.div(inputValue)
.mul(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(inputDecimals)))
.div(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(outputDecimals)))
}
}
} catch {}
}
const { getBalance } = selectors()
const { value: liquidityBalance, decimals: liquidityDecimals } = getBalance(account, exchangeAddress)
if (liquidityBalance.isLessThan(BN(value).multipliedBy(10 ** liquidityDecimals))) {
return { isValid: false, errorMessage: t('insufficientBalance') }
}
function getMarketRate(reserveETH, reserveToken, decimals, invert = false) {
return getExchangeRate(reserveETH, 18, reserveToken, decimals, invert)
}
function calculateSlippageBounds(value) {
if (value) {
const offset = value.mul(ALLOWED_SLIPPAGE).div(ethers.utils.bigNumberify(10000))
const minimum = value.sub(offset)
const maximum = value.add(offset)
return {
isValid: true
minimum: minimum.lt(ethers.constants.Zero) ? ethers.constants.Zero : minimum,
maximum: maximum.gt(ethers.constants.MaxUint256) ? ethers.constants.MaxUint256 : maximum
}
} else {
return {}
}
}
onTokenSelect = async tokenAddress => {
const {
exchangeAddresses: { fromToken },
web3
} = this.props
const exchangeAddress = fromToken[tokenAddress]
this.setState({ tokenAddress })
if (!web3 || !exchangeAddress) {
return
}
const exchange = new web3.eth.Contract(EXCHANGE_ABI, exchangeAddress)
const totalSupply = await exchange.methods.totalSupply().call()
this.setState({
totalSupply: BN(totalSupply)
})
}
export default function RemoveLiquidity() {
const { account, active } = useWeb3Context()
const { t } = useTranslation()
onInputChange = value => {
this.setState({ value })
}
const { addTransaction } = useTransactionContext()
onRemoveLiquidity = async () => {
const { tokenAddress, value: input, totalSupply } = this.state
const {
exchangeAddresses: { fromToken },
web3,
selectors,
account
} = this.props
const exchangeAddress = fromToken[tokenAddress]
const { getBalance } = selectors()
if (!web3 || !exchangeAddress) {
return
}
const exchange = new web3.eth.Contract(EXCHANGE_ABI, exchangeAddress)
const SLIPPAGE = 0.02
const { decimals } = getBalance(account, exchangeAddress)
const { value: ethReserve } = getBalance(exchangeAddress)
const { value: tokenReserve } = getBalance(exchangeAddress, tokenAddress)
const amount = BN(input).multipliedBy(10 ** decimals)
const ownership = amount.dividedBy(totalSupply)
const ethWithdrawn = ethReserve.multipliedBy(ownership)
const tokenWithdrawn = tokenReserve.multipliedBy(ownership)
let deadline
const [outputCurrency, setOutputCurrency] = useState('')
const [value, setValue] = useState('')
const [inputError, setInputError] = useState()
const [valueParsed, setValueParsed] = useState()
// parse value
useEffect(() => {
try {
deadline = await retry(() => getBlockDeadline(web3, 600))
} catch (e) {
// TODO: Handle error.
return
const parsedValue = ethers.utils.parseUnits(value, 18)
setValueParsed(parsedValue)
} catch {
if (value !== '') {
setInputError(t('inputNotValid'))
}
}
exchange.methods
.removeLiquidity(
amount.toFixed(0),
ethWithdrawn.multipliedBy(1 - SLIPPAGE).toFixed(0),
tokenWithdrawn.multipliedBy(1 - SLIPPAGE).toFixed(0),
deadline
)
.send({ from: account }, (err, data) => {
if (data) {
this.reset()
this.props.addPendingTx(data)
ReactGA.event({
category: 'Pool',
action: 'RemoveLiquidity'
})
}
})
}
getBalance = () => {
const {
exchangeAddresses: { fromToken },
account,
web3,
selectors
} = this.props
const { tokenAddress } = this.state
if (!web3) {
return ''
return () => {
setInputError()
setValueParsed()
}
const exchangeAddress = fromToken[tokenAddress]
if (!exchangeAddress) {
return ''
}, [t, value])
const { symbol, decimals, exchangeAddress } = useTokenDetails(outputCurrency)
const [totalPoolTokens, setTotalPoolTokens] = useState()
const poolTokenBalance = useAddressBalance(account, exchangeAddress)
const exchangeETHBalance = useAddressBalance(exchangeAddress, 'ETH')
const exchangeTokenBalance = useAddressBalance(exchangeAddress, outputCurrency)
// input validation
useEffect(() => {
if (valueParsed && poolTokenBalance) {
if (valueParsed.gt(poolTokenBalance)) {
setInputError(t('insufficientBalance'))
} else {
setInputError(null)
}
}
const { value, decimals } = selectors().getBalance(account, exchangeAddress)
if (!decimals) {
return ''
}, [poolTokenBalance, t, valueParsed])
const exchange = useExchangeContract(exchangeAddress)
const ownershipPercentage =
poolTokenBalance && totalPoolTokens && !totalPoolTokens.isZero()
? poolTokenBalance.mul(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(18))).div(totalPoolTokens)
: undefined
const ownershipPercentageFormatted = ownershipPercentage && amountFormatter(ownershipPercentage, 16, 4)
const ETHOwnShare =
exchangeETHBalance &&
ownershipPercentage &&
exchangeETHBalance.mul(ownershipPercentage).div(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(18)))
const TokenOwnShare =
exchangeTokenBalance &&
ownershipPercentage &&
exchangeTokenBalance.mul(ownershipPercentage).div(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(18)))
const ETHPer =
exchangeETHBalance && totalPoolTokens && !totalPoolTokens.isZero()
? exchangeETHBalance.mul(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(18))).div(totalPoolTokens)
: undefined
const tokenPer =
exchangeTokenBalance && totalPoolTokens && !totalPoolTokens.isZero()
? exchangeTokenBalance.mul(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(18))).div(totalPoolTokens)
: undefined
const ethWithdrawn =
ETHPer &&
valueParsed &&
ETHPer.mul(valueParsed).div(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(18)))
const tokenWithdrawn =
tokenPer &&
valueParsed &&
tokenPer.mul(valueParsed).div(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(18)))
const ethWithdrawnMin = ethWithdrawn ? calculateSlippageBounds(ethWithdrawn).minimum : undefined
const tokenWithdrawnMin = tokenWithdrawn ? calculateSlippageBounds(tokenWithdrawn).minimum : undefined
const fetchPoolTokens = useCallback(() => {
if (exchange) {
exchange.totalSupply().then(totalSupply => {
setTotalPoolTokens(totalSupply)
})
}
}, [exchange])
useEffect(() => {
fetchPoolTokens()
}, [fetchPoolTokens])
useBlockEffect(fetchPoolTokens)
return `Balance: ${value.dividedBy(10 ** decimals).toFixed(7)}`
}
async function onRemoveLiquidity() {
ReactGA.event({
category: 'Pool',
action: 'RemoveLiquidity'
})
renderSummary(errorMessage) {
const {
t,
account,
selectors,
exchangeAddresses: { fromToken }
} = this.props
const { value: input, tokenAddress } = this.state
const inputIsZero = BN(input).isZero()
let contextualInfo = ''
let isError = false
const deadline = Math.ceil(Date.now() / 1000) + DEADLINE_FROM_NOW
if (!account) {
contextualInfo = t('noWallet')
isError = true
} else if (errorMessage) {
contextualInfo = errorMessage
isError = true
} else if (!tokenAddress) {
contextualInfo = t('selectTokenCont')
} else if (inputIsZero) {
contextualInfo = t('noZero')
} else if (!input) {
const { label } = selectors().getTokenBalance(tokenAddress, fromToken[tokenAddress])
contextualInfo = t('enterLabelCont', { label })
}
return (
<ContextualInfo
key="context-info"
openDetailsText={t('transactionDetails')}
closeDetailsText={t('hideDetails')}
contextualInfo={contextualInfo}
isError={isError}
renderTransactionDetails={this.renderTransactionDetails}
/>
const estimatedGasLimit = await exchange.estimate.removeLiquidity(
valueParsed,
ethWithdrawnMin,
tokenWithdrawnMin,
deadline
)
exchange
.removeLiquidity(valueParsed, ethWithdrawnMin, tokenWithdrawnMin, deadline, {
gasLimit: calculateGasMargin(estimatedGasLimit, GAS_MARGIN)
})
.then(response => {
addTransaction(response.hash, response)
})
}
renderTransactionDetails = () => {
const { tokenAddress, value: input, totalSupply } = this.state
const {
t,
exchangeAddresses: { fromToken },
selectors,
account
} = this.props
const exchangeAddress = fromToken[tokenAddress]
const { getBalance } = selectors()
if (!exchangeAddress) {
return null
}
const b = text => <span className="swap__highlight-text">{text}</span>
function renderTransactionDetails() {
ReactGA.event({
category: 'TransactionDetail',
action: 'Open'
})
const SLIPPAGE = 0.025
const { decimals } = getBalance(account, exchangeAddress)
const { value: ethReserve } = getBalance(exchangeAddress)
const { value: tokenReserve, label } = getBalance(exchangeAddress, tokenAddress)
const ethPer = ethReserve.dividedBy(totalSupply)
const tokenPer = tokenReserve.dividedBy(totalSupply)
const ethWithdrawn = ethPer.multipliedBy(input)
const tokenWithdrawn = tokenPer.multipliedBy(input)
const minTokenWithdrawn = tokenWithdrawn.multipliedBy(1 - SLIPPAGE).toFixed(7)
const maxTokenWithdrawn = tokenWithdrawn.multipliedBy(1 + SLIPPAGE).toFixed(7)
const adjTotalSupply = totalSupply.dividedBy(10 ** decimals).minus(input)
return (
<div>
<div className="pool__summary-modal__item">
{t('youAreRemoving')} {b(`${+BN(ethWithdrawn).toFixed(7)} ETH`)} {t('and')}{' '}
{b(`${+minTokenWithdrawn} - ${+maxTokenWithdrawn} ${label}`)} {t('outPool')}
{t('youAreRemoving')} {b(`${amountFormatter(ethWithdrawnMin, 18, 4)} ETH`)} {t('and')}{' '}
{b(`${amountFormatter(tokenWithdrawnMin, decimals, Math.min(decimals, 4))} ${symbol}`)} {t('outPool')}
</div>
<div className="pool__summary-modal__item">
{t('youWillRemove')} {b(+input)} {t('liquidityTokens')}
{t('youWillRemove')} {b(amountFormatter(valueParsed, 18, 4))} {t('liquidityTokens')}
</div>
<div className="pool__summary-modal__item">
{t('totalSupplyIs')} {b(+adjTotalSupply.toFixed(7))}
{t('totalSupplyIs')} {b(amountFormatter(totalPoolTokens, 18, 4))}
</div>
<div className="pool__summary-modal__item">
{t('tokenWorth')} {b(+ethReserve.dividedBy(totalSupply).toFixed(7))} ETH {t('and')}{' '}
{b(+tokenReserve.dividedBy(totalSupply).toFixed(7))} {label}
{t('tokenWorth')} {b(amountFormatter(ETHPer, 18, 4))} ETH {t('and')}{' '}
{b(amountFormatter(tokenPer, decimals, Math.min(4, decimals)))} {symbol}
</div>
</div>
)
}
renderOutput() {
const {
t,
exchangeAddresses: { fromToken },
account,
web3,
selectors
} = this.props
const { getBalance } = selectors()
const { tokenAddress, totalSupply, value: input } = this.state
const blank = [
<CurrencyInputPanel
key="remove-liquidity-input"
title={t('output')}
description={`(${t('estimated')})`}
renderInput={() => <div className="remove-liquidity__output" />}
disableTokenSelect
disableUnlock
/>,
<OversizedPanel key="remove-liquidity-input-under" hideBottom>
<div className="pool__summary-panel">
<div className="pool__exchange-rate-wrapper">
<span className="pool__exchange-rate">{t('exchangeRate')}</span>
<span> - </span>
</div>
<div className="pool__exchange-rate-wrapper">
<span className="swap__exchange-rate">{t('currentPoolSize')}</span>
<span> - </span>
</div>
<div className="pool__exchange-rate-wrapper">
<span className="swap__exchange-rate">{t('yourPoolShare')}</span>
<span> - </span>
</div>
</div>
</OversizedPanel>
]
function renderSummary() {
let contextualInfo = ''
let isError = false
const exchangeAddress = fromToken[tokenAddress]
if (!exchangeAddress || !web3) {
return blank
if (inputError) {
contextualInfo = inputError
isError = true
} else if (!outputCurrency || outputCurrency === 'ETH') {
contextualInfo = t('selectTokenCont')
} else if (!valueParsed) {
contextualInfo = t('enterValueCont')
} else if (!account) {
contextualInfo = t('noWallet')
isError = true
}
const { value: liquidityBalance } = getBalance(account, exchangeAddress)
const { value: ethReserve } = getBalance(exchangeAddress)
const { value: tokenReserve, decimals: tokenDecimals, label } = getBalance(exchangeAddress, tokenAddress)
return (
<ContextualInfo
key="context-info"
openDetailsText={t('transactionDetails')}
closeDetailsText={t('hideDetails')}
contextualInfo={contextualInfo}
isError={isError}
renderTransactionDetails={renderTransactionDetails}
/>
)
}
if (!tokenDecimals) {
return blank
}
function formatBalance(value) {
return `Balance: ${value}`
}
const ownership = liquidityBalance.dividedBy(totalSupply)
const ethPer = ethReserve.dividedBy(totalSupply)
const tokenPer = tokenReserve.multipliedBy(10 ** (18 - tokenDecimals)).dividedBy(totalSupply)
const exchangeRate = tokenReserve.multipliedBy(10 ** (18 - tokenDecimals)).div(ethReserve)
const isActive = active && account
const isValid = inputError === null
const ownedEth = ethPer.multipliedBy(liquidityBalance).dividedBy(10 ** 18)
const ownedToken = tokenPer.multipliedBy(liquidityBalance).dividedBy(10 ** tokenDecimals)
const marketRate = getMarketRate(exchangeETHBalance, exchangeTokenBalance, decimals)
return [
return (
<>
<CurrencyInputPanel
title={t('poolTokens')}
extraText={poolTokenBalance && formatBalance(amountFormatter(poolTokenBalance, 18, 4))}
extraTextClickHander={() => {
if (poolTokenBalance) {
const valueToSet = poolTokenBalance
if (valueToSet.gt(ethers.constants.Zero)) {
setValue(amountFormatter(valueToSet, 18, 18, false))
}
}
}}
onCurrencySelected={setOutputCurrency}
onValueChange={setValue}
value={value}
errorMessage={inputError}
selectedTokenAddress={outputCurrency}
/>
<OversizedPanel>
<div className="swap__down-arrow-background">
<img className="swap__down-arrow" src={isValid ? ArrowDownBlue : ArrowDownGrey} alt="arrow" />
</div>
</OversizedPanel>
<CurrencyInputPanel
title={t('output')}
description={`(${t('estimated')})`}
key="remove-liquidity-input"
renderInput={() =>
input ? (
ethWithdrawn && tokenWithdrawn ? (
<div className="remove-liquidity__output">
<div className="remove-liquidity__output-text">{`${ethPer.multipliedBy(input).toFixed(3)} ETH`}</div>
<div className="remove-liquidity__output-text">{`${amountFormatter(
ethWithdrawn,
18,
4,
false
)} ETH`}</div>
<div className="remove-liquidity__output-plus"> + </div>
<div className="remove-liquidity__output-text">
{`${tokenPer.multipliedBy(input).toFixed(3)} ${label}`}
</div>
<div className="remove-liquidity__output-text">{`${amountFormatter(
tokenWithdrawn,
decimals,
Math.min(4, decimals)
)} ${symbol}`}</div>
</div>
) : (
<div className="remove-liquidity__output" />
......@@ -345,93 +299,55 @@ class RemoveLiquidity extends Component {
}
disableTokenSelect
disableUnlock
/>,
/>
<OversizedPanel key="remove-liquidity-input-under" hideBottom>
<div className="pool__summary-panel">
<div className="pool__exchange-rate-wrapper">
<span className="pool__exchange-rate">{t('exchangeRate')}</span>
<span>{`1 ETH = ${exchangeRate.toFixed(4)} ${label}`}</span>
{marketRate ? <span>{`1 ETH = ${amountFormatter(marketRate, 18, 4)} ${symbol}`}</span> : ' - '}
</div>
<div className="pool__exchange-rate-wrapper">
<span className="swap__exchange-rate">{t('currentPoolSize')}</span>
<span>{`${ethReserve.dividedBy(10 ** 18).toFixed(2)} ETH + ${tokenReserve
.dividedBy(10 ** tokenDecimals)
.toFixed(2)} ${label}`}</span>
{exchangeETHBalance && exchangeTokenBalance && decimals ? (
<span>{`${amountFormatter(exchangeETHBalance, 18, 4)} ETH + ${amountFormatter(
exchangeTokenBalance,
decimals,
Math.min(decimals, 4)
)} ${symbol}`}</span>
) : (
' - '
)}
</div>
<div className="pool__exchange-rate-wrapper">
<span className="swap__exchange-rate">
{t('yourPoolShare')} ({ownership.multipliedBy(100).toFixed(2)}%)
{t('yourPoolShare')} ({ownershipPercentageFormatted && ownershipPercentageFormatted}%)
</span>
<span>{`${ownedEth.toFixed(2)} ETH + ${ownedToken.toFixed(2)} ${label}`}</span>
{ETHOwnShare && TokenOwnShare ? (
<span>
{`${amountFormatter(ETHOwnShare, 18, 4)} ETH + ${amountFormatter(
TokenOwnShare,
decimals,
Math.min(decimals, 4)
)} ${symbol}`}
</span>
) : (
' - '
)}
</div>
</div>
</OversizedPanel>
]
}
render() {
const { t } = this.props
const { tokenAddress, value } = this.state
const { isValid, errorMessage } = this.validate()
return (
<>
<CurrencyInputPanel
title={t('poolTokens')}
extraText={this.getBalance(tokenAddress)}
onValueChange={this.onInputChange}
value={value}
errorMessage={errorMessage}
selectedTokenAddress={tokenAddress}
onCurrencySelected={this.onTokenSelect}
filteredTokens={['ETH']}
/>
<OversizedPanel>
<div className="swap__down-arrow-background">
<img className="swap__down-arrow" src={isValid ? ArrowDownBlue : ArrowDownGrey} alt="arrow" />
</div>
</OversizedPanel>
{this.renderOutput()}
{this.renderSummary(errorMessage)}
<div className="pool__cta-container">
<RemoveLiquidityButton callOnClick={this.onRemoveLiquidity} isValid={isValid} />
</div>
</>
)
}
}
function RemoveLiquidityButton({ callOnClick, isValid }) {
const { t } = useTranslation()
const context = useWeb3Context()
const isActive = context.active && context.account
return (
<button
className={classnames('pool__cta-btn', {
'pool__cta-btn--inactive': !isActive
})}
disabled={!isValid}
onClick={callOnClick}
>
{t('removeLiquidity')}
</button>
{renderSummary()}
<div className="pool__cta-container">
<button
className={classnames('pool__cta-btn', {
'pool__cta-btn--inactive': !isActive
})}
disabled={!isValid}
onClick={onRemoveLiquidity}
>
{t('removeLiquidity')}
</button>
</div>
</>
)
}
export default connect(
state => ({
web3: state.web3connect.web3,
balances: state.web3connect.balances,
account: state.web3connect.account,
exchangeAddresses: state.addresses.exchangeAddresses
}),
dispatch => ({
selectors: () => dispatch(selectors()),
addPendingTx: id => dispatch(addPendingTx(id))
})
)(withTranslation()(RemoveLiquidity))
function b(text) {
return <span className="swap__highlight-text">{text}</span>
}
......@@ -69,6 +69,7 @@
}
&__new-exchange-warning {
margin-top: 1rem;
padding: 1rem;
margin-bottom: 2rem;
border: 1px solid rgba($pizazz-orange, 0.4);
......
......@@ -362,24 +362,36 @@ export default function Swap() {
if (amount && reserveETHFirst && reserveTokenFirst && reserveETHSecond && reserveTokenSecond) {
try {
const intermediateValue =
independentField === INPUT
? calculateEtherTokenOutputFromInput(amount, reserveTokenFirst, reserveETHFirst)
: calculateEtherTokenInputFromOutput(amount, reserveTokenFirst, reserveETHFirst)
if (intermediateValue.lte(ethers.constants.Zero)) {
throw Error()
}
const calculatedDependentValue =
independentField === INPUT
? calculateEtherTokenOutputFromInput(intermediateValue, reserveETHSecond, reserveTokenSecond)
: calculateEtherTokenInputFromOutput(intermediateValue, reserveETHSecond, reserveTokenSecond)
if (calculatedDependentValue.lte(ethers.constants.Zero)) {
throw Error()
if (independentField === INPUT) {
const intermediateValue = calculateEtherTokenOutputFromInput(amount, reserveTokenFirst, reserveETHFirst)
if (intermediateValue.lte(ethers.constants.Zero)) {
throw Error()
}
const calculatedDependentValue = calculateEtherTokenOutputFromInput(
intermediateValue,
reserveETHSecond,
reserveTokenSecond
)
if (calculatedDependentValue.lte(ethers.constants.Zero)) {
throw Error()
}
dispatchSwapState({ type: 'UPDATE_DEPENDENT', payload: calculatedDependentValue })
} else {
const intermediateValue = calculateEtherTokenInputFromOutput(amount, reserveETHSecond, reserveTokenSecond)
if (intermediateValue.lte(ethers.constants.Zero)) {
throw Error()
}
// console.log('hi!', amountFormatter(intermediateValue, ))
const calculatedDependentValue = calculateEtherTokenInputFromOutput(
intermediateValue,
reserveTokenFirst,
reserveETHFirst
)
if (calculatedDependentValue.lte(ethers.constants.Zero)) {
throw Error()
}
dispatchSwapState({ type: 'UPDATE_DEPENDENT', payload: calculatedDependentValue })
}
dispatchSwapState({ type: 'UPDATE_DEPENDENT', payload: calculatedDependentValue })
} catch {
setIndependentError(t('insufficientLiquidity'))
}
......
......@@ -357,24 +357,36 @@ export default function Swap() {
if (amount && reserveETHFirst && reserveTokenFirst && reserveETHSecond && reserveTokenSecond) {
try {
const intermediateValue =
independentField === INPUT
? calculateEtherTokenOutputFromInput(amount, reserveTokenFirst, reserveETHFirst)
: calculateEtherTokenInputFromOutput(amount, reserveTokenFirst, reserveETHFirst)
if (intermediateValue.lte(ethers.constants.Zero)) {
throw Error()
}
const calculatedDependentValue =
independentField === INPUT
? calculateEtherTokenOutputFromInput(intermediateValue, reserveETHSecond, reserveTokenSecond)
: calculateEtherTokenInputFromOutput(intermediateValue, reserveETHSecond, reserveTokenSecond)
if (calculatedDependentValue.lte(ethers.constants.Zero)) {
throw Error()
if (independentField === INPUT) {
const intermediateValue = calculateEtherTokenOutputFromInput(amount, reserveTokenFirst, reserveETHFirst)
if (intermediateValue.lte(ethers.constants.Zero)) {
throw Error()
}
const calculatedDependentValue = calculateEtherTokenOutputFromInput(
intermediateValue,
reserveETHSecond,
reserveTokenSecond
)
if (calculatedDependentValue.lte(ethers.constants.Zero)) {
throw Error()
}
dispatchSwapState({ type: 'UPDATE_DEPENDENT', payload: calculatedDependentValue })
} else {
const intermediateValue = calculateEtherTokenInputFromOutput(amount, reserveETHSecond, reserveTokenSecond)
if (intermediateValue.lte(ethers.constants.Zero)) {
throw Error()
}
// console.log('hi!', amountFormatter(intermediateValue, ))
const calculatedDependentValue = calculateEtherTokenInputFromOutput(
intermediateValue,
reserveTokenFirst,
reserveETHFirst
)
if (calculatedDependentValue.lte(ethers.constants.Zero)) {
throw Error()
}
dispatchSwapState({ type: 'UPDATE_DEPENDENT', payload: calculatedDependentValue })
}
dispatchSwapState({ type: 'UPDATE_DEPENDENT', payload: calculatedDependentValue })
} catch {
setIndependentError(t('insufficientLiquidity'))
}
......
import store from './store.dev'
export default store
import { applyMiddleware, compose, createStore } from 'redux'
import thunk from 'redux-thunk'
import initialState from './initial-state'
import reducer from '../ducks'
const middleware = [thunk]
const enhancers = []
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
const store = createStore(reducer, initialState, composeEnhancers(applyMiddleware(...middleware), ...enhancers))
export default store
......@@ -44,12 +44,12 @@ export function getContract(address, ABI, library, account) {
// account is optional
export function getFactoryContract(networkId, library, account) {
return getContract(FACTORY_ADDRESSES[networkId], FACTORY_ABI, getProviderOrSigner(library, account))
return getContract(FACTORY_ADDRESSES[networkId], FACTORY_ABI, library, account)
}
// account is optional
export function getExchangeContract(exchangeAddress, library, account) {
return getContract(exchangeAddress, EXCHANGE_ABI, getProviderOrSigner(library, account))
return getContract(exchangeAddress, EXCHANGE_ABI, library, account)
}
// get token name
......
......@@ -1349,7 +1349,7 @@
dependencies:
"@babel/types" "^7.3.0"
"@types/bn.js@^4.11.0", "@types/bn.js@^4.11.4":
"@types/bn.js@^4.11.0":
version "4.11.5"
resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.5.tgz#40e36197433f78f807524ec623afcf0169ac81dc"
integrity sha512-AEAZcIZga0JgVMHNtl1CprA/hXX7/wPt79AgR4XqaDt7jyj3QWYw6LPoOiznPtugDmlubUnAahMs2PFxGcQrng==
......@@ -1387,7 +1387,7 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-11.13.4.tgz#f83ec3c3e05b174b7241fadeb6688267fe5b22ca"
integrity sha512-+rabAZZ3Yn7tF/XPGHupKIL5EcAbrLxnTr/hgQICxbeuAfWtT0UZSfULE+ndusckBItcv4o6ZeOJplQikVcLvQ==
"@types/node@^10.12.18", "@types/node@^10.3.2":
"@types/node@^10.3.2":
version "10.14.4"
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.4.tgz#1c586b991457cbb58fef51bc4e0cfcfa347714b5"
integrity sha512-DT25xX/YgyPKiHFOpNuANIQIVvYEwCWXgK2jYYwqgaMrYE6+tq+DtmMwlD3drl6DJbUwtlIDnn0d7tIn/EbXBg==
......@@ -2774,7 +2774,7 @@ big.js@^5.2.2:
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
bignumber.js@7.2.1, bignumber.js@^7.2.1:
bignumber.js@7.2.1:
version "7.2.1"
resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-7.2.1.tgz#80c048759d826800807c4bfd521e50edbba57a5f"
integrity sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ==
......@@ -3451,13 +3451,6 @@ cli-width@^2.0.0:
resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639"
integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=
cli@0.4.3:
version "0.4.3"
resolved "https://registry.yarnpkg.com/cli/-/cli-0.4.3.tgz#e6819c8d5faa957f64f98f66a8506268c1d1f17d"
integrity sha1-5oGcjV+qlX9k+Y9mqFBiaMHR8X0=
dependencies:
glob ">= 3.1.4"
cliui@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d"
......@@ -3591,11 +3584,6 @@ color@^3.0.0:
color-convert "^1.9.1"
color-string "^1.5.2"
colors@>=0.6.x:
version "1.3.3"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.3.3.tgz#39e005d546afe01e01f9c4ca8fa50f686a01205d"
integrity sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==
combined-stream@^1.0.6, combined-stream@~1.0.6:
version "1.0.7"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.7.tgz#2d1d24317afb8abe95d6d2c0b07b57813539d828"
......@@ -5011,14 +4999,6 @@ eth-block-tracker@^3.0.0:
pify "^2.3.0"
tape "^4.6.3"
eth-ens-namehash@2.0.8:
version "2.0.8"
resolved "https://registry.yarnpkg.com/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz#229ac46eca86d52e0c991e7cb2aef83ff0f68bcf"
integrity sha1-IprEbsqG1S4MmR58sq74P/D2i88=
dependencies:
idna-uts46-hx "^2.3.1"
js-sha3 "^0.5.7"
eth-json-rpc-infura@^3.1.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/eth-json-rpc-infura/-/eth-json-rpc-infura-3.2.0.tgz#62c3f516b51351038c32a548704467cec113ca8f"
......@@ -5071,15 +5051,6 @@ eth-lib@0.2.7:
elliptic "^6.4.0"
xhr-request-promise "^0.1.2"
eth-lib@0.2.8:
version "0.2.8"
resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.2.8.tgz#b194058bef4b220ad12ea497431d6cb6aa0623c8"
integrity sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw==
dependencies:
bn.js "^4.11.6"
elliptic "^6.4.0"
xhr-request-promise "^0.1.2"
eth-lightwallet@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/eth-lightwallet/-/eth-lightwallet-3.0.1.tgz#297022932aa568f4e4eb0873bff257f5e5b78709"
......@@ -5222,7 +5193,7 @@ ethereumjs-common@^1.1.0:
resolved "https://registry.yarnpkg.com/ethereumjs-common/-/ethereumjs-common-1.1.0.tgz#5ec9086c314d619d8f05e79a0525829fcb0e93cb"
integrity sha512-LUmYkKV/HcZbWRyu3OU9YOevsH3VJDXtI6kEd8VZweQec+JjDGKCmAVKUyzhYUHqxRJu7JNALZ3A/b3NXOP6tA==
ethereumjs-tx@1.3.7, ethereumjs-tx@^1.1.1, ethereumjs-tx@^1.2.0, ethereumjs-tx@^1.2.2, ethereumjs-tx@^1.3.3, ethereumjs-tx@^1.3.5, ethereumjs-tx@^1.3.7:
ethereumjs-tx@1.3.7, ethereumjs-tx@^1.1.1, ethereumjs-tx@^1.2.0, ethereumjs-tx@^1.2.2, ethereumjs-tx@^1.3.3, ethereumjs-tx@^1.3.5:
version "1.3.7"
resolved "https://registry.yarnpkg.com/ethereumjs-tx/-/ethereumjs-tx-1.3.7.tgz#88323a2d875b10549b8347e09f4862b546f3d89a"
integrity sha512-wvLMxzt1RPhAQ9Yi3/HKZTn0FZYpnsmQdbKYfUUpi4j1SEIcbkd9tndVjcPrufY3V7j2IebOpC00Zp2P/Ay2kA==
......@@ -5314,7 +5285,7 @@ ethers@^4.0.27, ethers@~4.0.4:
uuid "2.0.1"
xmlhttprequest "1.8.0"
ethjs-unit@0.1.6, ethjs-unit@^0.1.6:
ethjs-unit@0.1.6:
version "0.1.6"
resolved "https://registry.yarnpkg.com/ethjs-unit/-/ethjs-unit-0.1.6.tgz#c665921e476e87bce2a9d588a6fe0405b2c41699"
integrity sha1-xmWSHkduh7ziqdWIpv4EBbLEFpk=
......@@ -5340,7 +5311,7 @@ eventemitter3@1.1.1:
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.1.1.tgz#47786bdaa087caf7b1b75e73abc5c7d540158cd0"
integrity sha1-R3hr2qCHyvext15zq8XH1UAVjNA=
eventemitter3@3.1.0, eventemitter3@^3.0.0, eventemitter3@^3.1.0:
eventemitter3@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.0.tgz#090b4d6cdbd645ed10bf750d4b5407942d7ba163"
integrity sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==
......@@ -5925,17 +5896,6 @@ functional-red-black-tree@^1.0.1:
resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
fuse@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/fuse/-/fuse-0.4.0.tgz#2c38eaf888abb0a9ba7960cfe3339d1f3f53f6e6"
integrity sha1-LDjq+IirsKm6eWDP4zOdHz9T9uY=
dependencies:
colors ">=0.6.x"
jshint "0.9.x"
optimist ">=0.3.5"
uglify-js ">=2.2.x"
underscore ">=1.4.x"
ganache-core@^2.5.3:
version "2.5.5"
resolved "https://registry.yarnpkg.com/ganache-core/-/ganache-core-2.5.5.tgz#84da56de1957b6200a2d02934853fe7af89d260b"
......@@ -6057,7 +6017,7 @@ glob-to-regexp@^0.3.0:
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab"
integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=
"glob@>= 3.1.4", glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@~7.1.1, glob@~7.1.3:
glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@~7.1.1, glob@~7.1.3:
version "7.1.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==
......@@ -6607,13 +6567,6 @@ identity-obj-proxy@3.0.0:
dependencies:
harmony-reflect "^1.4.6"
idna-uts46-hx@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz#a1dc5c4df37eee522bf66d969cc980e00e8711f9"
integrity sha512-PWoF9Keq6laYdIRwwCdhTPl60xRqAloYNMQLiyUnG42VjT53oW07BXIRM+NK7eQjzXjAk2gUvX9caRxlnF9TAA==
dependencies:
punycode "2.1.0"
ieee754@^1.1.4:
version "1.1.13"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
......@@ -7655,7 +7608,7 @@ js-levenshtein@^1.1.3:
resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d"
integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==
js-sha3@0.5.7, js-sha3@^0.5.7:
js-sha3@0.5.7:
version "0.5.7"
resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7"
integrity sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=
......@@ -7772,14 +7725,6 @@ jsesc@~0.5.0:
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=
jshint@0.9.x:
version "0.9.1"
resolved "https://registry.yarnpkg.com/jshint/-/jshint-0.9.1.tgz#ff32ec7f09f84001f7498eeafd63c9e4fbb2dc0e"
integrity sha1-/zLsfwn4QAH3SY7q/WPJ5Puy3A4=
dependencies:
cli "0.4.3"
minimatch "0.0.x"
json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
......@@ -8192,11 +8137,6 @@ locate-path@^3.0.0:
p-locate "^3.0.0"
path-exists "^3.0.0"
lodash-es@^4.2.1:
version "4.17.11"
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.11.tgz#145ab4a7ac5c5e52a3531fb4f310255a152b4be0"
integrity sha512-DHb1ub+rMjjrxqlB3H56/6MXtm1lSksDp2rA2cNWjG8mlDUYFhUj3Di2Zn5IwSU87xLv8tNIQ7sSwE/YOX/D/Q==
lodash._reinterpolate@~3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"
......@@ -8267,7 +8207,7 @@ lodash.values@^4.3.0:
resolved "https://registry.yarnpkg.com/lodash.values/-/lodash.values-4.3.0.tgz#a3a6c2b0ebecc5c2cba1c17e6e620fe81b53d347"
integrity sha1-o6bCsOvsxcLLocF+bmIP6BtT00c=
lodash@4.17.11, lodash@=4.17.11, "lodash@>=3.5 <5", lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.1, lodash@~4.17.10:
lodash@4.17.11, lodash@=4.17.11, "lodash@>=3.5 <5", lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.17.5, lodash@~4.17.10:
version "4.17.11"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
......@@ -8339,11 +8279,6 @@ lru-cache@^5.1.1:
dependencies:
yallist "^3.0.2"
lru-cache@~1.0.2:
version "1.0.6"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-1.0.6.tgz#aa50f97047422ac72543bda177a9c9d018d98452"
integrity sha1-qlD5cEdCKsclQ72hd6nJ0BjZhFI=
ltgt@^2.1.2, ltgt@~2.2.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5"
......@@ -8628,13 +8563,6 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
minimatch@0.0.x:
version "0.0.5"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.0.5.tgz#96bb490bbd3ba6836bbfac111adf75301b1584de"
integrity sha1-lrtJC707poNrv6wRGt91MBsVhN4=
dependencies:
lru-cache "~1.0.2"
minimatch@3.0.4, minimatch@^3.0.3, minimatch@^3.0.4, minimatch@~3.0.2:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
......@@ -8782,7 +8710,7 @@ nan@2.10.0:
resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f"
integrity sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==
nan@^2.0.8, nan@^2.10.0, nan@^2.11.0, nan@^2.12.1, nan@^2.2.1, nan@^2.3.3, nan@^2.8.0, nan@^2.9.2:
nan@^2.0.8, nan@^2.10.0, nan@^2.12.1, nan@^2.2.1, nan@^2.3.3, nan@^2.8.0, nan@^2.9.2:
version "2.13.2"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.13.2.tgz#f51dc7ae66ba7d5d55e1e6d4d8092e802c9aefe7"
integrity sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==
......@@ -9179,11 +9107,6 @@ object-keys@~0.4.0:
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-0.4.0.tgz#28a6aae7428dd2c3a92f3d95f21335dd204e0336"
integrity sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=
object-path@^0.11.3:
version "0.11.4"
resolved "https://registry.yarnpkg.com/object-path/-/object-path-0.11.4.tgz#370ae752fbf37de3ea70a861c23bba8915691949"
integrity sha1-NwrnUvvzfePqcKhhwju6iRVpGUk=
object-visit@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb"
......@@ -9288,7 +9211,7 @@ opn@^5.1.0:
dependencies:
is-wsl "^1.1.0"
optimist@>=0.3.5, optimist@^0.6.1:
optimist@^0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686"
integrity sha1-2j6nRob6IaGaERwybpDrFaAZZoY=
......@@ -10489,7 +10412,7 @@ prompts@^2.0.1:
kleur "^3.0.2"
sisteransi "^1.0.0"
prop-types@^15.5.6, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2:
prop-types@^15.5.6, prop-types@^15.6.2:
version "15.7.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
......@@ -10626,11 +10549,6 @@ punycode@1.3.2:
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=
punycode@2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.0.tgz#5f863edc89b96db09074bad7947bf09056ca4e7d"
integrity sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0=
punycode@2.x.x, punycode@^2.1.0, punycode@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
......@@ -10810,24 +10728,6 @@ react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16"
integrity sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==
react-lifecycles-compat@^3.0.0:
version "3.0.4"
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
react-redux@^5.0.7:
version "5.1.1"
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.1.1.tgz#88e368682c7fa80e34e055cd7ac56f5936b0f52f"
integrity sha512-LE7Ned+cv5qe7tMV5BPYkGQ5Lpg8gzgItK07c67yHvJ8t0iaD9kPFPAli/mYkiyJYrs2pJgExR2ZgsGqlrOApg==
dependencies:
"@babel/runtime" "^7.1.2"
hoist-non-react-statics "^3.1.0"
invariant "^2.2.4"
loose-envify "^1.1.0"
prop-types "^15.6.1"
react-is "^16.6.0"
react-lifecycles-compat "^3.0.0"
react-router-dom@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.0.0.tgz#542a9b86af269a37f0b87218c4c25ea8dcf0c073"
......@@ -11060,28 +10960,6 @@ redent@^1.0.0:
indent-string "^2.1.0"
strip-indent "^1.0.1"
redux-subscriber@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/redux-subscriber/-/redux-subscriber-1.1.0.tgz#3cac28a674cec07b6e9e015ca7aabbbdac155543"
integrity sha1-PKwopnTOwHtungFcp6q7vawVVUM=
dependencies:
object-path "^0.11.3"
redux-thunk@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622"
integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==
redux@^3.7.2:
version "3.7.2"
resolved "https://registry.yarnpkg.com/redux/-/redux-3.7.2.tgz#06b73123215901d25d065be342eb026bc1c8537b"
integrity sha512-pNqnf9q1hI5HHZRBkj3bAngGZW/JMCmexDlOxw4XagXY2o1327nHH54LoTjiPJ0gizoqPDRqWyX/00g0hD6w+A==
dependencies:
lodash "^4.2.1"
lodash-es "^4.2.1"
loose-envify "^1.1.0"
symbol-observable "^1.0.3"
regenerate-unicode-properties@^8.0.2:
version "8.0.2"
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.0.2.tgz#7b38faa296252376d363558cfbda90c9ce709662"
......@@ -12366,29 +12244,6 @@ swarm-js@0.1.37:
tar.gz "^1.0.5"
xhr-request-promise "^0.1.2"
swarm-js@^0.1.39:
version "0.1.39"
resolved "https://registry.yarnpkg.com/swarm-js/-/swarm-js-0.1.39.tgz#79becb07f291d4b2a178c50fee7aa6e10342c0e8"
integrity sha512-QLMqL2rzF6n5s50BptyD6Oi0R1aWlJC5Y17SRIVXRj6OR1DRIPM7nepvrxxkjA1zNzFz6mUOMjfeqeDaWB7OOg==
dependencies:
bluebird "^3.5.0"
buffer "^5.0.5"
decompress "^4.0.0"
eth-lib "^0.1.26"
fs-extra "^4.0.2"
got "^7.1.0"
mime-types "^2.1.16"
mkdirp-promise "^5.0.1"
mock-fs "^4.1.0"
setimmediate "^1.0.5"
tar "^4.0.2"
xhr-request-promise "^0.1.2"
symbol-observable@^1.0.3:
version "1.2.0"
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==
symbol-tree@^3.2.2:
version "3.2.2"
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6"
......@@ -12471,7 +12326,7 @@ tar@^2.0.0, tar@^2.1.1:
fstream "^1.0.2"
inherits "2"
tar@^4, tar@^4.0.2:
tar@^4:
version "4.4.8"
resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.8.tgz#b19eec3fde2a96e64666df9fdb40c5ca1bc3747d"
integrity sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==
......@@ -12754,7 +12609,7 @@ type-is@~1.6.16:
media-typer "0.3.0"
mime-types "~2.1.18"
typedarray-to-buffer@^3.1.2, typedarray-to-buffer@^3.1.5:
typedarray-to-buffer@^3.1.2:
version "3.1.5"
resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080"
integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==
......@@ -12801,7 +12656,7 @@ uglify-js@3.4.x:
commander "~2.19.0"
source-map "~0.6.1"
uglify-js@>=2.2.x, uglify-js@^3.1.4:
uglify-js@^3.1.4:
version "3.5.4"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.5.4.tgz#4a64d57f590e20a898ba057f838dcdfb67a939b9"
integrity sha512-GpKo28q/7Bm5BcX9vOu4S46FwisbPbAmkkqPnGIpKvKTM96I85N6XHQV+k4I6FA2wxgLhcsSyHoNhzucwCflvA==
......@@ -12827,11 +12682,6 @@ underscore@1.8.3:
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022"
integrity sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=
underscore@>=1.4.x:
version "1.9.1"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961"
integrity sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==
unicode-canonical-property-names-ecmascript@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"
......@@ -12981,14 +12831,6 @@ url-parse-lax@^1.0.0:
dependencies:
prepend-http "^1.0.1"
url-parse@1.4.4:
version "1.4.4"
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.4.tgz#cac1556e95faa0303691fec5cf9d5a1bc34648f8"
integrity sha512-/92DTTorg4JjktLNLe6GPS2/RvAd/RGr6LuktmWSMLEOa6rjnlrFXNgSbSmkNvCoL2T028A0a1JaJLzRMlFoHg==
dependencies:
querystringify "^2.0.0"
requires-port "^1.0.0"
url-parse@^1.4.3:
version "1.4.6"
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.6.tgz#baf91d6e6783c8a795eb476892ffef2737fc0456"
......@@ -13085,7 +12927,7 @@ uuid@2.0.1:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.1.tgz#c2a30dedb3e535d72ccf82e343941a50ba8533ac"
integrity sha1-wqMN7bPlNdcsz4LjQ5QaULqFM6w=
uuid@3.3.2, uuid@^3.0.1, uuid@^3.3.2:
uuid@^3.0.1, uuid@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==
......@@ -13235,16 +13077,6 @@ web3-bzz@1.0.0-beta.35:
swarm-js "0.1.37"
underscore "1.8.3"
web3-bzz@1.0.0-beta.52:
version "1.0.0-beta.52"
resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.0.0-beta.52.tgz#36e79ba3b6310485c41528be635fa2b18518903e"
integrity sha512-yva0KW0cIEdFyHaGMHuMGjl4jea/esNBVwfFsejNRJy2W2jSMjnji+AFXnkcq8MhOAyNtQD4WzNdad9F51PaZg==
dependencies:
"@babel/runtime" "^7.3.1"
"@types/node" "^10.12.18"
lodash "^4.17.11"
swarm-js "^0.1.39"
web3-core-helpers@1.0.0-beta.35:
version "1.0.0-beta.35"
resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.0.0-beta.35.tgz#d681d218a0c6e3283ee1f99a078ab9d3eef037f1"
......@@ -13254,16 +13086,6 @@ web3-core-helpers@1.0.0-beta.35:
web3-eth-iban "1.0.0-beta.35"
web3-utils "1.0.0-beta.35"
web3-core-helpers@1.0.0-beta.52:
version "1.0.0-beta.52"
resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.0.0-beta.52.tgz#9ba41078f7e144cc2c9887a2a0039cc8d44c485f"
integrity sha512-VJCJMEplvrU7jCgn0MCuLLa+XWkQVttQpShM5i0XkcKs2gmisMtoLO3lATx2b32ruu29EriBYkkAVzc0/nxppg==
dependencies:
"@babel/runtime" "^7.3.1"
lodash "^4.17.11"
web3-eth-iban "1.0.0-beta.52"
web3-utils "1.0.0-beta.52"
web3-core-method@1.0.0-beta.35:
version "1.0.0-beta.35"
resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.0.0-beta.35.tgz#fc10e2d546cf4886038e6130bd5726b0952a4e5f"
......@@ -13275,15 +13097,6 @@ web3-core-method@1.0.0-beta.35:
web3-core-subscriptions "1.0.0-beta.35"
web3-utils "1.0.0-beta.35"
web3-core-method@1.0.0-beta.52:
version "1.0.0-beta.52"
resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.0.0-beta.52.tgz#0f80551047e097dde5c60ab82f42b16e1686c421"
integrity sha512-lLbDsV2pxxrUDIWvRI/u6MsvG8mKGfCYOifXqb+yqAruhoNs/Gahoa/1UTjsn0qhVQafsffFRXaEhi6BQDQOYA==
dependencies:
"@babel/runtime" "^7.3.1"
eventemitter3 "3.1.0"
lodash "^4.17.11"
web3-core-promievent@1.0.0-beta.35:
version "1.0.0-beta.35"
resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.0.0-beta.35.tgz#4f1b24737520fa423fee3afee110fbe82bcb8691"
......@@ -13312,15 +13125,6 @@ web3-core-subscriptions@1.0.0-beta.35:
underscore "1.8.3"
web3-core-helpers "1.0.0-beta.35"
web3-core-subscriptions@1.0.0-beta.52:
version "1.0.0-beta.52"
resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.0.0-beta.52.tgz#50a10dd1ee80095cc1d117a7bb35da76b3af174e"
integrity sha512-ZE6VsTVZ9PHV3FOJHnXOxUr4RuwXUpqC+RxCESb/UXvwHNnPbKNPpQjcoc5shaFrtOVtY14bIl35qTxVHSeGWg==
dependencies:
"@babel/runtime" "^7.3.1"
eventemitter3 "^3.1.0"
lodash "^4.17.11"
web3-core@1.0.0-beta.35:
version "1.0.0-beta.35"
resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.0.0-beta.35.tgz#0c44d3c50d23219b0b1531d145607a9bc7cd4b4f"
......@@ -13331,17 +13135,6 @@ web3-core@1.0.0-beta.35:
web3-core-requestmanager "1.0.0-beta.35"
web3-utils "1.0.0-beta.35"
web3-core@1.0.0-beta.52:
version "1.0.0-beta.52"
resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.0.0-beta.52.tgz#11c949c109871fa1c19b26c70e0e5a3b03f700d2"
integrity sha512-UKHNBIj5b4M40DrGJRQKgWTtbqZCCZck38oQgBbtLAUUQmvlZybLf8jGWUfMamyhJg/eBqT/t1l7OcAn5i9zrA==
dependencies:
"@babel/runtime" "^7.3.1"
"@types/bn.js" "^4.11.4"
"@types/node" "^10.12.18"
lodash "^4.17.11"
web3-utils "1.0.0-beta.52"
web3-eth-abi@1.0.0-beta.35:
version "1.0.0-beta.35"
resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.0.0-beta.35.tgz#2eb9c1c7c7233db04010defcb192293e0db250e6"
......@@ -13352,16 +13145,6 @@ web3-eth-abi@1.0.0-beta.35:
web3-core-helpers "1.0.0-beta.35"
web3-utils "1.0.0-beta.35"
web3-eth-abi@1.0.0-beta.52:
version "1.0.0-beta.52"
resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.0.0-beta.52.tgz#88dc2d36e2f99dfe255f8f64b6f613bad82779d8"
integrity sha512-c03sH6y7ncp9tBPt0EZEcyFyou4kyYdr72VJMY8ip0JAfZgl4WI9XcGpD207z0lR4Ki1PSCfkh+ZigoXxggouw==
dependencies:
"@babel/runtime" "^7.3.1"
ethers "^4.0.27"
lodash "^4.17.11"
web3-utils "1.0.0-beta.52"
web3-eth-accounts@1.0.0-beta.35:
version "1.0.0-beta.35"
resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.0.0-beta.35.tgz#7d0e5a69f510dc93874471599eb7abfa9ddf3e63"
......@@ -13378,23 +13161,6 @@ web3-eth-accounts@1.0.0-beta.35:
web3-core-method "1.0.0-beta.35"
web3-utils "1.0.0-beta.35"
web3-eth-accounts@1.0.0-beta.52:
version "1.0.0-beta.52"
resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.0.0-beta.52.tgz#06be3e1dd3a50fc4457c1cea77fc9a51a7baf0b1"
integrity sha512-B52yDVK2/3NKce1CESTZ/sD+6lU9pdNk4tPAtTkWTTPlejAbNlI04SdCX+hn2XJpDjsvU2HRSY3uNugVTrRQ6w==
dependencies:
"@babel/runtime" "^7.3.1"
crypto-browserify "3.12.0"
eth-lib "0.2.8"
lodash "^4.17.11"
scrypt.js "0.2.0"
uuid "3.3.2"
web3-core "1.0.0-beta.52"
web3-core-helpers "1.0.0-beta.52"
web3-core-method "1.0.0-beta.52"
web3-providers "1.0.0-beta.52"
web3-utils "1.0.0-beta.52"
web3-eth-contract@1.0.0-beta.35:
version "1.0.0-beta.35"
resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.0.0-beta.35.tgz#5276242d8a3358d9f1ce92b71575c74f9015935c"
......@@ -13409,40 +13175,6 @@ web3-eth-contract@1.0.0-beta.35:
web3-eth-abi "1.0.0-beta.35"
web3-utils "1.0.0-beta.35"
web3-eth-contract@1.0.0-beta.52:
version "1.0.0-beta.52"
resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.0.0-beta.52.tgz#9db66edd01433b3edb2bde9bde1314b64890dd04"
integrity sha512-X5Eqi/onxaBw04urowcYXl4L7eS3NuAFdBxSYP14rdTtP03TbgZEJ1GZDftF3cgMorvfGKcTyxyK0VYj/l+lfg==
dependencies:
"@babel/runtime" "^7.3.1"
"@types/bn.js" "^4.11.4"
lodash "^4.17.11"
web3-core "1.0.0-beta.52"
web3-core-helpers "1.0.0-beta.52"
web3-core-method "1.0.0-beta.52"
web3-core-subscriptions "1.0.0-beta.52"
web3-eth-abi "1.0.0-beta.52"
web3-eth-accounts "1.0.0-beta.52"
web3-providers "1.0.0-beta.52"
web3-utils "1.0.0-beta.52"
web3-eth-ens@1.0.0-beta.52:
version "1.0.0-beta.52"
resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.0.0-beta.52.tgz#430ad56c70076a2cc868cfe999f4958f7f992b9d"
integrity sha512-8VTOF+v5pAjq1FmakXRceY6VrIoPB7DqfSc+K4aAOJ/tuIMIpe6pt0Bl9IYAbpgFrFqL7ow22d9xcd77ULIwiQ==
dependencies:
"@babel/runtime" "^7.3.1"
eth-ens-namehash "2.0.8"
lodash "^4.17.11"
web3-core "1.0.0-beta.52"
web3-core-helpers "1.0.0-beta.52"
web3-core-method "1.0.0-beta.52"
web3-eth-abi "1.0.0-beta.52"
web3-eth-contract "1.0.0-beta.52"
web3-net "1.0.0-beta.52"
web3-providers "1.0.0-beta.52"
web3-utils "1.0.0-beta.52"
web3-eth-iban@1.0.0-beta.35:
version "1.0.0-beta.35"
resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.0.0-beta.35.tgz#5aa10327a9abb26bcfc4ba79d7bad18a002b332c"
......@@ -13451,15 +13183,6 @@ web3-eth-iban@1.0.0-beta.35:
bn.js "4.11.6"
web3-utils "1.0.0-beta.35"
web3-eth-iban@1.0.0-beta.52:
version "1.0.0-beta.52"
resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.0.0-beta.52.tgz#4b953ef78b073701458858872f494edfca4adeab"
integrity sha512-LJZRZ+hZZPU9Fb7xR54mX1li5aKMp9xj9wgZZa4ikdL7iWi0rg1tOacEhbxWGQsjEYhGZYcvxhW9RIdHOwAySg==
dependencies:
"@babel/runtime" "^7.3.1"
bn.js "4.11.8"
web3-utils "1.0.0-beta.52"
web3-eth-personal@1.0.0-beta.35:
version "1.0.0-beta.35"
resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.0.0-beta.35.tgz#ecac95b7a53d04a567447062d5cae5f49879e89f"
......@@ -13471,19 +13194,6 @@ web3-eth-personal@1.0.0-beta.35:
web3-net "1.0.0-beta.35"
web3-utils "1.0.0-beta.35"
web3-eth-personal@1.0.0-beta.52:
version "1.0.0-beta.52"
resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.0.0-beta.52.tgz#5a032d1ea938207be0c077da07ea410dbf7c4c82"
integrity sha512-tNjoB9KztpZL2ayjWYxaInwMrEGxBV7rGMt3hkhk9y4UxlK+8rZtrboz5hggzcgzHaVGnG73rdynhbuPU/cSAQ==
dependencies:
"@babel/runtime" "^7.3.1"
web3-core "1.0.0-beta.52"
web3-core-helpers "1.0.0-beta.52"
web3-core-method "1.0.0-beta.52"
web3-net "1.0.0-beta.52"
web3-providers "1.0.0-beta.52"
web3-utils "1.0.0-beta.52"
web3-eth@1.0.0-beta.35:
version "1.0.0-beta.35"
resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.0.0-beta.35.tgz#c52c804afb95e6624b6f5e72a9af90fbf5005b68"
......@@ -13502,28 +13212,6 @@ web3-eth@1.0.0-beta.35:
web3-net "1.0.0-beta.35"
web3-utils "1.0.0-beta.35"
web3-eth@1.0.0-beta.52:
version "1.0.0-beta.52"
resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.0.0-beta.52.tgz#16b03293f648b3f80bf8d19703dfbe467029d06d"
integrity sha512-mpuIFSIke/ZVdWfEzq0QRJAxcDh50BJflsdUMbNaxf5prP7Sz8FZJz/fHiu4H4cEM6aW8Bi0Hsoqad4+caX6Uw==
dependencies:
"@babel/runtime" "^7.3.1"
ethereumjs-tx "^1.3.7"
rxjs "^6.4.0"
web3-core "1.0.0-beta.52"
web3-core-helpers "1.0.0-beta.52"
web3-core-method "1.0.0-beta.52"
web3-core-subscriptions "1.0.0-beta.52"
web3-eth-abi "1.0.0-beta.52"
web3-eth-accounts "1.0.0-beta.52"
web3-eth-contract "1.0.0-beta.52"
web3-eth-ens "1.0.0-beta.52"
web3-eth-iban "1.0.0-beta.52"
web3-eth-personal "1.0.0-beta.52"
web3-net "1.0.0-beta.52"
web3-providers "1.0.0-beta.52"
web3-utils "1.0.0-beta.52"
web3-net@1.0.0-beta.35:
version "1.0.0-beta.35"
resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.0.0-beta.35.tgz#5c6688e0dea71fcd910ee9dc5437b94b7f6b3354"
......@@ -13533,19 +13221,6 @@ web3-net@1.0.0-beta.35:
web3-core-method "1.0.0-beta.35"
web3-utils "1.0.0-beta.35"
web3-net@1.0.0-beta.52:
version "1.0.0-beta.52"
resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.0.0-beta.52.tgz#2c6c04639bd24e44ef3b220845669926c323aff7"
integrity sha512-rw66c5A5VTF/m3Acnr9ebIAgbr28q5jhXs8A8/F4VjbsDmUhQuQr3deyTPfyOEyupcnn6QRLoQ1EFVzeaUP7Ng==
dependencies:
"@babel/runtime" "^7.3.1"
lodash "^4.17.11"
web3-core "1.0.0-beta.52"
web3-core-helpers "1.0.0-beta.52"
web3-core-method "1.0.0-beta.52"
web3-providers "1.0.0-beta.52"
web3-utils "1.0.0-beta.52"
web3-provider-engine@14.0.6:
version "14.0.6"
resolved "https://registry.yarnpkg.com/web3-provider-engine/-/web3-provider-engine-14.0.6.tgz#cbdd66fe20c0136a3a495cbe40d18b6c4160d5f0"
......@@ -13625,19 +13300,6 @@ web3-providers-ws@1.0.0-beta.35:
web3-core-helpers "1.0.0-beta.35"
websocket "git://github.com/frozeman/WebSocket-Node.git#browserifyCompatible"
web3-providers@1.0.0-beta.52:
version "1.0.0-beta.52"
resolved "https://registry.yarnpkg.com/web3-providers/-/web3-providers-1.0.0-beta.52.tgz#04a514430dc9740ff6da35334590886a9df4f71c"
integrity sha512-eRmWOn6BeYfAt8UQmCRnqXo1++IjSiIz7+EY9WJ+m7J5ncq/gQN3idWQxT3QZzGRiAvZlO8ZUuF7ff0vuufakg==
dependencies:
"@babel/runtime" "^7.3.1"
"@types/node" "^10.12.18"
eventemitter3 "3.1.0"
lodash "^4.17.11"
url-parse "1.4.4"
websocket "^1.0.28"
xhr2-cookies "1.1.0"
web3-react@^5.0.4:
version "5.0.4"
resolved "https://registry.yarnpkg.com/web3-react/-/web3-react-5.0.4.tgz#6006ee51c17255707f73d927f16eb48857957034"
......@@ -13655,20 +13317,6 @@ web3-shh@1.0.0-beta.35:
web3-core-subscriptions "1.0.0-beta.35"
web3-net "1.0.0-beta.35"
web3-shh@1.0.0-beta.52:
version "1.0.0-beta.52"
resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.0.0-beta.52.tgz#02ce376cb7a20276e49701684b5a9eda4e49c6ab"
integrity sha512-Qu9cb9fifUFIDTGujnBnInO3D2OCXDAmCowqMWebEazoZxk9P4oYWmumym1ZErixqEWREb4lkoayyEDSpARzSg==
dependencies:
"@babel/runtime" "^7.3.1"
web3-core "1.0.0-beta.52"
web3-core-helpers "1.0.0-beta.52"
web3-core-method "1.0.0-beta.52"
web3-core-subscriptions "1.0.0-beta.52"
web3-net "1.0.0-beta.52"
web3-providers "1.0.0-beta.52"
web3-utils "1.0.0-beta.52"
web3-utils@1.0.0-beta.35:
version "1.0.0-beta.35"
resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.0.0-beta.35.tgz#ced9e1df47c65581c441c5f2af76b05a37a273d7"
......@@ -13682,22 +13330,6 @@ web3-utils@1.0.0-beta.35:
underscore "1.8.3"
utf8 "2.1.1"
web3-utils@1.0.0-beta.52:
version "1.0.0-beta.52"
resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.0.0-beta.52.tgz#27f9beeac3e1ea981eba9824d79e2971f156eebc"
integrity sha512-WdHyzPcZu/sOnNrkcOZT20QEX9FhwD9OJJXENojQNvMK2a1xo3n8JWBcC2gzAGwsa0Aah6z2B3Xwa1P//8FaoA==
dependencies:
"@babel/runtime" "^7.3.1"
"@types/bn.js" "^4.11.4"
"@types/node" "^10.12.18"
bn.js "4.11.8"
eth-lib "0.2.8"
ethjs-unit "^0.1.6"
lodash "^4.17.11"
number-to-bn "1.7.0"
randomhex "0.1.5"
utf8 "2.1.1"
web3@0.20.2:
version "0.20.2"
resolved "https://registry.yarnpkg.com/web3/-/web3-0.20.2.tgz#c54dac5fc0e377399c04c1a6ecbb12e4513278d6"
......@@ -13722,24 +13354,6 @@ web3@1.0.0-beta.35:
web3-shh "1.0.0-beta.35"
web3-utils "1.0.0-beta.35"
web3@1.0.0-beta.52:
version "1.0.0-beta.52"
resolved "https://registry.yarnpkg.com/web3/-/web3-1.0.0-beta.52.tgz#a524888b84206d1ceea8abe1d6056bd7a0a8e33f"
integrity sha512-IWBV1gS7sElHqD2mkwjdyeEmY4YhWn7C+b+pdOWgDJ6j70ux2bqVMfsP0saZ9nWeF1TWMvUnrFsyZ7C4/VnhTA==
dependencies:
"@babel/runtime" "^7.3.1"
"@types/node" "^10.12.18"
web3-bzz "1.0.0-beta.52"
web3-core "1.0.0-beta.52"
web3-core-helpers "1.0.0-beta.52"
web3-core-method "1.0.0-beta.52"
web3-eth "1.0.0-beta.52"
web3-eth-personal "1.0.0-beta.52"
web3-net "1.0.0-beta.52"
web3-providers "1.0.0-beta.52"
web3-shh "1.0.0-beta.52"
web3-utils "1.0.0-beta.52"
webidl-conversions@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
......@@ -13869,16 +13483,6 @@ websocket@1.0.26:
typedarray-to-buffer "^3.1.2"
yaeti "^0.0.6"
websocket@^1.0.28:
version "1.0.28"
resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.28.tgz#9e5f6fdc8a3fe01d4422647ef93abdd8d45a78d3"
integrity sha512-00y/20/80P7H4bCYkzuuvvfDvh+dgtXi5kzDf3UcZwN6boTYaKvsrtZ5lIYm1Gsg48siMErd9M4zjSYfYFHTrA==
dependencies:
debug "^2.2.0"
nan "^2.11.0"
typedarray-to-buffer "^3.1.5"
yaeti "^0.0.6"
"websocket@git://github.com/frozeman/WebSocket-Node.git#browserifyCompatible":
version "1.0.26"
resolved "git://github.com/frozeman/WebSocket-Node.git#6c72925e3f8aaaea8dc8450f97627e85263999f2"
......
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