mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Allow sending up to 15 decimals on the send screen (#12707)
This commit is contained in:
parent
8f2144fdb0
commit
8597cd1401
5
shared/constants/decimal.js
Normal file
5
shared/constants/decimal.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/**
|
||||||
|
* MAX_DECIMAL represensts the maximum number of decimal values allow for the input,
|
||||||
|
* the limitation in javascript <input/> retstricts us to keep it as 15 and below.
|
||||||
|
*/
|
||||||
|
export const MAX_DECIMAL = 15;
|
@ -14,6 +14,7 @@ import {
|
|||||||
getNativeCurrency,
|
getNativeCurrency,
|
||||||
} from '../../../ducks/metamask/metamask';
|
} from '../../../ducks/metamask/metamask';
|
||||||
import { getCurrentCurrency, getShouldShowFiat } from '../../../selectors';
|
import { getCurrentCurrency, getShouldShowFiat } from '../../../selectors';
|
||||||
|
import { MAX_DECIMAL } from '../../../../shared/constants/decimal';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that allows user to enter currency values as a number, and props receive a converted
|
* Component that allows user to enter currency values as a number, and props receive a converted
|
||||||
@ -25,12 +26,14 @@ import { getCurrentCurrency, getShouldShowFiat } from '../../../selectors';
|
|||||||
* @param options0.featureSecondary
|
* @param options0.featureSecondary
|
||||||
* @param options0.onChange
|
* @param options0.onChange
|
||||||
* @param options0.onPreferenceToggle
|
* @param options0.onPreferenceToggle
|
||||||
|
* @param options0.primaryNumberOfDecimals
|
||||||
*/
|
*/
|
||||||
export default function CurrencyInput({
|
export default function CurrencyInput({
|
||||||
hexValue,
|
hexValue,
|
||||||
featureSecondary,
|
featureSecondary,
|
||||||
onChange,
|
onChange,
|
||||||
onPreferenceToggle,
|
onPreferenceToggle,
|
||||||
|
primaryNumberOfDecimals = 8,
|
||||||
}) {
|
}) {
|
||||||
const t = useContext(I18nContext);
|
const t = useContext(I18nContext);
|
||||||
|
|
||||||
@ -64,7 +67,10 @@ export default function CurrencyInput({
|
|||||||
: getValueFromWeiHex({
|
: getValueFromWeiHex({
|
||||||
value: hexValue,
|
value: hexValue,
|
||||||
toCurrency: ETH,
|
toCurrency: ETH,
|
||||||
numberOfDecimals: 8,
|
numberOfDecimals:
|
||||||
|
primaryNumberOfDecimals <= MAX_DECIMAL
|
||||||
|
? primaryNumberOfDecimals
|
||||||
|
: MAX_DECIMAL,
|
||||||
});
|
});
|
||||||
|
|
||||||
return Number(decimalValueString) || 0;
|
return Number(decimalValueString) || 0;
|
||||||
@ -127,7 +133,10 @@ export default function CurrencyInput({
|
|||||||
if (shouldUseFiat()) {
|
if (shouldUseFiat()) {
|
||||||
// Display ETH
|
// Display ETH
|
||||||
currency = preferredCurrency || ETH;
|
currency = preferredCurrency || ETH;
|
||||||
numberOfDecimals = 8;
|
numberOfDecimals =
|
||||||
|
primaryNumberOfDecimals <= MAX_DECIMAL
|
||||||
|
? primaryNumberOfDecimals
|
||||||
|
: MAX_DECIMAL;
|
||||||
} else {
|
} else {
|
||||||
// Display Fiat
|
// Display Fiat
|
||||||
currency = secondaryCurrency;
|
currency = secondaryCurrency;
|
||||||
@ -143,7 +152,6 @@ export default function CurrencyInput({
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<UnitInput
|
<UnitInput
|
||||||
{...{
|
{...{
|
||||||
@ -173,4 +181,5 @@ CurrencyInput.propTypes = {
|
|||||||
featureSecondary: PropTypes.bool,
|
featureSecondary: PropTypes.bool,
|
||||||
onChange: PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
onPreferenceToggle: PropTypes.func,
|
onPreferenceToggle: PropTypes.func,
|
||||||
|
primaryNumberOfDecimals: PropTypes.number,
|
||||||
};
|
};
|
||||||
|
@ -6,6 +6,7 @@ import { Provider } from 'react-redux';
|
|||||||
import configureMockStore from 'redux-mock-store';
|
import configureMockStore from 'redux-mock-store';
|
||||||
import UnitInput from '../../ui/unit-input';
|
import UnitInput from '../../ui/unit-input';
|
||||||
import CurrencyDisplay from '../../ui/currency-display';
|
import CurrencyDisplay from '../../ui/currency-display';
|
||||||
|
import { MAX_DECIMAL } from '../../../../shared/constants/decimal';
|
||||||
import CurrencyInput from './currency-input';
|
import CurrencyInput from './currency-input';
|
||||||
|
|
||||||
describe('CurrencyInput Component', () => {
|
describe('CurrencyInput Component', () => {
|
||||||
@ -145,7 +146,11 @@ describe('CurrencyInput Component', () => {
|
|||||||
|
|
||||||
const wrapper = mount(
|
const wrapper = mount(
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<CurrencyInput hexValue="f602f2234d0ea" featureSecondary />
|
<CurrencyInput
|
||||||
|
hexValue="f602f2234d0ea"
|
||||||
|
featureSecondary
|
||||||
|
primaryNumberOfDecimals={MAX_DECIMAL}
|
||||||
|
/>
|
||||||
</Provider>,
|
</Provider>,
|
||||||
{
|
{
|
||||||
context: { t: (str) => `${str}_t` },
|
context: { t: (str) => `${str}_t` },
|
||||||
@ -157,7 +162,7 @@ describe('CurrencyInput Component', () => {
|
|||||||
expect(wrapper.find('.unit-input__suffix')).toHaveLength(1);
|
expect(wrapper.find('.unit-input__suffix')).toHaveLength(1);
|
||||||
expect(wrapper.find('.unit-input__suffix').text()).toStrictEqual('ETH');
|
expect(wrapper.find('.unit-input__suffix').text()).toStrictEqual('ETH');
|
||||||
expect(wrapper.find('.unit-input__input').props().value).toStrictEqual(
|
expect(wrapper.find('.unit-input__input').props().value).toStrictEqual(
|
||||||
0.00432788,
|
0.004327880204276,
|
||||||
);
|
);
|
||||||
expect(
|
expect(
|
||||||
wrapper.find('.currency-input__conversion-component').text(),
|
wrapper.find('.currency-input__conversion-component').text(),
|
||||||
@ -193,7 +198,11 @@ describe('CurrencyInput Component', () => {
|
|||||||
const store = configureMockStore()(mockStore);
|
const store = configureMockStore()(mockStore);
|
||||||
const wrapper = mount(
|
const wrapper = mount(
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<CurrencyInput onChange={handleChangeSpy} hexValue="f602f2234d0ea" />
|
<CurrencyInput
|
||||||
|
onChange={handleChangeSpy}
|
||||||
|
hexValue="f602f2234d0ea"
|
||||||
|
primaryNumberOfDecimals={MAX_DECIMAL}
|
||||||
|
/>
|
||||||
</Provider>,
|
</Provider>,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -202,7 +211,7 @@ describe('CurrencyInput Component', () => {
|
|||||||
expect(handleBlurSpy.callCount).toStrictEqual(0);
|
expect(handleBlurSpy.callCount).toStrictEqual(0);
|
||||||
|
|
||||||
const input = wrapper.find('input');
|
const input = wrapper.find('input');
|
||||||
expect(input.props().value).toStrictEqual(0.00432788);
|
expect(input.props().value).toStrictEqual(0.004327880204276);
|
||||||
|
|
||||||
input.simulate('change', { target: { value: 1 } });
|
input.simulate('change', { target: { value: 1 } });
|
||||||
expect(handleChangeSpy.callCount).toStrictEqual(2);
|
expect(handleChangeSpy.callCount).toStrictEqual(2);
|
||||||
|
@ -11,6 +11,7 @@ import {
|
|||||||
|
|
||||||
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';
|
||||||
|
import { MAX_DECIMAL } from '../../../../shared/constants/decimal';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that allows user to enter token values as a number, and props receive a converted
|
* Component that allows user to enter token values as a number, and props receive a converted
|
||||||
@ -34,6 +35,7 @@ export default class TokenInput extends PureComponent {
|
|||||||
symbol: PropTypes.string,
|
symbol: PropTypes.string,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
tokenExchangeRates: PropTypes.object,
|
tokenExchangeRates: PropTypes.object,
|
||||||
|
primaryNumberOfDecimals: PropTypes.number,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@ -108,6 +110,7 @@ export default class TokenInput extends PureComponent {
|
|||||||
currentCurrency,
|
currentCurrency,
|
||||||
hideConversion,
|
hideConversion,
|
||||||
token,
|
token,
|
||||||
|
primaryNumberOfDecimals = 8,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { decimalValue } = this.state;
|
const { decimalValue } = this.state;
|
||||||
|
|
||||||
@ -129,7 +132,10 @@ export default class TokenInput extends PureComponent {
|
|||||||
} else {
|
} else {
|
||||||
// Display ETH
|
// Display ETH
|
||||||
currency = ETH;
|
currency = ETH;
|
||||||
numberOfDecimals = 6;
|
numberOfDecimals =
|
||||||
|
primaryNumberOfDecimals <= MAX_DECIMAL
|
||||||
|
? primaryNumberOfDecimals
|
||||||
|
: MAX_DECIMAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
const decimalEthValue = decimalValue * tokenExchangeRate || 0;
|
const decimalEthValue = decimalValue * tokenExchangeRate || 0;
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
import { MAX_DECIMAL } from '../../../../shared/constants/decimal';
|
||||||
|
|
||||||
|
const DECIMAL_REGEX = /\.(\d*)/u;
|
||||||
|
|
||||||
function removeLeadingZeroes(str) {
|
function removeLeadingZeroes(str) {
|
||||||
return str.replace(/^0*(?=\d)/u, '');
|
return str.replace(/^0*(?=\d)/u, '');
|
||||||
@ -62,6 +65,11 @@ export default class UnitInput extends PureComponent {
|
|||||||
|
|
||||||
handleChange = (event) => {
|
handleChange = (event) => {
|
||||||
const { value: userInput } = event.target;
|
const { value: userInput } = event.target;
|
||||||
|
const match = DECIMAL_REGEX.exec(userInput);
|
||||||
|
if (match?.[1]?.length > MAX_DECIMAL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let value = userInput;
|
let value = userInput;
|
||||||
|
|
||||||
if (userInput.length && userInput.length > 1) {
|
if (userInput.length && userInput.length > 1) {
|
||||||
|
@ -4,6 +4,7 @@ import SendRowWrapper from '../send-row-wrapper';
|
|||||||
import UserPreferencedCurrencyInput from '../../../../components/app/user-preferenced-currency-input';
|
import UserPreferencedCurrencyInput from '../../../../components/app/user-preferenced-currency-input';
|
||||||
import UserPreferencedTokenInput from '../../../../components/app/user-preferenced-token-input';
|
import UserPreferencedTokenInput from '../../../../components/app/user-preferenced-token-input';
|
||||||
import { ASSET_TYPES } from '../../../../ducks/send';
|
import { ASSET_TYPES } from '../../../../ducks/send';
|
||||||
|
import { MAX_DECIMAL } from '../../../../../shared/constants/decimal';
|
||||||
import AmountMaxButton from './amount-max-button';
|
import AmountMaxButton from './amount-max-button';
|
||||||
|
|
||||||
export default class SendAmountRow extends Component {
|
export default class SendAmountRow extends Component {
|
||||||
@ -31,12 +32,14 @@ export default class SendAmountRow extends Component {
|
|||||||
onChange={this.handleChange}
|
onChange={this.handleChange}
|
||||||
token={asset.details}
|
token={asset.details}
|
||||||
value={amount}
|
value={amount}
|
||||||
|
primaryNumberOfDecimals={asset.decimals}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<UserPreferencedCurrencyInput
|
<UserPreferencedCurrencyInput
|
||||||
error={inError}
|
error={inError}
|
||||||
onChange={this.handleChange}
|
onChange={this.handleChange}
|
||||||
hexValue={amount}
|
hexValue={amount}
|
||||||
|
primaryNumberOfDecimals={MAX_DECIMAL}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user