Show contract parameters in MethodDecoding (#4024)
* Add decoding of Inner Contract Deployment params #3715 * Fixed TypedInput when formatted value * Fix TypedInput * PR Grumble * Add test to `Param.toParams`
This commit is contained in:
parent
ddeb06d9cc
commit
d16ab5eac5
@ -31,6 +31,12 @@ export default class Param {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static toParams (params) {
|
static toParams (params) {
|
||||||
return params.map((param) => new Param(param.name, param.type));
|
return params.map((param) => {
|
||||||
|
if (param instanceof Param) {
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Param(param.name, param.type);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,5 +34,14 @@ describe('abi/spec/Param', () => {
|
|||||||
expect(params[0].name).to.equal('foo');
|
expect(params[0].name).to.equal('foo');
|
||||||
expect(params[0].kind.type).to.equal('uint');
|
expect(params[0].kind.type).to.equal('uint');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('converts only if needed', () => {
|
||||||
|
const _params = Param.toParams([{ name: 'foo', type: 'uint' }]);
|
||||||
|
const params = Param.toParams(_params);
|
||||||
|
|
||||||
|
expect(params.length).to.equal(1);
|
||||||
|
expect(params[0].name).to.equal('foo');
|
||||||
|
expect(params[0].kind.type).to.equal('uint');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -26,7 +26,9 @@ export function decodeCallData (data) {
|
|||||||
|
|
||||||
if (data.substr(0, 2) === '0x') {
|
if (data.substr(0, 2) === '0x') {
|
||||||
return decodeCallData(data.slice(2));
|
return decodeCallData(data.slice(2));
|
||||||
} else if (data.length < 8) {
|
}
|
||||||
|
|
||||||
|
if (data.length < 8) {
|
||||||
throw new Error('Input to decodeCallData should be method signature + data');
|
throw new Error('Input to decodeCallData should be method signature + data');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,10 +44,14 @@ export function decodeCallData (data) {
|
|||||||
export function decodeMethodInput (methodAbi, paramdata) {
|
export function decodeMethodInput (methodAbi, paramdata) {
|
||||||
if (!methodAbi) {
|
if (!methodAbi) {
|
||||||
throw new Error('decodeMethodInput should receive valid method-specific ABI');
|
throw new Error('decodeMethodInput should receive valid method-specific ABI');
|
||||||
} else if (paramdata && paramdata.length) {
|
}
|
||||||
|
|
||||||
|
if (paramdata && paramdata.length) {
|
||||||
if (!isHex(paramdata)) {
|
if (!isHex(paramdata)) {
|
||||||
throw new Error('Input to decodeMethodInput should be a hex value');
|
throw new Error('Input to decodeMethodInput should be a hex value');
|
||||||
} else if (paramdata.substr(0, 2) === '0x') {
|
}
|
||||||
|
|
||||||
|
if (paramdata.substr(0, 2) === '0x') {
|
||||||
return decodeMethodInput(methodAbi, paramdata.slice(2));
|
return decodeMethodInput(methodAbi, paramdata.slice(2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,7 @@ class AddressSelect extends Component {
|
|||||||
// Optional props
|
// Optional props
|
||||||
allowCopy: PropTypes.bool,
|
allowCopy: PropTypes.bool,
|
||||||
allowInput: PropTypes.bool,
|
allowInput: PropTypes.bool,
|
||||||
|
className: PropTypes.string,
|
||||||
disabled: PropTypes.bool,
|
disabled: PropTypes.bool,
|
||||||
error: nodeOrStringProptype(),
|
error: nodeOrStringProptype(),
|
||||||
hint: nodeOrStringProptype(),
|
hint: nodeOrStringProptype(),
|
||||||
@ -123,13 +124,14 @@ class AddressSelect extends Component {
|
|||||||
|
|
||||||
renderInput () {
|
renderInput () {
|
||||||
const { focused } = this.state;
|
const { focused } = this.state;
|
||||||
const { accountsInfo, allowCopy, disabled, error, hint, label, readOnly, value } = this.props;
|
const { accountsInfo, allowCopy, className, disabled, error, hint, label, readOnly, value } = this.props;
|
||||||
|
|
||||||
const input = (
|
const input = (
|
||||||
<InputAddress
|
<InputAddress
|
||||||
accountsInfo={ accountsInfo }
|
accountsInfo={ accountsInfo }
|
||||||
allowCopy={ allowCopy }
|
allowCopy={ allowCopy }
|
||||||
disabled={ disabled }
|
className={ className }
|
||||||
|
disabled={ disabled || readOnly }
|
||||||
error={ error }
|
error={ error }
|
||||||
hint={ hint }
|
hint={ hint }
|
||||||
focused={ focused }
|
focused={ focused }
|
||||||
|
@ -75,6 +75,12 @@ class InputAddress extends Component {
|
|||||||
containerClasses.push(styles.small);
|
containerClasses.push(styles.small);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const props = {};
|
||||||
|
|
||||||
|
if (!readOnly && !disabled) {
|
||||||
|
props.focused = focused;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={ containerClasses.join(' ') }>
|
<div className={ containerClasses.join(' ') }>
|
||||||
<Input
|
<Input
|
||||||
@ -82,7 +88,6 @@ class InputAddress extends Component {
|
|||||||
className={ classes.join(' ') }
|
className={ classes.join(' ') }
|
||||||
disabled={ disabled }
|
disabled={ disabled }
|
||||||
error={ error }
|
error={ error }
|
||||||
focused={ focused }
|
|
||||||
hideUnderline={ hideUnderline }
|
hideUnderline={ hideUnderline }
|
||||||
hint={ hint }
|
hint={ hint }
|
||||||
label={ label }
|
label={ label }
|
||||||
@ -96,7 +101,9 @@ class InputAddress extends Component {
|
|||||||
text && account
|
text && account
|
||||||
? account.name
|
? account.name
|
||||||
: (nullName || value)
|
: (nullName || value)
|
||||||
} />
|
}
|
||||||
|
{ ...props }
|
||||||
|
/>
|
||||||
{ icon }
|
{ icon }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -27,6 +27,7 @@ class InputAddressSelect extends Component {
|
|||||||
contracts: PropTypes.object.isRequired,
|
contracts: PropTypes.object.isRequired,
|
||||||
|
|
||||||
allowCopy: PropTypes.bool,
|
allowCopy: PropTypes.bool,
|
||||||
|
className: PropTypes.string,
|
||||||
error: PropTypes.string,
|
error: PropTypes.string,
|
||||||
hint: PropTypes.string,
|
hint: PropTypes.string,
|
||||||
label: PropTypes.string,
|
label: PropTypes.string,
|
||||||
@ -36,13 +37,14 @@ class InputAddressSelect extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { accounts, allowCopy, contacts, contracts, label, hint, error, value, onChange, readOnly } = this.props;
|
const { accounts, allowCopy, className, contacts, contracts, label, hint, error, value, onChange, readOnly } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AddressSelect
|
<AddressSelect
|
||||||
allowCopy={ allowCopy }
|
allowCopy={ allowCopy }
|
||||||
allowInput
|
allowInput
|
||||||
accounts={ accounts }
|
accounts={ accounts }
|
||||||
|
className={ className }
|
||||||
contacts={ contacts }
|
contacts={ contacts }
|
||||||
contracts={ contracts }
|
contracts={ contracts }
|
||||||
error={ error }
|
error={ error }
|
||||||
|
@ -41,6 +41,7 @@ export default class TypedInput extends Component {
|
|||||||
|
|
||||||
accounts: PropTypes.object,
|
accounts: PropTypes.object,
|
||||||
allowCopy: PropTypes.bool,
|
allowCopy: PropTypes.bool,
|
||||||
|
className: PropTypes.string,
|
||||||
error: PropTypes.any,
|
error: PropTypes.any,
|
||||||
hint: PropTypes.string,
|
hint: PropTypes.string,
|
||||||
isEth: PropTypes.bool,
|
isEth: PropTypes.bool,
|
||||||
@ -91,7 +92,7 @@ export default class TypedInput extends Component {
|
|||||||
const { type } = param;
|
const { type } = param;
|
||||||
|
|
||||||
if (type === ABI_TYPES.ARRAY) {
|
if (type === ABI_TYPES.ARRAY) {
|
||||||
const { accounts, label, value = param.default } = this.props;
|
const { accounts, className, label, value = param.default } = this.props;
|
||||||
const { subtype, length } = param;
|
const { subtype, length } = param;
|
||||||
|
|
||||||
const fixedLength = !!length;
|
const fixedLength = !!length;
|
||||||
@ -107,6 +108,7 @@ export default class TypedInput extends Component {
|
|||||||
<TypedInput
|
<TypedInput
|
||||||
accounts={ accounts }
|
accounts={ accounts }
|
||||||
allowCopy={ allowCopy }
|
allowCopy={ allowCopy }
|
||||||
|
className={ className }
|
||||||
key={ `${subtype.type}_${index}` }
|
key={ `${subtype.type}_${index}` }
|
||||||
onChange={ onChange }
|
onChange={ onChange }
|
||||||
param={ subtype }
|
param={ subtype }
|
||||||
@ -236,17 +238,34 @@ export default class TypedInput extends Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getNumberValue (value) {
|
||||||
|
if (!value) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { readOnly } = this.props;
|
||||||
|
|
||||||
|
const rawValue = typeof value === 'string'
|
||||||
|
? value.replace(/,/g, '')
|
||||||
|
: value;
|
||||||
|
|
||||||
|
const bnValue = new BigNumber(rawValue);
|
||||||
|
|
||||||
|
return readOnly
|
||||||
|
? bnValue.toFormat()
|
||||||
|
: bnValue.toNumber();
|
||||||
|
}
|
||||||
|
|
||||||
renderInteger (value = this.props.value, onChange = this.onChange) {
|
renderInteger (value = this.props.value, onChange = this.onChange) {
|
||||||
const { allowCopy, label, error, hint, min, max, readOnly } = this.props;
|
const { allowCopy, className, label, error, hint, min, max, readOnly } = this.props;
|
||||||
const param = this.getParam();
|
const param = this.getParam();
|
||||||
|
|
||||||
const realValue = value
|
const realValue = this.getNumberValue(value);
|
||||||
? (new BigNumber(value))[readOnly ? 'toFormat' : 'toNumber']()
|
|
||||||
: value;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Input
|
<Input
|
||||||
allowCopy={ allowCopy }
|
allowCopy={ allowCopy }
|
||||||
|
className={ className }
|
||||||
label={ label }
|
label={ label }
|
||||||
hint={ hint }
|
hint={ hint }
|
||||||
value={ realValue }
|
value={ realValue }
|
||||||
@ -269,16 +288,15 @@ export default class TypedInput extends Component {
|
|||||||
* @see https://github.com/facebook/react/issues/1549
|
* @see https://github.com/facebook/react/issues/1549
|
||||||
*/
|
*/
|
||||||
renderFloat (value = this.props.value, onChange = this.onChange) {
|
renderFloat (value = this.props.value, onChange = this.onChange) {
|
||||||
const { allowCopy, label, error, hint, min, max, readOnly } = this.props;
|
const { allowCopy, className, label, error, hint, min, max, readOnly } = this.props;
|
||||||
const param = this.getParam();
|
const param = this.getParam();
|
||||||
|
|
||||||
const realValue = value
|
const realValue = this.getNumberValue(value);
|
||||||
? (new BigNumber(value))[readOnly ? 'toFormat' : 'toNumber']()
|
|
||||||
: value;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Input
|
<Input
|
||||||
allowCopy={ allowCopy }
|
allowCopy={ allowCopy }
|
||||||
|
className={ className }
|
||||||
label={ label }
|
label={ label }
|
||||||
hint={ hint }
|
hint={ hint }
|
||||||
value={ realValue }
|
value={ realValue }
|
||||||
@ -293,11 +311,12 @@ export default class TypedInput extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderDefault () {
|
renderDefault () {
|
||||||
const { allowCopy, label, value, error, hint, readOnly } = this.props;
|
const { allowCopy, className, label, value, error, hint, readOnly } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Input
|
<Input
|
||||||
allowCopy={ allowCopy }
|
allowCopy={ allowCopy }
|
||||||
|
className={ className }
|
||||||
label={ label }
|
label={ label }
|
||||||
hint={ hint }
|
hint={ hint }
|
||||||
value={ value }
|
value={ value }
|
||||||
@ -309,12 +328,13 @@ export default class TypedInput extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderAddress () {
|
renderAddress () {
|
||||||
const { accounts, allowCopy, label, value, error, hint, readOnly } = this.props;
|
const { accounts, allowCopy, className, label, value, error, hint, readOnly } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InputAddressSelect
|
<InputAddressSelect
|
||||||
allowCopy={ allowCopy }
|
allowCopy={ allowCopy }
|
||||||
accounts={ accounts }
|
accounts={ accounts }
|
||||||
|
className={ className }
|
||||||
error={ error }
|
error={ error }
|
||||||
hint={ hint }
|
hint={ hint }
|
||||||
label={ label }
|
label={ label }
|
||||||
@ -326,7 +346,7 @@ export default class TypedInput extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderBoolean () {
|
renderBoolean () {
|
||||||
const { allowCopy, label, value, error, hint, readOnly } = this.props;
|
const { allowCopy, className, label, value, error, hint, readOnly } = this.props;
|
||||||
|
|
||||||
if (readOnly) {
|
if (readOnly) {
|
||||||
return this.renderDefault();
|
return this.renderDefault();
|
||||||
@ -346,6 +366,7 @@ export default class TypedInput extends Component {
|
|||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
allowCopy={ allowCopy }
|
allowCopy={ allowCopy }
|
||||||
|
className={ className }
|
||||||
error={ error }
|
error={ error }
|
||||||
hint={ hint }
|
hint={ hint }
|
||||||
label={ label }
|
label={ label }
|
||||||
|
@ -14,12 +14,11 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import BigNumber from 'bignumber.js';
|
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import CircularProgress from 'material-ui/CircularProgress';
|
import CircularProgress from 'material-ui/CircularProgress';
|
||||||
|
|
||||||
import { Input, InputAddress } from '../Form';
|
import { TypedInput, InputAddress } from '../Form';
|
||||||
import MethodDecodingStore from './methodDecodingStore';
|
import MethodDecodingStore from './methodDecodingStore';
|
||||||
|
|
||||||
import styles from './methodDecoding.css';
|
import styles from './methodDecoding.css';
|
||||||
@ -245,6 +244,7 @@ class MethodDecoding extends Component {
|
|||||||
|
|
||||||
renderDeploy () {
|
renderDeploy () {
|
||||||
const { historic, transaction } = this.props;
|
const { historic, transaction } = this.props;
|
||||||
|
const { methodInputs } = this.state;
|
||||||
|
|
||||||
if (!historic) {
|
if (!historic) {
|
||||||
return (
|
return (
|
||||||
@ -261,6 +261,14 @@ class MethodDecoding extends Component {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{ this.renderAddressName(transaction.creates, false) }
|
{ this.renderAddressName(transaction.creates, false) }
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{ methodInputs && methodInputs.length ? 'with the following parameters:' : ''}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={ styles.inputs }>
|
||||||
|
{ this.renderInputs() }
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -364,39 +372,31 @@ class MethodDecoding extends Component {
|
|||||||
renderInputs () {
|
renderInputs () {
|
||||||
const { methodInputs } = this.state;
|
const { methodInputs } = this.state;
|
||||||
|
|
||||||
return methodInputs.map((input, index) => {
|
if (!methodInputs || methodInputs.length === 0) {
|
||||||
switch (input.type) {
|
return null;
|
||||||
case 'address':
|
}
|
||||||
return (
|
|
||||||
<InputAddress
|
|
||||||
disabled
|
|
||||||
text
|
|
||||||
key={ index }
|
|
||||||
className={ styles.input }
|
|
||||||
value={ input.value }
|
|
||||||
label={ input.type } />
|
|
||||||
);
|
|
||||||
|
|
||||||
default:
|
const inputs = methodInputs.map((input, index) => {
|
||||||
return (
|
return (
|
||||||
<Input
|
<TypedInput
|
||||||
readOnly
|
allowCopy
|
||||||
allowCopy
|
className={ styles.input }
|
||||||
key={ index }
|
label={ input.type }
|
||||||
className={ styles.input }
|
key={ index }
|
||||||
value={ this.renderValue(input.value) }
|
param={ input.type }
|
||||||
label={ input.type } />
|
readOnly
|
||||||
);
|
value={ this.renderValue(input.value) }
|
||||||
}
|
/>
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return inputs;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderValue (value) {
|
renderValue (value) {
|
||||||
const { api } = this.context;
|
const { api } = this.context;
|
||||||
|
|
||||||
if (api.util.isInstanceOf(value, BigNumber)) {
|
if (api.util.isArray(value)) {
|
||||||
return value.toFormat(0);
|
|
||||||
} else if (api.util.isArray(value)) {
|
|
||||||
return api.util.bytesToHex(value);
|
return api.util.bytesToHex(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,8 @@ import Contracts from '~/contracts';
|
|||||||
import Abi from '~/abi';
|
import Abi from '~/abi';
|
||||||
import * as abis from '~/contracts/abi';
|
import * as abis from '~/contracts/abi';
|
||||||
|
|
||||||
|
import { decodeMethodInput } from '~/api/util/decode';
|
||||||
|
|
||||||
const CONTRACT_CREATE = '0x60606040';
|
const CONTRACT_CREATE = '0x60606040';
|
||||||
|
|
||||||
let instance = null;
|
let instance = null;
|
||||||
@ -26,6 +28,8 @@ export default class MethodDecodingStore {
|
|||||||
|
|
||||||
api = null;
|
api = null;
|
||||||
|
|
||||||
|
_bytecodes = {};
|
||||||
|
_contractsAbi = {};
|
||||||
_isContract = {};
|
_isContract = {};
|
||||||
_methods = {};
|
_methods = {};
|
||||||
|
|
||||||
@ -46,12 +50,17 @@ export default class MethodDecodingStore {
|
|||||||
if (!contract || !contract.meta || !contract.meta.abi) {
|
if (!contract || !contract.meta || !contract.meta.abi) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.loadFromAbi(contract.meta.abi);
|
this.loadFromAbi(contract.meta.abi, contract.address);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
loadFromAbi (_abi) {
|
loadFromAbi (_abi, contractAddress) {
|
||||||
const abi = new Abi(_abi);
|
const abi = new Abi(_abi);
|
||||||
|
|
||||||
|
if (contractAddress && abi) {
|
||||||
|
this._contractsAbi[contractAddress] = abi;
|
||||||
|
}
|
||||||
|
|
||||||
abi
|
abi
|
||||||
.functions
|
.functions
|
||||||
.map((f) => ({ sign: f.signature, abi: f.abi }))
|
.map((f) => ({ sign: f.signature, abi: f.abi }))
|
||||||
@ -111,6 +120,7 @@ export default class MethodDecodingStore {
|
|||||||
const contractAddress = isReceived ? transaction.from : transaction.to;
|
const contractAddress = isReceived ? transaction.from : transaction.to;
|
||||||
const input = transaction.input || transaction.data;
|
const input = transaction.input || transaction.data;
|
||||||
|
|
||||||
|
result.input = input;
|
||||||
result.received = isReceived;
|
result.received = isReceived;
|
||||||
|
|
||||||
// No input, should be a ETH transfer
|
// No input, should be a ETH transfer
|
||||||
@ -118,17 +128,20 @@ export default class MethodDecodingStore {
|
|||||||
return Promise.resolve(result);
|
return Promise.resolve(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
let signature;
|
||||||
const { signature } = this.api.util.decodeCallData(input);
|
|
||||||
|
|
||||||
if (signature === CONTRACT_CREATE || transaction.creates) {
|
try {
|
||||||
result.contract = true;
|
const decodeCallDataResult = this.api.util.decodeCallData(input);
|
||||||
return Promise.resolve({ ...result, deploy: true });
|
signature = decodeCallDataResult.signature;
|
||||||
}
|
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
|
// Contract deployment
|
||||||
|
if (!signature || signature === CONTRACT_CREATE || transaction.creates) {
|
||||||
|
return this.decodeContractCreation(result, contractAddress || transaction.creates);
|
||||||
|
}
|
||||||
|
|
||||||
return this
|
return this
|
||||||
.isContract(contractAddress || transaction.creates)
|
.isContract(contractAddress)
|
||||||
.then((isContract) => {
|
.then((isContract) => {
|
||||||
result.contract = isContract;
|
result.contract = isContract;
|
||||||
|
|
||||||
@ -140,11 +153,6 @@ export default class MethodDecodingStore {
|
|||||||
result.signature = signature;
|
result.signature = signature;
|
||||||
result.params = paramdata;
|
result.params = paramdata;
|
||||||
|
|
||||||
// Contract deployment
|
|
||||||
if (!signature) {
|
|
||||||
return Promise.resolve({ ...result, deploy: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
return this
|
return this
|
||||||
.fetchMethodAbi(signature)
|
.fetchMethodAbi(signature)
|
||||||
.then((abi) => {
|
.then((abi) => {
|
||||||
@ -173,6 +181,68 @@ export default class MethodDecodingStore {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
decodeContractCreation (data, contractAddress) {
|
||||||
|
const result = {
|
||||||
|
...data,
|
||||||
|
contract: true,
|
||||||
|
deploy: true
|
||||||
|
};
|
||||||
|
|
||||||
|
const { input } = data;
|
||||||
|
const abi = this._contractsAbi[contractAddress];
|
||||||
|
|
||||||
|
if (!input || !abi || !abi.constructors || abi.constructors.length === 0) {
|
||||||
|
return Promise.resolve(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
const constructorAbi = abi.constructors[0];
|
||||||
|
|
||||||
|
const rawInput = /^(?:0x)?(.*)$/.exec(input)[1];
|
||||||
|
|
||||||
|
return this
|
||||||
|
.getCode(contractAddress)
|
||||||
|
.then((code) => {
|
||||||
|
if (!code || /^(0x)0*?$/.test(code)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rawCode = /^(?:0x)?(.*)$/.exec(code)[1];
|
||||||
|
const codeOffset = rawInput.indexOf(rawCode);
|
||||||
|
|
||||||
|
if (codeOffset === -1) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Params are the last bytes of the transaction Input
|
||||||
|
// (minus the bytecode). It seems that they are repeated
|
||||||
|
// twice
|
||||||
|
const params = rawInput.slice(codeOffset + rawCode.length);
|
||||||
|
const paramsBis = params.slice(params.length / 2);
|
||||||
|
|
||||||
|
let decodedInputs;
|
||||||
|
|
||||||
|
try {
|
||||||
|
decodedInputs = decodeMethodInput(constructorAbi, params);
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!decodedInputs) {
|
||||||
|
decodedInputs = decodeMethodInput(constructorAbi, paramsBis);
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
if (decodedInputs && decodedInputs.length > 0) {
|
||||||
|
result.inputs = decodedInputs
|
||||||
|
.map((value, index) => {
|
||||||
|
const type = constructorAbi.inputs[index].kind.type;
|
||||||
|
return { type, value };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
fetchMethodAbi (signature) {
|
fetchMethodAbi (signature) {
|
||||||
if (this._methods[signature] !== undefined) {
|
if (this._methods[signature] !== undefined) {
|
||||||
return Promise.resolve(this._methods[signature]);
|
return Promise.resolve(this._methods[signature]);
|
||||||
@ -209,7 +279,7 @@ export default class MethodDecodingStore {
|
|||||||
return Promise.resolve(this._isContract[contractAddress]);
|
return Promise.resolve(this._isContract[contractAddress]);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._isContract[contractAddress] = this.api.eth
|
this._isContract[contractAddress] = this
|
||||||
.getCode(contractAddress)
|
.getCode(contractAddress)
|
||||||
.then((bytecode) => {
|
.then((bytecode) => {
|
||||||
// Is a contract if the address contains *valid* bytecode
|
// Is a contract if the address contains *valid* bytecode
|
||||||
@ -222,4 +292,24 @@ export default class MethodDecodingStore {
|
|||||||
return Promise.resolve(this._isContract[contractAddress]);
|
return Promise.resolve(this._isContract[contractAddress]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getCode (contractAddress) {
|
||||||
|
// If zero address, resolve to '0x'
|
||||||
|
if (!contractAddress || /^(0x)?0*$/.test(contractAddress)) {
|
||||||
|
return Promise.resolve('0x');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._bytecodes[contractAddress]) {
|
||||||
|
return Promise.resolve(this._bytecodes[contractAddress]);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._bytecodes[contractAddress] = this.api.eth
|
||||||
|
.getCode(contractAddress)
|
||||||
|
.then((bytecode) => {
|
||||||
|
this._bytecodes[contractAddress] = bytecode;
|
||||||
|
return this._bytecodes[contractAddress];
|
||||||
|
});
|
||||||
|
|
||||||
|
return Promise.resolve(this._bytecodes[contractAddress]);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user