Unverified Commit 254ecab9 authored by Amaury Martiny's avatar Amaury Martiny Committed by GitHub
Browse files

Merge pull request #126 from paritytech/am-max

Fix sending more than max balance
parents 040d1e03 9e6e209a
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
"start": "npm-run-all -p start-*", "start": "npm-run-all -p start-*",
"start-css": "npm run build-css -- --watch --recursive", "start-css": "npm run build-css -- --watch --recursive",
"start-js": "react-app-rewired start", "start-js": "react-app-rewired start",
"test": "react-app-rewired test --env=jsdom" "test": "react-app-rewired test --env=jsdom --coverage"
}, },
"dependencies": { "dependencies": {
"@parity/api": "^2.1.22", "@parity/api": "^2.1.22",
......
...@@ -29,6 +29,7 @@ class Send extends Component { ...@@ -29,6 +29,7 @@ class Send extends Component {
amount: '', // In Ether or in token amount: '', // In Ether or in token
gasPrice: 4, // in Gwei gasPrice: 4, // in Gwei
to: '', to: '',
estimating: false, // Currently estimating gasPrice
...this.props.sendStore.tx ...this.props.sendStore.tx
}; };
...@@ -51,29 +52,44 @@ class Send extends Component { ...@@ -51,29 +52,44 @@ class Send extends Component {
}; };
} }
componentDidUpdate () { componentDidMount () {
if (!this.hasError()) { this.handleEstimateGasPrice();
const { amount, gasPrice, to } = this.state;
this.props.sendStore.setTx({ amount, gasPrice, to });
this.estimateGas();
}
} }
estimateGas = debounce(() => { estimateGas = debounce(
this.props.sendStore.estimateGas(); () =>
}, 1000); this.props.sendStore
.estimateGas()
.then(() => this.setState({ estimating: false }))
.catch(() => this.setState({ estimating: false })),
1000
);
handleChangeAmount = ({ target: { value } }) => handleChangeAmount = ({ target: { value } }) =>
this.setState({ amount: value }); this.setState({ amount: value }, this.handleEstimateGasPrice);
handleChangeGasPrice = ({ target: { value } }) => handleChangeGasPrice = ({ target: { value } }) =>
this.setState({ gasPrice: value }); this.setState({ gasPrice: value }, this.handleEstimateGasPrice);
handleChangeTo = ({ target: { value } }) => { handleChangeTo = ({ target: { value } }) => {
this.setState({ to: value }); this.setState({ to: value }, this.handleEstimateGasPrice);
};
handleEstimateGasPrice = () => {
if (this.hasError()) {
return;
}
const { amount, gasPrice, to } = this.state;
this.props.sendStore.setTx({ amount, gasPrice, to });
this.setState({ estimating: true }, this.estimateGas);
}; };
handleMax = () => this.setState({ amount: this.state.maxAmount }); handleMax = () =>
this.setState(
{ amount: this.state.maxAmount },
this.handleEstimateGasPrice
);
handleSubmit = event => { handleSubmit = event => {
event.preventDefault(); event.preventDefault();
...@@ -112,7 +128,7 @@ class Send extends Component { ...@@ -112,7 +128,7 @@ class Send extends Component {
sendStore: { tokenAddress }, sendStore: { tokenAddress },
tokensStore tokensStore
} = this.props; } = this.props;
const { amount, gasPrice, maxAmount, to } = this.state; const { amount, estimating, gasPrice, maxAmount, to } = this.state;
const token = tokensStore.tokens[tokenAddress]; const token = tokensStore.tokens[tokenAddress];
const error = this.hasError(); const error = this.hasError();
...@@ -210,8 +226,8 @@ class Send extends Component { ...@@ -210,8 +226,8 @@ class Send extends Component {
</fieldset> </fieldset>
<nav className='form-nav'> <nav className='form-nav'>
<span data-tip={error || ''}> <span data-tip={error || ''}>
<button disabled={error} className='button'> <button disabled={error || estimating} className='button'>
Send {estimating ? 'Checking...' : 'Send'}
</button> </button>
</span> </span>
</nav> </nav>
......
...@@ -16,8 +16,8 @@ import parityStore from './parityStore'; ...@@ -16,8 +16,8 @@ import parityStore from './parityStore';
import tokensStore from './tokensStore'; import tokensStore from './tokensStore';
const debug = Debug('sendStore'); const debug = Debug('sendStore');
const DEFAULT_GAS = new BigNumber(21000); // Default gas amount to show const GAS_MULT_FACTOR = 1.25; // Since estimateGas is not always accurate, we add a 33% factor for buffer.
const GAS_MULT_FACTOR = 1.33; // Since estimateGas is not always accurate, we add a 33% factor for buffer. const DEFAULT_GAS = new BigNumber(21000 * GAS_MULT_FACTOR); // Default gas amount
export class SendStore { export class SendStore {
@observable blockNumber; // Current block number, used to calculate tx confirmations. @observable blockNumber; // Current block number, used to calculate tx confirmations.
...@@ -71,7 +71,7 @@ export class SendStore { ...@@ -71,7 +71,7 @@ export class SendStore {
*/ */
estimateGas = () => { estimateGas = () => {
if (!this.tx || !Object.keys(this.tx).length) { if (!this.tx || !Object.keys(this.tx).length) {
return; return Promise.reject(new Error('Tx not set in sendStore.'));
} }
if (this.tokenAddress === 'ETH') { if (this.tokenAddress === 'ETH') {
...@@ -85,23 +85,27 @@ export class SendStore { ...@@ -85,23 +85,27 @@ export class SendStore {
* Estimate gas to transfer in ERC20 contract. Expensive function, so we * Estimate gas to transfer in ERC20 contract. Expensive function, so we
* memoize it. * memoize it.
*/ */
estimateGasForErc20 = memoize(txForErc20 => { estimateGasForErc20 = memoize(
return this.contract.contractObject.instance.transfer txForErc20 =>
.estimateGas(txForErc20.options, txForErc20.args) this.contract.contractObject.instance.transfer
.then(this.setEstimated) .estimateGas(txForErc20.options, txForErc20.args)
.catch(noop); .then(this.setEstimated)
}, JSON.stringify); .catch(noop),
JSON.stringify
);
/** /**
* Estimate gas to transfer to an ETH address. Expensive function, so we * Estimate gas to transfer to an ETH address. Expensive function, so we
* memoize it. * memoize it.
*/ */
estimateGasForEth = memoize(txForEth => { estimateGasForEth = memoize(
return parityStore.api.eth txForEth =>
.estimateGas(txForEth) parityStore.api.eth
.then(this.setEstimated) .estimateGas(txForEth)
.catch(noop); .then(this.setEstimated)
}, JSON.stringify); .catch(noop),
JSON.stringify
);
/** /**
* Create a transaction. * Create a transaction.
...@@ -174,7 +178,7 @@ export class SendStore { ...@@ -174,7 +178,7 @@ export class SendStore {
@action @action
setEstimated = estimated => { setEstimated = estimated => {
this.estimated = estimated.mul(GAS_MULT_FACTOR); this.estimated = estimated.mul(GAS_MULT_FACTOR);
debug('Estimated gas.', +estimated); debug('Estimated gas,', +estimated, ', with buffer,', +this.estimated);
}; };
@action @action
......
...@@ -127,10 +127,13 @@ describe('@computed contract', () => { ...@@ -127,10 +127,13 @@ describe('@computed contract', () => {
}); });
describe('method estimateGas', () => { describe('method estimateGas', () => {
test('should not estimate if no tx is set', () => { test('should reject and not estimate if no tx is set', () => {
sendStore.estimateGasForErc20 = jest.fn(); sendStore.estimateGasForErc20 = jest.fn();
sendStore.estimateGasForEth = jest.fn(); sendStore.estimateGasForEth = jest.fn();
expect(sendStore.estimateGas()).toBe(undefined); expect(sendStore.estimateGas()).rejects.toHaveProperty(
'message',
'Tx not set in sendStore.'
);
expect(sendStore.estimateGasForErc20).not.toHaveBeenCalled(); expect(sendStore.estimateGasForErc20).not.toHaveBeenCalled();
expect(sendStore.estimateGasForEth).not.toHaveBeenCalled(); expect(sendStore.estimateGasForEth).not.toHaveBeenCalled();
}); });
...@@ -223,9 +226,9 @@ describe('method send', () => { ...@@ -223,9 +226,9 @@ describe('method send', () => {
}); });
describe('setter setEstimated', () => { describe('setter setEstimated', () => {
test('should add a 1.33 factor', () => { test('should add a 1.25 factor', () => {
sendStore.setEstimated(new BigNumber(2)); sendStore.setEstimated(new BigNumber(2));
expect(sendStore.estimated).toEqual(new BigNumber(2 * 1.33)); expect(sendStore.estimated).toEqual(new BigNumber(2 * 1.25));
}); });
}); });
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment