Gas estimations for approving TX wip, fix notifications reducer which led to infinite loop during HMR

This commit is contained in:
Mikhail Mikheev 2019-10-10 18:25:33 +04:00
parent ee483a98e4
commit de16a0cdec
7 changed files with 51 additions and 15 deletions

View File

@ -1,11 +1,17 @@
// @flow // @flow
import React, { Component } from 'react' import { Component } from 'react'
import { List } from 'immutable'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import { withSnackbar } from 'notistack' import { withSnackbar } from 'notistack'
import actions from './actions' import { type Notification } from '~/logic/notifications/store/models/notification'
import actions, {type Actions } from './actions'
import selector from './selector' import selector from './selector'
class Notifier extends Component { type Props = Actions & {
notifications: List<Notification>,
}
class Notifier extends Component<Props> {
displayed = [] displayed = []
shouldComponentUpdate({ notifications: newSnacks = [] }) { shouldComponentUpdate({ notifications: newSnacks = [] }) {

View File

@ -29,7 +29,5 @@ export default handleActions<NotificationReducerState, *>(
return state.delete(key) return state.delete(key)
}, },
}, },
Map({ Map(),
notifications: Map(),
}),
) )

View File

@ -5,7 +5,7 @@ import { type GlobalState } from '~/store'
import { NOTIFICATIONS_REDUCER_ID } from '~/logic/notifications/store/reducer/notifications' import { NOTIFICATIONS_REDUCER_ID } from '~/logic/notifications/store/reducer/notifications'
import { type Notification } from '~/logic/notifications/store/models/notification' import { type Notification } from '~/logic/notifications/store/models/notification'
export const notificationsMapSelector = ( const notificationsMapSelector = (
state: GlobalState, state: GlobalState,
): Map<string, Notification> => state[NOTIFICATIONS_REDUCER_ID] ): Map<string, Notification> => state[NOTIFICATIONS_REDUCER_ID]

View File

