-
Notifications
You must be signed in to change notification settings - Fork 73
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added a warning icon if transfer cost is higher than token market value #236
Changes from all commits
6d72543
5f66e71
e658a92
a0d6b7c
60ca749
b4e0ea7
5804693
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,8 @@ import React, { useState, useEffect, useMemo, useCallback } from 'react'; | |
import { Title, Text } from '@gnosis.pm/safe-react-components'; | ||
import { useSafeAppsSDK } from '@gnosis.pm/safe-apps-react-sdk'; | ||
import web3Utils from 'web3-utils'; | ||
import { BigNumber } from 'bignumber.js'; | ||
|
||
import useBalances, { BalancesType } from '../hooks/use-balances'; | ||
import { tokenToTx } from '../utils/sdk-helpers'; | ||
import FormContainer from './FormContainer'; | ||
|
@@ -26,6 +28,7 @@ const App: React.FC = () => { | |
const [toAddress, setToAddress] = useState<string>(''); | ||
const [isFinished, setFinished] = useState<boolean>(false); | ||
const [error, setError] = useState<string>(''); | ||
const [gasPrice, setGasPrice] = useState<BigNumber>(new BigNumber(0)); | ||
const [networkPrefix, setNetworkPrefix] = useState<string>(''); | ||
|
||
const onError = (userMsg: string, err: Error) => { | ||
|
@@ -117,6 +120,14 @@ const App: React.FC = () => { | |
} | ||
}, [balancesError]); | ||
|
||
useEffect(() => { | ||
sdk.eth.getGasPrice().then((gasPrice) => { | ||
setGasPrice(new BigNumber(gasPrice)); | ||
}); | ||
}, [sdk.eth]); | ||
|
||
const ethFiatPrice = Number(assets[0]?.fiatConversion || 0); | ||
|
||
useEffect(() => { | ||
const getChainInfo = async () => { | ||
try { | ||
|
@@ -136,7 +147,14 @@ const App: React.FC = () => { | |
<Logo /> | ||
<Title size="md">Drain Account</Title> | ||
</Flex> | ||
<Balances assets={assets} exclude={excludedTokens} onExcludeChange={handleExcludeChange} /> | ||
<Balances | ||
ethFiatPrice={ethFiatPrice} | ||
gasPrice={gasPrice} | ||
web3={web3} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With the useWeb3 new hook you don't really need to pass it down right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, I decided to keep the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Really in CurrencyCell. In Balances you don't need i think There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. but as we talked in the other thread we will need a context provider for avoid instance recreation. And we will address it in a different ticket. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ahh right 😂 ... so let's improve it later |
||
assets={assets} | ||
exclude={excludedTokens} | ||
onExcludeChange={handleExcludeChange} | ||
/> | ||
{error && <Text size="lg">{error}</Text>} | ||
{isFinished && ( | ||
<Text size="lg"> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import { TokenBalance } from '@gnosis.pm/safe-apps-sdk'; | ||
import { Icon, Tooltip } from '@gnosis.pm/safe-react-components'; | ||
import BigNumber from 'bignumber.js'; | ||
import { useEffect, useState } from 'react'; | ||
import styled from 'styled-components'; | ||
import web3Utils from 'web3-utils'; | ||
import Web3 from 'web3'; | ||
|
||
import { formatCurrencyValue } from '../utils/formatters'; | ||
import { tokenToTx } from '../utils/sdk-helpers'; | ||
import Flex from './Flex'; | ||
import { useSafeAppsSDK } from '@gnosis.pm/safe-apps-react-sdk'; | ||
|
||
function CurrencyCell({ | ||
item, | ||
currency, | ||
gasPrice, | ||
ethFiatPrice, | ||
web3, | ||
}: { | ||
item: TokenBalance; | ||
currency: string; | ||
gasPrice: BigNumber; | ||
ethFiatPrice: number; | ||
web3: Web3 | undefined; | ||
}) { | ||
const label = formatCurrencyValue(item.fiatBalance, currency); | ||
const [transferCostInFiat, setTransferCostInFiat] = useState(new BigNumber(0)); | ||
|
||
const { safe } = useSafeAppsSDK(); | ||
|
||
// Transfer cost estimation | ||
useEffect(() => { | ||
const estimateTransferCost = async () => { | ||
try { | ||
const sendTokenTx = tokenToTx(safe.safeAddress, item); | ||
|
||
const estimatedTransferGas = await web3?.eth.estimateGas({ ...sendTokenTx, from: safe.safeAddress }); | ||
|
||
const gasCostInWei = gasPrice.multipliedBy(estimatedTransferGas || 21000); | ||
const gasCostInEther = new BigNumber(web3Utils.fromWei(gasCostInWei.toString(), 'ether')); | ||
|
||
const transferCostInFiat = gasCostInEther.multipliedBy(ethFiatPrice); | ||
|
||
setTransferCostInFiat(transferCostInFiat); | ||
} catch (e) { | ||
console.log('Error: ', e); | ||
} | ||
}; | ||
estimateTransferCost(); | ||
}, [gasPrice, ethFiatPrice, item, web3, safe]); | ||
|
||
// if transfer cost is higher than token market value, we show a warning icon & tooltip in the cell | ||
const showWarningIcon = | ||
ethFiatPrice > 0 && gasPrice.toNumber() > 0 && transferCostInFiat.toNumber() >= Number(item.fiatBalance); | ||
|
||
const warningTooltip = `Beware that the cost of this token transfer could be higher than its current market value (Estimated transfer cost: ${formatCurrencyValue( | ||
transferCostInFiat.toString(), | ||
currency, | ||
)})`; | ||
|
||
return showWarningIcon ? ( | ||
<Tooltip title={warningTooltip} placement="top"> | ||
<Flex> | ||
{label} | ||
<StyledIcon size="md" type="alert" color="warning" /> | ||
</Flex> | ||
</Tooltip> | ||
) : ( | ||
<Flex>{label}</Flex> | ||
); | ||
} | ||
|
||
export default CurrencyCell; | ||
|
||
const StyledIcon = styled(Icon)` | ||
margin-left: 4px; | ||
`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think about creating a useWeb3 hook? for avoid passing down props to de final CurrencyCell. You can gather the chains.ts info there as well as we are going to remove it later
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that it's a good idea to create a
useWeb3
hook, but I think that we should keep it in theApp.tsx
because we only want instantiate web3 and callgetGasPrice
only once and not in eachCurrencyCell
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 for a separate hook, I find that it vastly improves readability
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could perhaps be a separate ticket @dasanra ? as TX Builder and Drain Safe can share the same hook and as @DaniSomoza mentioned me offline, we will need a context provider as well for avoid instance recreation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would we want to avoid it? In the drain-safe app, it's only used in one component and then passed down via props. To me, it looks like just moving it to a separate file
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For avoid the prop to only exist to be sent down. In this case in the Balances I think
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but the context is fine isn't it ? I mean, a context for the web3 instance and a hook to instantiate it using our getChainInfo populated with RPC url (task in progress) could be helpful for retrieving it in any level of the component tree
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done! Added useWeb3.js hook