UNPKG

8.69 kBJavaScriptView Raw
1/*
2# Why starts with uppercase?
3 - emphasis there are kind of type
4 - less names conflict
5
6# Why use lambda?
7 - should be pure functions
8 */
9
10const lodash = require('lodash');
11const BigNumber = require('bignumber.js');
12
13BigNumber.config({
14 EXPONENTIAL_AT: 1e9,
15 ROUNDING_MODE: BigNumber.ROUND_HALF_UP,
16});
17
18// ----------------------------------- Hex ------------------------------------
19/**
20 * Hex formatter, trans value to hex string
21 *
22 * @memberOf type
23 * @param value {string|number|Buffer|Date|BigNumber|null} - The value to gen hex string.
24 * @return {string} Hex string.
25 *
26 * @example
27 * > Hex(null)
28 "0x"
29 * > Hex(1) // also BigNumber
30 "0x01"
31 * > Hex('10') // from naked hex string
32 "0x10"
33 * > Hex('0x1') // pad prefix 0 auto
34 "0x01"
35 * > Hex(Buffer.from([1, 2]))
36 "0x0102"
37 */
38const Hex = value => {
39 if (value === null) {
40 return '0x';
41 }
42
43 if (lodash.isNumber(value) || BigNumber.isBigNumber(value)) {
44 return Hex(value.toString(16));
45 }
46
47 if (lodash.isString(value)) {
48 if (Hex.isHex(value)) { // In order not to copy hex string in most case.
49 return value;
50 }
51
52 let string = value.toLowerCase();
53 string = string.startsWith('0x') ? string : `0x${string}`;
54 string = string.length % 2 ? `0x0${string.substring(2)}` : string;
55 if (!Hex.isHex(string)) {
56 throw new Error(`"${value}" do not match hex string`);
57 }
58 return string;
59 }
60
61 if (Buffer.isBuffer(value)) {
62 return `0x${value.toString('hex')}`;
63 }
64
65 if (lodash.isDate(value)) {
66 return Hex(value.valueOf());
67 }
68
69 return Hex(`${value}`); // for magic method `toString`
70};
71
72/**
73 * Check if is hex string.
74 *
75 * > Hex: /^0x([0-9a-f][0-9a-f])*$/
76 *
77 * @param hex {string} - Value to be check.
78 * @return {boolean}
79 *
80 * @example
81 * > Hex.isHex('0x')
82 true
83 * > Hex.isHex('0x01')
84 true
85 * > Hex.isHex('0x1')
86 false
87 * > Hex.isHex('01')
88 false
89 */
90Hex.isHex = hex => {
91 return /^0x([0-9a-f][0-9a-f])*$/.test(hex);
92};
93
94/**
95 * Get hex string from number.
96 *
97 * @param value {number|BigNumber|string}
98 * @return {string}
99 *
100 * @example
101 * > Hex.fromNumber('10')
102 "0x0a"
103 * > Hex('10')
104 "0x10"
105 */
106Hex.fromNumber = value => {
107 return Hex(lodash.isNumber(value) ? value : BigNumber(value));
108};
109
110/**
111 * Get `Buffer` by `Hex` string.
112 *
113 * > NOTE: It's importance to only support `Hex` string, cause `Transaction.encode` will not check hex again.
114 *
115 * @param hex {string} - The hex string.
116 * @return {Buffer}
117 *
118 * @example
119 * > Hex.toBuffer('0x0102')
120 <Buffer 01 02>
121 */
122Hex.toBuffer = hex => {
123 if (!Hex.isHex(hex)) {
124 throw new Error(`"${hex}" do not match hex string`);
125 }
126
127 return Buffer.from(hex.substring(2), 'hex');
128};
129
130/**
131 * Concat `Hex` string by order.
132 *
133 * @param values {array} - Array of hex string
134 * @return {string}
135 *
136 * @example
137 * > Hex.concat('0x01', '0x02', '0x0304')
138 "0x01020304"
139 * > Hex.concat()
140 "0x"
141 */
142Hex.concat = (...values) => {
143 values.forEach((value, index) => {
144 if (!Hex.isHex(value)) {
145 throw new Error(`values[${index}] do not match hex string, got "${value}"`);
146 }
147 });
148
149 return `0x${values.map(v => Hex(v).substring(2)).join('')}`;
150};
151
152// ----------------------------------- Address --------------------------------
153/**
154 * Get and validate `Address` from value
155 *
156 * @memberOf type
157 * @param value {string|number|Buffer|BigNumber}
158 * @return {string}
159 *
160 * @example
161 * > Address('0123456789012345678901234567890123456789')
162 "0x0123456789012345678901234567890123456789"
163 */
164const Address = value => {
165 const hex = Hex(value);
166 if (hex.length !== 2 + 2 * 20) {
167 throw new Error(`${value} do not match Address`);
168 }
169 return hex;
170};
171
172// ------------------------------------ Hash ----------------------------------
173/**
174 *
175 * @memberOf type
176 * @param value {string|number|Buffer|BigNumber}
177 * @return {string}
178 *
179 * @example
180 * > Hex32('0123456789012345678901234567890123456789012345678901234567890123')
181 "0x0123456789012345678901234567890123456789012345678901234567890123"
182 */
183const Hex32 = value => {
184 const hex = Hex(value);
185 if (hex.length !== 2 + 2 * 32) {
186 throw new Error(`${value} do not match Hex32`);
187 }
188 return hex;
189};
190
191// ---------------------------------- PrivateKey ------------------------------
192/**
193 * Get and validate `PrivateKey` from value.
194 *
195 * > same as `Hex32` in coincidence
196 *
197 * @memberOf type
198 * @param value {string|number|Buffer|BigNumber}
199 * @return {string}
200 */
201const PrivateKey = value => {
202 try {
203 return Hex32(value);
204 } catch (e) {
205 throw new Error(`${value} do not match PrivateKey`);
206 }
207};
208
209// ----------------------------------- BlockHash ------------------------------
210/**
211 * Get and validate `BlockHash` from value
212 *
213 * > same as `Hex32` in coincidence
214 *
215 * @memberOf type
216 * @param value {string|number|Buffer|BigNumber}
217 * @return {string}
218 */
219const BlockHash = value => {
220 try {
221 return Hex32(value);
222 } catch (e) {
223 throw new Error(`${value} do not match BlockHash`);
224 }
225};
226
227// ----------------------------------- TxHash ---------------------------------
228/**
229 * Get and validate `TxHash` from value
230 *
231 * > same as `Hex32` in coincidence
232 *
233 * @memberOf type
234 * @param value {string|number|Buffer|BigNumber}
235 * @return {string}
236 *
237 * @example
238 * > TxHash('0123456789012345678901234567890123456789012345678901234567890123')
239 "0x0123456789012345678901234567890123456789012345678901234567890123"
240 */
241const TxHash = value => {
242 try {
243 return Hex32(value);
244 } catch (e) {
245 throw new Error(`${value} do not match TxHash`);
246 }
247};
248
249// ---------------------------------- Drip ------------------------------------
250/**
251 * @memberOf type
252 * @param value {string|number|Buffer|BigNumber}
253 * @return {string}
254 *
255 * @example
256 * > Drip(1)
257 "0x01"
258
259 * @example
260 * > Drip.toGDrip(Drip.fromCFX(1));
261 "1000000000"
262 */
263const Drip = value => {
264 return Hex.fromNumber(value);
265};
266
267/**
268 * Get Drip hex string by GDrip value.
269 *
270 * > NOTE: Rounds towards nearest neighbour. If equidistant, rounds towards zero.
271 *
272 * @param value {string|number|BigNumber} - Value in GDrip.
273 * @return {string} Hex string in drip.
274 *
275 * @example
276 * > Drip.fromGDrip(1)
277 "0x3b9aca00"
278 * > Drip.fromGDrip(0.1)
279 "0x05f5e100"
280 */
281Drip.fromGDrip = value => {
282 const number = BigNumber(value).times(1e9);
283 return Drip(number.integerValue());
284};
285
286/**
287 * Get Drip hex string by CFX value.
288 *
289 * > NOTE: Rounds towards nearest neighbour. If equidistant, rounds towards zero.
290 *
291 * @param value {string|number|BigNumber} - Value in CFX.
292 * @return {string} Hex string in drip.
293 *
294 * @example
295 * > Drip.fromCFX(1)
296 "0x0de0b6b3a7640000"
297 * > Drip.fromCFX(0.1)
298 "0x016345785d8a0000"
299 */
300Drip.fromCFX = value => {
301 const number = BigNumber(value).times(1e9).times(1e9); // XXX: 1e18 > Number.MAX_SAFE_INTEGER > 1e9
302 return Drip(number.integerValue());
303};
304
305/**
306 * Get `GDrip` from Drip.
307 *
308 * @param value {string|number|BigNumber}
309 * @return {BigNumber}
310 *
311 * @example
312 * > Drip.toGDrip(1e9)
313 "1"
314 * > Drip.toGDrip(Drip.fromCFX(1))
315 "1000000000"
316 */
317Drip.toGDrip = value => {
318 return BigNumber(value).div(1e9);
319};
320
321/**
322 * Get `CFX` from Drip.
323 *
324 * @param value {string|number|BigNumber}
325 * @return {BigNumber}
326 *
327 * @example
328 * > Drip.toCFX(1e18)
329 "1"
330 * > Drip.toCFX(Drip.fromGDrip(1e9))
331 "1"
332 */
333Drip.toCFX = value => {
334 return BigNumber(value).div(1e9).div(1e9); // XXX: 1e18 > Number.MAX_SAFE_INTEGER > 1e9
335};
336
337// ------------------------------- EpochNumber --------------------------------
338/**
339 * Get and validate `EpochNumber` from value
340 *
341 * @memberOf type
342 * @param value {string|number|Buffer|BigNumber}
343 * @return {string}
344 *
345 * @example
346 * > EpochNumber(0)
347 "0x00"
348 * > EpochNumber('100')
349 "0x64"
350 * > EpochNumber('LATEST_STATE')
351 "latest_state"
352 */
353const EpochNumber = value => {
354 if (lodash.isString(value)) {
355 value = value.toLowerCase();
356 }
357
358 if ([EpochNumber.LATEST_STATE, EpochNumber.LATEST_MINED].includes(value)) {
359 return value;
360 }
361
362 return Hex.fromNumber(value);
363};
364
365/**
366 * The latest epochNumber where the latest block with an executed state in.
367 *
368 * @var {string}
369 */
370EpochNumber.LATEST_STATE = 'latest_state';
371
372/**
373 * The latest epochNumber where the latest mined block in.
374 *
375 * @var {string}
376 */
377EpochNumber.LATEST_MINED = 'latest_mined';
378
379module.exports = {
380 Hex,
381 Address,
382 Hex32,
383 PrivateKey,
384 BlockHash,
385 TxHash,
386 Drip,
387 EpochNumber,
388};