Token sorting, zero-ETH transfer & token decimals (#2805)

* Error trapping for decimals (Fixes #2799)

* Sort tokens by tag (Closes #2789)

* PR comments

* Always display ETH

* Recalculate in all cases (traps >available)
This commit is contained in:
Jaco Greeff 2016-10-25 13:22:27 +02:00 committed by Gav Wood
parent a6fd922ffb
commit b6f2628018
4 changed files with 57 additions and 21 deletions

View File

@ -119,17 +119,19 @@ export default class Details extends Component {
const { balance, images, tag } = this.props; const { balance, images, tag } = this.props;
const items = balance.tokens const items = balance.tokens
.filter((token) => token.value.gt(0)) .filter((token, index) => !index || token.value.gt(0))
.map((balance, idx) => { .map((balance, index) => {
const token = balance.token; const token = balance.token;
const isEth = idx === 0; const isEth = index === 0;
const imagesrc = token.image || images[token.address] || imageUnknown; const imagesrc = token.image || images[token.address] || imageUnknown;
let value = 0; let value = 0;
if (isEth) { if (isEth) {
value = api.util.fromWei(balance.value).toFormat(3); value = api.util.fromWei(balance.value).toFormat(3);
} else { } else {
value = new BigNumber(balance.value).div(balance.token.format || 1).toFormat(3); const format = balance.token.format || 1;
const decimals = format === 1 ? 0 : Math.min(3, Math.floor(format / 10));
value = new BigNumber(balance.value).div(format).toFormat(decimals);
} }
const label = ( const label = (
@ -165,7 +167,7 @@ export default class Details extends Component {
); );
} }
onChangeToken = (event, idx, tag) => { onChangeToken = (event, index, tag) => {
this.props.onChange('tag', tag); this.props.onChange('tag', tag);
} }

View File

@ -18,6 +18,7 @@ const ERRORS = {
requireRecipient: 'a recipient network address is required for the transaction', requireRecipient: 'a recipient network address is required for the transaction',
invalidAddress: 'the supplied address is an invalid network address', invalidAddress: 'the supplied address is an invalid network address',
invalidAmount: 'the supplied amount should be a valid positive number', invalidAmount: 'the supplied amount should be a valid positive number',
invalidDecimals: 'the supplied amount exceeds the allowed decimals',
largeAmount: 'the transaction total is higher than the available balance' largeAmount: 'the transaction total is higher than the available balance'
}; };

View File

@ -297,6 +297,24 @@ export default class Transfer extends Component {
return null; return null;
} }
validateDecimals (num) {
const { balance } = this.props;
const { tag } = this.state;
if (tag === 'ETH') {
return null;
}
const token = balance.tokens.find((balance) => balance.token.tag === tag).token;
const s = new BigNumber(num).mul(token.format || 1).toString();
if (s.indexOf('.') !== -1) {
return ERRORS.invalidDecimals;
}
return null;
}
_onUpdateGas (gas) { _onUpdateGas (gas) {
const gasError = this.validatePositiveNumber(gas); const gasError = this.validatePositiveNumber(gas);
@ -341,7 +359,11 @@ export default class Transfer extends Component {
} }
_onUpdateValue (value) { _onUpdateValue (value) {
const valueError = this.validatePositiveNumber(value); let valueError = this.validatePositiveNumber(value);
if (!valueError) {
valueError = this.validateDecimals(value);
}
this.setState({ this.setState({
value, value,
@ -407,7 +429,7 @@ export default class Transfer extends Component {
to: token.address to: token.address
}, [ }, [
recipient, recipient,
new BigNumber(value).mul(token.format).toString() new BigNumber(value).mul(token.format).toFixed(0)
]); ]);
} }
@ -500,6 +522,7 @@ export default class Transfer extends Component {
}) })
.catch((error) => { .catch((error) => {
console.error('etimateGas', error); console.error('etimateGas', error);
this.recalculate();
}); });
} }
@ -565,7 +588,7 @@ export default class Transfer extends Component {
}, this.recalculate); }, this.recalculate);
}) })
.catch((error) => { .catch((error) => {
console.error('getDefaults', error); console.warn('getDefaults', error);
}); });
} }

View File

@ -100,21 +100,31 @@ export default class Balances {
}) })
.then(([_tokens, images]) => { .then(([_tokens, images]) => {
const tokens = {}; const tokens = {};
this._tokens = _tokens.map((_token, index) => { this._tokens = _tokens
const [address, tag, format, name] = _token; .map((_token, index) => {
const [address, tag, format, name] = _token;
const token = { const token = {
address, address,
name, name,
tag, tag,
format: format.toString(), format: format.toString(),
contract: this._api.newContract(abis.eip20, address) contract: this._api.newContract(abis.eip20, address)
}; };
tokens[address] = token; tokens[address] = token;
this._store.dispatch(setAddressImage(address, images[index])); this._store.dispatch(setAddressImage(address, images[index]));
return token; return token;
}); })
.sort((a, b) => {
if (a.tag < b.tag) {
return -1;
} else if (a.tag > b.tag) {
return 1;
}
return 0;
});
this._store.dispatch(getTokens(tokens)); this._store.dispatch(getTokens(tokens));
this._retrieveBalances(); this._retrieveBalances();