mirror of
https://github.com/status-im/safe-react.git
synced 2025-01-13 19:44:12 +00:00
simplify call/review buttons
- unify handleSubmit - use `.call` to identify a failing tx
This commit is contained in:
parent
a28c598af1
commit
ed995df5b7
@ -5,70 +5,41 @@ import { useField, useFormState } from 'react-final-form'
|
|||||||
import Button from 'src/components/layout/Button'
|
import Button from 'src/components/layout/Button'
|
||||||
import Row from 'src/components/layout/Row'
|
import Row from 'src/components/layout/Row'
|
||||||
import { styles } from 'src/routes/safe/components/Balances/SendModal/screens/ContractInteraction/style'
|
import { styles } from 'src/routes/safe/components/Balances/SendModal/screens/ContractInteraction/style'
|
||||||
import { createTxObject } from 'src/routes/safe/components/Balances/SendModal/screens/ContractInteraction/utils'
|
import { isReadMethod } from 'src/routes/safe/components/Balances/SendModal/screens/ContractInteraction/utils'
|
||||||
|
|
||||||
const useStyles = makeStyles(styles as any)
|
const useStyles = makeStyles(styles as any)
|
||||||
|
|
||||||
const Buttons = ({ onCallSubmit, onClose }) => {
|
const Buttons = ({ onClose }) => {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
const {
|
const {
|
||||||
input: { value: method },
|
input: { value: method },
|
||||||
} = useField('selectedMethod', { value: true })
|
} = useField('selectedMethod', { value: true })
|
||||||
const {
|
const { modifiedSinceLastSubmit, submitError, submitting, valid, validating } = useFormState({
|
||||||
input: { value: contractAddress },
|
|
||||||
} = useField('contractAddress', { valid: true } as any)
|
|
||||||
const { modifiedSinceLastSubmit, submitError, submitting, valid, validating, values } = useFormState({
|
|
||||||
subscription: {
|
subscription: {
|
||||||
modifiedSinceLastSubmit: true,
|
modifiedSinceLastSubmit: true,
|
||||||
submitError: true,
|
submitError: true,
|
||||||
submitting: true,
|
submitting: true,
|
||||||
valid: true,
|
valid: true,
|
||||||
values: true,
|
|
||||||
validating: true,
|
validating: true,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleCallSubmit = async () => {
|
|
||||||
const results = await createTxObject(method, contractAddress, values).call()
|
|
||||||
onCallSubmit(results)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row align="center" className={classes.buttonRow}>
|
<Row align="center" className={classes.buttonRow}>
|
||||||
<Button minWidth={140} onClick={onClose}>
|
<Button minWidth={140} onClick={onClose}>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
{method && (method as any).action === 'read' ? (
|
<Button
|
||||||
<Button
|
className={classes.submitButton}
|
||||||
className={classes.submitButton}
|
color="primary"
|
||||||
color="primary"
|
data-testid={`${isReadMethod(method) ? 'call' : 'review'}-tx-btn`}
|
||||||
data-testid="review-tx-btn"
|
disabled={submitting || validating || ((!valid || !!submitError) && !modifiedSinceLastSubmit) || !method}
|
||||||
disabled={validating || !valid}
|
minWidth={140}
|
||||||
minWidth={140}
|
type="submit"
|
||||||
onClick={handleCallSubmit}
|
variant="contained"
|
||||||
variant="contained"
|
>
|
||||||
>
|
{isReadMethod(method) ? 'Call' : 'Review'}
|
||||||
Call
|
</Button>
|
||||||
</Button>
|
|
||||||
) : (
|
|
||||||
<Button
|
|
||||||
className={classes.submitButton}
|
|
||||||
color="primary"
|
|
||||||
data-testid="review-tx-btn"
|
|
||||||
disabled={
|
|
||||||
submitting ||
|
|
||||||
validating ||
|
|
||||||
((!valid || !!submitError) && !modifiedSinceLastSubmit) ||
|
|
||||||
!method ||
|
|
||||||
(method as any).action === 'read'
|
|
||||||
}
|
|
||||||
minWidth={140}
|
|
||||||
type="submit"
|
|
||||||
variant="contained"
|
|
||||||
>
|
|
||||||
Review
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</Row>
|
</Row>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import { makeStyles } from '@material-ui/core/styles'
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
import { FORM_ERROR } from 'final-form'
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import { useSelector } from 'react-redux'
|
||||||
|
|
||||||
import { styles } from './style'
|
import { styles } from './style'
|
||||||
import GnoForm from 'src/components/forms/GnoForm'
|
import GnoForm from 'src/components/forms/GnoForm'
|
||||||
import Block from 'src/components/layout/Block'
|
import Block from 'src/components/layout/Block'
|
||||||
import Hairline from 'src/components/layout/Hairline'
|
import Hairline from 'src/components/layout/Hairline'
|
||||||
import SafeInfo from 'src/routes/safe/components/Balances/SendModal/SafeInfo'
|
import SafeInfo from 'src/routes/safe/components/Balances/SendModal/SafeInfo'
|
||||||
|
import { safeSelector } from 'src/routes/safe/store/selectors'
|
||||||
import Buttons from './Buttons'
|
import Buttons from './Buttons'
|
||||||
import ContractABI from './ContractABI'
|
import ContractABI from './ContractABI'
|
||||||
import EthAddressInput from './EthAddressInput'
|
import EthAddressInput from './EthAddressInput'
|
||||||
@ -17,12 +18,14 @@ import Header from './Header'
|
|||||||
import MethodsDropdown from './MethodsDropdown'
|
import MethodsDropdown from './MethodsDropdown'
|
||||||
import RenderInputParams from './RenderInputParams'
|
import RenderInputParams from './RenderInputParams'
|
||||||
import RenderOutputParams from './RenderOutputParams'
|
import RenderOutputParams from './RenderOutputParams'
|
||||||
import { abiExtractor, createTxObject, formMutators } from './utils'
|
import { abiExtractor, createTxObject, formMutators, handleSubmitError, isReadMethod } from './utils'
|
||||||
|
|
||||||
const useStyles = makeStyles(styles as any)
|
const useStyles = makeStyles(styles as any)
|
||||||
|
|
||||||
const ContractInteraction = ({ contractAddress, initialValues, onClose, onNext }) => {
|
const ContractInteraction = ({ contractAddress, initialValues, onClose, onNext }) => {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
const { address: safeAddress = '' } = useSelector(safeSelector)
|
||||||
|
let setCallResults
|
||||||
|
|
||||||
React.useMemo(() => {
|
React.useMemo(() => {
|
||||||
if (contractAddress) {
|
if (contractAddress) {
|
||||||
@ -35,17 +38,18 @@ const ContractInteraction = ({ contractAddress, initialValues, onClose, onNext }
|
|||||||
try {
|
try {
|
||||||
const txObject = createTxObject(selectedMethod, contractAddress, values)
|
const txObject = createTxObject(selectedMethod, contractAddress, values)
|
||||||
const data = txObject.encodeABI()
|
const data = txObject.encodeABI()
|
||||||
await txObject.estimateGas()
|
const result = await txObject.call({ from: safeAddress })
|
||||||
onNext({ contractAddress, data, selectedMethod, value, ...values })
|
|
||||||
} catch (e) {
|
if (isReadMethod(selectedMethod)) {
|
||||||
for (const key in values) {
|
setCallResults(result)
|
||||||
if (values.hasOwnProperty(key) && values[key] === e.value) {
|
|
||||||
return { [key]: e.reason }
|
// this was a read method, so we won't go to the 'review' screen
|
||||||
}
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// .estimateGas() failed
|
onNext({ contractAddress, data, selectedMethod, value, ...values })
|
||||||
return { [FORM_ERROR]: e.message }
|
} catch (error) {
|
||||||
|
return handleSubmitError(error, values)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -62,6 +66,8 @@ const ContractInteraction = ({ contractAddress, initialValues, onClose, onNext }
|
|||||||
subscription={{ submitting: true, pristine: true }}
|
subscription={{ submitting: true, pristine: true }}
|
||||||
>
|
>
|
||||||
{(submitting, validating, rest, mutators) => {
|
{(submitting, validating, rest, mutators) => {
|
||||||
|
setCallResults = mutators.setCallResults
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Block className={classes.formContainer}>
|
<Block className={classes.formContainer}>
|
||||||
@ -80,7 +86,7 @@ const ContractInteraction = ({ contractAddress, initialValues, onClose, onNext }
|
|||||||
<FormErrorMessage />
|
<FormErrorMessage />
|
||||||
</Block>
|
</Block>
|
||||||
<Hairline />
|
<Hairline />
|
||||||
<Buttons onCallSubmit={mutators.setCallResults} onClose={onClose} />
|
<Buttons onClose={onClose} />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { FORM_ERROR } from 'final-form'
|
||||||
import createDecorator from 'final-form-calculate'
|
import createDecorator from 'final-form-calculate'
|
||||||
|
|
||||||
import { mustBeEthereumAddress, mustBeEthereumContractAddress } from 'src/components/forms/validator'
|
import { mustBeEthereumAddress, mustBeEthereumContractAddress } from 'src/components/forms/validator'
|
||||||
@ -48,6 +49,17 @@ export const formMutators = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const handleSubmitError = (error, values) => {
|
||||||
|
for (const key in values) {
|
||||||
|
if (values.hasOwnProperty(key) && values[key] === error.value) {
|
||||||
|
return { [key]: error.reason }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// .call() failed and we're logging a generic error
|
||||||
|
return { [FORM_ERROR]: error.message }
|
||||||
|
}
|
||||||
|
|
||||||
export const createTxObject = (method, contractAddress, values) => {
|
export const createTxObject = (method, contractAddress, values) => {
|
||||||
const web3 = getWeb3()
|
const web3 = getWeb3()
|
||||||
const contract: any = new web3.eth.Contract([method], contractAddress)
|
const contract: any = new web3.eth.Contract([method], contractAddress)
|
||||||
@ -56,3 +68,5 @@ export const createTxObject = (method, contractAddress, values) => {
|
|||||||
|
|
||||||
return contract.methods[name](...args)
|
return contract.methods[name](...args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const isReadMethod = (method: any) => method && method.action === 'read'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user