Skip to content
This repository has been archived by the owner on Jun 24, 2022. It is now read-only.

Commit

Permalink
Warning claim too much eth (#2256)
Browse files Browse the repository at this point in the history
# Summary

Waterfalls into #2250

Show warning/error when using too much native currency to invest

Uses current gas estimation for 1 approval. Might need to adjust that for a higher amount

If it's a Smart contract wallet, show a warning and let user proceed.
If it's an EOA wallet, show an error and block from moving forward until amount is corrected.

## Smart contract:

![Screen Shot 2022-01-21 at 18 14 02](https://user-images.githubusercontent.com/43217/150620950-7f19b526-ab55-4ee8-b7c5-2b05ddc1be53.png)

## EOA:

![Screen Shot 2022-01-21 at 18 13 44](https://user-images.githubusercontent.com/43217/150620964-30a16c42-e9a0-4523-8e20-5393047f17a0.png)

  # To Test

1. With and EOA, load one account that has a ETH investment option
2. Fund the account with not enough ETH to cover the full investment
3. On approvals page, select to invest max
* You'll see the error as you are using everything in your wallet
4. With a Smart contract wallet, load one account that has a ETH investment option
5. Fund the account with exactly the amount needed to cover the full investment (this step is only need if claiming on behalf of an EOA. If you have a Safe with claims do the same as step `2`)
* On approvals page, you should see the warning and be able to move forward

  # Background

**Note:** Color is of course dumb and doesn't quite work on light more.
Neither does the error color for that matter.
Counting on @biocom to make it look good
  • Loading branch information
alfetopito authored Jan 24, 2022
1 parent f4ae160 commit b1335dc
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 30 deletions.
29 changes: 25 additions & 4 deletions src/custom/components/swap/EthWethWrap/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { parseUnits } from '@ethersproject/units'
import { CurrencyAmount, Currency } from '@uniswap/sdk-core'
import { BigNumber } from '@ethersproject/bignumber'
// eslint-disable-next-line no-restricted-imports
import { t } from '@lingui/macro'
import { useGasPrices } from 'state/gas/hooks'

export const MINIMUM_TXS = '10'
export const AVG_APPROVE_COST_GWEI = '50000'
Expand All @@ -18,11 +20,12 @@ export function _isLowBalanceCheck({
nativeInput,
balance,
}: {
threshold: CurrencyAmount<Currency>
txCost: CurrencyAmount<Currency>
threshold?: CurrencyAmount<Currency>
txCost?: CurrencyAmount<Currency>
nativeInput?: CurrencyAmount<Currency>
balance?: CurrencyAmount<Currency>
}) {
if (!threshold || !txCost) return false
if (!nativeInput || !balance || nativeInput.add(txCost).greaterThan(balance)) return true
// OK if: users_balance - (amt_input + 1_tx_cost) > low_balance_threshold
return balance.subtract(nativeInput.add(txCost)).lessThan(threshold)
Expand All @@ -35,11 +38,29 @@ export const _getAvailableTransactions = ({
}: {
nativeBalance?: CurrencyAmount<Currency>
nativeInput?: CurrencyAmount<Currency>
singleTxCost: CurrencyAmount<Currency>
singleTxCost?: CurrencyAmount<Currency>
}) => {
if (!nativeBalance || !nativeInput || nativeBalance.lessThan(nativeInput.add(singleTxCost))) return null
if (!nativeBalance || !nativeInput || !singleTxCost || nativeBalance.lessThan(nativeInput.add(singleTxCost))) {
return null
}

// USER_BALANCE - (USER_WRAP_AMT + 1_TX_CST) / 1_TX_COST = AVAILABLE_TXS
const txsAvailable = nativeBalance.subtract(nativeInput.add(singleTxCost)).divide(singleTxCost)
return txsAvailable.lessThan('1') ? null : txsAvailable.toSignificant(1)
}

export function _estimateTxCost(gasPrice: ReturnType<typeof useGasPrices>, native: Currency | undefined) {
if (!native) {
return {}
}
// TODO: should use DEFAULT_GAS_FEE from backup source
// when/if implemented
const gas = gasPrice?.standard || DEFAULT_GAS_FEE

const amount = BigNumber.from(gas).mul(MINIMUM_TXS).mul(AVG_APPROVE_COST_GWEI)

return {
multiTxCost: CurrencyAmount.fromRawAmount(native, amount.toString()),
singleTxCost: CurrencyAmount.fromFractionalAmount(native, amount.toString(), MINIMUM_TXS),
}
}
23 changes: 2 additions & 21 deletions src/custom/components/swap/EthWethWrap/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,7 @@ import { useIsTransactionPending } from 'state/enhancedTransactions/hooks'
import Modal from 'components/Modal'
import { useGasPrices } from 'state/gas/hooks'
import { useActiveWeb3React } from 'hooks/web3'
import { BigNumber } from '@ethersproject/bignumber'
import {
DEFAULT_GAS_FEE,
MINIMUM_TXS,
AVG_APPROVE_COST_GWEI,
_isLowBalanceCheck,
_setNativeLowBalanceError,
_getAvailableTransactions,
} from './helpers'
import { _isLowBalanceCheck, _setNativeLowBalanceError, _getAvailableTransactions, _estimateTxCost } from './helpers'
import { Trans } from '@lingui/macro'

const Wrapper = styled.div`
Expand Down Expand Up @@ -152,18 +144,7 @@ export default function EthWethWrap({ account, native, nativeInput, wrapped, wra
const gasPrice = useGasPrices(chainId)

// returns the cost of 1 tx and multi txs
const { multiTxCost, singleTxCost } = useMemo(() => {
// TODO: should use DEFAULT_GAS_FEE from backup source
// when/if implemented
const gas = gasPrice?.standard || DEFAULT_GAS_FEE

const amount = BigNumber.from(gas).mul(MINIMUM_TXS).mul(AVG_APPROVE_COST_GWEI)

return {
multiTxCost: CurrencyAmount.fromRawAmount(native, amount.toString()),
singleTxCost: CurrencyAmount.fromFractionalAmount(native, amount.toString(), MINIMUM_TXS),
}
}, [gasPrice, native])
const { multiTxCost, singleTxCost } = useMemo(() => _estimateTxCost(gasPrice, native), [gasPrice, native])

const isWrapPending = useIsTransactionPending(pendingHash)
const [nativeBalance, wrappedBalance] = useCurrencyBalances(account, [native, wrapped])
Expand Down
2 changes: 0 additions & 2 deletions src/custom/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,6 @@ export const WETH_LOGO_URI =
export const XDAI_LOGO_URI =
'https://raw.githubusercontent.com/1Hive/default-token-list/master/src/assets/xdai/0xe91d153e0b41518a2ce8dd3d7944fa863463a97d/logo.png'

// 0.1 balance threshold
export const LOW_NATIVE_BALANCE_THRESHOLD = new Fraction('1', '10')
export const DOCS_LINK = 'https://docs.cow.fi'
export const CONTRACTS_CODE_LINK = 'https://github.com/gnosis/gp-v2-contracts'
export const CODE_LINK = 'https://github.com/gnosis/gp-swap-ui'
Expand Down
30 changes: 27 additions & 3 deletions src/custom/pages/Claim/InvestmentFlow/InvestOption.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,17 @@ import { useErrorModal } from 'hooks/useErrorMessageAndModal'
import { tryParseAmount } from 'state/swap/hooks'
import { calculateInvestmentAmounts, calculatePercentage } from 'state/claim/hooks/utils'
import { AMOUNT_PRECISION, PERCENTAGE_PRECISION } from 'constants/index'
import { useGasPrices } from 'state/gas/hooks'
import { _estimateTxCost } from 'components/swap/EthWethWrap/helpers'
import { useWalletInfo } from 'hooks/useWalletInfo'

const ErrorMsgs = {
InsufficientBalance: (symbol = '') => `Insufficient ${symbol} balance to cover investment amount`,
OverMaxInvestment: `Your investment amount can't be above the maximum investment allowed`,
InvestmentIsZero: `Your investment amount can't be zero`,
NotApproved: (symbol = '') => `Please approve ${symbol} token`,
InsufficientNativeBalance: (symbol = '', action = "won't") =>
`You ${action} have enough ${symbol} to pay the network transaction fee`,
}

export default function InvestOption({ approveData, claim, optionIndex }: InvestOptionProps) {
Expand All @@ -35,10 +40,12 @@ export default function InvestOption({ approveData, claim, optionIndex }: Invest

const { handleSetError, handleCloseError, ErrorModal } = useErrorModal()

const { account } = useActiveWeb3React()
const { account, chainId } = useActiveWeb3React()
const { isSmartContractWallet } = useWalletInfo()

const [percentage, setPercentage] = useState<string>('0')
const [typedValue, setTypedValue] = useState<string>('0')
const [inputWarning, setInputWarning] = useState<string>('')

const investedAmount = investFlowData[optionIndex].investedAmount
const inputError = investFlowData[optionIndex].error
Expand All @@ -60,6 +67,12 @@ export default function InvestOption({ approveData, claim, optionIndex }: Invest
const token = currencyAmount?.currency
const balance = useCurrencyBalance(account || undefined, token)

const gasPrice = useGasPrices(chainId)
const { singleTxCost } = useMemo(
() => _estimateTxCost(gasPrice, token?.isNative ? token : undefined),
[gasPrice, token]
)

const isSelfClaiming = account === activeClaimAccount
const noBalance = !balance || balance.equalTo('0')

Expand Down Expand Up @@ -132,6 +145,7 @@ export default function InvestOption({ approveData, claim, optionIndex }: Invest
// handle input value change
useEffect(() => {
let error = null
let warning

const parsedAmount = tryParseAmount(typedValue, token)

Expand All @@ -150,7 +164,14 @@ export default function InvestOption({ approveData, claim, optionIndex }: Invest
error = ErrorMsgs.OverMaxInvestment
} else if (parsedAmount.greaterThan(balance)) {
error = ErrorMsgs.InsufficientBalance(token?.symbol)
} else if (isNative && parsedAmount && singleTxCost?.add(parsedAmount).greaterThan(balance)) {
if (isSmartContractWallet) {
warning = ErrorMsgs.InsufficientNativeBalance(token?.symbol, 'might not')
} else {
error = ErrorMsgs.InsufficientNativeBalance(token?.symbol)
}
}
setInputWarning(warning || '')

if (error) {
// if there is error set it in redux
Expand All @@ -162,7 +183,7 @@ export default function InvestOption({ approveData, claim, optionIndex }: Invest
}
// basically the magic happens in this block

// update redux state to remove errro for this field
// update redux state to remove error for this field
resetInputError()

// update redux state with new investAmount value
Expand All @@ -182,6 +203,8 @@ export default function InvestOption({ approveData, claim, optionIndex }: Invest
setInputError,
resetInputError,
setInvestedAmount,
isSmartContractWallet,
singleTxCost,
])

return (
Expand Down Expand Up @@ -284,7 +307,8 @@ export default function InvestOption({ approveData, claim, optionIndex }: Invest
</label>
<i>Receive: {formatSmartLocaleAware(vCowAmount, AMOUNT_PRECISION) || 0} vCOW</i>
{/* Insufficient balance validation error */}
{inputError ? <small>{inputError}</small> : ''}
{inputError && <small>{inputError}</small>}
{inputWarning && <small className="warn">{inputWarning}</small>}
</div>
</InvestInput>
</span>
Expand Down
4 changes: 4 additions & 0 deletions src/custom/pages/Claim/styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,10 @@ export const InvestInput = styled.span`
color: red;
margin: 12px 0;
font-size: 15px;
&.warn {
color: orange;
}
}
> div > i {
Expand Down

0 comments on commit b1335dc

Please sign in to comment.