Home Reference Source

packages/oo7-parity/src/utils/index.js

  1. // (C) Copyright 2016-2017 Parity Technologies (UK) Ltd.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14.  
  15. /* global parity */
  16. const BigNumber = require('bignumber.js');
  17. const oo7 = require('oo7');
  18. const ParityApi = require('@parity/api');
  19.  
  20. const asciiToHex = ParityApi.util.asciiToHex;
  21. const bytesToHex = ParityApi.util.bytesToHex;
  22. const hexToAscii = ParityApi.util.hexToAscii;
  23. const isAddressValid = h => oo7.Bond.instanceOf(h) ? h.map(ParityApi.util.isAddressValid) : ParityApi.util.isAddressValid(h);
  24. const toChecksumAddress = h => oo7.Bond.instanceOf(h) ? h.map(ParityApi.util.toChecksumAddress) : ParityApi.util.toChecksumAddress(h);
  25. const sha3 = h => oo7.Bond.instanceOf(h) ? h.map(ParityApi.util.sha3) : ParityApi.util.sha3(h);
  26.  
  27. const denominations = [ 'wei', 'Kwei', 'Mwei', 'Gwei', 'szabo', 'finney', 'ether', 'grand', 'Mether', 'Gether', 'Tether', 'Pether', 'Eether', 'Zether', 'Yether', 'Nether', 'Dether', 'Vether', 'Uether' ];
  28.  
  29. // Parity Utilities
  30. // TODO: move to parity.js, repackage or repot.
  31.  
  32. /**
  33. * Capitalizes the first letter of a string
  34. *
  35. * @param {string} s
  36. * @returns {string}
  37. */
  38. function capitalizeFirstLetter (s) {
  39. return s.charAt(0).toUpperCase() + s.slice(1);
  40. }
  41.  
  42. /**
  43. * Wrap `f` in a function that ensures it's called at most once.
  44. * The value returned from `f` is memoized and returned for all subsequent calls.
  45. *
  46. * @param {F} f
  47. * @returns {function(): F}
  48. */
  49. function singleton (f) {
  50. var instance = null;
  51. return function () {
  52. if (instance === null) { instance = f(); }
  53. return instance;
  54. };
  55. }
  56.  
  57. /**
  58. * Returns a {@link BigNumber} multiplier for give string denominator
  59. *
  60. * @param {string} denominator denominator (wei, eth, finney, Gwei, etc)
  61. * @returns {BigNumber} multiplier
  62. */
  63. function denominationMultiplier (s) {
  64. let i = denominations.indexOf(s);
  65. if (i < 0) { throw new Error('Invalid denomination'); }
  66. return (new BigNumber(1000)).pow(i);
  67. }
  68.  
  69. function interpretRender (s, defaultDenom = 6) {
  70. try {
  71. let m = s.toLowerCase().match(/([0-9,]+)(\.([0-9]*))? *([a-zA-Z]+)?/);
  72. let di = m[4] ? denominations.indexOf(m[4]) : defaultDenom;
  73. if (di === -1) {
  74. return null;
  75. }
  76. let n = (m[1].replace(',', '').replace(/^0*/, '')) || '0';
  77. let d = (m[3] || '').replace(/0*$/, '');
  78. return { denom: di, units: n, decimals: d, origNum: m[1] + (m[2] || ''), origDenom: m[4] || '' };
  79. } catch (e) {
  80. return null;
  81. }
  82. }
  83.  
  84. function combineValue (v) {
  85. let d = (new BigNumber(1000)).pow(v.denom);
  86. let n = v.units;
  87. if (v.decimals) {
  88. n += v.decimals;
  89. d = d.div((new BigNumber(10)).pow(v.decimals.length));
  90. }
  91. return new BigNumber(n).mul(d);
  92. }
  93.  
  94. /**
  95. * Add missing denominator to the value
  96. *
  97. * @param {BigNumber} v value
  98. * @param {string} d denominator
  99. * @returns {Value}
  100. */
  101. function defDenom (v, d) {
  102. if (v.denom === null) {
  103. v.denom = d;
  104. }
  105. return v;
  106. }
  107.  
  108. /**
  109. * Formats a value with denominator
  110. *
  111. * @param {Value} n value with denominator
  112. * @returns {string}
  113. */
  114. function formatValue (n) {
  115. return `${formatValueNoDenom(n)} ${denominations[n.denom]}`;
  116. }
  117.  
  118. /**
  119. * Format value without denominator
  120. * @param {Value} v
  121. * @returns {string}
  122. */
  123. function formatValueNoDenom (n) {
  124. return `${n.units.toString().replace(/(\d)(?=(\d{3})+$)/g, '$1,')}${n.decimals ? '.' + n.decimals : ''}`;
  125. }
  126.  
  127. /**
  128. * Format value without denominator
  129. *
  130. * @param {number|BigNumber} v
  131. * @param {number| exponent
  132. * @returns {string}
  133. */
  134. function formatToExponential (v, n = 4) {
  135. return new BigNumber(v).toExponential(n);
  136. }
  137.  
  138. function interpretQuantity (s) {
  139. try {
  140. let m = s.toLowerCase().match(/([0-9,]+)(\.([0-9]*))? *([a-zA-Z]+)?/);
  141. let d = denominationMultiplier(m[4] || 'ether');
  142. let n = +m[1].replace(',', '');
  143. if (m[2]) {
  144. n += m[3];
  145. for (let i = 0; i < m[3].length; ++i) {
  146. d = d.div(10);
  147. }
  148. }
  149. return new BigNumber(n).mul(d);
  150. } catch (e) {
  151. return null;
  152. }
  153. }
  154.  
  155. /**
  156. * Split value into base and denominator
  157. *
  158. * @param {number|BigNumber} a
  159. * @returns {Value}
  160. */
  161. function splitValue (a) {
  162. var i = 0;
  163. a = new BigNumber('' + a);
  164. if (a.gte(new BigNumber('10000000000000000')) && a.lt(new BigNumber('100000000000000000000000'))) {
  165. i = 6;
  166. } else {
  167. for (var aa = a; aa.gte(1000) && i < denominations.length - 1; aa = aa.div(1000)) { i++; }
  168. }
  169.  
  170. for (var j = 0; j < i; ++j) { a = a.div(1000); }
  171.  
  172. return { base: a, denom: i };
  173. }
  174.  
  175. /**
  176. * Display balance into human-readable format with denomnator
  177. *
  178. * @param {string|BigNumber} balance
  179. * @returns {string}
  180. */
  181. function formatBalance (n) {
  182. let a = splitValue(n);
  183. // let b = Math.floor(a.base * 1000) / 1000;
  184. return `${a.base} ${denominations[a.denom]}`;
  185. }
  186.  
  187. /**
  188. * Format block number into human-readable representation.
  189. * @param {string|number|BigNumber} blockNumber
  190. * @returns {string}
  191. */
  192. function formatBlockNumber (n) {
  193. return '#' + ('' + n).replace(/(\d)(?=(\d{3})+$)/g, '$1,');
  194. }
  195.  
  196. function isNullData (a) {
  197. return !a || typeof (a) !== 'string' || a.match(/^(0x)?0+$/) !== null;
  198. }
  199.  
  200. function splitSignature (sig) {
  201. if ((sig.substr(2, 2) === '1b' || sig.substr(2, 2) === '1c') && (sig.substr(66, 2) !== '1b' && sig.substr(66, 2) !== '1c')) {
  202. // vrs
  203. return [sig.substr(0, 4), `0x${sig.substr(4, 64)}`, `0x${sig.substr(68, 64)}`];
  204. } else {
  205. // rsv
  206. return [`0x${sig.substr(130, 2)}`, `0x${sig.substr(2, 64)}`, `0x${sig.substr(66, 64)}`];
  207. }
  208. }
  209.  
  210. function removeSigningPrefix (message) {
  211. if (!message.startsWith('\x19Ethereum Signed Message:\n')) {
  212. throw new Error('Invalid message - doesn\'t contain security prefix');
  213. }
  214. for (var i = 1; i < 6; ++i) {
  215. if (message.length === 26 + i + +message.substr(26, i)) {
  216. return message.substr(26 + i);
  217. }
  218. }
  219. throw new Error('Invalid message - invalid security prefix');
  220. }
  221.  
  222. function cleanup (value, type = 'bytes32', api = parity.api) {
  223. // TODO: make work with arbitrary depth arrays
  224. if (value instanceof Array && type.match(/bytes[0-9]+/)) {
  225. // figure out if it's an ASCII string hiding in there:
  226. var ascii = '';
  227. for (var i = 0, ended = false; i < value.length && ascii !== null; ++i) {
  228. if (value[i] === 0) {
  229. ended = true;
  230. } else {
  231. ascii += String.fromCharCode(value[i]);
  232. }
  233. if ((ended && value[i] !== 0) || (!ended && (value[i] < 32 || value[i] >= 128))) {
  234. ascii = null;
  235. }
  236. }
  237. value = ascii === null ? '0x' + value.map(n => ('0' + n.toString(16)).slice(-2)).join('') : ascii;
  238. }
  239. if (type.substr(0, 4) === 'uint' && +type.substr(4) <= 48) {
  240. value = +value;
  241. }
  242. return value;
  243. }
  244.  
  245. module.exports = {
  246. asciiToHex,
  247. bytesToHex,
  248. hexToAscii,
  249. isAddressValid,
  250. toChecksumAddress,
  251. sha3,
  252. capitalizeFirstLetter,
  253. singleton,
  254. denominations,
  255. denominationMultiplier,
  256. interpretRender,
  257. combineValue,
  258. defDenom,
  259. formatValue,
  260. formatValueNoDenom,
  261. formatToExponential,
  262. interpretQuantity,
  263. splitValue,
  264. formatBalance,
  265. formatBlockNumber,
  266. isNullData,
  267. splitSignature,
  268. removeSigningPrefix,
  269. cleanup
  270. };