diff --git a/package.json b/package.json index 777b4c66c..f9241bb25 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "mcd-cdp-portal", "description": "The primary web interface for interacting with the Multi-Collateral Dai smart contract system.", - "version": "0.2.5-rc.1", + "version": "0.2.6-rc.1", "license": "MIT", "private": true, "homepage": "https://mcd.makerdao.com", @@ -13,11 +13,11 @@ "dependencies": { "@craco/craco": "^3.4.1", "@hot-loader/react-dom": "^16.8.4", - "@makerdao/currency": "^0.9.3", + "@makerdao/currency": "^0.9.5", "@makerdao/dai": "0.15.3", "@makerdao/dai-plugin-config": "^0.0.3", "@makerdao/dai-plugin-ledger-web": "^0.9.7", - "@makerdao/dai-plugin-mcd": "0.1.6", + "@makerdao/dai-plugin-mcd": "0.1.8", "@makerdao/dai-plugin-trezor-web": "^0.9.6", "@makerdao/multicall": "0.6.1", "@makerdao/ui-components-core": "0.2.1", @@ -30,8 +30,7 @@ "body-scroll-lock": "^2.6.1", "eslint-plugin-react-hooks": "^1.5.0", "immer": "^3.1.1", - "lodash.round": "^4.0.4", - "lodash.uniqby": "^4.7.0", + "lodash": "^4.17.11", "mixpanel-browser": "^2.26.0", "navi": "^0.12.4", "prop-types": "^15.7.2", diff --git a/src/components/BrowserProviderConnect.js b/src/components/BrowserProviderConnect.js new file mode 100644 index 000000000..c48333d37 --- /dev/null +++ b/src/components/BrowserProviderConnect.js @@ -0,0 +1,73 @@ +import React from 'react'; + +import lang from 'languages'; +import styled from 'styled-components'; + +import { useNavigation } from 'react-navi'; +import { Button, Flex } from '@makerdao/ui-components-core'; +import { ReactComponent as MetaMaskLogo } from 'images/metamask.svg'; +import { ReactComponent as TrustLogo } from 'images/trust-logo.svg'; +import { ReactComponent as ImTokenLogo } from 'images/imtoken-logo.svg'; +import coinbaseWalletLogo from 'images/coinbase-wallet.png'; +import alphaWalletLogo from 'images/alpha-wallet-logo.png'; +import useMaker from 'hooks/useMaker'; +import { wallets } from 'utils/web3'; + +// hack to get around button padding for now +const MMLogo = styled(MetaMaskLogo)` + margin-top: -5px; + margin-bottom: -5px; +`; + +export default function BrowserProviderConnect({ provider }) { + const { + authenticated: makerAuthenticated, + connectBrowserProvider + } = useMaker(); + const navigation = useNavigation(); + + return ( + + ); +} diff --git a/src/components/CDPCreate.js b/src/components/CDPCreate.js index 31f1f915e..3f44bd852 100644 --- a/src/components/CDPCreate.js +++ b/src/components/CDPCreate.js @@ -1,21 +1,26 @@ -import React, { useReducer } from 'react'; +import React, { useReducer, useEffect } from 'react'; import { hot } from 'react-hot-loader/root'; import StepperUI from 'components/StepperUI'; import StepperHeader from 'components/StepperHeader'; import { CDPCreateSelectCollateral, + CDPCreateSetAllowance, CDPCreateConfirmCDP, CDPCreateDeposit } from 'components/CDPCreateScreens'; +import useMaker from 'hooks/useMaker'; const screens = [ ['Select Collateral', props => ], + ['Proxy Setup', props => ], ['Generate Dai', props => ], ['Confirmation', props => ] ]; const initialState = { step: 0, + proxyAddress: null, + hasAllowance: null, selectedIlk: { userGemBalance: '', currency: null, @@ -31,14 +36,28 @@ function reducer(state, action) { const { type, payload } = action; switch (type) { case 'increment-step': + const skipProxySetupForward = + state.step === 0 && state.proxyAddress && state.hasAllowance; return { ...state, - step: state.step + 1 + step: state.step + (skipProxySetupForward ? 2 : 1) }; case 'decrement-step': + const skipProxySetupBackwards = + state.step === 2 && state.proxyAddress && state.hasAllowance; return { ...state, - step: state.step - 1 + step: state.step - (skipProxySetupBackwards ? 2 : 1) + }; + case 'set-proxy-address': + return { + ...state, + proxyAddress: payload.address + }; + case 'set-ilk-allowance': + return { + ...state, + hasAllowance: payload.hasAllowance }; case 'set-ilk': return { @@ -72,13 +91,27 @@ function reducer(state, action) { } function CDPCreate({ onClose }) { - const [{ step, selectedIlk, ...cdpParams }, dispatch] = useReducer( - reducer, - initialState - ); + const { maker, account } = useMaker(); + const [ + { step, selectedIlk, proxyAddress, hasAllowance, ...cdpParams }, + dispatch + ] = useReducer(reducer, initialState); + + useEffect(() => { + const checkProxy = async () => { + try { + const address = await maker.service('proxy').currentProxy(); + dispatch({ type: 'set-proxy-address', payload: { address } }); + } catch (err) {} + }; + + checkProxy(); + }, [maker, account]); const screenProps = { selectedIlk, + proxyAddress, + hasAllowance, cdpParams, dispatch, onClose diff --git a/src/components/CDPCreateScreens/CDPCreateConfirmCDP.js b/src/components/CDPCreateScreens/CDPCreateConfirmCDP.js index aa3eef3c1..fde56efbe 100644 --- a/src/components/CDPCreateScreens/CDPCreateConfirmCDP.js +++ b/src/components/CDPCreateScreens/CDPCreateConfirmCDP.js @@ -9,11 +9,9 @@ import { Button, Link } from '@makerdao/ui-components-core'; -import LoadingLayout from 'layouts/LoadingLayout'; import useMaker from 'hooks/useMaker'; import useStore from 'hooks/useStore'; import lang from 'languages'; -import { MAX_UINT_BN } from 'utils/units'; import { calcCDPParams } from 'utils/cdp'; import { formatCollateralizationRatio } from 'utils/ui'; import { etherscanLink } from 'utils/ethereum'; @@ -115,9 +113,25 @@ const CDPCreateConfirmSummary = ({ const CDPCreateConfirmed = ({ hash, onClose }) => { const { maker } = useMaker(); + const [waitTime, setWaitTime] = useState('8 minutes'); const networkId = maker.service('web3').networkId(); const isTestchain = ![1, 42].includes(networkId); + useEffect(() => { + (async () => { + // this is the default transaction speed + const waitInMinutes = await maker.service('gas').getWaitTime('fast'); + const roundedWaitInMinutes = Math.round(waitInMinutes); + const roundedWaitInSeconds = Math.round(waitInMinutes * 6) * 10; + + const waitTime = + roundedWaitInMinutes === 0 + ? `${roundedWaitInSeconds} seconds` + : `${roundedWaitInMinutes} minutes`; + + setWaitTime(waitTime); + })(); + }); return ( { > @@ -167,7 +181,6 @@ const CDPCreateConfirmCDP = ({ dispatch, cdpParams, selectedIlk, onClose }) => { const { gemsToLock, daiToDraw } = cdpParams; - const [canCreateCDP, setCanCreateCDP] = useState(false); const [openCDPTxHash, setOpenCDPTxHash] = useState(null); async function capturedDispatch(payload) { @@ -192,30 +205,6 @@ const CDPCreateConfirmCDP = ({ dispatch, cdpParams, selectedIlk, onClose }) => { }); } - async function ensureProxyWithGemApprovals() { - try { - const proxyAddress = await maker.service('proxy').ensureProxy(); - if (selectedIlk.currency.symbol !== 'ETH') { - const gemToken = maker.getToken(selectedIlk.currency.symbol); - const gemAllowanceSet = (await gemToken.allowance( - maker.currentAddress(), - proxyAddress - )).eq(MAX_UINT_BN); - - if (!gemAllowanceSet) await gemToken.approveUnlimited(proxyAddress); - } - setCanCreateCDP(true); - } catch (err) { - console.error(err); - } - } - - useEffect(() => { - ensureProxyWithGemApprovals(); - }, []); - - if (!canCreateCDP) return ; - if (openCDPTxHash) return ; diff --git a/src/components/CDPCreateScreens/CDPCreateDeposit.js b/src/components/CDPCreateScreens/CDPCreateDeposit.js index 5867b909a..62ce22dd6 100644 --- a/src/components/CDPCreateScreens/CDPCreateDeposit.js +++ b/src/components/CDPCreateScreens/CDPCreateDeposit.js @@ -3,7 +3,11 @@ import { Box, Grid, Text, Input, Card } from '@makerdao/ui-components-core'; import { greaterThanOrEqual } from 'utils/bignumber'; import { TextBlock } from 'components/Typography'; import { getUsdPrice, calcCDPParams } from 'utils/cdp'; -import { cdpParamsAreValid, formatCollateralizationRatio } from 'utils/ui'; +import { + cdpParamsAreValid, + formatCollateralizationRatio, + prettifyNumber +} from 'utils/ui'; import lang from 'languages'; import ScreenFooter from './ScreenFooter'; @@ -67,7 +71,7 @@ function OpenCDPForm({ }); }} > - {selectedIlk.userGemBalance} {selectedIlk.data.gem} + {prettifyNumber(selectedIlk.userGemBalance)} {selectedIlk.data.gem} ], @@ -90,7 +94,6 @@ function OpenCDPForm({ {lang.cdp_create.deposit_form_field3_after2}{' '} - {selectedIlk.data.liquidationRatio}{' '} - {daiAvailable} DAI + {prettifyNumber(daiAvailable)} DAI diff --git a/src/components/CDPCreateScreens/CDPCreateSelectCollateral.js b/src/components/CDPCreateScreens/CDPCreateSelectCollateral.js index b64975d54..098359395 100644 --- a/src/components/CDPCreateScreens/CDPCreateSelectCollateral.js +++ b/src/components/CDPCreateScreens/CDPCreateSelectCollateral.js @@ -9,6 +9,7 @@ import { } from '@makerdao/ui-components-core'; import { TextBlock } from 'components/Typography'; +import { MAX_UINT_BN } from 'utils/units'; import { prettifyNumber } from 'utils/ui'; import ilkList from 'references/ilkList'; import { getIlkData } from 'reducers/feeds'; @@ -54,28 +55,22 @@ function IlkTableRow({ ilk, checked, dispatch }) { })(); }, []); + function selectIlk() { + dispatch({ + type: 'set-ilk', + payload: { + key: ilk.key, + gemBalance: userGemBalance.toNumber(), + currency: ilk.currency, + data: ilk.data + } + }); + } + return ( - - checked - ? dispatch({ - type: 'reset-ilk' - }) - : dispatch({ - type: 'set-ilk', - payload: { - key: ilk.key, - gemBalance: userGemBalance.toNumber(), - currency: ilk.currency, - data: ilk.data - } - }) - } - mr="xs" - /> + {ilk.symbol} {ilk.data.rate} % @@ -86,7 +81,31 @@ function IlkTableRow({ ilk, checked, dispatch }) { ); } -const CDPCreateSelectCollateral = ({ selectedIlk, dispatch }) => { +const CDPCreateSelectCollateral = ({ selectedIlk, proxyAddress, dispatch }) => { + const { maker, account } = useMaker(); + const [loading, setLoading] = useState(true); + + useEffect(() => { + (async () => { + setLoading(true); + try { + const gemToken = maker.getToken(selectedIlk.currency.symbol); + const hasAllowance = + selectedIlk.currency.symbol === 'ETH' || + (await gemToken.allowance(maker.currentAddress(), proxyAddress)).eq( + MAX_UINT_BN + ); + dispatch({ type: 'set-ilk-allowance', payload: { hasAllowance } }); + } catch (err) { + dispatch({ + type: 'set-ilk-allowance', + payload: { hasAllowance: false } + }); + } + setLoading(false); + })(); + }, [maker, account, selectedIlk.key]); + return ( { gridGap="m" my="l" > - - - - - - - - - - - - - - {ilkList.map(ilk => ( - - ))} - -
- {lang.collateral_type}{lang.stability_fee}{lang.liquidation_ratio_shortened}{lang.liquidation_penalty_shortened}{lang.your_balance}
-
-
+
+ + + + + + + + + + + + + + {ilkList.map(ilk => ( + + ))} + +
+ {lang.collateral_type}{lang.stability_fee}{lang.liquidation_ratio_shortened}{lang.liquidation_penalty_shortened}{lang.your_balance}
+
+
+
@@ -144,7 +165,7 @@ const CDPCreateSelectCollateral = ({ selectedIlk, dispatch }) => {
); diff --git a/src/components/CDPCreateScreens/CDPCreateSetAllowance.js b/src/components/CDPCreateScreens/CDPCreateSetAllowance.js new file mode 100644 index 000000000..c8b7c1806 --- /dev/null +++ b/src/components/CDPCreateScreens/CDPCreateSetAllowance.js @@ -0,0 +1,106 @@ +import React, { useState } from 'react'; +import { Box, Text, Card, Button, Grid } from '@makerdao/ui-components-core'; + +import lang from 'languages'; +import ScreenFooter from './ScreenFooter'; +import useMaker from 'hooks/useMaker'; + +import { ReactComponent as Checkmark } from 'images/checkmark.svg'; + +const SuccessButton = () => { + return ( + + ); +}; + +const CDPCreateSetAllowance = ({ + selectedIlk, + proxyAddress, + hasAllowance, + dispatch +}) => { + const { maker } = useMaker(); + const [isDeployingProxy, setIsDeployingProxy] = useState(false); + const [isSettingAllowance, setIsSettingAllowance] = useState(false); + + async function deployProxy() { + setIsDeployingProxy(true); + try { + const proxyAddress = await maker.service('proxy').ensureProxy(); + dispatch({ + type: 'set-proxy-address', + payload: { address: proxyAddress } + }); + } catch (err) {} + setIsDeployingProxy(false); + } + + async function setAllowance() { + setIsSettingAllowance(true); + try { + const gemToken = maker.getToken(selectedIlk.currency.symbol); + await gemToken.approveUnlimited(proxyAddress); + dispatch({ type: 'set-ilk-allowance', payload: { hasAllowance: true } }); + } catch (err) {} + setIsSettingAllowance(false); + } + + return ( + + + {lang.cdp_create.setup_proxy_title} + + + + Deploy proxy + + {lang.cdp_create.setup_proxy_proxy_text} + + {proxyAddress ? ( + + ) : ( + + )} + + + Set allowance + + {lang.formatString( + lang.cdp_create.setup_proxy_allowance_text, + selectedIlk.currency.symbol + )} + + {hasAllowance ? ( + + ) : ( + + )} + + + + + ); +}; + +export default CDPCreateSetAllowance; diff --git a/src/components/CDPCreateScreens/index.js b/src/components/CDPCreateScreens/index.js index 90abde029..d5890ca1d 100644 --- a/src/components/CDPCreateScreens/index.js +++ b/src/components/CDPCreateScreens/index.js @@ -1,5 +1,11 @@ import CDPCreateDeposit from './CDPCreateDeposit'; import CDPCreateSelectCollateral from './CDPCreateSelectCollateral'; +import CDPCreateSetAllowance from './CDPCreateSetAllowance'; import CDPCreateConfirmCDP from './CDPCreateConfirmCDP'; -export { CDPCreateSelectCollateral, CDPCreateConfirmCDP, CDPCreateDeposit }; +export { + CDPCreateSelectCollateral, + CDPCreateSetAllowance, + CDPCreateConfirmCDP, + CDPCreateDeposit +}; diff --git a/src/components/CDPList.js b/src/components/CDPList.js index a61e80b1b..350a1a8d1 100644 --- a/src/components/CDPList.js +++ b/src/components/CDPList.js @@ -1,4 +1,4 @@ -import React, { memo, Fragment, useState, useEffect } from 'react'; +import React, { memo, Fragment, useEffect, useState } from 'react'; import styled from 'styled-components'; // import { ReactComponent as MakerSmall } from '../images/maker-small.svg'; import { ReactComponent as Plus } from '../images/plus.svg'; @@ -9,6 +9,7 @@ import useModal from 'hooks/useModal'; import useMaker from 'hooks/useMaker'; import useStore from 'hooks/useStore'; import { fetchCdps } from 'reducers/cdps'; +import round from 'lodash/round'; const NavbarItemContainer = styled(Link)` display: block; @@ -41,6 +42,7 @@ const NavbarItem = ({ href, label, ratio, owned, active, ...props }) => ( const CDPList = memo(function({ currentPath, viewedAddress, currentQuery }) { const { maker, account } = useMaker(); const [{ cdps }, dispatch] = useStore(); + const [ratios, setRatios] = useState(); useEffect(() => { (async () => { @@ -48,7 +50,16 @@ const CDPList = memo(function({ currentPath, viewedAddress, currentQuery }) { const action = await fetchCdps(maker, address); dispatch(action); })(); - }, [maker, viewedAddress, account]); + }, [maker, viewedAddress, account, dispatch]); + + useEffect(() => { + (async () => { + const ratios = await Promise.all( + cdps.items.map(cdp => cdp.getCollateralizationRatio()) + ); + setRatios(ratios); + })(); + }, [cdps]); const { show } = useModal(); @@ -61,15 +72,19 @@ const CDPList = memo(function({ currentPath, viewedAddress, currentQuery }) { active={currentPath.includes('/overview/')} /> */} {cdps.items.map((cdp, idx) => { - const linkPath = `/${cdp.cdp.id}`; + const ratio = ratios[idx] + ? round(ratios[idx].times(100).toFixed(0)) + : null; + const linkPath = `/${cdp.id}`; const active = currentPath === linkPath; return ( ); })} diff --git a/src/components/MetaMaskConnect.js b/src/components/MetaMaskConnect.js deleted file mode 100644 index 1f3d85bad..000000000 --- a/src/components/MetaMaskConnect.js +++ /dev/null @@ -1,47 +0,0 @@ -import React from 'react'; - -import lang from 'languages'; -import styled from 'styled-components'; - -import { useNavigation } from 'react-navi'; -import { Button, Flex } from '@makerdao/ui-components-core'; -import { ReactComponent as MetaMaskLogo } from 'images/metamask.svg'; -import useMaker from 'hooks/useMaker'; - -// hack to get around button padding for now -const MMLogo = styled(MetaMaskLogo)` - margin-top: -5px; - margin-bottom: -5px; -`; - -export default function MetaMaskConnect() { - const { authenticated: makerAuthenticated, connectMetamask } = useMaker(); - const navigation = useNavigation(); - - return ( - - ); -} diff --git a/src/components/RatioDisplay.js b/src/components/RatioDisplay.js index 2528186ce..2adcff0a2 100644 --- a/src/components/RatioDisplay.js +++ b/src/components/RatioDisplay.js @@ -2,7 +2,6 @@ import React from 'react'; import { getColor } from 'styles/theme'; import { CDP_SAFETY_LEVELS } from 'utils/constants'; import { Text } from '@makerdao/ui-components-core'; -import { prettifyNumber } from 'utils/ui'; const CDP_SAFETY_COLOR_PALETTE = { [CDP_SAFETY_LEVELS.DANGER]: getColor('orange.500'), @@ -26,7 +25,7 @@ export default function RatioDisplay({ ratio, active }) { t="p6" color={active ? 'white' : CDP_SAFETY_COLOR_PALETTE[safetyLevel]} > - {prettifyNumber(ratio, true)}% + {ratio}% ); } diff --git a/src/components/SidebarAccountConnect.js b/src/components/SidebarAccountConnect.js index 3e0142872..e14fbcbca 100644 --- a/src/components/SidebarAccountConnect.js +++ b/src/components/SidebarAccountConnect.js @@ -4,11 +4,11 @@ import { Button, Text } from '@makerdao/ui-components-core'; import useMaker from 'hooks/useMaker'; export default function AccountConnect() { - const { connectMetamask } = useMaker(); + const { connectBrowserProvider } = useMaker(); const connectOnClick = async () => { try { - await connectMetamask(); + await connectBrowserProvider(); } catch (err) { window.alert(err); } diff --git a/src/components/SidebarSystem.js b/src/components/SidebarSystem.js index 617889750..ebabd694e 100644 --- a/src/components/SidebarSystem.js +++ b/src/components/SidebarSystem.js @@ -16,11 +16,6 @@ const CURRENT_DEBT = system => [ const BASE_RATE = system => [lang.sidebar.base_rate, `${system.baseRate} %`]; -const NUMBER_OF_LIQUIDATIONS = system => [ - lang.sidebar.number_of_liquidations, - prettifyNumber(system.numberOfLiquidations) -]; - const SURPLUS_AUCTION_LOT_SIZE = system => [ lang.sidebar.buy_and_burn_lot_size, prettifyNumber(system.surplusAuctionLotSize) @@ -36,7 +31,6 @@ const SidebarSystem = ({ system }) => { GLOBAL_DEBT_CEILING, CURRENT_DEBT, BASE_RATE, - NUMBER_OF_LIQUIDATIONS, SURPLUS_AUCTION_LOT_SIZE, DEBT_AUCTION_LOT_SIZE ].map(f => f(system)); diff --git a/src/components/Sidebars/Deposit.js b/src/components/Sidebars/Deposit.js index 912542d6e..ce8c60c6e 100644 --- a/src/components/Sidebars/Deposit.js +++ b/src/components/Sidebars/Deposit.js @@ -11,15 +11,16 @@ import { } from '../../utils/ui'; import lang from 'languages'; -const Deposit = ({ cdp, cdpId, reset }) => { +const Deposit = ({ cdp, reset }) => { const { maker, newTxListener } = useMaker(); const [amount, setAmount] = useState(''); const [gemBalance, setGemBalance] = useState(0); const [liquidationPrice, setLiquidationPrice] = useState(0); const [collateralizationRatio, setCollateralizationRatio] = useState(0); + const { symbol } = cdp.type.currency; maker - .getToken(cdp.ilkData.gem) + .getToken(symbol) .balance() .then(balance => { setGemBalance(balance.toNumber()); @@ -37,22 +38,14 @@ const Deposit = ({ cdp, cdpId, reset }) => { }); setLiquidationPrice(liquidationPrice); setCollateralizationRatio(collateralizationRatio); - }, [amount]); - - const setMax = () => { - setAmount(gemBalance); - }; + }, [amount, cdp.collateral, cdp.debt, cdp.ilkData]); const deposit = async () => { - const managedCdp = await maker.service('mcd:cdpManager').getCdp(cdpId); - - newTxListener( - managedCdp.lockCollateral(parseFloat(amount)), - `Locking ${cdp.ilkData.gem}` - ); + newTxListener(cdp.lockCollateral(parseFloat(amount)), `Locking ${symbol}`); reset(); }; + const setMax = () => setAmount(gemBalance); const lessThanBalance = amount === '' || parseFloat(amount) <= gemBalance; const inputNotEmpty = amount !== ''; const valid = inputNotEmpty && lessThanBalance; @@ -62,16 +55,13 @@ const Deposit = ({ cdp, cdpId, reset }) => { - {lang.formatString( - lang.action_sidebar.deposit_title, - cdp.ilkData.gem - )} + {lang.formatString(lang.action_sidebar.deposit_title, symbol)}

{lang.formatString( lang.action_sidebar.deposit_description, - cdp.ilkData.gem + symbol )}

@@ -80,7 +70,7 @@ const Deposit = ({ cdp, cdpId, reset }) => { min="0" value={amount} onChange={evt => setAmount(evt.target.value)} - placeholder={`0.00 ${cdp.ilkData.gem}`} + placeholder={`0.00 ${symbol}`} after={ {lang.action_sidebar.set_max} @@ -91,7 +81,7 @@ const Deposit = ({ cdp, cdpId, reset }) => { ? null : lang.formatString( lang.action_sidebar.insufficient_balance, - cdp.ilkData.gem + symbol ) } /> @@ -107,14 +97,14 @@ const Deposit = ({ cdp, cdpId, reset }) => { { - const { maker, newTxListener } = useMaker(); +const Generate = ({ cdp, reset }) => { + const { newTxListener } = useMaker(); const [amount, setAmount] = useState(''); const [daiAvailable, setDaiAvailable] = useState(0); const [liquidationPrice, setLiquidationPrice] = useState(0); @@ -33,19 +33,16 @@ const Generate = ({ cdp, cdpId, reset }) => { setDaiAvailable(daiAvailable - cdp.debt.toNumber()); setLiquidationPrice(liquidationPrice); setCollateralizationRatio(collateralizationRatio); - }, [amount]); + }, [amount, cdp.collateral, cdp.debt, cdp.ilkData]); const undercollateralized = collateralizationRatio < cdp.ilkData.liquidationRatio; const valid = parseFloat(amount) >= 0 && !undercollateralized; - const setMax = () => { - setAmount(daiAvailable); - }; + const setMax = () => setAmount(daiAvailable); const generate = async () => { - const managedCdp = await maker.service('mcd:cdpManager').getCdp(cdpId); - newTxListener(managedCdp.drawDai(parseFloat(amount)), 'Drawing DAI'); + newTxListener(cdp.drawDai(parseFloat(amount)), 'Drawing DAI'); reset(); }; diff --git a/src/components/Sidebars/Payback.js b/src/components/Sidebars/Payback.js index bfea6e6cd..482a783fc 100644 --- a/src/components/Sidebars/Payback.js +++ b/src/components/Sidebars/Payback.js @@ -12,7 +12,7 @@ import InfoContainer from './shared/InfoContainer'; import lang from 'languages'; import { MAX_UINT_BN } from '../../utils/units'; -const Payback = ({ cdp, cdpId, reset }) => { +const Payback = ({ cdp, reset }) => { const { maker, newTxListener } = useMaker(); const [amount, setAmount] = useState(''); const [daiBalance, setDaiBalance] = useState(0); @@ -22,9 +22,7 @@ const Payback = ({ cdp, cdpId, reset }) => { maker .getToken('MDAI') .balance() - .then(daiBalance => { - setDaiBalance(daiBalance.toNumber()); - }); + .then(daiBalance => setDaiBalance(daiBalance.toNumber())); useEffect(() => { const amountToPayback = parseFloat(amount || 0); @@ -36,11 +34,9 @@ const Payback = ({ cdp, cdpId, reset }) => { setLiquidationPrice(liquidationPrice); setCollateralizationRatio(collateralizationRatio); - }, [amount]); + }, [amount, cdp.collateral, cdp.debt, cdp.ilkData]); - const setMax = () => { - setAmount(Math.min(cdp.debt.toNumber(), daiBalance)); - }; + const setMax = () => setAmount(Math.min(cdp.debt.toNumber(), daiBalance)); const payback = async () => { const proxyAddress = await maker.service('proxy').ensureProxy(); @@ -55,8 +51,7 @@ const Payback = ({ cdp, cdpId, reset }) => { await daiToken.approveUnlimited(proxyAddress); } - const managedCdp = await maker.service('mcd:cdpManager').getCdp(cdpId); - newTxListener(managedCdp.wipeDai(parseFloat(amount)), 'Paying Back DAI'); + newTxListener(cdp.wipeDai(parseFloat(amount)), 'Paying Back DAI'); reset(); }; diff --git a/src/components/Sidebars/Withdraw.js b/src/components/Sidebars/Withdraw.js index e65603331..d86a13864 100644 --- a/src/components/Sidebars/Withdraw.js +++ b/src/components/Sidebars/Withdraw.js @@ -15,14 +15,15 @@ import { } from '../../utils/ui'; import lang from 'languages'; -const Withdraw = ({ cdp, cdpId, reset }) => { - const { maker, newTxListener } = useMaker(); +const Withdraw = ({ cdp, reset }) => { + const { newTxListener } = useMaker(); const [amount, setAmount] = useState(''); const [liquidationPrice, setLiquidationPrice] = useState(0); const [collateralizationRatio, setCollateralizationRatio] = useState(0); const collateralPrice = getUsdPrice(cdp.ilkData); const { free: freeCollateral } = getLockedAndFreeCollateral(cdp); + const { symbol } = cdp.type.currency; useEffect(() => { let val = parseFloat(amount); @@ -34,21 +35,17 @@ const Withdraw = ({ cdp, cdpId, reset }) => { }); setLiquidationPrice(liquidationPrice); setCollateralizationRatio(collateralizationRatio); - }, [amount]); - - const setMax = () => { - setAmount(freeCollateral); - }; + }, [amount, cdp.collateral, cdp.debt, cdp.ilkData]); + const setMax = () => setAmount(freeCollateral); const lessThanMax = amount === '' || parseFloat(amount) <= freeCollateral; const moreThanZero = amount !== '' && amount > 0; const valid = moreThanZero && lessThanMax; const withdraw = async () => { - const managedCdp = await maker.service('mcd:cdpManager').getCdp(cdpId); newTxListener( - managedCdp.freeCollateral(parseFloat(amount)), - `Withdrawing ${cdp.ilkData.gem}` + cdp.freeCollateral(parseFloat(amount)), + `Withdrawing ${symbol}` ); reset(); }; @@ -58,15 +55,12 @@ const Withdraw = ({ cdp, cdpId, reset }) => { - {lang.formatString( - lang.action_sidebar.withdraw_title, - cdp.ilkData.gem - )} + {lang.formatString(lang.action_sidebar.withdraw_title, symbol)} {lang.formatString( lang.action_sidebar.withdraw_description, - cdp.ilkData.gem + symbol )} { acc.address.toUpperCase() === address.toUpperCase()); - const connectMetamask = async () => { + const connectBrowserProvider = async () => { const networkId = maker.service('web3').networkId(); const browserProvider = await checkEthereumProvider(); @@ -56,25 +56,25 @@ function useMaker() { 'browser ethereum provider providing incorrect or non-existent address' ); - let metaMaskAccount; + let account; if (maker.service('accounts').hasAccount) { const matchedAccount = _getMatchedAccount(browserProvider.address); if (!matchedAccount) { - metaMaskAccount = await maker.addAccount({ + account = await maker.addAccount({ type: 'browser', autoSwitch: true }); } else { - metaMaskAccount = matchedAccount; + account = matchedAccount; } } else { - metaMaskAccount = await maker.addAccount({ + account = await maker.addAccount({ type: 'browser', autoSwitch: true }); } - maker.useAccountWithAddress(metaMaskAccount.address); + maker.useAccountWithAddress(account.address); const connectedAddress = maker.currentAddress(); return connectedAddress; }; @@ -83,7 +83,7 @@ function useMaker() { maker, authenticated, isConnectedToProvider, - connectMetamask, + connectBrowserProvider, account, transactions, newTxListener, diff --git a/src/hooks/useWatcher.js b/src/hooks/useWatcher.js deleted file mode 100644 index d08c16279..000000000 --- a/src/hooks/useWatcher.js +++ /dev/null @@ -1,10 +0,0 @@ -import { useContext } from 'react'; - -import { WatcherContext } from 'providers/WatcherProvider'; - -function useWatcher() { - const { watcher } = useContext(WatcherContext); - return { watcher }; -} - -export default useWatcher; diff --git a/src/images/alpha-wallet-logo.png b/src/images/alpha-wallet-logo.png new file mode 100644 index 000000000..d25e68d8d Binary files /dev/null and b/src/images/alpha-wallet-logo.png differ diff --git a/src/images/checkmark.svg b/src/images/checkmark.svg new file mode 100644 index 000000000..5f482aed9 --- /dev/null +++ b/src/images/checkmark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/images/coinbase-wallet.png b/src/images/coinbase-wallet.png new file mode 100644 index 000000000..f565e818b Binary files /dev/null and b/src/images/coinbase-wallet.png differ diff --git a/src/images/imtoken-logo.svg b/src/images/imtoken-logo.svg new file mode 100644 index 000000000..d14a03e2e --- /dev/null +++ b/src/images/imtoken-logo.svg @@ -0,0 +1,27 @@ + + + + + + + + diff --git a/src/images/trust-logo.svg b/src/images/trust-logo.svg new file mode 100644 index 000000000..38a9e335a --- /dev/null +++ b/src/images/trust-logo.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/languages/_english.json b/src/languages/_english.json index d71d9671d..bb485f6f5 100644 --- a/src/languages/_english.json +++ b/src/languages/_english.json @@ -22,6 +22,10 @@ "title": "Spend crypto without leaving your position.", "subtitle": "Leverage your crypto assets to generate Dai, a decentralized cryptocurrency pegged at the value of 1 US dollar.", "metamask": "MetaMask", + "trust": "Trust", + "coinbase": "Coinbase Wallet", + "imtoken": "ImToken", + "alphawallet": "Alpha Wallet", "ledger_nano": "Ledger Nano", "trezor": "Trezor", "read_only": "Read-Only", @@ -44,6 +48,12 @@ "select_title": "Select a collateral type", "select_text": "Each collateral type has its own risk parameters. You can lock up additional collateral types later.", + "setup_proxy_title": "Deploy Proxy and Set Allowance", + "setup_proxy_proxy_text": "Proxies are used in the CDP Portal to bundle multiple transactions into one, saving transaction time and gas costs. This only has to be done once.", + "setup_proxy_allowance_text": "This permission allows Maker smart contracts to interact with your {0}. This has to be done once for each new collateral type.", + "setup_proxy_proxy_button": "Setup", + "setup_proxy_allowance_button": "Set", + "deposit_title": "Deposit {0} and Generate Dai", "deposit_text": "Different collateral types have different risk parameters and collateralization ratios.", "deposit_sidebar_title": "{0} Risk Parameters", @@ -55,11 +65,11 @@ "deposit_form_field3_title": "How much DAI would you like to generate?", "deposit_form_field3_text": "You should generate an amount that is safely above the liquidation ratio.", "deposit_form_field3_after1": "Max at target ratio", - "deposit_form_field3_after2": "Max at liq. ratio", + "deposit_form_field3_after2": "Max avail to generate", "confirm_title": "Confirm CDP", "confirmed_title": "Your CDP is being created", - "confirmed_text": "The estimated time is 8 minutes. You can safely leave this page.", + "confirmed_text": "The estimated time is {0}. You can safely leave this page.", "proxy_title": "Creating a proxy", "proxy_text": "Before creating your first CDP, you must create a personal proxy contract.", "insufficient_ilk_balance": "Insufficient {0} balance", diff --git a/src/pages/CDP.js b/src/pages/CDP.js index 61a6bd4f8..a3ec4ee99 100644 --- a/src/pages/CDP.js +++ b/src/pages/CDP.js @@ -19,10 +19,7 @@ import useSidebar from 'hooks/useSidebar'; import useStore from 'hooks/useStore'; import { getIlkData } from 'reducers/feeds'; import ExternalLink from 'components/ExternalLink'; - -function round(value, decimals) { - return Number(Math.round(value + 'e' + decimals) + 'e-' + decimals); -} +import round from 'lodash/round'; const WithSeparators = styled(Box).attrs(() => ({ borderBottom: '1px solid', @@ -223,7 +220,7 @@ function CDPView({ cdpId: _cdpId }) { liquidationPrice, daiAvailable, minCollateral, - freeCollateral + collateralAvailable ] = await Promise.all([ cdp.getDebtValue(), cdp.getCollateralAmount(), @@ -234,10 +231,10 @@ function CDPView({ cdpId: _cdpId }) { cdp.minCollateral(), cdp.getCollateralAvailable() ]); - if (didCancel) return; - setCDP({ - cdp, + + // FIXME well this is an interesting way to store local state + Object.assign(cdp, { ilkData, debt, collateral, @@ -246,8 +243,10 @@ function CDPView({ cdpId: _cdpId }) { liquidationPrice, daiAvailable, minCollateral, - freeCollateral + collateralAvailable }); + + setCDP(cdp); return () => (didCancel = true); })(); }, [cdpId, feeds, maker]); @@ -265,14 +264,14 @@ function CDPView({ cdpId: _cdpId }) { } function CDPViewPresentation({ cdp, showSidebar, account, daiBalance }) { - const cdpId = cdp.cdp.id; // FIXME const liquidationPrice = round(cdp.liquidationPrice.toNumber(), 2).toFixed(2); - const gem = cdp.ilkData.gem; + const gem = cdp.type.currency.symbol; const collateralPrice = round(cdp.collateralPrice.toNumber(), 2); const liquidationPenalty = cdp.ilkData.liquidationPenalty + '%'; - const collateralizationRatio = ( - parseFloat(cdp.collateralizationRatio) * 100 - ).toFixed(2); + const collateralizationRatio = round( + cdp.collateralizationRatio.times(100).toNumber(), + 2 + ); const liquidationRatio = cdp.ilkData.liquidationRatio + '.00%'; const stabilityFee = cdp.ilkData.rate * 100 + '%'; const collateralAmount = round(cdp.collateral.toNumber(), 2).toFixed(2); @@ -286,21 +285,23 @@ function CDPViewPresentation({ cdp, showSidebar, account, daiBalance }) { cdp.minCollateral.times(cdp.collateralPrice).toNumber(), 2 ).toFixed(2); - const freeCollateralAmount = round(cdp.freeCollateral.toNumber(), 2); - const freeCollateralValue = round( - cdp.freeCollateral.times(cdp.collateralPrice).toNumber(), + const collateralAvailableAmount = round( + cdp.collateralAvailable.toNumber(), + 2 + ); + const collateralAvailableValue = round( + cdp.collateralAvailable.times(cdp.collateralPrice).toNumber(), 2 ); const debtAmount = round(cdp.debt.toNumber(), 2).toFixed(2); const debtSymbol = cdp.debt.symbol; const daiAvailable = round(cdp.daiAvailable.toNumber(), 2).toFixed(2); - const mockAddr = '0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF'; return ( - {lang.cdp} {cdpId} + {lang.cdp} {cdp.id} showSidebar({ sidebarType: 'deposit', - sidebarProps: { cdp, cdpId } + sidebarProps: { cdp } }) } > @@ -370,15 +371,15 @@ function CDPViewPresentation({ cdp, showSidebar, account, daiBalance }) { /> showSidebar({ sidebarType: 'withdraw', - sidebarProps: { cdp, cdpId } + sidebarProps: { cdp } }) } > @@ -404,7 +405,7 @@ function CDPViewPresentation({ cdp, showSidebar, account, daiBalance }) { onClick={() => showSidebar({ sidebarType: 'payback', - sidebarProps: { cdp, cdpId } + sidebarProps: { cdp } }) } > @@ -423,7 +424,7 @@ function CDPViewPresentation({ cdp, showSidebar, account, daiBalance }) { onClick={() => showSidebar({ sidebarType: 'generate', - sidebarProps: { cdp, cdpId } + sidebarProps: { cdp } }) } > @@ -434,48 +435,48 @@ function CDPViewPresentation({ cdp, showSidebar, account, daiBalance }) { - , - - ], - [ - 'ETH', - 'Sent 1,000.00 DAI', - 'Feb 12, 2019', - , - - ], - [ - 'ETH', - 'Locked 1,000.00 DAI', - 'Feb 09, 2019', - , - - ], - [ - 'ETH', - 'Withdrew 3,468.72 ETH', - 'Feb 03, 2019', - , - - ], - [ - 'ETH', - 'Opened CDP', - 'Jan 15, 2019', - , - - ] - ]} - /> + ); } export default hot(CDPView); + +const mockAddr = '0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF'; +const mockHistoryData = [ + [ + 'ETH', + 'Paid back 1,000.00 DAI', + 'Feb 15, 2019', + , + + ], + [ + 'ETH', + 'Sent 1,000.00 DAI', + 'Feb 12, 2019', + , + + ], + [ + 'ETH', + 'Locked 1,000.00 DAI', + 'Feb 09, 2019', + , + + ], + [ + 'ETH', + 'Withdrew 3,468.72 ETH', + 'Feb 03, 2019', + , + + ], + [ + 'ETH', + 'Opened CDP', + 'Jan 15, 2019', + , + + ] +]; diff --git a/src/pages/Landing.js b/src/pages/Landing.js index 1f67c5a91..e4005662f 100644 --- a/src/pages/Landing.js +++ b/src/pages/Landing.js @@ -2,10 +2,10 @@ import React from 'react'; import { hot } from 'react-hot-loader/root'; import Footer from '@makerdao/ui-components-footer'; import Header from '@makerdao/ui-components-header'; -import { Box } from '@makerdao/ui-components-core'; +import { Box, Grid } from '@makerdao/ui-components-core'; import lang from 'languages'; -import MetaMaskConnect from 'components/MetaMaskConnect'; +import BrowserProviderConnect from 'components/BrowserProviderConnect'; import ReadOnlyConnect from 'components/ReadOnlyConnect'; import HardwareWalletConnect from 'components/HardwareWalletConnect'; @@ -14,8 +14,11 @@ import WalletConnect from 'components/WalletConnect'; import { AccountTypes } from '../utils/constants'; import LandingHeroLayout from 'layouts/LandingHeroLayout'; import { Title, Subtitle } from 'components/Typography'; +import { getWebClientProviderName } from 'utils/web3'; function Landing() { + const providerName = getWebClientProviderName(); + return (
@@ -32,19 +35,13 @@ function Landing() { {lang.landing_page.subtitle} - - {[ - , - , - , - , - - ].map(comp => ( - - {comp} - - ))} - + + + + + + +