diff options
author | Chi Kei Chan <chikeichan@gmail.com> | 2019-03-22 07:03:30 +0800 |
---|---|---|
committer | Dan J Miller <danjm.com@gmail.com> | 2019-03-22 07:03:30 +0800 |
commit | 31175625b446cb5d18b17db23018bca8b14d280c (patch) | |
tree | f54e159883deef003fb281267025edf796eb8004 /ui/app/helpers/utils/conversion-util.js | |
parent | 7287133e15fab22299e07704206e85bc855d1064 (diff) | |
download | tangerine-wallet-browser-31175625b446cb5d18b17db23018bca8b14d280c.tar tangerine-wallet-browser-31175625b446cb5d18b17db23018bca8b14d280c.tar.gz tangerine-wallet-browser-31175625b446cb5d18b17db23018bca8b14d280c.tar.bz2 tangerine-wallet-browser-31175625b446cb5d18b17db23018bca8b14d280c.tar.lz tangerine-wallet-browser-31175625b446cb5d18b17db23018bca8b14d280c.tar.xz tangerine-wallet-browser-31175625b446cb5d18b17db23018bca8b14d280c.tar.zst tangerine-wallet-browser-31175625b446cb5d18b17db23018bca8b14d280c.zip |
Folder restructure (#6304)
* Remove ui/app/keychains/
* Remove ui/app/img/ (unused images)
* Move conversion-util to helpers/utils/
* Move token-util to helpers/utils/
* Move /helpers/*.js inside /helpers/utils/
* Move util tests inside /helpers/utils/
* Renameand move confirm-transaction/util.js to helpers/utils/
* Move higher-order-components to helpers/higher-order-components/
* Move infura-conversion.json to helpers/constants/
* Move all utility functions to helpers/utils/
* Move pages directory to top-level
* Move all constants to helpers/constants/
* Move metametrics inside helpers/
* Move app and root inside pages/
* Move routes inside helpers/
* Re-organize ducks/
* Move reducers to ducks/
* Move selectors inside selectors/
* Move test out of test folder
* Move action, reducer, store inside store/
* Move ui components inside ui/
* Move UI components inside ui/
* Move connected components inside components/app/
* Move i18n-helper inside helpers/
* Fix unit tests
* Fix unit test
* Move pages components
* Rename routes component
* Move reducers to ducks/index
* Fix bad path in unit test
Diffstat (limited to 'ui/app/helpers/utils/conversion-util.js')
-rw-r--r-- | ui/app/helpers/utils/conversion-util.js | 251 |
1 files changed, 251 insertions, 0 deletions
diff --git a/ui/app/helpers/utils/conversion-util.js b/ui/app/helpers/utils/conversion-util.js new file mode 100644 index 000000000..8cc531773 --- /dev/null +++ b/ui/app/helpers/utils/conversion-util.js @@ -0,0 +1,251 @@ +/* Currency Conversion Utility +* This utility function can be used for converting currency related values within metamask. +* The caller should be able to pass it a value, along with information about the value's +* numeric base, denomination and currency, and the desired numeric base, denomination and +* currency. It should return a single value. +* +* @param {(number | string | BN)} value The value to convert. +* @param {Object} [options] Options to specify details of the conversion +* @param {string} [options.fromCurrency = 'ETH' | 'USD'] The currency of the passed value +* @param {string} [options.toCurrency = 'ETH' | 'USD'] The desired currency of the result +* @param {string} [options.fromNumericBase = 'hex' | 'dec' | 'BN'] The numeric basic of the passed value. +* @param {string} [options.toNumericBase = 'hex' | 'dec' | 'BN'] The desired numeric basic of the result. +* @param {string} [options.fromDenomination = 'WEI'] The denomination of the passed value +* @param {string} [options.numberOfDecimals] The desired number of decimals in the result +* @param {string} [options.roundDown] The desired number of decimals to round down to +* @param {number} [options.conversionRate] The rate to use to make the fromCurrency -> toCurrency conversion +* @returns {(number | string | BN)} +* +* The utility passes value along with the options as a single object to the `converter` function. +* `converter` uses Ramda.js to apply a composition of conditional setters to the `value` property, depending +* on the accompanying options. Some of these conditional setters are selected via key-value maps, where +* the keys are specified in the options parameters and the values are setter functions. +*/ + +const BigNumber = require('bignumber.js') +const ethUtil = require('ethereumjs-util') +const BN = ethUtil.BN +const R = require('ramda') +const { stripHexPrefix } = require('ethereumjs-util') + +BigNumber.config({ + ROUNDING_MODE: BigNumber.ROUND_HALF_DOWN, +}) + +// Big Number Constants +const BIG_NUMBER_WEI_MULTIPLIER = new BigNumber('1000000000000000000') +const BIG_NUMBER_GWEI_MULTIPLIER = new BigNumber('1000000000') +const BIG_NUMBER_ETH_MULTIPLIER = new BigNumber('1') + +// Individual Setters +const convert = R.invoker(1, 'times') +const round = R.invoker(2, 'round')(R.__, BigNumber.ROUND_HALF_DOWN) +const roundDown = R.invoker(2, 'round')(R.__, BigNumber.ROUND_DOWN) +const invertConversionRate = conversionRate => () => new BigNumber(1.0).div(conversionRate) +const decToBigNumberViaString = n => R.pipe(String, toBigNumber['dec']) + +// Setter Maps +const toBigNumber = { + hex: n => new BigNumber(stripHexPrefix(n), 16), + dec: n => new BigNumber(String(n), 10), + BN: n => new BigNumber(n.toString(16), 16), +} +const toNormalizedDenomination = { + WEI: bigNumber => bigNumber.div(BIG_NUMBER_WEI_MULTIPLIER), + GWEI: bigNumber => bigNumber.div(BIG_NUMBER_GWEI_MULTIPLIER), + ETH: bigNumber => bigNumber.div(BIG_NUMBER_ETH_MULTIPLIER), +} +const toSpecifiedDenomination = { + WEI: bigNumber => bigNumber.times(BIG_NUMBER_WEI_MULTIPLIER).round(), + GWEI: bigNumber => bigNumber.times(BIG_NUMBER_GWEI_MULTIPLIER).round(9), + ETH: bigNumber => bigNumber.times(BIG_NUMBER_ETH_MULTIPLIER).round(9), +} +const baseChange = { + hex: n => n.toString(16), + dec: n => (new BigNumber(n)).toString(10), + BN: n => new BN(n.toString(16)), +} + +// Predicates +const fromAndToCurrencyPropsNotEqual = R.compose( + R.not, + R.eqBy(R.__, 'fromCurrency', 'toCurrency'), + R.flip(R.prop) +) + +// Lens +const valuePropertyLens = R.over(R.lensProp('value')) +const conversionRateLens = R.over(R.lensProp('conversionRate')) + +// conditional conversionRate setting wrapper +const whenPredSetCRWithPropAndSetter = (pred, prop, setter) => R.when( + pred, + R.converge( + conversionRateLens, + [R.pipe(R.prop(prop), setter), R.identity] + ) +) + +// conditional 'value' setting wrappers +const whenPredSetWithPropAndSetter = (pred, prop, setter) => R.when( + pred, + R.converge( + valuePropertyLens, + [R.pipe(R.prop(prop), setter), R.identity] + ) +) +const whenPropApplySetterMap = (prop, setterMap) => whenPredSetWithPropAndSetter( + R.prop(prop), + prop, + R.prop(R.__, setterMap) +) + +// Conversion utility function +const converter = R.pipe( + whenPredSetCRWithPropAndSetter(R.prop('conversionRate'), 'conversionRate', decToBigNumberViaString), + whenPredSetCRWithPropAndSetter(R.prop('invertConversionRate'), 'conversionRate', invertConversionRate), + whenPropApplySetterMap('fromNumericBase', toBigNumber), + whenPropApplySetterMap('fromDenomination', toNormalizedDenomination), + whenPredSetWithPropAndSetter(fromAndToCurrencyPropsNotEqual, 'conversionRate', convert), + whenPropApplySetterMap('toDenomination', toSpecifiedDenomination), + whenPredSetWithPropAndSetter(R.prop('numberOfDecimals'), 'numberOfDecimals', round), + whenPredSetWithPropAndSetter(R.prop('roundDown'), 'roundDown', roundDown), + whenPropApplySetterMap('toNumericBase', baseChange), + R.view(R.lensProp('value')) +) + +const conversionUtil = (value, { + fromCurrency = null, + toCurrency = fromCurrency, + fromNumericBase, + toNumericBase, + fromDenomination, + toDenomination, + numberOfDecimals, + conversionRate, + invertConversionRate, +}) => converter({ + fromCurrency, + toCurrency, + fromNumericBase, + toNumericBase, + fromDenomination, + toDenomination, + numberOfDecimals, + conversionRate, + invertConversionRate, + value: value || '0', +}) + +const addCurrencies = (a, b, options = {}) => { + const { + aBase, + bBase, + ...conversionOptions + } = options + const value = (new BigNumber(a.toString(), aBase)).add(b.toString(), bBase) + + return converter({ + value, + ...conversionOptions, + }) +} + +const subtractCurrencies = (a, b, options = {}) => { + const { + aBase, + bBase, + ...conversionOptions + } = options + const value = (new BigNumber(String(a), aBase)).minus(b, bBase) + + return converter({ + value, + ...conversionOptions, + }) +} + +const multiplyCurrencies = (a, b, options = {}) => { + const { + multiplicandBase, + multiplierBase, + ...conversionOptions + } = options + + const bigNumberA = new BigNumber(String(a), multiplicandBase) + const bigNumberB = new BigNumber(String(b), multiplierBase) + + const value = bigNumberA.times(bigNumberB) + + return converter({ + value, + ...conversionOptions, + }) +} + +const conversionGreaterThan = ( + { ...firstProps }, + { ...secondProps }, +) => { + const firstValue = converter({ ...firstProps }) + const secondValue = converter({ ...secondProps }) + + return firstValue.gt(secondValue) +} + +const conversionLessThan = ( + { ...firstProps }, + { ...secondProps }, +) => { + const firstValue = converter({ ...firstProps }) + const secondValue = converter({ ...secondProps }) + + return firstValue.lt(secondValue) +} + +const conversionMax = ( + { ...firstProps }, + { ...secondProps }, +) => { + const firstIsGreater = conversionGreaterThan( + { ...firstProps }, + { ...secondProps } + ) + + return firstIsGreater ? firstProps.value : secondProps.value +} + +const conversionGTE = ( + { ...firstProps }, + { ...secondProps }, +) => { + const firstValue = converter({ ...firstProps }) + const secondValue = converter({ ...secondProps }) + return firstValue.greaterThanOrEqualTo(secondValue) +} + +const conversionLTE = ( + { ...firstProps }, + { ...secondProps }, +) => { + const firstValue = converter({ ...firstProps }) + const secondValue = converter({ ...secondProps }) + return firstValue.lessThanOrEqualTo(secondValue) +} + +const toNegative = (n, options = {}) => { + return multiplyCurrencies(n, -1, options) +} + +module.exports = { + conversionUtil, + addCurrencies, + multiplyCurrencies, + conversionGreaterThan, + conversionLessThan, + conversionGTE, + conversionLTE, + conversionMax, + toNegative, + subtractCurrencies, +} |