UNPKG

15.7 kBJavaScriptView Raw
1const JSBI = require('jsbi');
2const Big = require('big.js');
3const lodash = require('lodash');
4const CONST = require('../CONST');
5const parser = require('./parser');
6const sign = require('./sign');
7
8// ----------------------------------------------------------------------------
9function toHex(value) {
10 let hex;
11
12 if (lodash.isString(value)) {
13 hex = value.toLowerCase(); // XXX: lower case for support checksum address
14 } else if (Number.isInteger(value) || (value instanceof JSBI)) {
15 hex = `0x${value.toString(16)}`;
16 } else if (Buffer.isBuffer(value)) {
17 hex = `0x${value.toString('hex')}`;
18 } else if (lodash.isBoolean(value)) {
19 hex = value ? '0x01' : '0x00';
20 } else if (value === null) {
21 hex = '0x';
22 } else {
23 hex = `${value}`;
24 }
25
26 if (!/^0x[0-9a-f]*$/.test(hex)) {
27 throw new Error(`${value} not match hex`);
28 }
29 return hex.length % 2 ? `0x0${hex.slice(2)}` : hex;
30}
31
32function toNumber(value) {
33 if (value === null) {
34 throw new Error(`${value} not match number`);
35 } else if (Buffer.isBuffer(value)) {
36 value = `0x${value.toString('hex')}`;
37 }
38 return Number(value);
39}
40
41function toBigInt(value) {
42 if (Number.isInteger(value) || (value instanceof JSBI)) {
43 return JSBI.BigInt(value);
44 }
45 if (lodash.isBoolean(value)) {
46 throw new Error(`${value} not match BigInt`);
47 }
48 if (Buffer.isBuffer(value)) {
49 throw new Error(`${value} not match BigInt`);
50 }
51
52 value = `${value}`.replace(/^(-?\d+)(.0+)?$/, '$1'); // replace "number.000" to "number"
53 return JSBI.BigInt(value);
54}
55
56function toBig(value) {
57 if (/^0[xob]/i.test(value)) {
58 value = JSBI.BigInt(value);
59 }
60 return new Big(value);
61}
62
63// ----------------------------------------------------------------------------
64const format = {};
65
66/**
67 * @param arg {any}
68 * @return {any} arg
69 *
70 * @example
71 * > format.any(1)
72 1
73 */
74format.any = parser(v => v);
75
76/**
77 * @param arg {number|JSBI|string|boolean}
78 * @return {Number}
79 *
80 * @example
81 * > format.uInt(-3.14)
82 Error("cannot be converted to a JSBI")
83 * > format.uInt(null)
84 Error("Cannot convert null to a JSBI")
85 * > format.uInt('0')
86 0
87 * > format.uInt(1)
88 1
89 * > format.uInt(JSBI(100))
90 100
91 * > format.uInt('0x10')
92 16
93 * > format.uInt('')
94 0
95 * > format.uInt(true)
96 1
97 * > format.uInt(false)
98 0
99 * > format.uInt(Number.MAX_SAFE_INTEGER + 1) // unsafe integer
100 Error("not match uint")
101 */
102format.uInt = parser(toNumber).$validate(v => Number.isSafeInteger(v) && v >= 0, 'uint');
103
104/**
105 * @param arg {number|string|JSBI}
106 * @return {JSBI}
107 *
108 * @example
109 * > format.bigInt(-3.14)
110 Error("not match bigInt")
111 * > format.bigInt('0.0')
112 JSBI.BigInt(0)
113 * > format.bigInt('-1')
114 JSBI.BigInt(-1)
115 * > format.bigInt(1)
116 JSBI.BigInt(1)
117 * > format.bigInt(JSBI.BigInt(100))
118 JSBI.BigInt(100)
119 * > format.bigInt('0x10')
120 JSBI.BigInt(16)
121 * > format.bigInt(Number.MAX_SAFE_INTEGER + 1) // unsafe integer
122 Error("not match uint")
123 */
124format.bigInt = parser(toBigInt);
125
126/**
127 * @param arg {number|string|JSBI}
128 * @return {JSBI}
129 *
130 * @example
131 * > format.bigUInt('0.0')
132 JSBI.BigInt(0)
133 * > format.bigUInt('-1')
134 Error("not match bigUInt")
135 */
136format.bigUInt = format.bigInt.$validate(v => v >= 0, 'bigUInt');
137
138/**
139 * When encoding QUANTITIES (integers, numbers): encode as hex, prefix with "0x", the most compact representation (slight exception: zero should be represented as "0x0")
140 *
141 * @param arg {number|string|JSBI}
142 * @return {string} Hex string
143 *
144 * @example
145 * > format.bigUIntHex(100)
146 "0x64"
147 * > format.bigUIntHex('0x0a')
148 "0xa"
149 * > format.bigUIntHex(-1))
150 Error("not match uintHex")
151 */
152format.bigUIntHex = format.bigUInt.$after(v => `0x${v.toString(16)}`);
153
154/**
155 * @param arg {number|string|JSBI}
156 * @return {Big} Big instance
157 *
158 * @example
159 * > format.big('0b10').toString()
160 '2'
161 * > format.big('0O10').toString()
162 '8'
163 * > format.big('010').toString()
164 '10'
165 * > format.big('0x10').toString()
166 '16'
167 * > format.big(3.14).toString()
168 '3.14'
169 * > format.big('-03.140').toString()
170 '-3.14'
171 * > format.big(null)
172 Error('Invalid number')
173 */
174format.big = parser(toBig);
175
176/**
177 * @param arg {string|number|JSBI|Big}
178 * @return {Number}
179 *
180 * @example
181 * > format.fixed64('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff')
182 1
183 * > format.fixed64('0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff')
184 0.5
185 */
186format.fixed64 = format.big.$after(v => Number(v.div(CONST.MAX_UINT)));
187
188/**
189 * @param arg {number|string} - number or string
190 * @return {string}
191 *
192 * @example
193 * > format.epochNumber(10)
194 "0xa"
195 * > format.epochNumber(EPOCH_NUMBER.LATEST_STATE)
196 "latest_state"
197 * > format.epochNumber('latest_mined')
198 "latest_mined"
199 */
200format.epochNumber = format.bigUIntHex
201 .$or(CONST.EPOCH_NUMBER.LATEST_MINED)
202 .$or(CONST.EPOCH_NUMBER.LATEST_STATE)
203 .$or(CONST.EPOCH_NUMBER.LATEST_CONFIRMED)
204 .$or(CONST.EPOCH_NUMBER.LATEST_CHECKPOINT)
205 .$or(CONST.EPOCH_NUMBER.EARLIEST);
206
207/**
208 * When encoding UNFORMATTED DATA (byte arrays, account addresses, hashes, bytecode arrays): encode as hex, prefix with "0x", two hex digits per byte.
209 *
210 * @param arg {number|JSBI|string|Buffer|boolean|null}
211 * @return {string} Hex string
212 *
213 * @example
214 * > format.hex(null)
215 '0x'
216 * > format.hex(1)
217 "0x01"
218 * > format.hex(256)
219 "0x0100"
220 * > format.hex(true)
221 "0x01"
222 * > format.hex(Buffer.from([1,10,255]))
223 "0x010aff"
224 * > format.hex("0x0a")
225 "0x0a"
226 */
227format.hex = parser(toHex);
228
229format.hex40 = format.hex.$validate(v => v.length === 2 + 40, 'hex40');
230
231/**
232 * Checks if a given string is a valid address.
233 * It will also check the checksum, if the address has upper and lowercase letters.
234 *
235 * @param arg {string|Buffer}
236 * @return {string} Hex string
237 *
238 * @example
239 * > format.address('0x0123456789012345678901234567890123456789')
240 "0x0123456789012345678901234567890123456789"
241 * > format.address('0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef')
242 Error("not match address")
243 */
244format.address = format.hex40.$before(address => {
245 if (lodash.isString(address)
246 && address !== address.toLowerCase()
247 && address !== address.toUpperCase()
248 && address !== sign.checksumAddress(address)
249 ) {
250 throw new Error(`address "${address}" checksum error`);
251 }
252 return address;
253});
254
255/**
256 * Will convert an upper or lowercase address to a checksum address.
257 *
258 * @param arg {string|Buffer}
259 * @return {string} Checksum address hex string
260 *
261 * @example
262 * > format.checksumAddress('0x1b716c51381e76900ebaa7999a488511a4e1fd0a')
263 "0x1B716c51381e76900EBAA7999A488511A4E1fD0a"
264 * > format.checksumAddress('0X1B716C51381E76900EBAA7999A488511A4E1FD0A')
265 "0x1B716c51381e76900EBAA7999A488511A4E1fD0a"
266 * > format.checksumAddress('0x1B716c51381e76900EBAA7999A488511A4E1fD0A')
267 "0x1B716c51381e76900EBAA7999A488511A4E1fD0a"
268 */
269format.checksumAddress = format.hex40.$after(sign.checksumAddress);
270
271format.hex64 = format.hex.$validate(v => v.length === 2 + 64, 'hex64');
272
273/**
274 * @param arg {string|Buffer}
275 * @return {string} Hex string
276 *
277 * @example
278 * > format.privateKey('0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef')
279 "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
280 * > format.privateKey('0x0123456789012345678901234567890123456789')
281 Error("not match hex64")
282 */
283format.blockHash = format.hex64; // alias
284
285/**
286 * @param arg {string|Buffer}
287 * @return {string} Hex string
288 *
289 * @example
290 * > format.privateKey('0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef')
291 "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
292 * > format.privateKey('0x0123456789012345678901234567890123456789')
293 Error("not match hex64")
294 */
295format.transactionHash = format.hex64; // alias
296
297/**
298 * @param arg {string|Buffer}
299 * @return {string} Hex string
300 *
301 * @example
302 * > format.privateKey('0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef')
303 "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
304 * > format.privateKey('0x0123456789012345678901234567890123456789')
305 Error("not match hex64")
306 */
307format.privateKey = format.hex64; // alias
308
309/**
310 * @param arg {string|Buffer}
311 * @return {string} Hex string
312 *
313 * @example
314 * > format.publicKey('0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef')
315 "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
316 * > format.publicKey('0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef')
317 Error("not match publicKey")
318 */
319format.publicKey = format.hex.$validate(v => v.length === 2 + 128, 'publicKey');
320
321/**
322 * @param arg {number|string|JSBI|Buffer|boolean|null}
323 * @return {Buffer}
324 *
325 * @example
326 * > format.hexBuffer(Buffer.from([0, 1]))
327 <Buffer 00 01>
328 * > format.hexBuffer(null)
329 <Buffer >
330 * > format.hexBuffer(1024)
331 <Buffer 04 00>
332 * > format.hexBuffer('0x0a')
333 <Buffer 0a>
334 * > format.hexBuffer(true)
335 <Buffer 01>
336 * > format.hexBuffer(3.14)
337 Error("not match hex")
338 */
339format.hexBuffer = format.hex.$after(v => Buffer.from(v.substr(2), 'hex'));
340
341/**
342 * @param arg {string|Buffer|array}
343 * @return {Buffer}
344 *
345 * @example
346 * > format.bytes('abcd')
347 <Buffer 61 62 63 64>
348 * > format.bytes([0, 1])
349 <Buffer 00 01>
350 * > format.bytes(Buffer.from([0, 1]))
351 <Buffer 00 01>
352 */
353format.bytes = parser(v => (Buffer.isBuffer(v) ? v : Buffer.from(v)));
354
355/**
356 * @param arg {boolean}
357 * @return {boolean}
358 *
359 * @example
360 * > format.boolean(true)
361 true
362 * > format.boolean(false)
363 false
364 */
365format.boolean = format.any.$validate(lodash.isBoolean, 'boolean');
366
367/**
368 * Compute the keccak256 cryptographic hash of a value, returned as a hex string.
369 *
370 * @param arg {string|Buffer}
371 * @return {string}
372 *
373 * @example
374 * > format.keccak256('Transfer(address,address,uint256)')
375 "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
376
377 * > format.keccak256(Buffer.from([0x42]))
378 "0x1f675bff07515f5df96737194ea945c36c41e7b4fcef307b7cd4d0e602a69111"
379 * > format.keccak256(format.hexBuffer('0x42'))
380 "0x1f675bff07515f5df96737194ea945c36c41e7b4fcef307b7cd4d0e602a69111"
381 * > format.keccak256('0x42') // "0x42" as string and transfer to <Buffer 30 78 34 32> by ascii
382 "0x3c1b2d38851281e9a7b59d10973b0c87c340ff1e76bde7d06bf6b9f28df2b8c0"
383 */
384format.keccak256 = format.bytes.$after(sign.keccak256).$after(format.hex);
385
386// -------------------------- format method arguments -------------------------
387format.getLogs = parser({
388 limit: format.bigUIntHex.$or(undefined),
389 fromEpoch: format.epochNumber.$or(undefined),
390 toEpoch: format.epochNumber.$or(undefined),
391 blockHashes: format.blockHash.$or([format.blockHash]).$or(undefined),
392 address: format.address.$or([format.address]).$or(undefined),
393 topics: parser([format.hex64.$or([format.hex64]).$or(null)]).$or(undefined),
394}, true);
395
396format.signTx = parser({
397 nonce: format.bigUIntHex.$after(format.hexBuffer),
398 gasPrice: format.bigUIntHex.$after(format.hexBuffer),
399 gas: format.bigUIntHex.$after(format.hexBuffer),
400 to: parser(format.address.$or(null).$default(null)).$after(format.hexBuffer),
401 value: format.bigUIntHex.$default(0).$after(format.hexBuffer),
402 storageLimit: format.bigUIntHex.$after(format.hexBuffer),
403 epochHeight: format.uInt.$after(format.hexBuffer),
404 chainId: format.uInt.$default(0).$after(format.hexBuffer),
405 data: format.hex.$default('0x').$after(format.hexBuffer),
406 r: (format.bigUIntHex.$after(format.hexBuffer)).$or(undefined),
407 s: (format.bigUIntHex.$after(format.hexBuffer)).$or(undefined),
408 v: (format.uInt.$after(format.hexBuffer)).$or(undefined),
409}, true);
410
411format.sendTx = parser({
412 from: format.address,
413 nonce: format.bigUIntHex.$or(undefined),
414 gasPrice: format.bigUIntHex,
415 gas: format.bigUIntHex,
416 to: format.address.$or(null).$or(undefined),
417 value: format.bigUIntHex.$or(undefined),
418 storageLimit: format.bigUIntHex,
419 epochHeight: format.bigUIntHex.$or(undefined),
420 chainId: format.bigUIntHex.$or(undefined),
421 data: format.hex.$or(undefined),
422}, true);
423
424format.callTx = parser({
425 from: format.address.$or(undefined),
426 nonce: format.bigUIntHex.$or(undefined),
427 gasPrice: format.bigUIntHex.$or(undefined),
428 gas: format.bigUIntHex.$or(undefined),
429 to: format.address.$or(null),
430 value: format.bigUIntHex.$or(undefined),
431 storageLimit: format.bigUIntHex.$or(undefined),
432 epochHeight: format.uInt.$or(undefined),
433 chainId: format.uInt.$or(undefined),
434 data: format.hex.$or(undefined),
435}, true);
436
437format.estimateTx = parser({
438 from: format.address.$or(undefined),
439 nonce: format.bigUIntHex.$or(undefined),
440 gasPrice: format.bigUIntHex.$or(undefined),
441 gas: format.bigUIntHex.$or(undefined),
442 to: format.address.$or(null).$or(undefined),
443 value: format.bigUIntHex.$or(undefined),
444 storageLimit: format.bigUIntHex.$or(undefined),
445 epochHeight: format.uInt.$or(undefined),
446 chainId: format.uInt.$or(undefined),
447 data: format.hex.$or(undefined),
448}, true);
449
450// ----------------------------- parse rpc returned ---------------------------
451format.status = parser({
452 chainId: format.uInt,
453 epochNumber: format.uInt,
454 blockNumber: format.uInt,
455 pendingTxNumber: format.uInt,
456});
457
458format.account = parser({
459 accumulatedInterestReturn: format.bigUInt,
460 balance: format.bigUInt,
461 collateralForStorage: format.bigUInt,
462 nonce: format.bigUInt,
463 stakingBalance: format.bigUInt,
464});
465
466format.estimate = parser({
467 gasUsed: format.bigUInt,
468 storageCollateralized: format.bigUInt,
469});
470
471format.transaction = parser({
472 nonce: format.bigUInt,
473 gasPrice: format.bigUInt,
474 gas: format.bigUInt,
475 value: format.bigUInt,
476 storageLimit: format.bigUInt,
477 epochHeight: format.uInt,
478 chainId: format.uInt,
479 v: format.uInt,
480 status: format.uInt.$or(null),
481 transactionIndex: format.uInt.$or(null),
482});
483
484format.block = parser({
485 epochNumber: format.uInt.$or(null),
486 blame: format.uInt,
487 height: format.uInt,
488 size: format.uInt,
489 timestamp: format.uInt,
490 gasLimit: format.bigUInt,
491 gasUsed: format.bigUInt.$or(null).$or(undefined), // XXX: undefined before main net upgrade
492 difficulty: format.bigUInt,
493 transactions: [(format.transaction).$or(format.transactionHash)],
494});
495
496format.receipt = parser({
497 index: format.uInt,
498 epochNumber: format.uInt,
499 outcomeStatus: format.uInt.$or(null),
500 gasUsed: format.bigUInt,
501 gasFee: format.bigUInt,
502});
503
504format.log = parser({
505 epochNumber: format.uInt,
506 logIndex: format.uInt,
507 transactionIndex: format.uInt,
508 transactionLogIndex: format.uInt,
509});
510
511format.logs = parser([format.log]);
512
513format.sponsorInfo = parser({
514 sponsorBalanceForCollateral: format.bigUInt,
515 sponsorBalanceForGas: format.bigUInt,
516 sponsorGasBound: format.bigUInt,
517});
518
519format.rewardInfo = parser([
520 {
521 baseReward: format.bigUInt,
522 totalReward: format.bigUInt,
523 txFee: format.bigUInt,
524 },
525]);
526
527// ---------------------------- parse subscribe event -------------------------
528format.head = parser({
529 difficulty: format.bigUInt,
530 epochNumber: format.uInt.$or(null),
531 gasLimit: format.bigUInt,
532 height: format.uInt,
533 timestamp: format.uInt,
534});
535
536format.revert = parser({
537 revertTo: format.uInt,
538});
539
540format.epoch = parser({
541 epochNumber: format.uInt,
542});
543
544module.exports = format;