Add support for wallets without getOwner() interface (#3779)
* Make Wallet Mist compatible #3282 * Owners icons on load * Fix oversized logo on load * Don't fetch registry twice (even when pending) * Better logging... * Better contract view : show if no events // show loading events * Better decimal typed input * PR grumble
This commit is contained in:
parent
923f85d90d
commit
6655e7e3c0
@ -19,7 +19,10 @@ import * as abis from './abi';
|
|||||||
export default class Registry {
|
export default class Registry {
|
||||||
constructor (api) {
|
constructor (api) {
|
||||||
this._api = api;
|
this._api = api;
|
||||||
this._contracts = [];
|
|
||||||
|
this._contracts = {};
|
||||||
|
this._pendingContracts = {};
|
||||||
|
|
||||||
this._instance = null;
|
this._instance = null;
|
||||||
this._fetching = false;
|
this._fetching = false;
|
||||||
this._queue = [];
|
this._queue = [];
|
||||||
@ -59,20 +62,25 @@ export default class Registry {
|
|||||||
getContract (_name) {
|
getContract (_name) {
|
||||||
const name = _name.toLowerCase();
|
const name = _name.toLowerCase();
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
if (this._contracts[name]) {
|
||||||
if (this._contracts[name]) {
|
return Promise.resolve(this._contracts[name]);
|
||||||
resolve(this._contracts[name]);
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this
|
if (this._pendingContracts[name]) {
|
||||||
.lookupAddress(name)
|
return this._pendingContracts[name];
|
||||||
.then((address) => {
|
}
|
||||||
this._contracts[name] = this._api.newContract(abis[name], address);
|
|
||||||
resolve(this._contracts[name]);
|
const promise = this
|
||||||
})
|
.lookupAddress(name)
|
||||||
.catch(reject);
|
.then((address) => {
|
||||||
});
|
this._contracts[name] = this._api.newContract(abis[name], address);
|
||||||
|
delete this._pendingContracts[name];
|
||||||
|
return this._contracts[name];
|
||||||
|
});
|
||||||
|
|
||||||
|
this._pendingContracts[name] = promise;
|
||||||
|
|
||||||
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
getContractInstance (_name) {
|
getContractInstance (_name) {
|
||||||
@ -89,7 +97,7 @@ export default class Registry {
|
|||||||
return instance.getAddress.call({}, [sha3, 'A']);
|
return instance.getAddress.call({}, [sha3, 'A']);
|
||||||
})
|
})
|
||||||
.then((address) => {
|
.then((address) => {
|
||||||
console.log('lookupAddress', name, sha3, address);
|
console.log('[lookupAddress]', `(${sha3}) ${name}: ${address}`);
|
||||||
return address;
|
return address;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ import { wallet as walletAbi } from '~/contracts/abi';
|
|||||||
import { wallet as walletCode, walletLibraryRegKey, fullWalletCode } from '~/contracts/code/wallet';
|
import { wallet as walletCode, walletLibraryRegKey, fullWalletCode } from '~/contracts/code/wallet';
|
||||||
|
|
||||||
import { validateUint, validateAddress, validateName } from '~/util/validation';
|
import { validateUint, validateAddress, validateName } from '~/util/validation';
|
||||||
|
import { toWei } from '~/api/util/wei';
|
||||||
import WalletsUtils from '~/util/wallets';
|
import WalletsUtils from '~/util/wallets';
|
||||||
|
|
||||||
const STEPS = {
|
const STEPS = {
|
||||||
@ -47,7 +48,7 @@ export default class CreateWalletStore {
|
|||||||
address: '',
|
address: '',
|
||||||
owners: [],
|
owners: [],
|
||||||
required: 1,
|
required: 1,
|
||||||
daylimit: 0,
|
daylimit: toWei(1),
|
||||||
|
|
||||||
name: '',
|
name: '',
|
||||||
description: ''
|
description: ''
|
||||||
|
@ -36,9 +36,6 @@ export default class Personal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._store.dispatch(personalAccountsInfo(accountsInfo));
|
this._store.dispatch(personalAccountsInfo(accountsInfo));
|
||||||
})
|
|
||||||
.then((subscriptionId) => {
|
|
||||||
console.log('personal._subscribeAccountsInfo', 'subscriptionId', subscriptionId);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,9 +34,6 @@ export default class Signer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._store.dispatch(signerRequestsToConfirm(pending || []));
|
this._store.dispatch(signerRequestsToConfirm(pending || []));
|
||||||
})
|
|
||||||
.then((subscriptionId) => {
|
|
||||||
console.log('signer._subscribeRequestsToConfirm', 'subscriptionId', subscriptionId);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,9 +59,6 @@ export default class Status {
|
|||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.warn('status._subscribeBlockNumber', 'getBlockByNumber', error);
|
console.warn('status._subscribeBlockNumber', 'getBlockByNumber', error);
|
||||||
});
|
});
|
||||||
})
|
|
||||||
.then((subscriptionId) => {
|
|
||||||
console.log('status._subscribeBlockNumber', 'subscriptionId', subscriptionId);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,32 +113,38 @@ export default class Input extends Component {
|
|||||||
<TextField
|
<TextField
|
||||||
autoComplete='off'
|
autoComplete='off'
|
||||||
className={ className }
|
className={ className }
|
||||||
style={ textFieldStyle }
|
|
||||||
|
|
||||||
readOnly={ readOnly }
|
|
||||||
|
|
||||||
errorText={ error }
|
errorText={ error }
|
||||||
|
|
||||||
floatingLabelFixed
|
floatingLabelFixed
|
||||||
floatingLabelText={ label }
|
floatingLabelText={ label }
|
||||||
fullWidth
|
|
||||||
hintText={ hint }
|
hintText={ hint }
|
||||||
|
id={ NAME_ID }
|
||||||
|
inputStyle={ inputStyle }
|
||||||
|
fullWidth
|
||||||
|
|
||||||
|
max={ max }
|
||||||
|
min={ min }
|
||||||
|
|
||||||
multiLine={ multiLine }
|
multiLine={ multiLine }
|
||||||
name={ NAME_ID }
|
name={ NAME_ID }
|
||||||
id={ NAME_ID }
|
|
||||||
rows={ rows }
|
|
||||||
type={ type || 'text' }
|
|
||||||
underlineDisabledStyle={ UNDERLINE_DISABLED }
|
|
||||||
underlineStyle={ readOnly ? UNDERLINE_READONLY : UNDERLINE_NORMAL }
|
|
||||||
underlineFocusStyle={ readOnly ? { display: 'none' } : null }
|
|
||||||
underlineShow={ !hideUnderline }
|
|
||||||
value={ value }
|
|
||||||
onBlur={ this.onBlur }
|
onBlur={ this.onBlur }
|
||||||
onChange={ this.onChange }
|
onChange={ this.onChange }
|
||||||
onKeyDown={ this.onKeyDown }
|
onKeyDown={ this.onKeyDown }
|
||||||
onPaste={ this.onPaste }
|
onPaste={ this.onPaste }
|
||||||
inputStyle={ inputStyle }
|
|
||||||
min={ min }
|
readOnly={ readOnly }
|
||||||
max={ max }
|
rows={ rows }
|
||||||
|
style={ textFieldStyle }
|
||||||
|
type={ type || 'text' }
|
||||||
|
|
||||||
|
underlineDisabledStyle={ UNDERLINE_DISABLED }
|
||||||
|
underlineStyle={ readOnly ? UNDERLINE_READONLY : UNDERLINE_NORMAL }
|
||||||
|
underlineFocusStyle={ readOnly ? { display: 'none' } : null }
|
||||||
|
underlineShow={ !hideUnderline }
|
||||||
|
|
||||||
|
value={ value }
|
||||||
>
|
>
|
||||||
{ children }
|
{ children }
|
||||||
</TextField>
|
</TextField>
|
||||||
|
@ -53,13 +53,13 @@ export default class TypedInput extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
isEth: true,
|
isEth: false,
|
||||||
ethValue: 0
|
ethValue: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount () {
|
componentWillMount () {
|
||||||
if (this.props.isEth && this.props.value) {
|
if (this.props.isEth && this.props.value) {
|
||||||
this.setState({ ethValue: fromWei(this.props.value) });
|
this.setState({ isEth: true, ethValue: fromWei(this.props.value) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,28 +164,32 @@ export default class TypedInput extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (type === ABI_TYPES.INT) {
|
if (type === ABI_TYPES.INT) {
|
||||||
return this.renderNumber();
|
return this.renderEth();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type === ABI_TYPES.FIXED) {
|
if (type === ABI_TYPES.FIXED) {
|
||||||
return this.renderNumber();
|
return this.renderFloat();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.renderDefault();
|
return this.renderDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
renderEth () {
|
renderEth () {
|
||||||
const { ethValue } = this.state;
|
const { ethValue, isEth } = this.state;
|
||||||
|
|
||||||
const value = ethValue && typeof ethValue.toNumber === 'function'
|
const value = ethValue && typeof ethValue.toNumber === 'function'
|
||||||
? ethValue.toNumber()
|
? ethValue.toNumber()
|
||||||
: ethValue;
|
: ethValue;
|
||||||
|
|
||||||
|
const input = isEth
|
||||||
|
? this.renderFloat(value, this.onEthValueChange)
|
||||||
|
: this.renderInteger(value, this.onEthValueChange);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={ styles.ethInput }>
|
<div className={ styles.ethInput }>
|
||||||
<div className={ styles.input }>
|
<div className={ styles.input }>
|
||||||
{ this.renderNumber(value, this.onEthValueChange) }
|
{ input }
|
||||||
{ this.state.isEth ? (<div className={ styles.label }>ETH</div>) : null }
|
{ isEth ? (<div className={ styles.label }>ETH</div>) : null }
|
||||||
</div>
|
</div>
|
||||||
<div className={ styles.toggle }>
|
<div className={ styles.toggle }>
|
||||||
<Toggle
|
<Toggle
|
||||||
@ -198,8 +202,9 @@ export default class TypedInput extends Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderNumber (value = this.props.value, onChange = this.onChange) {
|
renderInteger (value = this.props.value, onChange = this.onChange) {
|
||||||
const { label, error, param, hint, min, max } = this.props;
|
const { label, error, param, hint, min, max } = this.props;
|
||||||
|
|
||||||
const realValue = value && typeof value.toNumber === 'function'
|
const realValue = value && typeof value.toNumber === 'function'
|
||||||
? value.toNumber()
|
? value.toNumber()
|
||||||
: value;
|
: value;
|
||||||
@ -212,6 +217,35 @@ export default class TypedInput extends Component {
|
|||||||
error={ error }
|
error={ error }
|
||||||
onChange={ onChange }
|
onChange={ onChange }
|
||||||
type='number'
|
type='number'
|
||||||
|
step={ 1 }
|
||||||
|
min={ min !== null ? min : (param.signed ? null : 0) }
|
||||||
|
max={ max !== null ? max : null }
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decimal numbers have to be input via text field
|
||||||
|
* because of some react issues with input number fields.
|
||||||
|
* Once the issue is fixed, this could be a number again.
|
||||||
|
*
|
||||||
|
* @see https://github.com/facebook/react/issues/1549
|
||||||
|
*/
|
||||||
|
renderFloat (value = this.props.value, onChange = this.onChange) {
|
||||||
|
const { label, error, param, hint, min, max } = this.props;
|
||||||
|
|
||||||
|
const realValue = value && typeof value.toNumber === 'function'
|
||||||
|
? value.toNumber()
|
||||||
|
: value;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Input
|
||||||
|
label={ label }
|
||||||
|
hint={ hint }
|
||||||
|
value={ realValue }
|
||||||
|
error={ error }
|
||||||
|
onChange={ onChange }
|
||||||
|
type='text'
|
||||||
min={ min !== null ? min : (param.signed ? null : 0) }
|
min={ min !== null ? min : (param.signed ? null : 0) }
|
||||||
max={ max !== null ? max : null }
|
max={ max !== null ? max : null }
|
||||||
/>
|
/>
|
||||||
|
@ -17,8 +17,8 @@
|
|||||||
|
|
||||||
.layout {
|
.layout {
|
||||||
padding: 0.25em 0.25em 1em 0.25em;
|
padding: 0.25em 0.25em 1em 0.25em;
|
||||||
}
|
|
||||||
|
|
||||||
.layout>div {
|
> * {
|
||||||
padding-bottom: 0.75em;
|
margin-bottom: 0.75em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,9 +14,10 @@
|
|||||||
// 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 { range } from 'lodash';
|
import { range, uniq } from 'lodash';
|
||||||
|
|
||||||
import { bytesToHex, toHex } from '~/api/util/format';
|
import { bytesToHex, toHex } from '~/api/util/format';
|
||||||
|
import { validateAddress } from '~/util/validation';
|
||||||
|
|
||||||
export default class WalletsUtils {
|
export default class WalletsUtils {
|
||||||
|
|
||||||
@ -26,10 +27,82 @@ export default class WalletsUtils {
|
|||||||
|
|
||||||
static fetchOwners (walletContract) {
|
static fetchOwners (walletContract) {
|
||||||
const walletInstance = walletContract.instance;
|
const walletInstance = walletContract.instance;
|
||||||
|
|
||||||
return walletInstance
|
return walletInstance
|
||||||
.m_numOwners.call()
|
.m_numOwners.call()
|
||||||
.then((mNumOwners) => {
|
.then((mNumOwners) => {
|
||||||
return Promise.all(range(mNumOwners.toNumber()).map((idx) => walletInstance.getOwner.call({}, [ idx ])));
|
const promises = range(mNumOwners.toNumber())
|
||||||
|
.map((idx) => walletInstance.getOwner.call({}, [ idx ]));
|
||||||
|
|
||||||
|
return Promise
|
||||||
|
.all(promises)
|
||||||
|
.then((owners) => {
|
||||||
|
const uniqOwners = uniq(owners);
|
||||||
|
|
||||||
|
// If all owners are the zero account : must be Mist wallet contract
|
||||||
|
if (uniqOwners.length === 1 && /^(0x)?0*$/.test(owners[0])) {
|
||||||
|
return WalletsUtils.fetchMistOwners(walletContract, mNumOwners.toNumber());
|
||||||
|
}
|
||||||
|
|
||||||
|
return owners;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static fetchMistOwners (walletContract, mNumOwners) {
|
||||||
|
const walletAddress = walletContract.address;
|
||||||
|
|
||||||
|
return WalletsUtils
|
||||||
|
.getMistOwnersOffset(walletContract)
|
||||||
|
.then((result) => {
|
||||||
|
if (!result || result.offset === -1) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const owners = [ result.address ];
|
||||||
|
|
||||||
|
if (mNumOwners === 1) {
|
||||||
|
return owners;
|
||||||
|
}
|
||||||
|
|
||||||
|
const initOffset = result.offset + 1;
|
||||||
|
let promise = Promise.resolve();
|
||||||
|
|
||||||
|
range(initOffset, initOffset + mNumOwners - 1).forEach((offset) => {
|
||||||
|
promise = promise
|
||||||
|
.then(() => {
|
||||||
|
return walletContract.api.eth.getStorageAt(walletAddress, offset);
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
const resultAddress = '0x' + (result || '').slice(-40);
|
||||||
|
const { address } = validateAddress(resultAddress);
|
||||||
|
|
||||||
|
owners.push(address);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return promise.then(() => owners);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static getMistOwnersOffset (walletContract, offset = 3) {
|
||||||
|
return walletContract.api.eth
|
||||||
|
.getStorageAt(walletContract.address, offset)
|
||||||
|
.then((result) => {
|
||||||
|
if (result && !/^(0x)?0*$/.test(result)) {
|
||||||
|
const resultAddress = '0x' + result.slice(-40);
|
||||||
|
const { address, addressError } = validateAddress(resultAddress);
|
||||||
|
|
||||||
|
if (!addressError) {
|
||||||
|
return { offset, address };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset >= 100) {
|
||||||
|
return { offset: -1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
return WalletsUtils.getMistOwnersOffset(walletContract, offset + 1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,6 +75,13 @@ export default class Summary extends Component {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const prevOwners = this.props.owners;
|
||||||
|
const nextOwners = nextProps.owners;
|
||||||
|
|
||||||
|
if (!isEqual(prevOwners, nextOwners)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,8 +130,8 @@ export default class Summary extends Component {
|
|||||||
return (
|
return (
|
||||||
<div className={ styles.owners }>
|
<div className={ styles.owners }>
|
||||||
{
|
{
|
||||||
ownersValid.map((owner) => (
|
ownersValid.map((owner, index) => (
|
||||||
<div key={ owner.address }>
|
<div key={ `${index}_${owner.address}` }>
|
||||||
<div
|
<div
|
||||||
data-tip
|
data-tip
|
||||||
data-for={ `owner_${owner.address}` }
|
data-for={ `owner_${owner.address}` }
|
||||||
|
@ -188,7 +188,7 @@ class TabBar extends Component {
|
|||||||
return (
|
return (
|
||||||
<ToolbarGroup>
|
<ToolbarGroup>
|
||||||
<div className={ styles.logo }>
|
<div className={ styles.logo }>
|
||||||
<img src={ imagesEthcoreBlock } />
|
<img src={ imagesEthcoreBlock } height={ 28 } />
|
||||||
</div>
|
</div>
|
||||||
</ToolbarGroup>
|
</ToolbarGroup>
|
||||||
);
|
);
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import { uniq } from 'lodash';
|
import { uniq } from 'lodash';
|
||||||
|
|
||||||
import { Container } from '~/ui';
|
import { Container, Loading } from '~/ui';
|
||||||
|
|
||||||
import Event from './Event';
|
import Event from './Event';
|
||||||
import styles from '../contract.css';
|
import styles from '../contract.css';
|
||||||
@ -25,18 +25,38 @@ import styles from '../contract.css';
|
|||||||
export default class Events extends Component {
|
export default class Events extends Component {
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
api: PropTypes.object
|
api: PropTypes.object
|
||||||
}
|
};
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
events: PropTypes.array,
|
isTest: PropTypes.bool.isRequired,
|
||||||
isTest: PropTypes.bool.isRequired
|
isLoading: PropTypes.bool,
|
||||||
}
|
events: PropTypes.array
|
||||||
|
};
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
isLoading: false,
|
||||||
|
events: []
|
||||||
|
};
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { events, isTest } = this.props;
|
const { events, isTest, isLoading } = this.props;
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<Container title='events'>
|
||||||
|
<div>
|
||||||
|
<Loading size={ 2 } />
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (!events || !events.length) {
|
if (!events || !events.length) {
|
||||||
return null;
|
return (
|
||||||
|
<Container title='events'>
|
||||||
|
<p>No events has been sent from this contract.</p>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const eventsKey = uniq(events.map((e) => e.key));
|
const eventsKey = uniq(events.map((e) => e.key));
|
||||||
|
@ -54,6 +54,10 @@ export default class Queries extends Component {
|
|||||||
.filter((fn) => fn.inputs.length > 0)
|
.filter((fn) => fn.inputs.length > 0)
|
||||||
.map((fn) => this.renderInputQuery(fn));
|
.map((fn) => this.renderInputQuery(fn));
|
||||||
|
|
||||||
|
if (queries.length + noInputQueries.length + withInputQueries.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container title='queries'>
|
<Container title='queries'>
|
||||||
<div className={ styles.methods }>
|
<div className={ styles.methods }>
|
||||||
|
@ -40,7 +40,7 @@ import styles from './contract.css';
|
|||||||
class Contract extends Component {
|
class Contract extends Component {
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
api: React.PropTypes.object.isRequired
|
api: React.PropTypes.object.isRequired
|
||||||
}
|
};
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
setVisibleAccounts: PropTypes.func.isRequired,
|
setVisibleAccounts: PropTypes.func.isRequired,
|
||||||
@ -50,7 +50,7 @@ class Contract extends Component {
|
|||||||
contracts: PropTypes.object,
|
contracts: PropTypes.object,
|
||||||
isTest: PropTypes.bool,
|
isTest: PropTypes.bool,
|
||||||
params: PropTypes.object
|
params: PropTypes.object
|
||||||
}
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
contract: null,
|
contract: null,
|
||||||
@ -64,8 +64,9 @@ class Contract extends Component {
|
|||||||
allEvents: [],
|
allEvents: [],
|
||||||
minedEvents: [],
|
minedEvents: [],
|
||||||
pendingEvents: [],
|
pendingEvents: [],
|
||||||
queryValues: {}
|
queryValues: {},
|
||||||
}
|
loadingEvents: true
|
||||||
|
};
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
const { api } = this.context;
|
const { api } = this.context;
|
||||||
@ -115,7 +116,7 @@ class Contract extends Component {
|
|||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { balances, contracts, params, isTest } = this.props;
|
const { balances, contracts, params, isTest } = this.props;
|
||||||
const { allEvents, contract, queryValues } = this.state;
|
const { allEvents, contract, queryValues, loadingEvents } = this.state;
|
||||||
const account = contracts[params.address];
|
const account = contracts[params.address];
|
||||||
const balance = balances[params.address];
|
const balance = balances[params.address];
|
||||||
|
|
||||||
@ -134,12 +135,17 @@ class Contract extends Component {
|
|||||||
account={ account }
|
account={ account }
|
||||||
balance={ balance }
|
balance={ balance }
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Queries
|
<Queries
|
||||||
contract={ contract }
|
contract={ contract }
|
||||||
values={ queryValues } />
|
values={ queryValues }
|
||||||
|
/>
|
||||||
|
|
||||||
<Events
|
<Events
|
||||||
isTest={ isTest }
|
isTest={ isTest }
|
||||||
events={ allEvents } />
|
isLoading={ loadingEvents }
|
||||||
|
events={ allEvents }
|
||||||
|
/>
|
||||||
|
|
||||||
{ this.renderDetails(account) }
|
{ this.renderDetails(account) }
|
||||||
</Page>
|
</Page>
|
||||||
@ -358,6 +364,10 @@ class Contract extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_receiveEvents = (error, logs) => {
|
_receiveEvents = (error, logs) => {
|
||||||
|
if (this.state.loadingEvents) {
|
||||||
|
this.setState({ loadingEvents: false });
|
||||||
|
}
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error('_receiveEvents', error);
|
console.error('_receiveEvents', error);
|
||||||
return;
|
return;
|
||||||
|
@ -55,9 +55,9 @@ export default class WalletDetails extends Component {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ownersList = owners.map((address) => (
|
const ownersList = owners.map((address, idx) => (
|
||||||
<InputAddress
|
<InputAddress
|
||||||
key={ address }
|
key={ `${idx}_${address}` }
|
||||||
value={ address }
|
value={ address }
|
||||||
disabled
|
disabled
|
||||||
text
|
text
|
||||||
|
Loading…
Reference in New Issue
Block a user