@ -1,16 +1,17 @@
// @flow // @flow
import GnosisSafeSol from '@gnosis.pm/safe-contracts/build/contracts/GnosisSafe.json' import GnosisSafeSol from '@gnosis.pm/safe-contracts/build/contracts/GnosisSafe.json'
import { List } from 'immutable'
import { type Confirmation } from '~/routes/safe/store/models/confirmation'
import { getWeb3, getAccountFrom } from '~/logic/wallets/getWeb3' import { getWeb3, getAccountFrom } from '~/logic/wallets/getWeb3'
import { calculateGasOf, calculateGasPrice } from '~/logic/wallets/ethTransactions' import { calculateGasOf, calculateGasPrice } from '~/logic/wallets/ethTransactions'
import { ZERO_ADDRESS } from '~/logic/wallets/ethAddresses' import { ZERO_ADDRESS } from '~/logic/wallets/ethAddresses'
import { type Transaction } from '~/routes/safe/store/models/transaction'
import { CALL } from '.' import { CALL } from '.'
export const estimateTxGasCosts = async ( export const estimateTxGasCosts = async (
safeAddress: string, safeAddress: string,
to: string, to: string,
data: string, data: string,
tx?: Transaction, confirmations?: List<Confirmation>,
): Promise<number> => { ): Promise<number> => {
try { try {
const web3 = getWeb3() const web3 = getWeb3()
@ -19,12 +20,12 @@ export const estimateTxGasCosts = async (
const nonce = await safeInstance.methods.nonce().call() const nonce = await safeInstance.methods.nonce().call()
const threshold = await safeInstance.methods.getThreshold().call() const threshold = await safeInstance.methods.getThreshold().call()
const isExecution = (tx && tx.confirmations.size) || threshold === '1' const isExecution = (confirmations && confirmations.size) || threshold === '1'
let txData let txData
if (isExecution) { if (isExecution) {
// https://gnosis-safe.readthedocs.io/en/latest/contracts/signatures.html#pre-validated-signatures // https://gnosis-safe.readthedocs.io/en/latest/contracts/signatures.html#pre-validated-signatures
const signatures = tx const signatures = confirmations
|| `0x000000000000000000000000${from.replace( || `0x000000000000000000000000${from.replace(
'0x', '0x',
'', '',

View File

@ -1,5 +1,5 @@
// @flow // @flow
import React, { useState } from 'react' import React, { useState, useEffect } from 'react'
import Close from '@material-ui/icons/Close' import Close from '@material-ui/icons/Close'
import IconButton from '@material-ui/core/IconButton' import IconButton from '@material-ui/core/IconButton'
import { withStyles } from '@material-ui/core/styles' import { withStyles } from '@material-ui/core/styles'
@ -13,6 +13,9 @@ import Row from '~/components/layout/Row'
import Bold from '~/components/layout/Bold' import Bold from '~/components/layout/Bold'
import Block from '~/components/layout/Block' import Block from '~/components/layout/Block'
import Paragraph from '~/components/layout/Paragraph' import Paragraph from '~/components/layout/Paragraph'
import { getWeb3 } from '~/logic/wallets/getWeb3'
import { estimateTxGasCosts } from '~/logic/safe/transactions/gasNew'
import { formatAmount } from '~/logic/tokens/utils/formatAmount'
import { TX_NOTIFICATION_TYPES } from '~/logic/safe/transactions' import { TX_NOTIFICATION_TYPES } from '~/logic/safe/transactions'
import { type Transaction } from '~/routes/safe/store/models/transaction' import { type Transaction } from '~/routes/safe/store/models/transaction'
import { styles } from './style' import { styles } from './style'
@ -62,9 +65,32 @@ const ApproveTxModal = ({
closeSnackbar, closeSnackbar,
}: Props) => { }: Props) => {
const [approveAndExecute, setApproveAndExecute] = useState<boolean>(true) const [approveAndExecute, setApproveAndExecute] = useState<boolean>(true)
const [gasCosts, setGasCosts] = useState<string>('< 0.001')
const { title, description } = getModalTitleAndDescription(thresholdReached) const { title, description } = getModalTitleAndDescription(thresholdReached)
const oneConfirmationLeft = tx.confirmations.size + 1 === threshold const oneConfirmationLeft = tx.confirmations.size + 1 === threshold
useEffect(() => {
let isCurrent = true
const estimateGas = async () => {
const web3 = getWeb3()
const { fromWei, toBN } = web3.utils
const estimatedGasCosts = await estimateTxGasCosts(safeAddress, tx.recipient, tx.data, tx.confirmations)
const gasCostsAsEth = fromWei(toBN(estimatedGasCosts), 'ether')
const formattedGasCosts = formatAmount(gasCostsAsEth)
if (isCurrent) {
setGasCosts(formattedGasCosts)
}
}
estimateGas()
return () => {
isCurrent = false
}
}, [approveAndExecute])
const handleExecuteCheckbox = () => setApproveAndExecute((prevApproveAndExecute) => !prevApproveAndExecute) const handleExecuteCheckbox = () => setApproveAndExecute((prevApproveAndExecute) => !prevApproveAndExecute)
const approveTx = () => { const approveTx = () => {
@ -112,6 +138,11 @@ const ApproveTxModal = ({
</> </>
)} )}
</Row> </Row>
<Row>
<Paragraph>
{`You're about to ${approveAndExecute ? 'execute' : 'approve'} a transaction and will have to confirm it with your currently connected wallet. Make sure you have ${gasCosts} (fee price) ETH in this wallet to fund this confirmation.`}
</Paragraph>
</Row>
</Block> </Block>
<Row align="center" className={classes.buttonRow}> <Row align="center" className={classes.buttonRow}>
<Button minWidth={140} minHeight={42} onClick={onClose}> <Button minWidth={140} minHeight={42} onClick={onClose}>

View File

@ -10,7 +10,7 @@ export type SafeProps = {
address: string, address: string,
threshold: number, threshold: number,
owners: List<Owner>, owners: List<Owner>,
balances: Map<string, string>, balances?: Map<string, string>,
activeTokens: Set<string>, activeTokens: Set<string>,
ethBalance?: string, ethBalance?: string,
} }
@ -21,7 +21,7 @@ const SafeRecord: RecordFactory<SafeProps> = Record({
threshold: 0, threshold: 0,
ethBalance: 0, ethBalance: 0,
owners: List([]), owners: List([]),
activeTokens: new Set([]), activeTokens: new Set(),
balances: Map({}), balances: Map({}),
}) })

View File

@ -15,7 +15,7 @@ import provider, { PROVIDER_REDUCER_ID, type State as ProviderState } from '~/lo
import tokens, { TOKEN_REDUCER_ID, type State as TokensState } from '~/logic/tokens/store/reducer/tokens' import tokens, { TOKEN_REDUCER_ID, type State as TokensState } from '~/logic/tokens/store/reducer/tokens'
import notifications, { import notifications, {
NOTIFICATIONS_REDUCER_ID, NOTIFICATIONS_REDUCER_ID,
type State as NotificationsState, type NotificationReducerState as NotificationsState,
} from '~/logic/notifications/store/reducer/notifications' } from '~/logic/notifications/store/reducer/notifications'
export const history = createBrowserHistory() export const history = createBrowserHistory()