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:
William O'Beirne 2017-12-11 15:36:22 -05:00 committed by Daniel Ternyak
parent e6a958d6c1
commit 610805aadd
3 changed files with 66 additions and 34 deletions

View File

@ -5,6 +5,7 @@ import { State } from 'reducers/rates';
import { rateSymbols, TFetchCCRates } from 'actions/rates';
import { TokenBalance } from 'selectors/wallet';
import { Balance } from 'libs/wallet';
import { ETH_DECIMAL, convertTokenBase } from 'libs/units';
import Spinner from 'components/ui/Spinner';
import UnitDisplay from 'components/ui/UnitDisplay';
import './EquivalentValues.scss';
@ -28,7 +29,8 @@ export default class EquivalentValues extends React.Component<Props, CmpState> {
currency: ALL_OPTION
};
private balanceLookup: { [key: string]: Balance['wei'] | undefined } = {};
private requestedCurrencies: string[] = [];
private decimalLookup: { [key: string]: number } = {};
private requestedCurrencies: string[] | null = null;
public constructor(props) {
super(props);
@ -41,10 +43,7 @@ export default class EquivalentValues extends React.Component<Props, CmpState> {
public componentWillReceiveProps(nextProps) {
const { balance, tokenBalances } = this.props;
if (
nextProps.balance !== balance ||
nextProps.tokenBalances !== tokenBalances
) {
if (nextProps.balance !== balance || nextProps.tokenBalances !== tokenBalances) {
this.makeBalanceLookup(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
// while we have incomplete data that's being fetched.
const isFetching =
!balance ||
balance.isPending ||
!tokenBalances ||
Object.keys(rates).length === 0;
!balance || balance.isPending || !tokenBalances || Object.keys(rates).length === 0;
let valuesEl;
if (!isFetching && (rates[currency] || currency === ALL_OPTION)) {
@ -72,15 +68,9 @@ export default class EquivalentValues extends React.Component<Props, CmpState> {
return (
<li className="EquivalentValues-values-currency" key={key}>
<span className="EquivalentValues-values-currency-label">
{key}:
</span>{' '}
<span className="EquivalentValues-values-currency-label">{key}:</span>{' '}
<span className="EquivalentValues-values-currency-value">
<UnitDisplay
unit={'ether'}
value={values[key]}
displayShortBalance={3}
/>
<UnitDisplay unit={'ether'} value={values[key]} displayShortBalance={3} />
</span>
</li>
);
@ -137,10 +127,10 @@ export default class EquivalentValues extends React.Component<Props, CmpState> {
const tokenBalances = props.tokenBalances || [];
this.balanceLookup = tokenBalances.reduce(
(prev, tk) => {
return {
...prev,
[tk.symbol]: tk.balance
};
// Piggy-back off of this reduce to add to decimal lookup
this.decimalLookup[tk.symbol] = tk.decimal;
prev[tk.symbol] = tk.balance;
return prev;
},
{ ETH: props.balance && props.balance.wei }
);
@ -159,7 +149,7 @@ export default class EquivalentValues extends React.Component<Props, CmpState> {
.sort();
// 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;
}
@ -175,12 +165,10 @@ export default class EquivalentValues extends React.Component<Props, CmpState> {
} {
// Recursively call on all currencies
if (currency === ALL_OPTION) {
return ['ETH'].concat(this.requestedCurrencies).reduce(
return ['ETH'].concat(this.requestedCurrencies || []).reduce(
(prev, curr) => {
const currValues = this.getEquivalentValues(curr);
rateSymbols.forEach(
sym => (prev[sym] = prev[sym].add(currValues[sym] || new BN(0)))
);
rateSymbols.forEach(sym => (prev[sym] = prev[sym].add(currValues[sym] || new BN(0))));
return prev;
},
rateSymbols.reduce((prev, sym) => {
@ -197,8 +185,13 @@ export default class EquivalentValues extends React.Component<Props, CmpState> {
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) => {
prev[sym] = balance ? balance.muln(rates[currency][sym]) : null;
prev[sym] = adjustedBalance.muln(rates[currency][sym]);
return prev;
}, {});
}

View File

@ -5,6 +5,8 @@ type UnitKey = keyof typeof Units;
type Wei = BN;
type TokenValue = BN;
export const ETH_DECIMAL = 18;
const Units = {
wei: '1',
kwei: '1000',
@ -33,9 +35,7 @@ const Units = {
};
const handleValues = (input: string | BN) => {
if (typeof input === 'string') {
return input.startsWith('0x')
? new BN(stripHexPrefix(input), 16)
: new BN(input);
return input.startsWith('0x') ? new BN(stripHexPrefix(input), 16) : new BN(input);
}
if (typeof input === 'number') {
return new BN(input);
@ -92,12 +92,20 @@ const fromTokenBase = (value: TokenValue, decimal: number) =>
const toTokenBase = (value: string, decimal: number) =>
TokenValue(convertedToBaseUnit(value, decimal));
const convertTokenBase = (value: TokenValue, oldDecimal: number, newDecimal: number) => {
if (oldDecimal === newDecimal) {
return value;
}
return toTokenBase(fromTokenBase(value, oldDecimal), newDecimal);
};
export {
TokenValue,
fromWei,
toWei,
toTokenBase,
fromTokenBase,
convertTokenBase,
Wei,
getDecimal,
UnitKey

View File

@ -4,6 +4,7 @@ import {
toWei,
toTokenBase,
fromTokenBase,
convertTokenBase,
getDecimal,
TokenValue
} from 'libs/units';
@ -77,10 +78,10 @@ describe('Units', () => {
const tokens = '732156.34592016';
const decimal = 18;
const tokenBase = toTokenBase(tokens, decimal);
it('should equal 732156345920160000000000', () => {
it('toTokenBase should equal 732156345920160000000000', () => {
expect(tokenBase.toString()).toEqual('732156345920160000000000');
});
it('should equal 732156.34592016', () => {
it('fromTokenBase should equal 732156.34592016', () => {
expect(fromTokenBase(tokenBase, decimal)).toEqual(tokens);
});
});
@ -88,12 +89,42 @@ describe('Units', () => {
const tokens = '8000';
const decimal = 8;
const converted = fromTokenBase(TokenValue(tokens), decimal);
it('should equal 0.00008', () => {
it('fromTokenBase should equal 0.00008', () => {
expect(converted).toEqual('0.00008');
});
it('should equal 8000', () => {
it('toTokenBase should equal 8000', () => {
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);
});
});
});
});
});