(Fix) Contract interaction ABI resets (#1394)

Co-authored-by: Daniel Sanchez <daniel.sanchez@gnosis.pm>
This commit is contained in:
Fernando 2020-10-05 12:13:19 -03:00 committed by GitHub
parent 21ea1ad9dd
commit 5a41c3857d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 51 additions and 33 deletions

View File

@ -1,13 +1,17 @@
import React from 'react' import React from 'react'
import { useField, useForm } from 'react-final-form'
import TextareaField from 'src/components/forms/TextareaField' import TextareaField from 'src/components/forms/TextareaField'
import { mustBeEthereumAddress, mustBeEthereumContractAddress } from 'src/components/forms/validator'
import Col from 'src/components/layout/Col' import Col from 'src/components/layout/Col'
import Row from 'src/components/layout/Row' import Row from 'src/components/layout/Row'
import { getNetwork } from 'src/config'
import { getConfiguredSource } from 'src/logic/contractInteraction/sources'
import { extractUsefulMethods } from 'src/logic/contractInteraction/sources/ABIService' import { extractUsefulMethods } from 'src/logic/contractInteraction/sources/ABIService'
export const NO_DATA = 'no data' export const NO_DATA = 'no data'
const mustBeValidABI = (abi: string): undefined | string => { const hasUsefulMethods = (abi: string): undefined | string => {
try { try {
const parsedABI = extractUsefulMethods(JSON.parse(abi)) const parsedABI = extractUsefulMethods(JSON.parse(abi))
@ -19,12 +23,44 @@ const mustBeValidABI = (abi: string): undefined | string => {
} }
} }
const ContractABI = () => ( const ContractABI = (): React.ReactElement => {
<Row margin="sm"> const {
<Col> input: { value: contractAddress },
<TextareaField name="abi" placeholder="ABI*" text="ABI*" type="text" validate={mustBeValidABI} /> } = useField('contractAddress', { subscription: { value: true } })
</Col> const { mutators } = useForm()
</Row> const setAbiValue = React.useRef(mutators.setAbiValue)
)
React.useEffect(() => {
const validateAndSetAbi = async () => {
const isEthereumAddress = mustBeEthereumAddress(contractAddress) === undefined
const isEthereumContractAddress = (await mustBeEthereumContractAddress(contractAddress)) === undefined
if (isEthereumAddress && isEthereumContractAddress) {
const network = getNetwork()
const source = getConfiguredSource()
const abi = await source.getContractABI(contractAddress, network)
const isValidABI = hasUsefulMethods(abi) === undefined
// this check may help in scenarios where the user first pastes the ABI,
// and then sets a Proxy contract that has no useful methods
if (isValidABI) {
setAbiValue.current(abi)
}
}
}
if (contractAddress) {
validateAndSetAbi()
}
}, [contractAddress])
return (
<Row margin="sm">
<Col>
<TextareaField name="abi" placeholder="ABI*" text="ABI*" type="text" validate={hasUsefulMethods} />
</Col>
</Row>
)
}
export default ContractABI export default ContractABI

View File

@ -19,7 +19,7 @@ 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, handleSubmitError, isReadMethod, ensResolver } from './utils' import { createTxObject, formMutators, handleSubmitError, isReadMethod, ensResolver } from './utils'
import { TransactionReviewType } from './Review' import { TransactionReviewType } from './Review'
const useStyles = makeStyles(styles) const useStyles = makeStyles(styles)
@ -92,7 +92,7 @@ const ContractInteraction: React.FC<ContractInteractionProps> = ({
<Header onClose={onClose} subTitle="1 of 2" title="Contract Interaction" /> <Header onClose={onClose} subTitle="1 of 2" title="Contract Interaction" />
<Hairline /> <Hairline />
<GnoForm <GnoForm
decorators={[abiExtractor, ensResolver]} decorators={[ensResolver]}
formMutators={formMutators} formMutators={formMutators}
initialValues={initialValues} initialValues={initialValues}
onSubmit={handleSubmit} onSubmit={handleSubmit}

View File

@ -2,9 +2,6 @@ import { FORM_ERROR, Mutator, SubmissionErrors } from 'final-form'
import createDecorator from 'final-form-calculate' import createDecorator from 'final-form-calculate'
import { ContractSendMethod } from 'web3-eth-contract' import { ContractSendMethod } from 'web3-eth-contract'
import { mustBeEthereumAddress, mustBeEthereumContractAddress } from 'src/components/forms/validator'
import { getNetwork } from 'src/config'
import { getConfiguredSource } from 'src/logic/contractInteraction/sources'
import { AbiItemExtended } from 'src/logic/contractInteraction/sources/ABIService' import { AbiItemExtended } from 'src/logic/contractInteraction/sources/ABIService'
import { getAddressFromENS, getWeb3 } from 'src/logic/wallets/getWeb3' import { getAddressFromENS, getWeb3 } from 'src/logic/wallets/getWeb3'
import { TransactionReviewType } from 'src/routes/safe/components/Balances/SendModal/screens/ContractInteraction/Review' import { TransactionReviewType } from 'src/routes/safe/components/Balances/SendModal/screens/ContractInteraction/Review'
@ -12,24 +9,6 @@ import { isValidEnsName } from 'src/logic/wallets/ethAddresses'
export const NO_CONTRACT = 'no contract' export const NO_CONTRACT = 'no contract'
export const abiExtractor = createDecorator({
field: 'contractAddress',
updates: {
abi: async (contractAddress) => {
if (
!contractAddress ||
mustBeEthereumAddress(contractAddress) ||
(await mustBeEthereumContractAddress(contractAddress))
) {
return
}
const network = getNetwork()
const source = getConfiguredSource()
return source.getContractABI(contractAddress, network)
},
},
})
export const ensResolver = createDecorator({ export const ensResolver = createDecorator({
field: 'contractAddress', field: 'contractAddress',
updates: { updates: {
@ -40,12 +19,12 @@ export const ensResolver = createDecorator({
if (resolvedAddress) { if (resolvedAddress) {
return resolvedAddress return resolvedAddress
} }
return contractAddress
} catch (e) { } catch (e) {
console.error(e.message) console.error(e.message)
return contractAddress return contractAddress
} }
return contractAddress
}, },
}, },
}) })
@ -71,6 +50,9 @@ export const formMutators: Record<string, Mutator<{ selectedMethod: { name: stri
setCallResults: (args, state, utils) => { setCallResults: (args, state, utils) => {
utils.changeValue(state, 'callResults', () => args[0]) utils.changeValue(state, 'callResults', () => args[0])
}, },
setAbiValue: (args, state, utils) => {
utils.changeValue(state, 'abi', () => args[0])
},
} }
export const isAddress = (type: string): boolean => type.indexOf('address') === 0 export const isAddress = (type: string): boolean => type.indexOf('address') === 0