@leofcoin/chain 1.10.7 → 1.10.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -30,6 +30,907 @@ class TransactionMessage extends FormatInterface {
30
30
  }
31
31
  }
32
32
 
33
+ /* Do NOT modify this file; see /src.ts/_admin/update-version.ts */
34
+ /**
35
+ * The current version of Ethers.
36
+ */
37
+ const version = "6.17.0";
38
+
39
+ /**
40
+ * Property helper functions.
41
+ *
42
+ * @_subsection api/utils:Properties [about-properties]
43
+ */
44
+ /**
45
+ * Assigns the %%values%% to %%target%% as read-only values.
46
+ *
47
+ * It %%types%% is specified, the values are checked.
48
+ */
49
+ function defineProperties(target, values, types) {
50
+ for (let key in values) {
51
+ let value = values[key];
52
+ Object.defineProperty(target, key, { enumerable: true, value, writable: false });
53
+ }
54
+ }
55
+
56
+ /**
57
+ * All errors in ethers include properties to ensure they are both
58
+ * human-readable (i.e. ``.message``) and machine-readable (i.e. ``.code``).
59
+ *
60
+ * The [[isError]] function can be used to check the error ``code`` and
61
+ * provide a type guard for the properties present on that error interface.
62
+ *
63
+ * @_section: api/utils/errors:Errors [about-errors]
64
+ */
65
+ function stringify(value, seen) {
66
+ if (value == null) {
67
+ return "null";
68
+ }
69
+ if (seen == null) {
70
+ seen = new Set();
71
+ }
72
+ if (typeof (value) === "object") {
73
+ if (seen.has(value)) {
74
+ return "[Circular]";
75
+ }
76
+ seen.add(value);
77
+ }
78
+ if (Array.isArray(value)) {
79
+ return "[ " + (value.map((v) => stringify(v, seen))).join(", ") + " ]";
80
+ }
81
+ if (value instanceof Uint8Array) {
82
+ const HEX = "0123456789abcdef";
83
+ let result = "0x";
84
+ for (let i = 0; i < value.length; i++) {
85
+ result += HEX[value[i] >> 4];
86
+ result += HEX[value[i] & 0xf];
87
+ }
88
+ return result;
89
+ }
90
+ if (typeof (value) === "object" && typeof (value.toJSON) === "function") {
91
+ return stringify(value.toJSON(), seen);
92
+ }
93
+ switch (typeof (value)) {
94
+ case "boolean":
95
+ case "number":
96
+ case "symbol":
97
+ return value.toString();
98
+ case "bigint":
99
+ return BigInt(value).toString();
100
+ case "string":
101
+ return JSON.stringify(value);
102
+ case "object": {
103
+ const keys = Object.keys(value);
104
+ keys.sort();
105
+ return "{ " + keys.map((k) => `${stringify(k, seen)}: ${stringify(value[k], seen)}`).join(", ") + " }";
106
+ }
107
+ }
108
+ return `[ COULD NOT SERIALIZE ]`;
109
+ }
110
+ /**
111
+ * Returns a new Error configured to the format ethers emits errors, with
112
+ * the %%message%%, [[api:ErrorCode]] %%code%% and additional properties
113
+ * for the corresponding EthersError.
114
+ *
115
+ * Each error in ethers includes the version of ethers, a
116
+ * machine-readable [[ErrorCode]], and depending on %%code%%, additional
117
+ * required properties. The error message will also include the %%message%%,
118
+ * ethers version, %%code%% and all additional properties, serialized.
119
+ */
120
+ function makeError(message, code, info) {
121
+ let shortMessage = message;
122
+ {
123
+ const details = [];
124
+ if (info) {
125
+ if ("message" in info || "code" in info || "name" in info) {
126
+ throw new Error(`value will overwrite populated values: ${stringify(info)}`);
127
+ }
128
+ for (const key in info) {
129
+ if (key === "shortMessage") {
130
+ continue;
131
+ }
132
+ const value = (info[key]);
133
+ // try {
134
+ details.push(key + "=" + stringify(value));
135
+ // } catch (error: any) {
136
+ // console.log("MMM", error.message);
137
+ // details.push(key + "=[could not serialize object]");
138
+ // }
139
+ }
140
+ }
141
+ details.push(`code=${code}`);
142
+ details.push(`version=${version}`);
143
+ if (details.length) {
144
+ message += " (" + details.join(", ") + ")";
145
+ }
146
+ }
147
+ let error;
148
+ switch (code) {
149
+ case "INVALID_ARGUMENT":
150
+ error = new TypeError(message);
151
+ break;
152
+ case "NUMERIC_FAULT":
153
+ case "BUFFER_OVERRUN":
154
+ error = new RangeError(message);
155
+ break;
156
+ default:
157
+ error = new Error(message);
158
+ }
159
+ defineProperties(error, { code });
160
+ if (info) {
161
+ Object.assign(error, info);
162
+ }
163
+ if (error.shortMessage == null) {
164
+ defineProperties(error, { shortMessage });
165
+ }
166
+ return error;
167
+ }
168
+ /**
169
+ * Throws an EthersError with %%message%%, %%code%% and additional error
170
+ * %%info%% when %%check%% is falsish..
171
+ *
172
+ * @see [[api:makeError]]
173
+ */
174
+ function assert(check, message, code, info) {
175
+ if (!check) {
176
+ throw makeError(message, code, info);
177
+ }
178
+ }
179
+ /**
180
+ * A simple helper to simply ensuring provided arguments match expected
181
+ * constraints, throwing if not.
182
+ *
183
+ * In TypeScript environments, the %%check%% has been asserted true, so
184
+ * any further code does not need additional compile-time checks.
185
+ */
186
+ function assertArgument(check, message, name, value) {
187
+ assert(check, message, "INVALID_ARGUMENT", { argument: name, value: value });
188
+ }
189
+ ["NFD", "NFC", "NFKD", "NFKC"].reduce((accum, form) => {
190
+ try {
191
+ // General test for normalize
192
+ /* c8 ignore start */
193
+ if ("test".normalize(form) !== "test") {
194
+ throw new Error("bad");
195
+ }
196
+ ;
197
+ /* c8 ignore stop */
198
+ if (form === "NFD") {
199
+ const check = String.fromCharCode(0xe9).normalize("NFD");
200
+ const expected = String.fromCharCode(0x65, 0x0301);
201
+ /* c8 ignore start */
202
+ if (check !== expected) {
203
+ throw new Error("broken");
204
+ }
205
+ /* c8 ignore stop */
206
+ }
207
+ accum.push(form);
208
+ }
209
+ catch (error) { }
210
+ return accum;
211
+ }, []);
212
+ /**
213
+ * Many classes use file-scoped values to guard the constructor,
214
+ * making it effectively private. This facilitates that pattern
215
+ * by ensuring the %%givenGaurd%% matches the file-scoped %%guard%%,
216
+ * throwing if not, indicating the %%className%% if provided.
217
+ */
218
+ function assertPrivate(givenGuard, guard, className) {
219
+ if (givenGuard !== guard) {
220
+ let method = className, operation = "new";
221
+ {
222
+ method += ".";
223
+ operation += " " + className;
224
+ }
225
+ assert(false, `private constructor; use ${method}from* methods`, "UNSUPPORTED_OPERATION", {
226
+ operation
227
+ });
228
+ }
229
+ }
230
+
231
+ /**
232
+ * Some data helpers.
233
+ *
234
+ *
235
+ * @_subsection api/utils:Data Helpers [about-data]
236
+ */
237
+ function _getBytes(value, name, copy) {
238
+ if (value instanceof Uint8Array) {
239
+ return value;
240
+ }
241
+ if (typeof (value) === "string" && (value.length % 2) === 0 &&
242
+ value.match(/^0x[0-9a-f]*$/i)) {
243
+ const result = new Uint8Array((value.length - 2) / 2);
244
+ let offset = 2;
245
+ for (let i = 0; i < result.length; i++) {
246
+ result[i] = parseInt(value.substring(offset, offset + 2), 16);
247
+ offset += 2;
248
+ }
249
+ return result;
250
+ }
251
+ assertArgument(false, "invalid BytesLike value", name, value);
252
+ }
253
+ /**
254
+ * Get a typed Uint8Array for %%value%%. If already a Uint8Array
255
+ * the original %%value%% is returned; if a copy is required use
256
+ * [[getBytesCopy]].
257
+ *
258
+ * @see: getBytesCopy
259
+ */
260
+ function getBytes(value, name) {
261
+ return _getBytes(value, name);
262
+ }
263
+
264
+ /**
265
+ * Some mathematic operations.
266
+ *
267
+ * @_subsection: api/utils:Math Helpers [about-maths]
268
+ */
269
+ const BN_0$1 = BigInt(0);
270
+ const BN_1$1 = BigInt(1);
271
+ //const BN_Max256 = (BN_1 << BigInt(256)) - BN_1;
272
+ // IEEE 754 support 53-bits of mantissa
273
+ const maxValue = 0x1fffffffffffff;
274
+ /**
275
+ * Convert %%value%% from a twos-compliment representation of %%width%%
276
+ * bits to its value.
277
+ *
278
+ * If the highest bit is ``1``, the result will be negative.
279
+ */
280
+ function fromTwos(_value, _width) {
281
+ const value = getUint(_value, "value");
282
+ const width = BigInt(getNumber(_width, "width"));
283
+ assert((value >> width) === BN_0$1, "overflow", "NUMERIC_FAULT", {
284
+ operation: "fromTwos", fault: "overflow", value: _value
285
+ });
286
+ // Top bit set; treat as a negative value
287
+ if (value >> (width - BN_1$1)) {
288
+ const mask = (BN_1$1 << width) - BN_1$1;
289
+ return -(((~value) & mask) + BN_1$1);
290
+ }
291
+ return value;
292
+ }
293
+ /**
294
+ * Mask %%value%% with a bitmask of %%bits%% ones.
295
+ */
296
+ function mask(_value, _bits) {
297
+ const value = getUint(_value, "value");
298
+ const bits = BigInt(getNumber(_bits, "bits"));
299
+ return value & ((BN_1$1 << bits) - BN_1$1);
300
+ }
301
+ /**
302
+ * Gets a BigInt from %%value%%. If it is an invalid value for
303
+ * a BigInt, then an ArgumentError will be thrown for %%name%%.
304
+ */
305
+ function getBigInt(value, name) {
306
+ switch (typeof (value)) {
307
+ case "bigint": return value;
308
+ case "number":
309
+ assertArgument(Number.isInteger(value), "underflow", name || "value", value);
310
+ assertArgument(value >= -maxValue && value <= maxValue, "overflow", name || "value", value);
311
+ return BigInt(value);
312
+ case "string":
313
+ try {
314
+ if (value === "") {
315
+ throw new Error("empty string");
316
+ }
317
+ if (value[0] === "-" && value[1] !== "-") {
318
+ return -BigInt(value.substring(1));
319
+ }
320
+ return BigInt(value);
321
+ }
322
+ catch (e) {
323
+ assertArgument(false, `invalid BigNumberish string: ${e.message}`, name || "value", value);
324
+ }
325
+ }
326
+ assertArgument(false, "invalid BigNumberish value", name || "value", value);
327
+ }
328
+ /**
329
+ * Returns %%value%% as a bigint, validating it is valid as a bigint
330
+ * value and that it is positive.
331
+ */
332
+ function getUint(value, name) {
333
+ const result = getBigInt(value, name);
334
+ assert(result >= BN_0$1, "unsigned value cannot be negative", "NUMERIC_FAULT", {
335
+ fault: "overflow", operation: "getUint", value
336
+ });
337
+ return result;
338
+ }
339
+ const Nibbles = "0123456789abcdef";
340
+ /*
341
+ * Converts %%value%% to a BigInt. If %%value%% is a Uint8Array, it
342
+ * is treated as Big Endian data.
343
+ */
344
+ function toBigInt(value) {
345
+ if (value instanceof Uint8Array) {
346
+ let result = "0x0";
347
+ for (const v of value) {
348
+ result += Nibbles[v >> 4];
349
+ result += Nibbles[v & 0x0f];
350
+ }
351
+ return BigInt(result);
352
+ }
353
+ return getBigInt(value);
354
+ }
355
+ /**
356
+ * Gets a //number// from %%value%%. If it is an invalid value for
357
+ * a //number//, then an ArgumentError will be thrown for %%name%%.
358
+ */
359
+ function getNumber(value, name) {
360
+ switch (typeof (value)) {
361
+ case "bigint":
362
+ assertArgument(value >= -maxValue && value <= maxValue, "overflow", name || "value", value);
363
+ return Number(value);
364
+ case "number":
365
+ assertArgument(Number.isInteger(value), "underflow", name || "value", value);
366
+ assertArgument(value >= -maxValue && value <= maxValue, "overflow", name || "value", value);
367
+ return value;
368
+ case "string":
369
+ try {
370
+ if (value === "") {
371
+ throw new Error("empty string");
372
+ }
373
+ return getNumber(BigInt(value), name);
374
+ }
375
+ catch (e) {
376
+ assertArgument(false, `invalid numeric string: ${e.message}`, name || "value", value);
377
+ }
378
+ }
379
+ assertArgument(false, "invalid numeric value", name || "value", value);
380
+ }
381
+
382
+ /**
383
+ * The **FixedNumber** class permits using values with decimal places,
384
+ * using fixed-pont math.
385
+ *
386
+ * Fixed-point math is still based on integers under-the-hood, but uses an
387
+ * internal offset to store fractional components below, and each operation
388
+ * corrects for this after each operation.
389
+ *
390
+ * @_section: api/utils/fixed-point-math:Fixed-Point Maths [about-fixed-point-math]
391
+ */
392
+ const BN_N1 = BigInt(-1);
393
+ const BN_0 = BigInt(0);
394
+ const BN_1 = BigInt(1);
395
+ const BN_5 = BigInt(5);
396
+ const _guard = {};
397
+ // Constant to pull zeros from for multipliers
398
+ let Zeros = "0000";
399
+ while (Zeros.length < 80) {
400
+ Zeros += Zeros;
401
+ }
402
+ // Returns a string "1" followed by decimal "0"s
403
+ function getTens(decimals) {
404
+ let result = Zeros;
405
+ while (result.length < decimals) {
406
+ result += result;
407
+ }
408
+ return BigInt("1" + result.substring(0, decimals));
409
+ }
410
+ function checkValue(val, format, safeOp) {
411
+ const width = BigInt(format.width);
412
+ if (format.signed) {
413
+ const limit = (BN_1 << (width - BN_1));
414
+ assert(safeOp == null || (val >= -limit && val < limit), "overflow", "NUMERIC_FAULT", {
415
+ operation: safeOp, fault: "overflow", value: val
416
+ });
417
+ if (val > BN_0) {
418
+ val = fromTwos(mask(val, width), width);
419
+ }
420
+ else {
421
+ val = -fromTwos(mask(-val, width), width);
422
+ }
423
+ }
424
+ else {
425
+ const limit = (BN_1 << width);
426
+ assert(safeOp == null || (val >= 0 && val < limit), "overflow", "NUMERIC_FAULT", {
427
+ operation: safeOp, fault: "overflow", value: val
428
+ });
429
+ val = (((val % limit) + limit) % limit) & (limit - BN_1);
430
+ }
431
+ return val;
432
+ }
433
+ function getFormat(value) {
434
+ if (typeof (value) === "number") {
435
+ value = `fixed128x${value}`;
436
+ }
437
+ let signed = true;
438
+ let width = 128;
439
+ let decimals = 18;
440
+ if (typeof (value) === "string") {
441
+ // Parse the format string
442
+ if (value === "fixed") ;
443
+ else if (value === "ufixed") {
444
+ signed = false;
445
+ }
446
+ else {
447
+ const match = value.match(/^(u?)fixed([0-9]+)x([0-9]+)$/);
448
+ assertArgument(match, "invalid fixed format", "format", value);
449
+ signed = (match[1] !== "u");
450
+ width = parseInt(match[2]);
451
+ decimals = parseInt(match[3]);
452
+ }
453
+ }
454
+ else if (value) {
455
+ // Extract the values from the object
456
+ const v = value;
457
+ const check = (key, type, defaultValue) => {
458
+ if (v[key] == null) {
459
+ return defaultValue;
460
+ }
461
+ assertArgument(typeof (v[key]) === type, "invalid fixed format (" + key + " not " + type + ")", "format." + key, v[key]);
462
+ return v[key];
463
+ };
464
+ signed = check("signed", "boolean", signed);
465
+ width = check("width", "number", width);
466
+ decimals = check("decimals", "number", decimals);
467
+ }
468
+ assertArgument((width % 8) === 0, "invalid FixedNumber width (not byte aligned)", "format.width", width);
469
+ assertArgument(decimals <= 80, "invalid FixedNumber decimals (too large)", "format.decimals", decimals);
470
+ const name = (signed ? "" : "u") + "fixed" + String(width) + "x" + String(decimals);
471
+ return { signed, width, decimals, name };
472
+ }
473
+ function toString(val, decimals) {
474
+ let negative = "";
475
+ if (val < BN_0) {
476
+ negative = "-";
477
+ val *= BN_N1;
478
+ }
479
+ let str = val.toString();
480
+ // No decimal point for whole values
481
+ if (decimals === 0) {
482
+ return (negative + str);
483
+ }
484
+ // Pad out to the whole component (including a whole digit)
485
+ while (str.length <= decimals) {
486
+ str = Zeros + str;
487
+ }
488
+ // Insert the decimal point
489
+ const index = str.length - decimals;
490
+ str = str.substring(0, index) + "." + str.substring(index);
491
+ // Trim the whole component (leaving at least one 0)
492
+ while (str[0] === "0" && str[1] !== ".") {
493
+ str = str.substring(1);
494
+ }
495
+ // Trim the decimal component (leaving at least one 0)
496
+ while (str[str.length - 1] === "0" && str[str.length - 2] !== ".") {
497
+ str = str.substring(0, str.length - 1);
498
+ }
499
+ return (negative + str);
500
+ }
501
+ /**
502
+ * A FixedNumber represents a value over its [[FixedFormat]]
503
+ * arithmetic field.
504
+ *
505
+ * A FixedNumber can be used to perform math, losslessly, on
506
+ * values which have decmial places.
507
+ *
508
+ * A FixedNumber has a fixed bit-width to store values in, and stores all
509
+ * values internally by multiplying the value by 10 raised to the power of
510
+ * %%decimals%%.
511
+ *
512
+ * If operations are performed that cause a value to grow too high (close to
513
+ * positive infinity) or too low (close to negative infinity), the value
514
+ * is said to //overflow//.
515
+ *
516
+ * For example, an 8-bit signed value, with 0 decimals may only be within
517
+ * the range ``-128`` to ``127``; so ``-128 - 1`` will overflow and become
518
+ * ``127``. Likewise, ``127 + 1`` will overflow and become ``-127``.
519
+ *
520
+ * Many operation have a normal and //unsafe// variant. The normal variant
521
+ * will throw a [[NumericFaultError]] on any overflow, while the //unsafe//
522
+ * variant will silently allow overflow, corrupting its value value.
523
+ *
524
+ * If operations are performed that cause a value to become too small
525
+ * (close to zero), the value loses precison and is said to //underflow//.
526
+ *
527
+ * For example, a value with 1 decimal place may store a number as small
528
+ * as ``0.1``, but the value of ``0.1 / 2`` is ``0.05``, which cannot fit
529
+ * into 1 decimal place, so underflow occurs which means precision is lost
530
+ * and the value becomes ``0``.
531
+ *
532
+ * Some operations have a normal and //signalling// variant. The normal
533
+ * variant will silently ignore underflow, while the //signalling// variant
534
+ * will thow a [[NumericFaultError]] on underflow.
535
+ */
536
+ class FixedNumber {
537
+ /**
538
+ * The specific fixed-point arithmetic field for this value.
539
+ */
540
+ format;
541
+ #format;
542
+ // The actual value (accounting for decimals)
543
+ #val;
544
+ // A base-10 value to multiple values by to maintain the magnitude
545
+ #tens;
546
+ /**
547
+ * This is a property so console.log shows a human-meaningful value.
548
+ *
549
+ * @private
550
+ */
551
+ _value;
552
+ // Use this when changing this file to get some typing info,
553
+ // but then switch to any to mask the internal type
554
+ //constructor(guard: any, value: bigint, format: _FixedFormat) {
555
+ /**
556
+ * @private
557
+ */
558
+ constructor(guard, value, format) {
559
+ assertPrivate(guard, _guard, "FixedNumber");
560
+ this.#val = value;
561
+ this.#format = format;
562
+ const _value = toString(value, format.decimals);
563
+ defineProperties(this, { format: format.name, _value });
564
+ this.#tens = getTens(format.decimals);
565
+ }
566
+ /**
567
+ * If true, negative values are permitted, otherwise only
568
+ * positive values and zero are allowed.
569
+ */
570
+ get signed() { return this.#format.signed; }
571
+ /**
572
+ * The number of bits available to store the value.
573
+ */
574
+ get width() { return this.#format.width; }
575
+ /**
576
+ * The number of decimal places in the fixed-point arithment field.
577
+ */
578
+ get decimals() { return this.#format.decimals; }
579
+ /**
580
+ * The value as an integer, based on the smallest unit the
581
+ * [[decimals]] allow.
582
+ */
583
+ get value() { return this.#val; }
584
+ #checkFormat(other) {
585
+ assertArgument(this.format === other.format, "incompatible format; use fixedNumber.toFormat", "other", other);
586
+ }
587
+ #checkValue(val, safeOp) {
588
+ /*
589
+ const width = BigInt(this.width);
590
+ if (this.signed) {
591
+ const limit = (BN_1 << (width - BN_1));
592
+ assert(safeOp == null || (val >= -limit && val < limit), "overflow", "NUMERIC_FAULT", {
593
+ operation: <string>safeOp, fault: "overflow", value: val
594
+ });
595
+
596
+ if (val > BN_0) {
597
+ val = fromTwos(mask(val, width), width);
598
+ } else {
599
+ val = -fromTwos(mask(-val, width), width);
600
+ }
601
+
602
+ } else {
603
+ const masked = mask(val, width);
604
+ assert(safeOp == null || (val >= 0 && val === masked), "overflow", "NUMERIC_FAULT", {
605
+ operation: <string>safeOp, fault: "overflow", value: val
606
+ });
607
+ val = masked;
608
+ }
609
+ */
610
+ val = checkValue(val, this.#format, safeOp);
611
+ return new FixedNumber(_guard, val, this.#format);
612
+ }
613
+ #add(o, safeOp) {
614
+ this.#checkFormat(o);
615
+ return this.#checkValue(this.#val + o.#val, safeOp);
616
+ }
617
+ /**
618
+ * Returns a new [[FixedNumber]] with the result of %%this%% added
619
+ * to %%other%%, ignoring overflow.
620
+ */
621
+ addUnsafe(other) { return this.#add(other); }
622
+ /**
623
+ * Returns a new [[FixedNumber]] with the result of %%this%% added
624
+ * to %%other%%. A [[NumericFaultError]] is thrown if overflow
625
+ * occurs.
626
+ */
627
+ add(other) { return this.#add(other, "add"); }
628
+ #sub(o, safeOp) {
629
+ this.#checkFormat(o);
630
+ return this.#checkValue(this.#val - o.#val, safeOp);
631
+ }
632
+ /**
633
+ * Returns a new [[FixedNumber]] with the result of %%other%% subtracted
634
+ * from %%this%%, ignoring overflow.
635
+ */
636
+ subUnsafe(other) { return this.#sub(other); }
637
+ /**
638
+ * Returns a new [[FixedNumber]] with the result of %%other%% subtracted
639
+ * from %%this%%. A [[NumericFaultError]] is thrown if overflow
640
+ * occurs.
641
+ */
642
+ sub(other) { return this.#sub(other, "sub"); }
643
+ #mul(o, safeOp) {
644
+ this.#checkFormat(o);
645
+ return this.#checkValue((this.#val * o.#val) / this.#tens, safeOp);
646
+ }
647
+ /**
648
+ * Returns a new [[FixedNumber]] with the result of %%this%% multiplied
649
+ * by %%other%%, ignoring overflow and underflow (precision loss).
650
+ */
651
+ mulUnsafe(other) { return this.#mul(other); }
652
+ /**
653
+ * Returns a new [[FixedNumber]] with the result of %%this%% multiplied
654
+ * by %%other%%. A [[NumericFaultError]] is thrown if overflow
655
+ * occurs.
656
+ */
657
+ mul(other) { return this.#mul(other, "mul"); }
658
+ /**
659
+ * Returns a new [[FixedNumber]] with the result of %%this%% multiplied
660
+ * by %%other%%. A [[NumericFaultError]] is thrown if overflow
661
+ * occurs or if underflow (precision loss) occurs.
662
+ */
663
+ mulSignal(other) {
664
+ this.#checkFormat(other);
665
+ const value = this.#val * other.#val;
666
+ assert((value % this.#tens) === BN_0, "precision lost during signalling mul", "NUMERIC_FAULT", {
667
+ operation: "mulSignal", fault: "underflow", value: this
668
+ });
669
+ return this.#checkValue(value / this.#tens, "mulSignal");
670
+ }
671
+ #div(o, safeOp) {
672
+ assert(o.#val !== BN_0, "division by zero", "NUMERIC_FAULT", {
673
+ operation: "div", fault: "divide-by-zero", value: this
674
+ });
675
+ this.#checkFormat(o);
676
+ return this.#checkValue((this.#val * this.#tens) / o.#val, safeOp);
677
+ }
678
+ /**
679
+ * Returns a new [[FixedNumber]] with the result of %%this%% divided
680
+ * by %%other%%, ignoring underflow (precision loss). A
681
+ * [[NumericFaultError]] is thrown if overflow occurs.
682
+ */
683
+ divUnsafe(other) { return this.#div(other); }
684
+ /**
685
+ * Returns a new [[FixedNumber]] with the result of %%this%% divided
686
+ * by %%other%%, ignoring underflow (precision loss). A
687
+ * [[NumericFaultError]] is thrown if overflow occurs.
688
+ */
689
+ div(other) { return this.#div(other, "div"); }
690
+ /**
691
+ * Returns a new [[FixedNumber]] with the result of %%this%% divided
692
+ * by %%other%%. A [[NumericFaultError]] is thrown if underflow
693
+ * (precision loss) occurs.
694
+ */
695
+ divSignal(other) {
696
+ assert(other.#val !== BN_0, "division by zero", "NUMERIC_FAULT", {
697
+ operation: "div", fault: "divide-by-zero", value: this
698
+ });
699
+ this.#checkFormat(other);
700
+ const value = (this.#val * this.#tens);
701
+ assert((value % other.#val) === BN_0, "precision lost during signalling div", "NUMERIC_FAULT", {
702
+ operation: "divSignal", fault: "underflow", value: this
703
+ });
704
+ return this.#checkValue(value / other.#val, "divSignal");
705
+ }
706
+ /**
707
+ * Returns a comparison result between %%this%% and %%other%%.
708
+ *
709
+ * This is suitable for use in sorting, where ``-1`` implies %%this%%
710
+ * is smaller, ``1`` implies %%this%% is larger and ``0`` implies
711
+ * both are equal.
712
+ */
713
+ cmp(other) {
714
+ let a = this.value, b = other.value;
715
+ // Coerce a and b to the same magnitude
716
+ const delta = this.decimals - other.decimals;
717
+ if (delta > 0) {
718
+ b *= getTens(delta);
719
+ }
720
+ else if (delta < 0) {
721
+ a *= getTens(-delta);
722
+ }
723
+ // Comnpare
724
+ if (a < b) {
725
+ return -1;
726
+ }
727
+ if (a > b) {
728
+ return 1;
729
+ }
730
+ return 0;
731
+ }
732
+ /**
733
+ * Returns true if %%other%% is equal to %%this%%.
734
+ */
735
+ eq(other) { return this.cmp(other) === 0; }
736
+ /**
737
+ * Returns true if %%other%% is less than to %%this%%.
738
+ */
739
+ lt(other) { return this.cmp(other) < 0; }
740
+ /**
741
+ * Returns true if %%other%% is less than or equal to %%this%%.
742
+ */
743
+ lte(other) { return this.cmp(other) <= 0; }
744
+ /**
745
+ * Returns true if %%other%% is greater than to %%this%%.
746
+ */
747
+ gt(other) { return this.cmp(other) > 0; }
748
+ /**
749
+ * Returns true if %%other%% is greater than or equal to %%this%%.
750
+ */
751
+ gte(other) { return this.cmp(other) >= 0; }
752
+ /**
753
+ * Returns a new [[FixedNumber]] which is the largest **integer**
754
+ * that is less than or equal to %%this%%.
755
+ *
756
+ * The decimal component of the result will always be ``0``.
757
+ */
758
+ floor() {
759
+ let val = this.#val;
760
+ if (this.#val < BN_0) {
761
+ val -= this.#tens - BN_1;
762
+ }
763
+ val = (this.#val / this.#tens) * this.#tens;
764
+ return this.#checkValue(val, "floor");
765
+ }
766
+ /**
767
+ * Returns a new [[FixedNumber]] which is the smallest **integer**
768
+ * that is greater than or equal to %%this%%.
769
+ *
770
+ * The decimal component of the result will always be ``0``.
771
+ */
772
+ ceiling() {
773
+ let val = this.#val;
774
+ if (this.#val > BN_0) {
775
+ val += this.#tens - BN_1;
776
+ }
777
+ val = (this.#val / this.#tens) * this.#tens;
778
+ return this.#checkValue(val, "ceiling");
779
+ }
780
+ /**
781
+ * Returns a new [[FixedNumber]] with the decimal component
782
+ * rounded up on ties at %%decimals%% places.
783
+ */
784
+ round(decimals) {
785
+ if (decimals == null) {
786
+ decimals = 0;
787
+ }
788
+ // Not enough precision to not already be rounded
789
+ if (decimals >= this.decimals) {
790
+ return this;
791
+ }
792
+ const delta = this.decimals - decimals;
793
+ const bump = BN_5 * getTens(delta - 1);
794
+ let value = this.value + bump;
795
+ const tens = getTens(delta);
796
+ value = (value / tens) * tens;
797
+ checkValue(value, this.#format, "round");
798
+ return new FixedNumber(_guard, value, this.#format);
799
+ }
800
+ /**
801
+ * Returns true if %%this%% is equal to ``0``.
802
+ */
803
+ isZero() { return (this.#val === BN_0); }
804
+ /**
805
+ * Returns true if %%this%% is less than ``0``.
806
+ */
807
+ isNegative() { return (this.#val < BN_0); }
808
+ /**
809
+ * Returns the string representation of %%this%%.
810
+ */
811
+ toString() { return this._value; }
812
+ /**
813
+ * Returns a float approximation.
814
+ *
815
+ * Due to IEEE 754 precission (or lack thereof), this function
816
+ * can only return an approximation and most values will contain
817
+ * rounding errors.
818
+ */
819
+ toUnsafeFloat() { return parseFloat(this.toString()); }
820
+ /**
821
+ * Return a new [[FixedNumber]] with the same value but has had
822
+ * its field set to %%format%%.
823
+ *
824
+ * This will throw if the value cannot fit into %%format%%.
825
+ */
826
+ toFormat(format) {
827
+ return FixedNumber.fromString(this.toString(), format);
828
+ }
829
+ /**
830
+ * Creates a new [[FixedNumber]] for %%value%% divided by
831
+ * %%decimal%% places with %%format%%.
832
+ *
833
+ * This will throw a [[NumericFaultError]] if %%value%% (once adjusted
834
+ * for %%decimals%%) cannot fit in %%format%%, either due to overflow
835
+ * or underflow (precision loss).
836
+ */
837
+ static fromValue(_value, _decimals, _format) {
838
+ const decimals = (_decimals == null) ? 0 : getNumber(_decimals);
839
+ const format = getFormat(_format);
840
+ let value = getBigInt(_value, "value");
841
+ const delta = decimals - format.decimals;
842
+ if (delta > 0) {
843
+ const tens = getTens(delta);
844
+ assert((value % tens) === BN_0, "value loses precision for format", "NUMERIC_FAULT", {
845
+ operation: "fromValue", fault: "underflow", value: _value
846
+ });
847
+ value /= tens;
848
+ }
849
+ else if (delta < 0) {
850
+ value *= getTens(-delta);
851
+ }
852
+ checkValue(value, format, "fromValue");
853
+ return new FixedNumber(_guard, value, format);
854
+ }
855
+ /**
856
+ * Creates a new [[FixedNumber]] for %%value%% with %%format%%.
857
+ *
858
+ * This will throw a [[NumericFaultError]] if %%value%% cannot fit
859
+ * in %%format%%, either due to overflow or underflow (precision loss).
860
+ */
861
+ static fromString(_value, _format) {
862
+ const match = _value.match(/^(-?)([0-9]*)\.?([0-9]*)$/);
863
+ assertArgument(match && (match[2].length + match[3].length) > 0, "invalid FixedNumber string value", "value", _value);
864
+ const format = getFormat(_format);
865
+ let whole = (match[2] || "0"), decimal = (match[3] || "");
866
+ // Pad out the decimals
867
+ while (decimal.length < format.decimals) {
868
+ decimal += Zeros;
869
+ }
870
+ // Check precision is safe
871
+ assert(decimal.substring(format.decimals).match(/^0*$/), "too many decimals for format", "NUMERIC_FAULT", {
872
+ operation: "fromString", fault: "underflow", value: _value
873
+ });
874
+ // Remove extra padding
875
+ decimal = decimal.substring(0, format.decimals);
876
+ const value = BigInt(match[1] + whole + decimal);
877
+ checkValue(value, format, "fromString");
878
+ return new FixedNumber(_guard, value, format);
879
+ }
880
+ /**
881
+ * Creates a new [[FixedNumber]] with the big-endian representation
882
+ * %%value%% with %%format%%.
883
+ *
884
+ * This will throw a [[NumericFaultError]] if %%value%% cannot fit
885
+ * in %%format%% due to overflow.
886
+ */
887
+ static fromBytes(_value, _format) {
888
+ let value = toBigInt(getBytes(_value, "value"));
889
+ const format = getFormat(_format);
890
+ if (format.signed) {
891
+ value = fromTwos(value, format.width);
892
+ }
893
+ checkValue(value, format, "fromBytes");
894
+ return new FixedNumber(_guard, value, format);
895
+ }
896
+ }
897
+ //const f1 = FixedNumber.fromString("12.56", "fixed16x2");
898
+ //const f2 = FixedNumber.fromString("0.3", "fixed16x2");
899
+ //console.log(f1.divSignal(f2));
900
+ //const BUMP = FixedNumber.from("0.5");
901
+
902
+ /**
903
+ * Most interactions with Ethereum requires integer values, which use
904
+ * the smallest magnitude unit.
905
+ *
906
+ * For example, imagine dealing with dollars and cents. Since dollars
907
+ * are divisible, non-integer values are possible, such as ``$10.77``.
908
+ * By using the smallest indivisible unit (i.e. cents), the value can
909
+ * be kept as the integer ``1077``.
910
+ *
911
+ * When receiving decimal input from the user (as a decimal string),
912
+ * the value should be converted to an integer and when showing a user
913
+ * a value, the integer value should be converted to a decimal string.
914
+ *
915
+ * This creates a clear distinction, between values to be used by code
916
+ * (integers) and values used for display logic to users (decimals).
917
+ *
918
+ * The native unit in Ethereum, //ether// is divisible to 18 decimal places,
919
+ * where each individual unit is called a //wei//.
920
+ *
921
+ * @_subsection api/utils:Unit Conversion [about-units]
922
+ */
923
+ /**
924
+ * Converts %%value%% into a //decimal string//, assuming %%unit%% decimal
925
+ * places. The %%unit%% may be the number of decimal places or the name of
926
+ * a unit (e.g. ``"gwei"`` for 9 decimal places).
927
+ *
928
+ */
929
+ function formatUnits(value, unit) {
930
+ let decimals = 18;
931
+ return FixedNumber.fromValue(value, decimals, { decimals, width: 512 }).toString();
932
+ }
933
+
33
934
  const byteFormats = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
34
935
  const formatBytes = (bytes, decimals = 2) => {
35
936
  if (bytes === 0)
@@ -74,6 +975,62 @@ var bytecodes = {
74
975
  validators: validators
75
976
  };
76
977
 
978
+ const TRANSACTION_FEE_BYTES = 1024n;
979
+ const TRANSACTION_FEE_UNIT = 10n;
980
+ const FEE_BURN_BASIS_POINTS = 1000n;
981
+ const FEE_BASIS_POINTS = 10000n;
982
+ const FEE_PROTOCOL_VERSION = '1.10.9';
983
+ const parseProtocolVersion = (version) => {
984
+ const match = /^(\d+)\.(\d+)\.(\d+)/.exec(version);
985
+ return match ? [Number(match[1]), Number(match[2]), Number(match[3])] : undefined;
986
+ };
987
+ const supportsTransactionFees = (version) => {
988
+ const actual = parseProtocolVersion(version);
989
+ const required = parseProtocolVersion(FEE_PROTOCOL_VERSION);
990
+ if (!actual)
991
+ return false;
992
+ for (let index = 0; index < required.length; index += 1) {
993
+ if (actual[index] !== required[index])
994
+ return actual[index] > required[index];
995
+ }
996
+ return true;
997
+ };
998
+ const feeRotationIndex = (transactionHash, validatorCount) => {
999
+ let value = 0n;
1000
+ for (const character of transactionHash)
1001
+ value = (value * 31n + BigInt(character.charCodeAt(0))) % 4294967291n;
1002
+ return Number(value % BigInt(validatorCount));
1003
+ };
1004
+ const distributeTransactionFee = (fee, transactionHash, validatorAddresses) => {
1005
+ const canonicalValidators = [...new Set(validatorAddresses)].sort();
1006
+ if (canonicalValidators.length === 0)
1007
+ throw new Error('cannot distribute transaction fee without validators');
1008
+ if (fee < 0n)
1009
+ throw new Error('transaction fee cannot be negative');
1010
+ const burned = (fee * FEE_BURN_BASIS_POINTS) / FEE_BASIS_POINTS;
1011
+ const validatorPool = fee - burned;
1012
+ const count = BigInt(canonicalValidators.length);
1013
+ const base = validatorPool / count;
1014
+ const remainder = Number(validatorPool % count);
1015
+ const start = feeRotationIndex(transactionHash, canonicalValidators.length);
1016
+ const validatorFees = new Map(canonicalValidators.map((validator) => [validator, base]));
1017
+ for (let index = 0; index < remainder; index += 1) {
1018
+ const validator = canonicalValidators[(start + index) % canonicalValidators.length];
1019
+ validatorFees.set(validator, validatorFees.get(validator) + 1n);
1020
+ }
1021
+ const payments = [...validatorFees.entries()]
1022
+ .filter(([, amount]) => amount > 0n)
1023
+ .map(([to, amount]) => ({ to, amount }));
1024
+ return { burned, validatorFees, payments };
1025
+ };
1026
+ const calculateFee = async (transaction, format = false) => {
1027
+ transaction = await new TransactionMessage(transaction);
1028
+ const encodedBytes = BigInt(transaction.encoded.length);
1029
+ const units = (encodedBytes + TRANSACTION_FEE_BYTES - 1n) / TRANSACTION_FEE_BYTES;
1030
+ const fee = units * TRANSACTION_FEE_UNIT;
1031
+ return format ? formatUnits(fee.toString()) : fee;
1032
+ };
1033
+
77
1034
  class LittlePubSub {
78
1035
  subscribers = new Map();
79
1036
  verbose;
@@ -254,7 +1211,7 @@ const runTask = async (id, taskName, input) => {
254
1211
  });
255
1212
  }
256
1213
  };
257
- const _executeTransaction = async (transaction) => {
1214
+ const _executeTransaction = async (transaction, validators, feesEnabled) => {
258
1215
  const hash = await new TransactionMessage(transaction).hash();
259
1216
  if (latestTransactions.includes(hash)) {
260
1217
  throw new Error(`double transaction found: ${hash}`);
@@ -264,6 +1221,11 @@ const _executeTransaction = async (transaction) => {
264
1221
  const { from, to, method, params, nonce } = transaction;
265
1222
  globalThis.msg = createMessage(from, to);
266
1223
  globalThis.state = await createState();
1224
+ if (feesEnabled) {
1225
+ const fee = BigInt(await calculateFee(transaction));
1226
+ const { payments, burned } = distributeTransactionFee(fee, hash, validators);
1227
+ await _.collectFee({ from, payments, burned });
1228
+ }
267
1229
  await _.execute({ contract: to, method, params });
268
1230
  worker.postMessage({
269
1231
  type: 'transactionLoaded',
@@ -303,9 +1265,11 @@ const _ = {
303
1265
  });
304
1266
  }
305
1267
  },
306
- execute: async ({ contract, method, params }) => {
1268
+ execute: async ({ contract, method, params, sender }) => {
307
1269
  try {
308
1270
  let result;
1271
+ if (sender)
1272
+ globalThis.msg = createMessage(sender, contract);
309
1273
  // don't execute the method on a proxy
310
1274
  if (contracts[contract].fallback) {
311
1275
  result = await contracts[contract].fallback(method, params);
@@ -345,6 +1309,24 @@ const _ = {
345
1309
  `);
346
1310
  }
347
1311
  },
1312
+ collectFee: ({ from, payments, burned = 0n }) => {
1313
+ burned = BigInt(burned);
1314
+ const total = payments.reduce((sum, payment) => sum + BigInt(payment.amount), burned);
1315
+ const balance = BigInt(contracts[nativeToken$2].balanceOf(from) || 0n);
1316
+ if (balance < total)
1317
+ throw new Error(`insufficient balance for transaction fee: need ${total}, got ${balance}`);
1318
+ globalThis.msg = createMessage(from, nativeToken$2);
1319
+ for (const payment of payments) {
1320
+ const amount = BigInt(payment.amount);
1321
+ if (amount > 0n)
1322
+ contracts[nativeToken$2].transfer(from, payment.to, amount);
1323
+ }
1324
+ if (burned > 0n) {
1325
+ globalThis.msg = createMessage(contracts[nativeToken$2].creator, nativeToken$2);
1326
+ contracts[nativeToken$2].burn(from, burned);
1327
+ }
1328
+ return total;
1329
+ },
348
1330
  init: async (message) => {
349
1331
  let { peerid, fromState, state, info } = message;
350
1332
  if (info)
@@ -447,16 +1429,17 @@ const _ = {
447
1429
  }
448
1430
  return message;
449
1431
  }));
450
- const priority = transactions
451
- .filter((transaction) => transaction.priority)
452
- ?.sort((a, b) => a.nonce - b.nonce);
453
- if (priority.length > 0)
454
- for (const transaction of priority) {
455
- await _executeTransaction(transaction);
456
- }
457
- await Promise.all(transactions
458
- .filter((transaction) => !transaction.priority)
459
- .map(async (transaction) => _executeTransaction(transaction)));
1432
+ const validators = block.validators.map(({ address }) => address);
1433
+ transactions.sort((a, b) => {
1434
+ if (a.priority !== b.priority)
1435
+ return a.priority ? -1 : 1;
1436
+ const left = BigInt(a.nonce);
1437
+ const right = BigInt(b.nonce);
1438
+ return left < right ? -1 : left > right ? 1 : 0;
1439
+ });
1440
+ const feesEnabled = supportsTransactionFees(block.protocolVersion);
1441
+ for (const transaction of transactions)
1442
+ await _executeTransaction(transaction, validators, feesEnabled);
460
1443
  block.loaded = true;
461
1444
  worker.postMessage({
462
1445
  type: 'debug',
@@ -521,6 +1504,9 @@ worker.onmessage(({ id, type, input }) => {
521
1504
  case 'addLoadedBlock':
522
1505
  runTask(id, 'addLoadedBlock', input);
523
1506
  break;
1507
+ case 'collectFee':
1508
+ runTask(id, 'collectFee', input);
1509
+ break;
524
1510
  case 'contracts':
525
1511
  respond(id, contracts);
526
1512
  break;