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
- [#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
- [#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

View File

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

View File

@ -1,5 +1,8 @@
import log from 'loglevel'
import { importTypes } from './enums'
import Web3 from 'web3'
import ethNetProps from 'eth-net-props'
import abi from 'web3-eth-abi'
const nestedJsonObjToArray = (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) => {
fetchABI(contractAddr, network)
.then((targetABI) => {
targetABI = targetABI && JSON.parse(targetABI)
let finalABI = targetABI
if (type === importTypes.CONTRACT.PROXY) {
if (!eth.contract(targetABI).at(contractAddr).implementation) {
if (!eth.contract(targetABI).at(contractAddr).implementation && !isMasterCopyPattern(targetABI)) {
const e = {
message: 'This is not a valid Delegate Proxy contract',
}
reject(e)
}
try {
eth.contract(targetABI).at(contractAddr).implementation.call((err, implAddr) => {
if (err) {
reject(err)
} else {
fetchABI(implAddr, network)
.then((implABI) => {
implABI = implABI && JSON.parse(implABI)
finalABI = implABI ? targetABI.concat(implABI) : targetABI
resolve(finalABI)
})
.catch(e => reject(e))
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) => {
if (err) {
reject(err)
} else {
fetchImplementationAndCombine(implAddr, targetABI, network, resolve, reject)
}
})
}
} catch (e) {
reject(e)
}
} else {
resolve(finalABI)
resolve(targetABI)
}
})
.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 = {
nestedJsonObjToArray,
getFullABI,

View File

@ -43,6 +43,8 @@ class AccountDropdowns extends Component {
style: PropTypes.object,
enableAccountOptions: PropTypes.bool,
enableAccountsSelector: PropTypes.bool,
RPC_URL: PropTypes.string,
provider: PropTypes.object,
}
constructor (props) {
@ -229,9 +231,9 @@ class AccountDropdowns extends Component {
}
updateABI = async () => {
const { actions, selected, network } = this.props
const { actions, selected, network, RPC_URL, provider } = this.props
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 => {
actions.updateABI(selected, network, finalABI)
.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) => {
return {
actions: {
@ -349,5 +360,5 @@ const mapDispatchToProps = (dispatch) => {
}
module.exports = {
AccountDropdowns: connect(null, mapDispatchToProps)(AccountDropdowns),
AccountDropdowns: connect(mapStateToProps, mapDispatchToProps)(AccountDropdowns),
}

View File

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