UNPKG

13.1 kBJavaScriptView Raw
1const JSBI = require('jsbi');
2const Big = require('big.js');
3const lodash = require('lodash');
4const parser = require('./parser');
5
6const MAX_UINT = JSBI.BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
7
8// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/BigInt
9JSBI.prototype.toJSON = function () {
10 return this.toString();
11};
12
13// ----------------------------------------------------------------------------
14function toHex(value) {
15 let hex;
16
17 if (lodash.isString(value)) {
18 hex = value.toLowerCase(); // XXX: lower case for support checksum address
19 } else if (Number.isInteger(value) || (value instanceof JSBI)) {
20 hex = `0x${value.toString(16)}`;
21 } else if (Buffer.isBuffer(value)) {
22 hex = `0x${value.toString('hex')}`;
23 } else if (lodash.isBoolean(value)) {
24 hex = value ? '0x01' : '0x00';
25 } else if (value === null) {
26 hex = '0x';
27 } else {
28 hex = `${value}`;
29 }
30
31 if (!/^0x[0-9a-f]*$/.test(hex)) {
32 throw new Error(`${value} not match hex`);
33 }
34 return hex.length % 2 ? `0x0${hex.slice(2)}` : hex;
35}
36
37function toNumber(value) {
38 if (value === null) {
39 throw new Error(`${value} not match number`);
40 } else if (Buffer.isBuffer(value)) {
41 value = `0x${value.toString('hex')}`;
42 }
43 return Number(value);
44}
45
46function toBigInt(value) {
47 if (lodash.isString(value)) {
48 value = value.replace(/^(-?\d+)(.0+)?$/, '$1'); // replace "number.000" to "number"
49 } else if (lodash.isBoolean(value)) {
50 throw new Error(`${value} not match BigInt`);
51 } else if (Buffer.isBuffer(value)) {
52 throw new Error(`${value} not match BigInt`);
53 }
54 return JSBI.BigInt(value);
55}
56
57// ----------------------------------------------------------------------------
58const format = {};
59
60/**
61 * @param arg {any}
62 * @return {any} arg
63 *
64 * @example
65 * > format.any(1)
66 1
67 */
68format.any = parser(v => v);
69
70/**
71 * When encoding UNFORMATTED DATA (byte arrays, account addresses, hashes, bytecode arrays): encode as hex, prefix with "0x", two hex digits per byte.
72 *
73 * @param arg {number|JSBI|string|Buffer|boolean|null}
74 * @return {string} Hex string
75 *
76 * @example
77 * > format.hex(null)
78 '0x'
79 * > format.hex(1)
80 "0x01"
81 * > format.hex(256)
82 "0x0100"
83 * > format.hex(true)
84 "0x01"
85 * > format.hex(Buffer.from([1,10,255]))
86 "0x010aff"
87 * > format.hex("0x0a")
88 "0x0a"
89 */
90format.hex = parser(toHex);
91format.hex64 = format.hex.$validate(v => v.length === 2 + 64, 'hex64');
92
93/**
94 * @param arg {number|JSBI|string|boolean}
95 * @return {Number}
96 *
97 * @example
98 * > format.uInt(-3.14)
99 Error("cannot be converted to a JSBI")
100 * > format.uInt(null)
101 Error("Cannot convert null to a JSBI")
102 * > format.uInt('0')
103 0
104 * > format.uInt(1)
105 1
106 * > format.uInt(JSBI(100))
107 100
108 * > format.uInt('0x10')
109 16
110 * > format.uInt('')
111 0
112 * > format.uInt(true)
113 1
114 * > format.uInt(false)
115 0
116 * > format.uInt(Number.MAX_SAFE_INTEGER + 1) // unsafe integer
117 Error("not match uint")
118 */
119format.uInt = parser(toNumber).$validate(v => Number.isSafeInteger(v) && v >= 0, 'uint');
120
121/**
122 * @param arg {number|JSBI|string|boolean}
123 * @return {JSBI}
124 *
125 * @example
126 * > format.bigInt(-3.14)
127 Error("not match bigInt")
128 * > format.bigInt('0.0')
129 JSBI.BigInt(0)
130 * > format.bigInt('-1')
131 JSBI.BigInt(-1)
132 * > format.bigInt(1)
133 JSBI.BigInt(1)
134 * > format.bigInt(JSBI(100))
135 JSBI.BigInt(100)
136 * > format.bigInt('0x10')
137 JSBI.BigInt(16)
138 * > format.bigInt(Number.MAX_SAFE_INTEGER + 1) // unsafe integer
139 Error("not match uint")
140 */
141format.bigInt = parser(toBigInt);
142
143/**
144 * @param arg {number|JSBI|string|boolean}
145 * @return {JSBI}
146 *
147 * @example
148 * > format.bigUInt('0.0')
149 JSBI.BigInt(0)
150 * > format.bigUInt('-1')
151 Error("not match bigUInt")
152 */
153format.bigUInt = format.bigInt.$validate(v => v >= 0, 'bigUInt');
154
155/**
156 * When encoding QUANTITIES (integers, numbers): encode as hex, prefix with "0x", the most compact representation (slight exception: zero should be represented as "0x0")
157 *
158 * @param arg {number|string|boolean}
159 * @return {string} Hex string
160 *
161 * @example
162 * > format.hexUInt(100)
163 "0x64"
164 * > format.hexUInt(10)
165 "0xa"
166 * > format.hexUInt(3.50)
167 "0x4"
168 * > format.hexUInt(3.49)
169 "0x3"
170 * > format.hexUInt(-1))
171 Error("not match uintHex")
172 */
173format.hexUInt = format.bigUInt
174 .$after(v => `0x${v.toString(16)}`)
175 .$validate(v => /^0x[0-9a-f]+$/.test(v), 'hexUInt');
176
177/**
178 * @param hex {string}
179 * @return {number}
180 *
181 * @example
182 * > format.riskNumber('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff')
183 1
184 * > format.riskNumber('0xe666666666666666666666666666666666666666666666666666666666666665')
185 0.9
186 */
187format.riskNumber = format.bigUInt.$after(v => Number(Big(v).div(MAX_UINT)));
188
189/**
190 * @param arg {number|string} - number or string in ['latest_state', 'latest_mined']
191 * @return {string}
192 *
193 * @example
194 * > format.epochNumber(10)
195 "0xa"
196 * > format.epochNumber('latest_state')
197 "latest_state"
198 * > format.epochNumber('latest_mined')
199 "latest_state"
200 */
201format.epochNumber = format.hexUInt
202 .$or('earliest')
203 .$or('latest_checkpoint')
204 .$or('latest_confirmed')
205 .$or('latest_state')
206 .$or('latest_mined');
207
208/**
209 * @param arg {string|Buffer}
210 * @return {string} Hex string
211 *
212 * @example
213 * > format.address('0x0123456789012345678901234567890123456789')
214 "0x0123456789012345678901234567890123456789"
215 * > format.address('0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef')
216 Error("not match address")
217 */
218format.address = format.hex.$validate(v => v.length === 2 + 40, 'address'); // alias
219
220/**
221 * @param arg {string|Buffer}
222 * @return {string} Hex string
223 *
224 * @example
225 * > format.publicKey('0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef')
226 "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
227 * > format.publicKey('0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef')
228 Error("not match publicKey")
229 */
230format.publicKey = format.hex.$validate(v => v.length === 2 + 128, 'publicKey');
231
232/**
233 * @param arg {string|Buffer}
234 * @return {string} Hex string
235 *
236 * @example
237 * > format.privateKey('0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef')
238 "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
239 * > format.privateKey('0x0123456789012345678901234567890123456789')
240 Error("not match hex64")
241 */
242format.privateKey = format.hex64; // alias
243
244/**
245 * @param arg {string|Buffer}
246 * @return {string} Hex string
247 */
248format.signature = format.hex.$validate(v => v.length === 2 + 130, 'signature');
249
250/**
251 * @param arg {string|Buffer}
252 * @return {string} Hex string
253 *
254 * @example
255 * > format.privateKey('0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef')
256 "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
257 * > format.privateKey('0x0123456789012345678901234567890123456789')
258 Error("not match hex64")
259 */
260format.blockHash = format.hex64; // alias
261
262/**
263 * @param arg {string|Buffer}
264 * @return {string} Hex string
265 *
266 * @example
267 * > format.privateKey('0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef')
268 "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
269 * > format.privateKey('0x0123456789012345678901234567890123456789')
270 Error("not match hex64")
271 */
272format.transactionHash = format.hex64; // alias
273
274/**
275 * @param arg {number|JSBI|string|Buffer|boolean|null}
276 * @return {Buffer}
277 *
278 * @example
279 * > format.buffer(Buffer.from([0, 1]))
280 <Buffer 00 01>
281 * > format.buffer(null)
282 <Buffer >
283 * > format.buffer(1024)
284 <Buffer 04 00>
285 * > format.buffer('0x0a')
286 <Buffer 0a>
287 * > format.buffer(true)
288 <Buffer 01>
289 * > format.buffer(3.14)
290 Error("not match hex")
291 */
292format.buffer = format.hex.$after(v => Buffer.from(v.substr(2), 'hex'));
293
294/**
295 * @param arg {string|Buffer|array}
296 * @return {Buffer}
297 *
298 * @example
299 * > format.bytes('abcd')
300 <Buffer 61 62 63 64>
301 * > format.bytes([0, 1])
302 <Buffer 00 01>
303 * > format.bytes(Buffer.from([0, 1]))
304 <Buffer 00 01>
305 */
306format.bytes = parser(v => (Buffer.isBuffer(v) ? v : Buffer.from(v)));
307
308/**
309 * @param arg {boolean}
310 * @return {boolean}
311 *
312 * @example
313 * > format.boolean(true)
314 true
315 * > format.boolean(false)
316 false
317 */
318format.boolean = format.any.$validate(lodash.isBoolean, 'boolean');
319
320// -------------------------- format method arguments -------------------------
321format.getLogs = parser({
322 limit: format.hexUInt.$or(undefined),
323 fromEpoch: format.epochNumber.$or(undefined),
324 toEpoch: format.epochNumber.$or(undefined),
325 blockHashes: format.blockHash.$or([format.blockHash]).$or(undefined),
326 address: format.address.$or([format.address]).$or(undefined),
327 topics: parser([format.hex64.$or([format.hex64]).$or(null)]).$or(undefined),
328}, true);
329
330format.signTx = parser({
331 nonce: format.hexUInt.$after(format.buffer),
332 gasPrice: format.hexUInt.$after(format.buffer),
333 gas: format.hexUInt.$after(format.buffer),
334 to: parser(format.address.$or(null).$default(null)).$after(format.buffer),
335 value: format.hexUInt.$default(0).$after(format.buffer),
336 storageLimit: format.hexUInt.$after(format.buffer),
337 epochHeight: format.uInt.$after(format.buffer),
338 chainId: format.uInt.$default(0).$after(format.buffer),
339 data: format.hex.$default('0x').$after(format.buffer),
340 r: (format.hexUInt.$after(format.buffer)).$or(undefined),
341 s: (format.hexUInt.$after(format.buffer)).$or(undefined),
342 v: (format.uInt.$after(format.buffer)).$or(undefined),
343}, true);
344
345format.sendTx = parser({
346 from: format.address,
347 nonce: format.hexUInt.$or(undefined),
348 gasPrice: format.hexUInt,
349 gas: format.hexUInt,
350 to: format.address.$or(null).$or(undefined),
351 value: format.hexUInt.$or(undefined),
352 storageLimit: format.hexUInt,
353 epochHeight: format.hexUInt.$or(undefined),
354 chainId: format.hexUInt.$or(undefined),
355 data: format.hex.$or(undefined),
356}, true);
357
358format.callTx = parser({
359 from: format.address.$or(undefined),
360 nonce: format.hexUInt.$or(undefined),
361 gasPrice: format.hexUInt.$or(undefined),
362 gas: format.hexUInt.$or(undefined),
363 to: format.address.$or(null),
364 value: format.hexUInt.$or(undefined),
365 storageLimit: format.hexUInt.$or(undefined),
366 epochHeight: format.uInt.$or(undefined),
367 chainId: format.uInt.$or(undefined),
368 data: format.hex.$or(undefined),
369}, true);
370
371format.estimateTx = parser({
372 from: format.address.$or(undefined),
373 nonce: format.hexUInt.$or(undefined),
374 gasPrice: format.hexUInt.$or(undefined),
375 gas: format.hexUInt.$or(undefined),
376 to: format.address.$or(null).$or(undefined),
377 value: format.hexUInt.$or(undefined),
378 storageLimit: format.hexUInt.$or(undefined),
379 epochHeight: format.uInt.$or(undefined),
380 chainId: format.uInt.$or(undefined),
381 data: format.hex.$or(undefined),
382}, true);
383
384// ----------------------------- parse rpc returned ---------------------------
385format.status = parser({
386 chainId: format.uInt,
387 epochNumber: format.uInt,
388 blockNumber: format.uInt,
389 pendingTxNumber: format.uInt,
390});
391
392format.account = parser({
393 accumulatedInterestReturn: format.bigUInt,
394 balance: format.bigUInt,
395 collateralForStorage: format.bigUInt,
396 nonce: format.bigUInt,
397 stakingBalance: format.bigUInt,
398});
399
400format.transaction = parser({
401 nonce: format.bigUInt,
402 value: format.bigUInt,
403 gasPrice: format.bigUInt,
404 gas: format.bigUInt,
405 v: format.uInt,
406 transactionIndex: format.uInt.$or(null),
407 status: format.uInt.$or(null), // XXX: might be remove in rpc returned
408 storageLimit: format.bigUInt,
409 chainId: format.uInt,
410 epochHeight: format.uInt,
411});
412
413format.estimate = parser({
414 gasUsed: format.bigUInt,
415 storageCollateralized: format.bigUInt,
416});
417
418format.block = parser({
419 epochNumber: format.uInt,
420 blame: format.uInt,
421 height: format.uInt,
422 size: format.uInt,
423 timestamp: format.uInt,
424 gasLimit: format.bigUInt,
425 difficulty: format.bigUInt,
426 transactions: [(format.transaction).$or(format.transactionHash)],
427});
428
429format.receipt = parser({
430 index: format.uInt,
431 epochNumber: format.uInt,
432 outcomeStatus: format.uInt.$or(null),
433 gasUsed: format.bigUInt,
434 gasFee: format.bigUInt,
435});
436
437format.logs = parser([
438 {
439 epochNumber: format.uInt,
440 logIndex: format.uInt,
441 transactionIndex: format.uInt,
442 transactionLogIndex: format.uInt,
443 },
444]);
445
446format.sponsorInfo = parser({
447 sponsorBalanceForCollateral: format.bigUInt,
448 sponsorBalanceForGas: format.bigUInt,
449 sponsorGasBound: format.bigUInt,
450});
451
452format.rewardInfo = parser([
453 {
454 baseReward: format.bigUInt,
455 totalReward: format.bigUInt,
456 txFee: format.bigUInt,
457 },
458]);
459
460module.exports = format;