mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Convert token input to BigNumber to handle decimals. (#12773)
* Fixes #12762 Adds a decimal length check for inputs and drops excess fractional part. Another edgecase not accounted for is when a token's decimal precision is 0 and attempting sending decimals will result in omitting the fractional part. * Change spies from sinon to jest and change onChange value to string. * Adjust * Remove sinon * Add test for issue case * DRY * Simplify logic by using BigNumber Co-authored-by: Dan Miller <danjm.com@gmail.com>
This commit is contained in:
parent
7929a92c66
commit
d179344712
@ -1,5 +1,6 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import BigNumber from 'bignumber.js';
|
||||||
import UnitInput from '../unit-input';
|
import UnitInput from '../unit-input';
|
||||||
import CurrencyDisplay from '../currency-display';
|
import CurrencyDisplay from '../currency-display';
|
||||||
import { getWeiHexFromDecimalValue } from '../../../helpers/utils/conversions.util';
|
import { getWeiHexFromDecimalValue } from '../../../helpers/utils/conversions.util';
|
||||||
@ -7,6 +8,7 @@ import {
|
|||||||
conversionUtil,
|
conversionUtil,
|
||||||
multiplyCurrencies,
|
multiplyCurrencies,
|
||||||
} from '../../../../shared/modules/conversion.utils';
|
} from '../../../../shared/modules/conversion.utils';
|
||||||
|
|
||||||
import { ETH } from '../../../helpers/constants/common';
|
import { ETH } from '../../../helpers/constants/common';
|
||||||
import { addHexPrefix } from '../../../../app/scripts/lib/util';
|
import { addHexPrefix } from '../../../../app/scripts/lib/util';
|
||||||
|
|
||||||
@ -81,7 +83,7 @@ export default class TokenInput extends PureComponent {
|
|||||||
let newDecimalValue = decimalValue;
|
let newDecimalValue = decimalValue;
|
||||||
|
|
||||||
if (decimals && applyDecimals) {
|
if (decimals && applyDecimals) {
|
||||||
newDecimalValue = parseFloat(decimalValue).toFixed(decimals);
|
newDecimalValue = new BigNumber(decimalValue, 10).toFixed(decimals);
|
||||||
}
|
}
|
||||||
|
|
||||||
const multiplier = Math.pow(10, Number(decimals || 0));
|
const multiplier = Math.pow(10, Number(decimals || 0));
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { shallow, mount } from 'enzyme';
|
import { shallow, mount } from 'enzyme';
|
||||||
import sinon from 'sinon';
|
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import configureMockStore from 'redux-mock-store';
|
import configureMockStore from 'redux-mock-store';
|
||||||
import UnitInput from '../unit-input';
|
import UnitInput from '../unit-input';
|
||||||
@ -207,12 +206,10 @@ describe('TokenInput Component', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('handling actions', () => {
|
describe('handling actions', () => {
|
||||||
const handleChangeSpy = sinon.spy();
|
const handleChangeSpy = jest.fn();
|
||||||
const handleBlurSpy = sinon.spy();
|
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
handleChangeSpy.resetHistory();
|
handleChangeSpy.mockClear();
|
||||||
handleBlurSpy.resetHistory();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call onChange on input changes with the hex value for ETH', () => {
|
it('should call onChange on input changes with the hex value for ETH', () => {
|
||||||
@ -238,8 +235,7 @@ describe('TokenInput Component', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
expect(wrapper).toHaveLength(1);
|
expect(wrapper).toHaveLength(1);
|
||||||
expect(handleChangeSpy.callCount).toStrictEqual(0);
|
expect(handleChangeSpy.mock.calls).toHaveLength(0);
|
||||||
expect(handleBlurSpy.callCount).toStrictEqual(0);
|
|
||||||
|
|
||||||
const tokenInputInstance = wrapper.find(TokenInput).at(0).instance();
|
const tokenInputInstance = wrapper.find(TokenInput).at(0).instance();
|
||||||
expect(tokenInputInstance.state.decimalValue).toStrictEqual(0);
|
expect(tokenInputInstance.state.decimalValue).toStrictEqual(0);
|
||||||
@ -250,13 +246,13 @@ describe('TokenInput Component', () => {
|
|||||||
const input = wrapper.find('input');
|
const input = wrapper.find('input');
|
||||||
expect(input.props().value).toStrictEqual(0);
|
expect(input.props().value).toStrictEqual(0);
|
||||||
|
|
||||||
input.simulate('change', { target: { value: 1 } });
|
input.simulate('change', { target: { value: '1' } });
|
||||||
expect(handleChangeSpy.callCount).toStrictEqual(1);
|
expect(handleChangeSpy.mock.calls).toHaveLength(1);
|
||||||
expect(handleChangeSpy.calledWith('2710')).toStrictEqual(true);
|
expect(handleChangeSpy.mock.calls[0][0]).toStrictEqual('2710');
|
||||||
expect(wrapper.find('.currency-display-component').text()).toStrictEqual(
|
expect(wrapper.find('.currency-display-component').text()).toStrictEqual(
|
||||||
'2ETH',
|
'2ETH',
|
||||||
);
|
);
|
||||||
expect(tokenInputInstance.state.decimalValue).toStrictEqual(1);
|
expect(tokenInputInstance.state.decimalValue).toStrictEqual('1');
|
||||||
expect(tokenInputInstance.state.hexValue).toStrictEqual('2710');
|
expect(tokenInputInstance.state.hexValue).toStrictEqual('2710');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -285,8 +281,7 @@ describe('TokenInput Component', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
expect(wrapper).toHaveLength(1);
|
expect(wrapper).toHaveLength(1);
|
||||||
expect(handleChangeSpy.callCount).toStrictEqual(0);
|
expect(handleChangeSpy.mock.calls).toHaveLength(0);
|
||||||
expect(handleBlurSpy.callCount).toStrictEqual(0);
|
|
||||||
|
|
||||||
const tokenInputInstance = wrapper.find(TokenInput).at(0).instance();
|
const tokenInputInstance = wrapper.find(TokenInput).at(0).instance();
|
||||||
expect(tokenInputInstance.state.decimalValue).toStrictEqual(0);
|
expect(tokenInputInstance.state.decimalValue).toStrictEqual(0);
|
||||||
@ -297,13 +292,13 @@ describe('TokenInput Component', () => {
|
|||||||
const input = wrapper.find('input');
|
const input = wrapper.find('input');
|
||||||
expect(input.props().value).toStrictEqual(0);
|
expect(input.props().value).toStrictEqual(0);
|
||||||
|
|
||||||
input.simulate('change', { target: { value: 1 } });
|
input.simulate('change', { target: { value: '1' } });
|
||||||
expect(handleChangeSpy.callCount).toStrictEqual(1);
|
expect(handleChangeSpy.mock.calls).toHaveLength(1);
|
||||||
expect(handleChangeSpy.calledWith('2710')).toStrictEqual(true);
|
expect(handleChangeSpy.mock.calls[0][0]).toStrictEqual('2710');
|
||||||
expect(wrapper.find('.currency-display-component').text()).toStrictEqual(
|
expect(wrapper.find('.currency-display-component').text()).toStrictEqual(
|
||||||
'$462.12USD',
|
'$462.12USD',
|
||||||
);
|
);
|
||||||
expect(tokenInputInstance.state.decimalValue).toStrictEqual(1);
|
expect(tokenInputInstance.state.decimalValue).toStrictEqual('1');
|
||||||
expect(tokenInputInstance.state.hexValue).toStrictEqual('2710');
|
expect(tokenInputInstance.state.hexValue).toStrictEqual('2710');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -345,4 +340,84 @@ describe('TokenInput Component', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Token Input Decimals Check', () => {
|
||||||
|
const handleChangeSpy = jest.fn();
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
handleChangeSpy.mockClear();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render incorrect hex onChange when input decimals is more than token decimals', () => {
|
||||||
|
const mockStore = {
|
||||||
|
metamask: {
|
||||||
|
currentCurrency: 'usd',
|
||||||
|
conversionRate: 231.06,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const store = configureMockStore()(mockStore);
|
||||||
|
const wrapper = mount(
|
||||||
|
<Provider store={store}>
|
||||||
|
<TokenInput
|
||||||
|
onChange={handleChangeSpy}
|
||||||
|
token={{
|
||||||
|
address: '0x1',
|
||||||
|
decimals: 4,
|
||||||
|
symbol: 'ABC',
|
||||||
|
}}
|
||||||
|
tokenExchangeRates={{ '0x1': 2 }}
|
||||||
|
showFiat
|
||||||
|
currentCurrency="usd"
|
||||||
|
/>
|
||||||
|
</Provider>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(wrapper).toHaveLength(1);
|
||||||
|
expect(handleChangeSpy.mock.calls).toHaveLength(0);
|
||||||
|
|
||||||
|
const input = wrapper.find('input');
|
||||||
|
expect(input.props().value).toStrictEqual(0);
|
||||||
|
|
||||||
|
input.simulate('change', { target: { value: '1.11111' } });
|
||||||
|
expect(handleChangeSpy.mock.calls).toHaveLength(1);
|
||||||
|
|
||||||
|
expect(handleChangeSpy.mock.calls[0][0]).toStrictEqual(
|
||||||
|
'2b67.1999999999999999999a',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render correct hex onChange when input decimals is more than token decimals by omitting excess fractional part on blur', () => {
|
||||||
|
const mockStore = {
|
||||||
|
metamask: {
|
||||||
|
currentCurrency: 'usd',
|
||||||
|
conversionRate: 231.06,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const store = configureMockStore()(mockStore);
|
||||||
|
|
||||||
|
const wrapper = mount(
|
||||||
|
<Provider store={store}>
|
||||||
|
<TokenInput
|
||||||
|
onChange={handleChangeSpy}
|
||||||
|
token={{
|
||||||
|
address: '0x1',
|
||||||
|
decimals: 4,
|
||||||
|
symbol: 'ABC',
|
||||||
|
}}
|
||||||
|
tokenExchangeRates={{ '0x1': 2 }}
|
||||||
|
showFiat
|
||||||
|
currentCurrency="usd"
|
||||||
|
/>
|
||||||
|
</Provider>,
|
||||||
|
);
|
||||||
|
expect(wrapper).toHaveLength(1);
|
||||||
|
|
||||||
|
const input = wrapper.find('input');
|
||||||
|
|
||||||
|
input.simulate('blur', { target: { value: '1.11111' } });
|
||||||
|
|
||||||
|
expect(handleChangeSpy.mock.calls).toHaveLength(1);
|
||||||
|
expect(handleChangeSpy.mock.calls[0][0]).toStrictEqual('2b67');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user