Ui 2 move to packages/* (#6113)

* Move secureApi to shell

* Extract isTestnet test

* Use mobx + subscriptions for status

* Re-add status indicator

* Add lerna

* Move intial packages to js/packages

* Move 3rdparty/{email,sms}-verification to correct location

* Move package.json & README to library src

* Move tests for library packages

* Move views & dapps to packages

* Move i18n to root

* Move shell to actual src (main app)

* Remove ~ references

* Change ~ to root (explicit imports)

* Finalise convert of ~

* Move views into dapps as well

* Move dapps to packages/

* Fix references

* Update css

* Update test spec locations

* Update tests

* Case fix

* Skip flakey tests

* Update enzyme

* Skip previously ignored tests

* Allow empty api for hw

* Re-add theme for embed
This commit is contained in:
Jaco Greeff
2017-07-21 15:46:53 +02:00
committed by GitHub
parent 7f3bb37b96
commit 49fdd23d58
1850 changed files with 1933 additions and 729 deletions

View File

@@ -0,0 +1,17 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default from './methodDecoding';

View File

@@ -0,0 +1,106 @@
/* Copyright 2015-2017 Parity Technologies (UK) Ltd.
/* This file is part of Parity.
/*
/* Parity is free software: you can redistribute it and/or modify
/* it under the terms of the GNU General Public License as published by
/* the Free Software Foundation, either version 3 of the License, or
/* (at your option) any later version.
/*
/* Parity is distributed in the hope that it will be useful,
/* but WITHOUT ANY WARRANTY; without even the implied warranty of
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/* GNU General Public License for more details.
/*
/* You should have received a copy of the GNU General Public License
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
*/
.container {
}
.clickable {
border: 1px dashed rgba(255, 255, 255, 0.4);
padding: 0.1em 0.3em;
margin: 0.1em 0.1em;
&:hover {
cursor: pointer;
}
&.noSelect {
user-select: none;
}
}
.loading {
display: flex;
align-items: center;
justify-content: center;
}
.details {
line-height: 1.75em;
}
.details,
.gasDetails {
color: #aaa;
}
.gasDetails {
padding-top: 0.75em;
font-size: 0.75em;
line-height: 1.5em;
opacity: 0.5;
}
.description {
line-height: 2em;
padding-bottom: 0.5em;
}
.name,
.highlight {
color: #333;
padding: 0.25em;
}
.name,
.inputs,
.highlight {
}
.inputs, .addressContainer {
display: flex;
align-items: center;
justify-content: flex-start;
padding-left: 2em;
}
.input {
margin-top: -16px;
font-family: inherit !important;
}
.inputs [data-address-img] {
position: absolute;
top: -16px;
}
.etherValue,
.tokenValue {
}
.address {
display: inline-block;
position: relative;
}
.identityicon {
margin-bottom: -10px;
margin-right: 0.5em;
}
.inputData {
word-break: break-all;
}

View File

@@ -0,0 +1,678 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import BigNumber from 'bignumber.js';
import moment from 'moment';
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
import IdentityIcon from '../IdentityIcon';
import { TypedInput, Label } from '../Form';
import Loading from '../Loading';
import MethodDecodingStore from './methodDecodingStore';
import styles from './methodDecoding.css';
const ASCII_INPUT = /^[a-z0-9\s,?;.:/!()-_@'"#]+$/i;
const TOKEN_METHODS = {
'0xa9059cbb': 'transfer(to,value)'
};
class MethodDecoding extends Component {
static contextTypes = {
api: PropTypes.object.isRequired
};
static propTypes = {
address: PropTypes.string,
compact: PropTypes.bool,
token: PropTypes.object,
transaction: PropTypes.object,
historic: PropTypes.bool
};
static defaultProps = {
address: '',
compact: false,
historic: false
};
state = {
contractAddress: null,
methodName: null,
methodInputs: null,
methodParams: null,
methodSignature: null,
isContract: false,
isDeploy: false,
isReceived: false,
isLoading: true,
expandInput: false,
inputType: 'auto'
};
methodDecodingStore = MethodDecodingStore.get(this.context.api);
componentWillMount () {
const { address, transaction } = this.props;
this
.methodDecodingStore
.lookup(address, transaction)
.then((lookup) => {
const newState = {
methodName: lookup.name,
methodInputs: lookup.inputs,
methodParams: lookup.params,
methodSignature: lookup.signature,
isContract: lookup.contract,
isDeploy: lookup.deploy,
isLoading: false,
isReceived: lookup.received
};
this.setState(newState);
});
}
render () {
const { transaction } = this.props;
const { isLoading } = this.state;
if (!transaction) {
return null;
}
if (isLoading) {
return (
<div className={ styles.loading }>
<Loading />
</div>
);
}
return (
<div className={ styles.container }>
{ this.renderAction() }
{ this.renderGas() }
</div>
);
}
renderGas () {
const { compact, historic, transaction } = this.props;
const { gas, gasPrice, value } = transaction;
if (!gas || !gasPrice || compact) {
return null;
}
const gasProvided = (
<span className={ styles.highlight }>
<FormattedMessage
id='ui.methodDecoding.gasValues'
defaultMessage='{gas} gas ({gasPrice}M/{tag})'
values={ {
gas: gas.toFormat(0),
gasPrice: gasPrice.div(1000000).toFormat(0),
tag: <small>ETH</small>
} }
/>
</span>
);
const totalEthValue = (
<span className={ styles.highlight }>
{ this.renderEtherValue(gas.mul(gasPrice).plus(value || 0)) }
</span>
);
const gasUsed = transaction.gasUsed
? (
<span className={ styles.highlight }>
<FormattedMessage
id='ui.methodDecoding.gasUsed'
defaultMessage=' ({gas} gas used)'
values={ {
gas: transaction.gasUsed.toFormat(0)
} }
/>
</span>
)
: '';
return (
<div className={ styles.gasDetails }>
<FormattedMessage
id='ui.methodDecoding.txValues'
defaultMessage='{historic, select, true {Provided} false {Provides}} {gasProvided}{gasUsed} for a total transaction value of {totalEthValue}'
values={ {
historic,
gasProvided,
gasUsed,
totalEthValue
} }
/>
{ this.renderMinBlock() }
</div>
);
}
renderMinBlock () {
const { historic, transaction } = this.props;
const { condition } = transaction;
if (!condition) {
return null;
}
const blockCondition = new BigNumber(condition.block || 0);
if (blockCondition.gt(0)) {
const blockNumber = (
<span className={ styles.highlight }>
#{ blockCondition.toFormat(0) }
</span>
);
return (
<div>
<FormattedMessage
id='ui.methodDecoding.condition.block'
defaultMessage='{historic, select, true {Will be submitted} false {To be submitted}} at block {blockNumber}'
values={ {
historic,
blockNumber
} }
/>
</div>
);
}
if (condition.time) {
const timestamp = (
<span className={ styles.highlight }>
{ moment(condition.time).format('LLLL') }
</span>
);
return (
<div>
<FormattedMessage
id='ui.methodDecoding.condition.time'
defaultMessage='{historic, select, true {Will be submitted} false {To be submitted}} {timestamp}'
values={ {
historic,
timestamp
} }
/>
</div>
);
}
return null;
}
renderAction () {
const { token } = this.props;
const { methodName, methodInputs, methodSignature, isDeploy, isReceived, isContract } = this.state;
if (isDeploy) {
return this.renderDeploy();
}
if (isContract && methodSignature) {
if (token && TOKEN_METHODS[methodSignature] && methodInputs) {
return this.renderTokenAction();
}
if (methodName) {
return this.renderSignatureMethod();
}
return this.renderUnknownMethod();
}
return isReceived
? this.renderValueReceipt()
: this.renderValueTransfer();
}
getAscii () {
const { api } = this.context;
const { transaction } = this.props;
const value = api.util.hexToAscii(transaction.input || transaction.data);
return {
value,
valid: ASCII_INPUT.test(value)
};
}
renderInputValue () {
const { compact, transaction } = this.props;
if (compact) {
return null;
}
const { expandInput, inputType } = this.state;
const input = transaction.input || transaction.data;
if (!/^(0x)?([0]*[1-9a-f]+[0]*)+$/.test(input)) {
return null;
}
const ascii = this.getAscii();
const type = inputType === 'auto'
? (ascii.valid ? 'ascii' : 'raw')
: inputType;
const text = type === 'ascii'
? ascii.value
: input;
const expandable = text.length > 50;
const textToShow = expandInput || !expandable
? text
: text.slice(0, 50) + '...';
const inputDesc = (
<span
onClick={ this.toggleInputType }
className={ [ styles.clickable, styles.noSelect ].join(' ') }
>
{
type === 'ascii'
? (
<FormattedMessage
id='ui.methodDecoding.input.input'
defaultMessage='input'
/>
)
: (
<FormattedMessage
id='ui.methodDecoding.input.data'
defaultMessage='data'
/>
)
}
</span>
);
const inputValue = (
<span
onClick={ this.toggleInputExpand }
className={ expandable ? styles.clickable : '' }
>
<code className={ styles.inputData }>
{ textToShow }
</code>
</span>
);
return (
<div className={ styles.details }>
<FormattedMessage
id='ui.methodDecoding.input.withInput'
defaultMessage='with the {inputDesc} {inputValue}'
values={ {
inputDesc,
inputValue
} }
/>
</div>
);
}
renderTokenAction () {
const { historic } = this.props;
const { methodSignature, methodInputs } = this.state;
const [to, value] = methodInputs;
const address = to.value;
switch (TOKEN_METHODS[methodSignature]) {
case 'transfer(to,value)':
default:
return (
<div className={ styles.details }>
<FormattedMessage
id='ui.methodDecoding.token.transfer'
defaultMessage='{historic, select, true {Transferred} false {Will transfer}} {value} to {address}'
values={ {
historic,
value: (
<span className={ styles.highlight }>
{ this.renderTokenValue(value.value) }
</span>
),
address: this.renderAddressName(address)
} }
/>
</div>
);
}
}
renderDeploy () {
const { compact, historic, transaction } = this.props;
const { methodInputs } = this.state;
const { value } = transaction;
if (!historic) {
return (
<div className={ styles.details }>
<FormattedMessage
id='ui.methodDecoding.deploy.willDeploy'
defaultMessage='Will deploy a contract'
/>
{
value && value.gt(0)
? (
<FormattedMessage
id='ui.methodDecoding.deploy.withValue'
defaultMessage=', sending {value}'
values={ {
value: this.renderEtherValue(value)
} }
/>
)
: null
}
</div>
);
}
return (
<div className={ styles.details }>
<div>
<FormattedMessage
id='ui.methodDecoding.deploy.address'
defaultMessage='Deployed a contract at address '
/>
</div>
{ this.renderAddressName(transaction.creates, false) }
{
!compact && methodInputs && methodInputs.length
? (
<div>
<FormattedMessage
id='ui.methodDecoding.deploy.params'
defaultMessage='with the following parameters:'
/>
<div className={ styles.inputs }>
{ this.renderInputs() }
</div>
</div>
)
: null
}
</div>
);
}
renderValueReceipt () {
const { historic, transaction } = this.props;
const { isContract } = this.state;
const valueEth = (
<span className={ styles.highlight }>
{ this.renderEtherValue(transaction.value) }
</span>
);
const aContract = isContract
? (
<FormattedMessage
id='ui.methodDecoding.receive.contract'
defaultMessage='the contract '
/>
)
: '';
return (
<div className={ styles.details }>
<FormattedMessage
id='ui.methodDecoding.receive.info'
defaultMessage='{historic, select, true {Received} false {Will receive}} {valueEth} from {aContract}{address}'
values={ {
historic,
valueEth,
aContract,
address: this.renderAddressName(transaction.from)
} }
/>
{ this.renderInputValue() }
</div>
);
}
renderValueTransfer () {
const { historic, transaction } = this.props;
const { isContract } = this.state;
const valueEth = (
<span className={ styles.highlight }>
{ this.renderEtherValue(transaction.value) }
</span>
);
const aContract = isContract
? (
<FormattedMessage
id='ui.methodDecoding.transfer.contract'
defaultMessage='the contract '
/>
)
: '';
return (
<div className={ styles.details }>
<FormattedMessage
id='ui.methodDecoding.transfer.info'
defaultMessage='{historic, select, true {Transferred} false {Will transfer}} {valueEth} to {aContract}{address}'
values={ {
historic,
valueEth,
aContract,
address: this.renderAddressName(transaction.to)
} }
/>
{ this.renderInputValue() }
</div>
);
}
renderSignatureMethod () {
const { compact, historic, transaction } = this.props;
const { methodName, methodInputs } = this.state;
const showInputs = !compact && methodInputs && methodInputs.length > 0;
const showEth = !!(transaction.value && transaction.value.gt(0));
const method = (
<span className={ styles.name }>
{ methodName }
</span>
);
const ethValue = showEth && (
<span className={ styles.highlight }>
{ this.renderEtherValue(transaction.value) }
</span>
);
return (
<div className={ styles.details }>
<div className={ styles.description }>
<FormattedMessage
id='ui.methodDecoding.signature.info'
defaultMessage='{historic, select, true {Executed} false {Will execute}} the {method} function on the contract {address} {showEth, select, true {transferring {ethValue}} false {}} {showInputs, select, false {} true {passing the following {inputLength, plural, one {parameter} other {parameters}}}}'
values={ {
historic,
method,
ethValue,
showEth,
showInputs,
address: this.renderAddressName(transaction.to),
inputLength: methodInputs.length
} }
/>
</div>
{
showInputs
? (
<div className={ styles.inputs }>
{ this.renderInputs() }
</div>
)
: null
}
</div>
);
}
renderUnknownMethod () {
const { historic, transaction } = this.props;
const method = (
<span className={ styles.name }>
an unknown/unregistered
</span>
);
const ethValue = (
<span className={ styles.highlight }>
{ this.renderEtherValue(transaction.value) }
</span>
);
return (
<div className={ styles.details }>
<FormattedMessage
id='ui.methodDecoding.unknown.info'
defaultMessage='{historic, select, true {Executed} false {Will execute}} the {method} on the contract {address} transferring {ethValue}.'
values={ {
historic,
method,
ethValue,
address: this.renderAddressName(transaction.to)
} }
/>
</div>
);
}
renderInputs () {
const { methodInputs } = this.state;
if (!methodInputs || methodInputs.length === 0) {
return null;
}
const inputs = methodInputs.map((input, index) => {
const label = input.name
? `${input.name}: ${input.type}`
: input.type;
return (
<TypedInput
allowCopy
className={ styles.input }
label={ label }
key={ index }
param={ input.type }
readOnly
value={ input.value }
/>
);
});
return inputs;
}
renderTokenValue (value) {
const { token } = this.props;
return (
<span className={ styles.tokenValue }>
{ value.div(token.format).toFormat(5) }<small> { token.tag }</small>
</span>
);
}
renderEtherValue (value) {
const { api } = this.context;
const ether = api.util.fromWei(value);
return (
<span className={ styles.etherValue }>
{ ether.toFormat(5) }<small> ETH</small>
</span>
);
}
renderAddressName (address, withName = true) {
return (
<div className={ styles.addressContainer }>
<IdentityIcon
address={ address }
center
inline
/>
<Label>{address}</Label>
</div>
);
}
toggleInputExpand = () => {
if (window.getSelection && window.getSelection().type === 'Range') {
return;
}
this.setState({
expandInput: !this.state.expandInput
});
}
toggleInputType = () => {
const { inputType } = this.state;
if (inputType !== 'auto') {
return this.setState({
inputType: this.state.inputType === 'raw' ? 'ascii' : 'raw'
});
}
const ascii = this.getAscii();
return this.setState({
inputType: ascii.valid ? 'raw' : 'ascii'
});
}
}
function mapStateToProps (initState, initProps) {
const { tokens } = initState;
const { transaction } = initProps;
const token = Object.values(tokens).find((token) => token.address === transaction.to);
return () => {
return { token };
};
}
export default connect(
mapStateToProps,
null
)(MethodDecoding);

View File

@@ -0,0 +1,367 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import Abi from '@parity/abi';
import { decodeMethodInput } from '@parity/api/util/decode';
import Contracts from '@parity/shared/contracts';
import * as abis from '@parity/shared/contracts/abi';
const CONTRACT_CREATE = '0x60606040';
let instance = null;
export default class MethodDecodingStore {
api = null;
_bytecodes = {};
_contractsAbi = {};
_isContract = {};
_methods = {};
constructor (api, contracts = {}) {
this.api = api;
// Load the signatures from the local ABIs
Object.keys(abis).forEach((abiKey) => {
this.loadFromAbi(abis[abiKey]);
});
this.addContracts(contracts);
}
addContracts (contracts = {}) {
// Load the User defined contracts
Object.values(contracts).forEach((contract) => {
if (!contract || !contract.meta || !contract.meta.abi) {
return;
}
this.loadFromAbi(contract.meta.abi, contract.address);
});
}
loadFromAbi (_abi, contractAddress) {
let abi;
try {
abi = new Abi(_abi);
} catch (error) {
console.warn('loadFromAbi', error, _abi);
}
if (!abi) {
return;
}
if (contractAddress) {
this._contractsAbi[contractAddress] = abi;
}
abi
.functions
.map((f) => ({ sign: f.signature, abi: f.abi }))
.forEach((mapping) => {
const sign = (/^0x/.test(mapping.sign) ? '' : '0x') + mapping.sign;
this._methods[sign] = mapping.abi;
});
}
static get (api, contracts = {}) {
if (!instance) {
instance = new MethodDecodingStore(api, contracts);
}
// Set API if not set yet
if (!instance.api) {
instance.api = api;
}
return instance;
}
static loadContracts (contracts = {}) {
if (!instance) {
// Just create the instance with null API
MethodDecodingStore.get(null, contracts);
} else {
instance.addContracts(contracts);
}
}
/**
* Looks up a transaction in the context of the given
* address
*
* @param {String} address The address contract
* @param {Object} transaction The transaction to lookup
* @return {Promise} The result of the lookup. Resolves with:
* {
* contract: Boolean,
* deploy: Boolean,
* inputs: Array,
* name: String,
* params: Array,
* received: Boolean,
* signature: String
* }
*/
lookup (currentAddress, transaction) {
const result = {};
if (!transaction) {
return Promise.resolve(result);
}
const isReceived = transaction.to === currentAddress;
const contractAddress = isReceived ? transaction.from : transaction.to;
const input = transaction.input || transaction.data;
result.input = input;
result.received = isReceived;
// No input, should be a ETH transfer
if (!input || input === '0x') {
return Promise.resolve(result);
}
if (!transaction.to) {
return this.decodeContractCreation(result);
}
let signature;
try {
const decodeCallDataResult = this.api.util.decodeCallData(input);
signature = decodeCallDataResult.signature;
} catch (e) {}
// Contract deployment
if (!signature || signature === CONTRACT_CREATE || transaction.creates) {
const address = contractAddress || transaction.creates;
return this.isContractCreation(input, address)
.then((isContractCreation) => {
if (!isContractCreation) {
result.contract = false;
result.deploy = false;
return result;
}
return this.decodeContractCreation(result, address);
});
}
return this
.isContract(contractAddress)
.then((isContract) => {
result.contract = isContract;
if (!isContract) {
return result;
}
const { signature, paramdata } = this.api.util.decodeCallData(input);
result.signature = signature;
result.params = paramdata;
return this
.fetchMethodAbi(signature)
.then((abi) => {
let methodName = null;
let methodInputs = null;
if (abi) {
methodName = abi.name;
methodInputs = this.api.util
.decodeMethodInput(abi, paramdata)
.map((value, index) => {
const { name, type } = abi.inputs[index];
return { name, type, value };
});
}
return {
...result,
name: methodName,
inputs: methodInputs
};
});
})
.catch((error) => {
console.warn('lookup', error);
});
}
decodeContractCreation (data, contractAddress = '') {
const result = {
...data,
contract: true,
deploy: true
};
const { input } = data;
const abi = this._contractsAbi[contractAddress];
if (!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) {
if (this._methods[signature] !== undefined) {
return Promise.resolve(this._methods[signature]);
}
this._methods[signature] = Contracts.get(this.api)
.signatureReg
.lookup(signature)
.then((method) => {
let abi = null;
if (method && method.length) {
abi = this.api.util.methodToAbi(method);
}
this._methods[signature] = abi;
return this._methods[signature];
});
return Promise.resolve(this._methods[signature]);
}
/**
* Checks (and caches) if the given address is a
* Contract or not, from its fetched bytecode
*/
isContract (contractAddress) {
// If zero address, it isn't a contract
if (!contractAddress || /^(0x)?0*$/.test(contractAddress)) {
return Promise.resolve(false);
}
if (this._isContract[contractAddress]) {
return Promise.resolve(this._isContract[contractAddress]);
}
this._isContract[contractAddress] = this
.getCode(contractAddress)
.then((bytecode) => {
// Is a contract if the address contains *valid* bytecode
const _isContract = bytecode && /^(0x)?([0]*[1-9a-f]+[0]*)+$/.test(bytecode);
this._isContract[contractAddress] = _isContract;
return this._isContract[contractAddress];
});
return Promise.resolve(this._isContract[contractAddress]);
}
/**
* Check if the input resulted in a contract creation
* by checking that the contract address code contains
* a part of the input, or vice-versa
*/
isContractCreation (input, contractAddress) {
return this.api.eth
.getCode(contractAddress)
.then((code) => {
if (/^(0x)?0*$/.test(code)) {
return false;
}
const strippedCode = code.replace(/^0x/, '');
const strippedInput = input.replace(/^0x/, '');
return strippedInput.indexOf(strippedInput) >= 0 || strippedCode.indexOf(strippedInput) >= 0;
})
.catch((error) => {
console.error(error);
return false;
});
}
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]);
}
}