Equivalent Values Fixes (#500)
* Initialize with no requested currencies, so that the initial request always fires. * Adjust tokens with different decimals for equivalent values. * Reuse libs units function. * Create lib function and tests for base conversion behavior.
This commit is contained in:
parent
e6a958d6c1
commit
610805aadd
|
@ -5,6 +5,7 @@ import { State } from 'reducers/rates';
|
||||||
import { rateSymbols, TFetchCCRates } from 'actions/rates';
|
import { rateSymbols, TFetchCCRates } from 'actions/rates';
|
||||||
import { TokenBalance } from 'selectors/wallet';
|
import { TokenBalance } from 'selectors/wallet';
|
||||||
import { Balance } from 'libs/wallet';
|
import { Balance } from 'libs/wallet';
|
||||||
|
import { ETH_DECIMAL, convertTokenBase } from 'libs/units';
|
||||||
import Spinner from 'components/ui/Spinner';
|
import Spinner from 'components/ui/Spinner';
|
||||||
import UnitDisplay from 'components/ui/UnitDisplay';
|
import UnitDisplay from 'components/ui/UnitDisplay';
|
||||||
import './EquivalentValues.scss';
|
import './EquivalentValues.scss';
|
||||||
|
@ -28,7 +29,8 @@ export default class EquivalentValues extends React.Component<Props, CmpState> {
|
||||||
currency: ALL_OPTION
|
currency: ALL_OPTION
|
||||||
};
|
};
|
||||||
private balanceLookup: { [key: string]: Balance['wei'] | undefined } = {};
|
private balanceLookup: { [key: string]: Balance['wei'] | undefined } = {};
|
||||||
private requestedCurrencies: string[] = [];
|
private decimalLookup: { [key: string]: number } = {};
|
||||||
|
private requestedCurrencies: string[] | null = null;
|
||||||
|
|
||||||
public constructor(props) {
|
public constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
@ -41,10 +43,7 @@ export default class EquivalentValues extends React.Component<Props, CmpState> {
|
||||||
|
|
||||||
public componentWillReceiveProps(nextProps) {
|
public componentWillReceiveProps(nextProps) {
|
||||||
const { balance, tokenBalances } = this.props;
|
const { balance, tokenBalances } = this.props;
|
||||||
if (
|
if (nextProps.balance !== balance || nextProps.tokenBalances !== tokenBalances) {
|
||||||
nextProps.balance !== balance ||
|
|
||||||
nextProps.tokenBalances !== tokenBalances
|
|
||||||
) {
|
|
||||||
this.makeBalanceLookup(nextProps);
|
this.makeBalanceLookup(nextProps);
|
||||||
this.fetchRates(nextProps);
|
this.fetchRates(nextProps);
|
||||||
}
|
}
|
||||||
|
@ -57,10 +56,7 @@ export default class EquivalentValues extends React.Component<Props, CmpState> {
|
||||||
// There are a bunch of reasons why the incorrect balances might be rendered
|
// There are a bunch of reasons why the incorrect balances might be rendered
|
||||||
// while we have incomplete data that's being fetched.
|
// while we have incomplete data that's being fetched.
|
||||||
const isFetching =
|
const isFetching =
|
||||||
!balance ||
|
!balance || balance.isPending || !tokenBalances || Object.keys(rates).length === 0;
|
||||||
balance.isPending ||
|
|
||||||
!tokenBalances ||
|
|
||||||
Object.keys(rates).length === 0;
|
|
||||||
|
|
||||||
let valuesEl;
|
let valuesEl;
|
||||||
if (!isFetching && (rates[currency] || currency === ALL_OPTION)) {
|
if (!isFetching && (rates[currency] || currency === ALL_OPTION)) {
|
||||||
|
@ -72,15 +68,9 @@ export default class EquivalentValues extends React.Component<Props, CmpState> {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li className="EquivalentValues-values-currency" key={key}>
|
<li className="EquivalentValues-values-currency" key={key}>
|
||||||
<span className="EquivalentValues-values-currency-label">
|
<span className="EquivalentValues-values-currency-label">{key}:</span>{' '}
|
||||||
{key}:
|
|
||||||
</span>{' '}
|
|
||||||
<span className="EquivalentValues-values-currency-value">
|
<span className="EquivalentValues-values-currency-value">
|
||||||
<UnitDisplay
|
<UnitDisplay unit={'ether'} value={values[key]} displayShortBalance={3} />
|
||||||
unit={'ether'}
|
|
||||||
value={values[key]}
|
|
||||||
displayShortBalance={3}
|
|
||||||
/>
|
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
|
@ -137,10 +127,10 @@ export default class EquivalentValues extends React.Component<Props, CmpState> {
|
||||||
const tokenBalances = props.tokenBalances || [];
|
const tokenBalances = props.tokenBalances || [];
|
||||||
this.balanceLookup = tokenBalances.reduce(
|
this.balanceLookup = tokenBalances.reduce(
|
||||||
(prev, tk) => {
|
(prev, tk) => {
|
||||||
return {
|
// Piggy-back off of this reduce to add to decimal lookup
|
||||||
...prev,
|
this.decimalLookup[tk.symbol] = tk.decimal;
|
||||||
[tk.symbol]: tk.balance
|
prev[tk.symbol] = tk.balance;
|
||||||
};
|
return prev;
|
||||||
},
|
},
|
||||||
{ ETH: props.balance && props.balance.wei }
|
{ ETH: props.balance && props.balance.wei }
|
||||||
);
|
);
|
||||||
|
@ -159,7 +149,7 @@ export default class EquivalentValues extends React.Component<Props, CmpState> {
|
||||||
.sort();
|
.sort();
|
||||||
|
|
||||||
// If it's the same currencies as we have, skip it
|
// If it's the same currencies as we have, skip it
|
||||||
if (currencies.join() === this.requestedCurrencies.join()) {
|
if (this.requestedCurrencies && currencies.join() === this.requestedCurrencies.join()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,12 +165,10 @@ export default class EquivalentValues extends React.Component<Props, CmpState> {
|
||||||
} {
|
} {
|
||||||
// Recursively call on all currencies
|
// Recursively call on all currencies
|
||||||
if (currency === ALL_OPTION) {
|
if (currency === ALL_OPTION) {
|
||||||
return ['ETH'].concat(this.requestedCurrencies).reduce(
|
return ['ETH'].concat(this.requestedCurrencies || []).reduce(
|
||||||
(prev, curr) => {
|
(prev, curr) => {
|
||||||
const currValues = this.getEquivalentValues(curr);
|
const currValues = this.getEquivalentValues(curr);
|
||||||
rateSymbols.forEach(
|
rateSymbols.forEach(sym => (prev[sym] = prev[sym].add(currValues[sym] || new BN(0))));
|
||||||
sym => (prev[sym] = prev[sym].add(currValues[sym] || new BN(0)))
|
|
||||||
);
|
|
||||||
return prev;
|
return prev;
|
||||||
},
|
},
|
||||||
rateSymbols.reduce((prev, sym) => {
|
rateSymbols.reduce((prev, sym) => {
|
||||||
|
@ -197,8 +185,13 @@ export default class EquivalentValues extends React.Component<Props, CmpState> {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tokens with non-ether like decimals need to be adjusted to match
|
||||||
|
const decimal =
|
||||||
|
this.decimalLookup[currency] === undefined ? ETH_DECIMAL : this.decimalLookup[currency];
|
||||||
|
const adjustedBalance = convertTokenBase(balance, decimal, ETH_DECIMAL);
|
||||||
|
|
||||||
return rateSymbols.reduce((prev, sym) => {
|
return rateSymbols.reduce((prev, sym) => {
|
||||||
prev[sym] = balance ? balance.muln(rates[currency][sym]) : null;
|
prev[sym] = adjustedBalance.muln(rates[currency][sym]);
|
||||||
return prev;
|
return prev;
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ type UnitKey = keyof typeof Units;
|
||||||
type Wei = BN;
|
type Wei = BN;
|
||||||
type TokenValue = BN;
|
type TokenValue = BN;
|
||||||
|
|
||||||
|
export const ETH_DECIMAL = 18;
|
||||||
|
|
||||||
const Units = {
|
const Units = {
|
||||||
wei: '1',
|
wei: '1',
|
||||||
kwei: '1000',
|
kwei: '1000',
|
||||||
|
@ -33,9 +35,7 @@ const Units = {
|
||||||
};
|
};
|
||||||
const handleValues = (input: string | BN) => {
|
const handleValues = (input: string | BN) => {
|
||||||
if (typeof input === 'string') {
|
if (typeof input === 'string') {
|
||||||
return input.startsWith('0x')
|
return input.startsWith('0x') ? new BN(stripHexPrefix(input), 16) : new BN(input);
|
||||||
? new BN(stripHexPrefix(input), 16)
|
|
||||||
: new BN(input);
|
|
||||||
}
|
}
|
||||||
if (typeof input === 'number') {
|
if (typeof input === 'number') {
|
||||||
return new BN(input);
|
return new BN(input);
|
||||||
|
@ -92,12 +92,20 @@ const fromTokenBase = (value: TokenValue, decimal: number) =>
|
||||||
const toTokenBase = (value: string, decimal: number) =>
|
const toTokenBase = (value: string, decimal: number) =>
|
||||||
TokenValue(convertedToBaseUnit(value, decimal));
|
TokenValue(convertedToBaseUnit(value, decimal));
|
||||||
|
|
||||||
|
const convertTokenBase = (value: TokenValue, oldDecimal: number, newDecimal: number) => {
|
||||||
|
if (oldDecimal === newDecimal) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return toTokenBase(fromTokenBase(value, oldDecimal), newDecimal);
|
||||||
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
TokenValue,
|
TokenValue,
|
||||||
fromWei,
|
fromWei,
|
||||||
toWei,
|
toWei,
|
||||||
toTokenBase,
|
toTokenBase,
|
||||||
fromTokenBase,
|
fromTokenBase,
|
||||||
|
convertTokenBase,
|
||||||
Wei,
|
Wei,
|
||||||
getDecimal,
|
getDecimal,
|
||||||
UnitKey
|
UnitKey
|
||||||
|
|
|
@ -4,6 +4,7 @@ import {
|
||||||
toWei,
|
toWei,
|
||||||
toTokenBase,
|
toTokenBase,
|
||||||
fromTokenBase,
|
fromTokenBase,
|
||||||
|
convertTokenBase,
|
||||||
getDecimal,
|
getDecimal,
|
||||||
TokenValue
|
TokenValue
|
||||||
} from 'libs/units';
|
} from 'libs/units';
|
||||||
|
@ -77,10 +78,10 @@ describe('Units', () => {
|
||||||
const tokens = '732156.34592016';
|
const tokens = '732156.34592016';
|
||||||
const decimal = 18;
|
const decimal = 18;
|
||||||
const tokenBase = toTokenBase(tokens, decimal);
|
const tokenBase = toTokenBase(tokens, decimal);
|
||||||
it('should equal 732156345920160000000000', () => {
|
it('toTokenBase should equal 732156345920160000000000', () => {
|
||||||
expect(tokenBase.toString()).toEqual('732156345920160000000000');
|
expect(tokenBase.toString()).toEqual('732156345920160000000000');
|
||||||
});
|
});
|
||||||
it('should equal 732156.34592016', () => {
|
it('fromTokenBase should equal 732156.34592016', () => {
|
||||||
expect(fromTokenBase(tokenBase, decimal)).toEqual(tokens);
|
expect(fromTokenBase(tokenBase, decimal)).toEqual(tokens);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -88,12 +89,42 @@ describe('Units', () => {
|
||||||
const tokens = '8000';
|
const tokens = '8000';
|
||||||
const decimal = 8;
|
const decimal = 8;
|
||||||
const converted = fromTokenBase(TokenValue(tokens), decimal);
|
const converted = fromTokenBase(TokenValue(tokens), decimal);
|
||||||
it('should equal 0.00008', () => {
|
it('fromTokenBase should equal 0.00008', () => {
|
||||||
expect(converted).toEqual('0.00008');
|
expect(converted).toEqual('0.00008');
|
||||||
});
|
});
|
||||||
it('should equal 8000', () => {
|
it('toTokenBase should equal 8000', () => {
|
||||||
expect(toTokenBase(converted, decimal));
|
expect(toTokenBase(converted, decimal));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
describe('convertTokenBase', () => {
|
||||||
|
const conversions = [
|
||||||
|
{
|
||||||
|
oldDecimal: 0,
|
||||||
|
newDecimal: 18,
|
||||||
|
startValue: '42',
|
||||||
|
endValue: '42000000000000000000'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oldDecimal: 6,
|
||||||
|
newDecimal: 12,
|
||||||
|
startValue: '547834782',
|
||||||
|
endValue: '547834782000000'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oldDecimal: 18,
|
||||||
|
newDecimal: 18,
|
||||||
|
startValue: '311095801958902158012580',
|
||||||
|
endValue: '311095801958902158012580'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
conversions.forEach(c => {
|
||||||
|
it(`should convert decimal ${c.oldDecimal} to decimal ${c.newDecimal}`, () => {
|
||||||
|
const tokenValue = TokenValue(c.startValue);
|
||||||
|
const converted = convertTokenBase(tokenValue, c.oldDecimal, c.newDecimal);
|
||||||
|
expect(converted.toString()).toEqual(c.endValue);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue