Skip to content

Commit

Permalink
chore: use a better API for stringifyRatio functions (#2795)
Browse files Browse the repository at this point in the history
  • Loading branch information
katelynsills authored Apr 3, 2021
1 parent c811c41 commit 6751d85
Show file tree
Hide file tree
Showing 6 changed files with 200 additions and 92 deletions.
3 changes: 2 additions & 1 deletion packages/ui-components/src/display/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './display';
export * from './natValue/stringifyRatioAsFraction';
export * from './natValue/stringifyRatio';
export * from './natValue/stringifyRatioNumerator';
export * from './natValue/stringifyRatioAsPercent';
73 changes: 34 additions & 39 deletions packages/ui-components/src/display/natValue/stringifyRatio.js
Original file line number Diff line number Diff line change
@@ -1,53 +1,48 @@
// @ts-check

import { assert } from '@agoric/assert';
import { isNatValue } from '@agoric/ertp';

// eslint-disable-next-line import/no-extraneous-dependencies
import '@agoric/zoe/exported';

import { stringifyNat } from './stringifyNat';
import { assert, details } from '@agoric/assert';
import { captureNum } from './helpers/captureNum';
import { roundToDecimalPlaces } from './helpers/roundToDecimalPlaces';

const PLACES_TO_SHOW = 2;

/**
*
* @param {Ratio} ratio
* @param {{ numDecimalPlaces?: number,
numPlacesToShow?: number,
denomDecimalPlaces?: number,
denomPlacesToShow?: number,
getDecimalPlaces?: Function }} [options]
*
* @param {Ratio} ratio
* @param {(brand: Brand) => number | undefined} getDecimalPlaces
* @param {number} [placesToShow]
* @returns {string}
*/
export const stringifyRatio = (ratio, options) => {
const {
numPlacesToShow = PLACES_TO_SHOW,
denomPlacesToShow = PLACES_TO_SHOW,
getDecimalPlaces = undefined,
} = options || {};
let { numDecimalPlaces, denomDecimalPlaces } = options || {};

assert(isNatValue(ratio.numerator.value));
assert(isNatValue(ratio.denominator.value));

if (getDecimalPlaces !== undefined) {
numDecimalPlaces = getDecimalPlaces(ratio.numerator.brand);
denomDecimalPlaces = getDecimalPlaces(ratio.denominator.brand);
export const stringifyRatio = (
ratio,
getDecimalPlaces,
placesToShow = PLACES_TO_SHOW,
) => {
if (ratio === null || ratio === undefined) {
return '0';
}
assert(
ratio && ratio.numerator,
details`Ratio ${ratio} did not look like a ratio`,
);

assert(numDecimalPlaces !== undefined, `numDecimalPlaces required`);
assert(denomDecimalPlaces !== undefined, `denomDecimalPlaces required`);
const numDecimalPlaces = getDecimalPlaces(ratio.numerator.brand);
const denomDecimalPlaces = getDecimalPlaces(ratio.denominator.brand);

const numeratorString = stringifyNat(
ratio.numerator.value,
assert(
numDecimalPlaces,
numPlacesToShow,
`decimalPlaces for numerator ${ratio.numerator} must be provided`,
);
const denominatorString = stringifyNat(
ratio.denominator.value,
assert(
denomDecimalPlaces,
denomPlacesToShow,
`decimalPlaces for denominator ${ratio.denominator} must be provided`,
);
return `${numeratorString} / ${denominatorString}`;
const denomPower = 10n ** BigInt(denomDecimalPlaces);
const numPower = 10n ** BigInt(numDecimalPlaces);
const numerator = ratio.numerator.value * denomPower;
const denominator = ratio.denominator.value * numPower;
const str = `${Number(numerator) / Number(denominator)}`;
const capturedNum = captureNum(str);
return `${capturedNum.left}.${roundToDecimalPlaces(
capturedNum.right,
placesToShow,
)}`;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// @ts-check

import { assert } from '@agoric/assert';
import { isNatValue } from '@agoric/ertp';

// eslint-disable-next-line import/no-extraneous-dependencies
import '@agoric/zoe/exported';

import { stringifyNat } from './stringifyNat';

const PLACES_TO_SHOW = 2;

/**
* @param {Ratio} ratio
* @param {(brand: Brand) => number | undefined } getDecimalPlaces
* @param {number} [numPlacesToShow]
* @param {number} [denomPlacesToShow]
* @returns {string}
*/
export const stringifyRatioAsFraction = (
ratio,
getDecimalPlaces,
numPlacesToShow = PLACES_TO_SHOW,
denomPlacesToShow = PLACES_TO_SHOW,
) => {
assert(isNatValue(ratio.numerator.value));
assert(isNatValue(ratio.denominator.value));

const numDecimalPlaces = getDecimalPlaces(ratio.numerator.brand);
const denomDecimalPlaces = getDecimalPlaces(ratio.denominator.brand);

assert(
numDecimalPlaces,
`decimalPlaces for numerator ${ratio.numerator} must be provided`,
);
assert(
denomDecimalPlaces,
`decimalPlaces for denominator ${ratio.denominator} must be provided`,
);
const numeratorString = stringifyNat(
// @ts-ignore value is BigInt
ratio.numerator.value,
numDecimalPlaces,
numPlacesToShow,
);
const denominatorString = stringifyNat(
// @ts-ignore value is BigInt
ratio.denominator.value,
denomDecimalPlaces,
denomPlacesToShow,
);
return `${numeratorString} / ${denominatorString}`;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// @ts-check

import { assert, details } from '@agoric/assert';
import '@agoric/zoe/exported';

import { captureNum } from './helpers/captureNum';
import { roundToDecimalPlaces } from './helpers/roundToDecimalPlaces';

const PERCENT_BASE = 100n;
const PLACES_TO_SHOW = 2;

/**
* @param {Ratio} ratio
* @param {(brand: Brand) => number | undefined } getDecimalPlaces
* @param {number} [placesToShow]
* @returns {string}
*/
export const stringifyRatioAsPercent = (
ratio,
getDecimalPlaces,
placesToShow = PLACES_TO_SHOW,
) => {
if (ratio === null || ratio === undefined) {
return '0';
}
assert(
ratio && ratio.numerator,
details`Ratio ${ratio} did not look like a ratio`,
);

const numDecimalPlaces = getDecimalPlaces(ratio.numerator.brand);
const denomDecimalPlaces = getDecimalPlaces(ratio.denominator.brand);

assert(
numDecimalPlaces,
`decimalPlaces for numerator ${ratio.numerator} must be provided`,
);
assert(
denomDecimalPlaces,
`decimalPlaces for denominator ${ratio.denominator} must be provided`,
);
const denomPower = 10n ** BigInt(denomDecimalPlaces);
const numPower = 10n ** BigInt(numDecimalPlaces);
// @ts-ignore value is BigInt
const numerator = ratio.numerator.value * denomPower * PERCENT_BASE;
// @ts-ignore value is BigInt
const denominator = ratio.denominator.value * numPower;
const str = `${Number(numerator) / Number(denominator)}`;
const capturedNum = captureNum(str);
return `${capturedNum.left}.${roundToDecimalPlaces(
capturedNum.right,
placesToShow,
)}`;
};

This file was deleted.

68 changes: 57 additions & 11 deletions packages/ui-components/test/display/natValue/test-stringifyRatio.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import { makeRatio } from '@agoric/zoe/src/contractSupport';
import { makeIssuerKit, MathKind } from '@agoric/ertp';

import { assert, details as X } from '@agoric/assert';
import { stringifyRatioAsFraction } from '../../../src/display/natValue/stringifyRatioAsFraction';
import { stringifyRatio } from '../../../src/display/natValue/stringifyRatio';
import { stringifyRatioNumerator } from '../../../src/display/natValue/stringifyRatioNumerator';
import { stringifyRatioAsPercent } from '../../../src/display/natValue/stringifyRatioAsPercent';

test('stringifyRatio dollars for one eth', t => {
// 1 dollar is 100 cents, or 2 decimal points to the right
Expand Down Expand Up @@ -35,25 +36,70 @@ test('stringifyRatio dollars for one eth', t => {
),
);

const options = {
numDecimalPlaces: 2,
denomDecimalPlaces: 18,
denomPlacesToShow: 0,
const getDecimalPlaces = brand => {
if (ethKit.brand === brand) {
return 18;
}
if (dollarKit.brand === brand) {
return 2;
}
assert.fail(X`brand ${brand} was not recognized`);
};

t.is(stringifyRatio(ethPrice, options), '1587.24 / 1');
t.is(stringifyRatioNumerator(ethPrice, options), '1587.24');
t.is(
stringifyRatioAsFraction(ethPrice, getDecimalPlaces, undefined, 0),
'1587.24 / 1',
);

t.is(stringifyRatioAsFraction(ethPrice, getDecimalPlaces), '1587.24 / 1.00');
t.is(stringifyRatio(ethPrice, getDecimalPlaces), '1587.24');
});

test('stringifyApproxRatio - marketPrice for ETH in RUN', t => {
// RUN has 6 decimalPlaces
// 1 eth is 10^18 wei, or 18 decimal points to the right

// $1,587.24 for 1 ETH

// denominator: {brand: Alleged: ETH brand, value: 1000000000000000000n}
// numerator: {brand: Alleged: Scones brand, value: 1909113516n}

const RUNKit = makeIssuerKit(
'RUN',
MathKind.NAT,
harden({ decimalPlaces: 6 }),
);

const ethKit = makeIssuerKit(
'ETH',
MathKind.NAT,
harden({ decimalPlaces: 18 }),
);

const ethPrice = harden(
makeRatio(
1909113516n, // value of 1 eth in smallest RUN denomination
RUNKit.brand,
10n ** 18n,
ethKit.brand,
),
);

const getDecimalPlaces = brand => {
if (ethKit.brand === brand) {
return 18;
}
if (dollarKit.brand === brand) {
return 2;
if (RUNKit.brand === brand) {
return 6;
}
assert.fail(X`brand ${brand} was not recognized`);
};

t.is(stringifyRatio(ethPrice, { getDecimalPlaces }), '1587.24 / 1.00');
t.is(stringifyRatioNumerator(ethPrice, { getDecimalPlaces }), '1587.24');
t.is(stringifyRatio(ethPrice, getDecimalPlaces), '1909.11');

t.is(stringifyRatio(ethPrice, getDecimalPlaces, 4), '1909.1135');

t.is(stringifyRatioAsPercent(ethPrice, getDecimalPlaces), '190911.35');

t.is(stringifyRatioAsPercent(ethPrice, getDecimalPlaces, 1), '190911.3');
});

0 comments on commit 6751d85

Please sign in to comment.