Allow proxy mater copy pattern

This commit is contained in:
Victor Baranov 2020-12-25 18:53:16 +03:00
parent deb1b8692c
commit 7a2c0b5c38
5 changed files with 85 additions and 22 deletions

View File

@ -2,6 +2,7 @@
## Current Master ## Current Master
- [#436](https://github.com/poanetwork/nifty-wallet/pull/436) - Allow master copy pattern in importing of proxy contract
- [#435](https://github.com/poanetwork/nifty-wallet/pull/435) - Allow array input type in contracts interactions - [#435](https://github.com/poanetwork/nifty-wallet/pull/435) - Allow array input type in contracts interactions
- [#434](https://github.com/poanetwork/nifty-wallet/pull/434) - Add support of tuple type at interaction with read-only contract methods - [#434](https://github.com/poanetwork/nifty-wallet/pull/434) - Add support of tuple type at interaction with read-only contract methods
- [#432](https://github.com/poanetwork/nifty-wallet/pull/432) - bump rsk-contract-metadata dependency - [#432](https://github.com/poanetwork/nifty-wallet/pull/432) - bump rsk-contract-metadata dependency

View File

@ -25,6 +25,8 @@ class ContractImportView extends Component {
static propTypes = { static propTypes = {
error: PropTypes.string, error: PropTypes.string,
network: PropTypes.string, network: PropTypes.string,
RPC_URL: PropTypes.string,
provider: PropTypes.object,
type: PropTypes.string, type: PropTypes.string,
displayWarning: PropTypes.func, displayWarning: PropTypes.func,
importNewAccount: PropTypes.func, importNewAccount: PropTypes.func,
@ -118,12 +120,12 @@ class ContractImportView extends Component {
autodetectContractABI = () => { autodetectContractABI = () => {
const { contractAddr, web3 } = this.state const { contractAddr, web3 } = this.state
const { type, network } = this.props const { type, network, provider, RPC_URL } = this.props
if (!contractAddr || !web3.isAddress(contractAddr)) { if (!contractAddr || !web3.isAddress(contractAddr)) {
this.clearABI() this.clearABI()
return return
} }
getFullABI(web3.eth, contractAddr, network, type) getFullABI(web3.eth, contractAddr, network, type, RPC_URL, provider)
.then(finalABI => { .then(finalABI => {
if (finalABI) { if (finalABI) {
finalABI = JSON.stringify(finalABI) finalABI = JSON.stringify(finalABI)
@ -218,6 +220,8 @@ function mapStateToProps (state) {
const result = { const result = {
error: warning && (warning || warning.message), error: warning && (warning || warning.message),
network: state.metamask.network, network: state.metamask.network,
provider: state.metamask.provider,
RPC_URL: state.appState.RPC_URL,
} }
return result return result

View File

@ -1,5 +1,8 @@
import log from 'loglevel' import log from 'loglevel'
import { importTypes } from './enums' import { importTypes } from './enums'
import Web3 from 'web3'
import ethNetProps from 'eth-net-props'
import abi from 'web3-eth-abi'
const nestedJsonObjToArray = (jsonObj) => { const nestedJsonObjToArray = (jsonObj) => {
return jsonObjToArray(jsonObj) return jsonObjToArray(jsonObj)
@ -100,44 +103,88 @@ const fetchABI = (addr, network) => {
}) })
} }
const getFullABI = (eth, contractAddr, network, type) => { const getFullABI = (eth, contractAddr, network, type, RPC_URL, provider) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
fetchABI(contractAddr, network) fetchABI(contractAddr, network)
.then((targetABI) => { .then((targetABI) => {
targetABI = targetABI && JSON.parse(targetABI) targetABI = targetABI && JSON.parse(targetABI)
let finalABI = targetABI
if (type === importTypes.CONTRACT.PROXY) { if (type === importTypes.CONTRACT.PROXY) {
if (!eth.contract(targetABI).at(contractAddr).implementation) { if (!eth.contract(targetABI).at(contractAddr).implementation && !isMasterCopyPattern(targetABI)) {
const e = { const e = {
message: 'This is not a valid Delegate Proxy contract', message: 'This is not a valid Delegate Proxy contract',
} }
reject(e) reject(e)
} }
try { try {
if (isMasterCopyPattern(targetABI)) {
let rpcUrl = RPC_URL || provider.rpcTarget
if (rpcUrl === '') {
rpcUrl = ethNetProps.RPCEndpoints(network)[0]
}
getImplAddrFromMasterCopyPattern(contractAddr, rpcUrl)
.then(implAddr => {
fetchImplementationAndCombine(implAddr, targetABI, network, resolve, reject)
})
.catch(err => {
reject(err)
})
} else {
eth.contract(targetABI).at(contractAddr).implementation.call((err, implAddr) => { eth.contract(targetABI).at(contractAddr).implementation.call((err, implAddr) => {
if (err) { if (err) {
reject(err) reject(err)
} else { } else {
fetchABI(implAddr, network) fetchImplementationAndCombine(implAddr, targetABI, network, resolve, reject)
.then((implABI) => {
implABI = implABI && JSON.parse(implABI)
finalABI = implABI ? targetABI.concat(implABI) : targetABI
resolve(finalABI)
})
.catch(e => reject(e))
} }
}) })
}
} catch (e) { } catch (e) {
reject(e) reject(e)
} }
} else { } else {
resolve(finalABI) resolve(targetABI)
} }
}) })
.catch(e => { reject(e) }) .catch(e => { reject(e) })
}) })
} }
const isMasterCopyPattern = (abi) => {
return abi.some(method => {
return isMasterCopyInput(method.inputs)
})
}
const isMasterCopyInput = (inputs) => {
return inputs && inputs.find(input => {
return input.name === '_masterCopy'
})
}
const getImplAddrFromMasterCopyPattern = (address, rpcUrl) => {
return new Promise((resolve, reject) => {
const web3 = new Web3(new Web3.providers.HttpProvider(rpcUrl))
web3.eth.getStorageAt(address, 0, 'latest', (err, result) => {
if (err) {
reject(err)
}
if (result) {
const implAddr = abi.decodeParameter('address', result)
resolve(implAddr)
}
})
})
}
const fetchImplementationAndCombine = (implAddr, proxyABI, network, resolve, reject) => {
return fetchABI(implAddr, network)
.then((implABI) => {
implABI = implABI && JSON.parse(implABI)
const finalABI = implABI ? proxyABI.concat(implABI) : proxyABI
resolve(finalABI)
})
.catch(e => reject(e))
}
module.exports = { module.exports = {
nestedJsonObjToArray, nestedJsonObjToArray,
getFullABI, getFullABI,

View File

@ -43,6 +43,8 @@ class AccountDropdowns extends Component {
style: PropTypes.object, style: PropTypes.object,
enableAccountOptions: PropTypes.bool, enableAccountOptions: PropTypes.bool,
enableAccountsSelector: PropTypes.bool, enableAccountsSelector: PropTypes.bool,
RPC_URL: PropTypes.string,
provider: PropTypes.object,
} }
constructor (props) { constructor (props) {
@ -229,9 +231,9 @@ class AccountDropdowns extends Component {
} }
updateABI = async () => { updateABI = async () => {
const { actions, selected, network } = this.props const { actions, selected, network, RPC_URL, provider } = this.props
actions.showLoadingIndication() actions.showLoadingIndication()
getFullABI(this.web3.eth, selected, network, importTypes.CONTRACT.PROXY) getFullABI(this.web3.eth, selected, network, importTypes.CONTRACT.PROXY, RPC_URL, provider)
.then(finalABI => { .then(finalABI => {
actions.updateABI(selected, network, finalABI) actions.updateABI(selected, network, finalABI)
.then() .then()
@ -331,6 +333,15 @@ class AccountDropdowns extends Component {
} }
} }
function mapStateToProps (state) {
const result = {
provider: state.metamask.provider,
RPC_URL: state.appState.RPC_URL,
}
return result
}
const mapDispatchToProps = (dispatch) => { const mapDispatchToProps = (dispatch) => {
return { return {
actions: { actions: {
@ -349,5 +360,5 @@ const mapDispatchToProps = (dispatch) => {
} }
module.exports = { module.exports = {
AccountDropdowns: connect(null, mapDispatchToProps)(AccountDropdowns), AccountDropdowns: connect(mapStateToProps, mapDispatchToProps)(AccountDropdowns),
} }

View File

@ -138,7 +138,7 @@ class SendTransactionScreen extends PersistentForm {
copyDisabled: true, copyDisabled: true,
} }
let rpcUrl = props.RPC_URL ? props.RPC_URL : props.provider.rpcTarget let rpcUrl = props.RPC_URL || props.provider.rpcTarget
if (rpcUrl === '') { if (rpcUrl === '') {
rpcUrl = ethNetProps.RPCEndpoints(props.network)[0] rpcUrl = ethNetProps.RPCEndpoints(props.network)[0]
} }