@btc-vision/btc-runtime 1.10.2 → 1.10.4

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.
@@ -19,7 +19,15 @@ export class SafeMath {
19
19
  * Constant representing zero in u256 format.
20
20
  * Useful for comparisons and initializations.
21
21
  */
22
- public static ZERO: u256 = u256.fromU32(0);
22
+ public static readonly ZERO: u256 = u256.Zero;
23
+
24
+ // GAS OPTIMIZATION: Static constants to avoid allocation in hot loops
25
+ public static readonly ONE: u256 = u256.One;
26
+ private static readonly CONST_2: u256 = u256.fromU32(2);
27
+ private static readonly CONST_3: u256 = u256.fromU32(3);
28
+ private static readonly CONST_10: u256 = u256.fromU32(10);
29
+ private static readonly LN2_SCALED: u64 = 693147; // ln(2)*1e6
30
+ private static readonly SCALE_1E6: u64 = 1_000_000;
23
31
 
24
32
  // ==================== Addition Operations ====================
25
33
 
@@ -42,7 +50,7 @@ export class SafeMath {
42
50
  * - Gas efficient for small values
43
51
  */
44
52
  public static add(a: u256, b: u256): u256 {
45
- const c: u256 = u256.add(a, b);
53
+ const c = u256.add(a, b);
46
54
  if (c < a) {
47
55
  throw new Revert('SafeMath: addition overflow');
48
56
  }
@@ -62,7 +70,7 @@ export class SafeMath {
62
70
  * - More gas efficient than u256 for values that fit in 128 bits
63
71
  */
64
72
  public static add128(a: u128, b: u128): u128 {
65
- const c: u128 = u128.add(a, b);
73
+ const c = u128.add(a, b);
66
74
  if (c < a) {
67
75
  throw new Revert('SafeMath: addition overflow');
68
76
  }
@@ -82,7 +90,7 @@ export class SafeMath {
82
90
  * - Most gas efficient for small values
83
91
  */
84
92
  public static add64(a: u64, b: u64): u64 {
85
- const c: u64 = a + b;
93
+ const c = a + b;
86
94
  if (c < a) {
87
95
  throw new Revert('SafeMath: addition overflow');
88
96
  }
@@ -106,7 +114,7 @@ export class SafeMath {
106
114
  * ```
107
115
  *
108
116
  * @warning Unsigned integers cannot represent negative values. Always ensure a >= b
109
- * before calling, or handle the potential revert in your contract logic.
117
+ * before calling, or handle the potential revert in your contract logic.
110
118
  *
111
119
  * @remarks
112
120
  * - Result is always non-negative
@@ -165,7 +173,7 @@ export class SafeMath {
165
173
  * ```
166
174
  *
167
175
  * @security The overflow check performs division after multiplication, which is safe
168
- * because if overflow occurred, the division result won't equal the original operand.
176
+ * because if overflow occurred, the division result won't equal the original operand.
169
177
  *
170
178
  * @remarks
171
179
  * - Returns 0 if either operand is 0
@@ -176,7 +184,8 @@ export class SafeMath {
176
184
  if (a.isZero() || b.isZero()) return u256.Zero;
177
185
 
178
186
  const c = u256.mul(a, b);
179
- const d = SafeMath.div(c, a);
187
+ // Use native div, it's faster than manual loop and handles edge cases correctly
188
+ const d = u256.div(c, a);
180
189
 
181
190
  if (u256.ne(d, b)) throw new Revert('SafeMath: multiplication overflow');
182
191
 
@@ -195,7 +204,7 @@ export class SafeMath {
195
204
  if (a.isZero() || b.isZero()) return u128.Zero;
196
205
 
197
206
  const c = u128.mul(a, b);
198
- const d = SafeMath.div128(c, a);
207
+ const d = u128.div(c, a);
199
208
 
200
209
  if (u128.ne(d, b)) throw new Revert('SafeMath: multiplication overflow');
201
210
 
@@ -240,10 +249,10 @@ export class SafeMath {
240
249
  * ```
241
250
  *
242
251
  * @warning Integer division always rounds down. For 10/3, the result is 3, not 3.333...
243
- * The remainder (1 in this case) is lost. Use `mod` to get the remainder.
252
+ * The remainder (1 in this case) is lost. Use `mod` to get the remainder.
244
253
  *
245
254
  * @security Division by zero is always checked and will revert the transaction,
246
- * preventing undefined behavior or exploits.
255
+ * preventing undefined behavior or exploits.
247
256
  *
248
257
  * @remarks
249
258
  * - Always rounds down (floor division)
@@ -257,33 +266,12 @@ export class SafeMath {
257
266
  }
258
267
 
259
268
  if (a.isZero()) {
260
- return new u256();
261
- }
262
-
263
- if (u256.lt(a, b)) {
264
- return new u256(); // Return 0 if a < b
265
- }
266
-
267
- if (u256.eq(a, b)) {
268
- return new u256(1); // Return 1 if a == b
269
- }
270
-
271
- let n = a.clone();
272
- let d = b.clone();
273
- let result = new u256();
274
-
275
- const shift = u256.clz(d) - u256.clz(n);
276
- d = SafeMath.shl(d, shift); // align d with n by shifting left
277
-
278
- for (let i = shift; i >= 0; i--) {
279
- if (u256.ge(n, d)) {
280
- n = u256.sub(n, d);
281
- result = u256.or(result, SafeMath.shl(u256.One, i));
282
- }
283
- d = u256.shr(d, 1); // restore d to original by shifting right
269
+ return u256.Zero;
284
270
  }
285
271
 
286
- return result;
272
+ // GAS OPTIMIZATION: Use native as-bignum division instead of manual shift loop
273
+ // The native implementation is likely optimized in AssemblyScript/WASM
274
+ return u256.div(a, b);
287
275
  }
288
276
 
289
277
  /**
@@ -295,7 +283,7 @@ export class SafeMath {
295
283
  * @throws {Revert} When b is zero
296
284
  *
297
285
  * @warning Integer division truncates decimals. Consider scaling your values
298
- * before division if you need to preserve precision.
286
+ * before division if you need to preserve precision.
299
287
  */
300
288
  public static div128(a: u128, b: u128): u128 {
301
289
  if (b.isZero()) {
@@ -303,33 +291,10 @@ export class SafeMath {
303
291
  }
304
292
 
305
293
  if (a.isZero()) {
306
- return new u128();
307
- }
308
-
309
- if (u128.lt(a, b)) {
310
- return new u128(); // Return 0 if a < b
311
- }
312
-
313
- if (u128.eq(a, b)) {
314
- return new u128(1); // Return 1 if a == b
315
- }
316
-
317
- let n = a.clone();
318
- let d = b.clone();
319
- let result = new u128();
320
-
321
- const shift = u128.clz(d) - u128.clz(n);
322
- d = SafeMath.shl128(d, shift);
323
-
324
- for (let i = shift; i >= 0; i--) {
325
- if (u128.ge(n, d)) {
326
- n = u128.sub(n, d);
327
- result = u128.or(result, SafeMath.shl128(u128.One, i));
328
- }
329
- d = u128.shr(d, 1);
294
+ return u128.Zero;
330
295
  }
331
296
 
332
- return result;
297
+ return u128.div(a, b);
333
298
  }
334
299
 
335
300
  /**
@@ -376,8 +341,8 @@ export class SafeMath {
376
341
  * ```
377
342
  *
378
343
  * @security The modulo operation is commonly used in access control patterns
379
- * (e.g., round-robin selection). Ensure the modulus is never zero
380
- * and be aware that patterns in modulo operations can be predictable.
344
+ * (e.g., round-robin selection). Ensure the modulus is never zero
345
+ * and be aware that patterns in modulo operations can be predictable.
381
346
  *
382
347
  * @remarks
383
348
  * - Result is always in range [0, b-1]
@@ -385,13 +350,11 @@ export class SafeMath {
385
350
  * - a = (a/b)*b + (a%b)
386
351
  */
387
352
  public static mod(a: u256, b: u256): u256 {
388
- if (u256.eq(b, u256.Zero)) {
353
+ if (b.isZero()) {
389
354
  throw new Revert('SafeMath: modulo by zero');
390
355
  }
391
-
392
- const divResult = SafeMath.div(a, b);
393
- const product = SafeMath.mul(divResult, b);
394
- return SafeMath.sub(a, product);
356
+ // Use optimized arithmetic: a - (a/b)*b
357
+ return u256.sub(a, u256.mul(u256.div(a, b), b));
395
358
  }
396
359
 
397
360
  /**
@@ -410,14 +373,14 @@ export class SafeMath {
410
373
  * ```
411
374
  *
412
375
  * @warning This function automatically reduces inputs modulo m before multiplication.
413
- * This means mulmod(2m, x, m) returns 0, not because 2m*x is computed,
414
- * but because 2m is reduced to 0 first. This is mathematically correct
415
- * for modular arithmetic but may surprise developers expecting raw multiplication.
376
+ * This means mulmod(2m, x, m) returns 0, not because 2m*x is computed,
377
+ * but because 2m is reduced to 0 first. This is mathematically correct
378
+ * for modular arithmetic but may surprise developers expecting raw multiplication.
416
379
  *
417
380
  * @security Critical for cryptographic operations. The automatic modular reduction
418
- * of inputs ensures all operations occur within the field Z/mZ, preventing
419
- * overflow attacks. Used extensively in ECC scalar multiplication and
420
- * RSA operations.
381
+ * of inputs ensures all operations occur within the field Z/mZ, preventing
382
+ * overflow attacks. Used extensively in ECC scalar multiplication and
383
+ * RSA operations.
421
384
  *
422
385
  * @remarks
423
386
  * - Critical for cryptographic operations (RSA, ECC)
@@ -428,18 +391,20 @@ export class SafeMath {
428
391
  * - Inputs are automatically reduced: a = a % m, b = b % m
429
392
  */
430
393
  public static mulmod(a: u256, b: u256, modulus: u256): u256 {
431
- if (u256.eq(modulus, u256.Zero)) throw new Revert('SafeMath: modulo by zero');
394
+ if (modulus.isZero()) throw new Revert('SafeMath: modulo by zero');
432
395
 
433
396
  // Keep invariants: 0 <= a,b < modulus
434
- a = SafeMath.mod(a, modulus);
435
- b = SafeMath.mod(b, modulus);
397
+ if (u256.ge(a, modulus)) a = SafeMath.mod(a, modulus);
398
+ if (u256.ge(b, modulus)) b = SafeMath.mod(b, modulus);
399
+
436
400
  if (a.isZero() || b.isZero()) return u256.Zero;
437
401
 
438
402
  let res = u256.Zero;
439
403
 
440
- // LSB-first ladder: at most 256 iterations
404
+ // Optimized LSB-first ladder
441
405
  while (!b.isZero()) {
442
- if (u256.ne(u256.and(b, u256.One), u256.Zero)) {
406
+ // if (b & 1) using raw access for speed
407
+ if ((b.lo1 & 1) != 0) {
443
408
  res = SafeMath.addModNoCarry(res, a, modulus);
444
409
  }
445
410
  b = u256.shr(b, 1);
@@ -458,9 +423,9 @@ export class SafeMath {
458
423
  * @param p - The modulus (must be > 1)
459
424
  * @returns x such that (k * x) % p = 1
460
425
  * @throws {Revert} When:
461
- * - p is 0 or 1 (invalid modulus)
462
- * - k is 0 (zero has no inverse)
463
- * - gcd(k, p) ≠ 1 (no inverse exists when k and p are not coprime)
426
+ * - p is 0 or 1 (invalid modulus)
427
+ * - k is 0 (zero has no inverse)
428
+ * - gcd(k, p) ≠ 1 (no inverse exists when k and p are not coprime)
464
429
  *
465
430
  * @example
466
431
  * ```typescript
@@ -470,13 +435,13 @@ export class SafeMath {
470
435
  * ```
471
436
  *
472
437
  * @warning Only works when gcd(k, p) = 1. For prime p, all non-zero k < p have inverses.
473
- * For composite moduli, check coprimality before calling.
438
+ * For composite moduli, check coprimality before calling.
474
439
  *
475
440
  * @security Essential for cryptographic protocols. Used in:
476
- * - RSA private key generation (d = e^(-1) mod φ(n))
477
- * - ECDSA signature generation (s = k^(-1)(h + rd) mod n)
478
- * - Point division in elliptic curves
479
- * Incorrect inverse computation breaks these protocols entirely.
441
+ * - RSA private key generation (d = e^(-1) mod φ(n))
442
+ * - ECDSA signature generation (s = k^(-1)(h + rd) mod n)
443
+ * - Point division in elliptic curves
444
+ * Incorrect inverse computation breaks these protocols entirely.
480
445
  *
481
446
  * @remarks
482
447
  * - Essential for RSA key generation and ECC operations
@@ -486,75 +451,62 @@ export class SafeMath {
486
451
  * - Common in cryptographic signatures and key exchanges
487
452
  */
488
453
  public static modInverse(k: u256, p: u256): u256 {
489
- // Input validation
490
- if (p.isZero() || u256.eq(p, u256.One)) {
454
+ if (p.isZero() || u256.eq(p, SafeMath.ONE)) {
491
455
  throw new Revert('SafeMath: modulus must be > 1');
492
456
  }
493
-
494
457
  if (k.isZero()) {
495
458
  throw new Revert('SafeMath: no inverse for zero');
496
459
  }
497
460
 
498
- let s: u256 = u256.Zero;
499
- let old_s: u256 = u256.One;
500
- let s_negative: boolean = false;
501
- let old_s_negative: boolean = false;
502
- let r: u256 = p.clone();
503
- let old_r: u256 = k.clone();
461
+ // Extended Euclidean Algo
462
+ let s = u256.Zero;
463
+ let old_s = u256.One;
464
+ let s_negative = false;
465
+ let old_s_negative = false;
466
+ let r = p.clone();
467
+ let old_r = k.clone();
504
468
 
505
469
  while (!r.isZero()) {
506
- const quotient = SafeMath.div(old_r, r);
470
+ const quotient = u256.div(old_r, r);
507
471
 
508
472
  // Update r
509
- const tmp_r = r.clone();
510
- r = SafeMath.sub(old_r, SafeMath.mul(quotient, r));
511
- old_r = tmp_r;
512
-
513
- // Update s with sign tracking
514
- const tmp_s = s.clone();
515
- const tmp_s_negative = s_negative;
516
- const product = SafeMath.mul(quotient, s);
517
-
518
- if (!old_s_negative && !s_negative) {
519
- if (u256.ge(old_s, product)) {
520
- s = SafeMath.sub(old_s, product);
521
- s_negative = false;
522
- } else {
523
- s = SafeMath.sub(product, old_s);
524
- s_negative = true;
525
- }
526
- } else if (old_s_negative && s_negative) {
527
- if (u256.ge(product, old_s)) {
528
- s = SafeMath.sub(product, old_s);
529
- s_negative = false;
473
+ const next_r = u256.sub(old_r, u256.mul(quotient, r));
474
+ old_r = r;
475
+ r = next_r;
476
+
477
+ // Update s
478
+ const prod = u256.mul(quotient, s);
479
+ let next_s: u256;
480
+ let next_s_negative: boolean;
481
+
482
+ // Logic optimized to avoid excessive object allocation
483
+ if (old_s_negative == s_negative) {
484
+ if (u256.ge(old_s, prod)) {
485
+ next_s = u256.sub(old_s, prod);
486
+ next_s_negative = old_s_negative;
530
487
  } else {
531
- s = SafeMath.sub(old_s, product);
532
- s_negative = true;
488
+ next_s = u256.sub(prod, old_s);
489
+ next_s_negative = !old_s_negative;
533
490
  }
534
- } else if (!old_s_negative && s_negative) {
535
- s = SafeMath.add(old_s, product);
536
- s_negative = false;
537
491
  } else {
538
- s = SafeMath.add(old_s, product);
539
- s_negative = true;
492
+ next_s = u256.add(old_s, prod);
493
+ next_s_negative = old_s_negative;
540
494
  }
541
495
 
542
- old_s = tmp_s;
543
- old_s_negative = tmp_s_negative;
496
+ old_s = s;
497
+ old_s_negative = s_negative;
498
+ s = next_s;
499
+ s_negative = next_s_negative;
544
500
  }
545
501
 
546
- // Check if inverse exists (gcd must be 1)
547
- if (!u256.eq(old_r, u256.One)) {
502
+ if (!u256.eq(old_r, SafeMath.ONE)) {
548
503
  throw new Revert('SafeMath: no modular inverse exists');
549
504
  }
550
505
 
551
- // Handle negative values
552
506
  if (old_s_negative) {
553
- const mod_result = SafeMath.mod(old_s, p);
554
- if (mod_result.isZero()) {
555
- return u256.Zero;
556
- }
557
- return SafeMath.sub(p, mod_result);
507
+ const mod_res = SafeMath.mod(old_s, p);
508
+ if (mod_res.isZero()) return u256.Zero;
509
+ return u256.sub(p, mod_res);
558
510
  }
559
511
 
560
512
  return SafeMath.mod(old_s, p);
@@ -576,16 +528,16 @@ export class SafeMath {
576
528
  * ```
577
529
  *
578
530
  * @warning CRITICAL: Unlike ALL other SafeMath operations, bit shifts do NOT throw on overflow!
579
- * Bits shifted beyond the type width are SILENTLY LOST. This is intentional
580
- * behavior that matches CPU bit shift semantics, but differs philosophically
581
- * from other SafeMath operations which fail safely on overflow.
531
+ * Bits shifted beyond the type width are SILENTLY LOST. This is intentional
532
+ * behavior that matches CPU bit shift semantics, but differs philosophically
533
+ * from other SafeMath operations which fail safely on overflow.
582
534
  *
583
535
  * @security If you need overflow detection for bit shifts, implement it manually:
584
536
  * ```typescript
585
537
  * const shifted = SafeMath.shl(value, n);
586
538
  * const restored = SafeMath.shr(shifted, n);
587
539
  * if (!u256.eq(restored, value)) {
588
- * throw new Revert('Shift overflow detected');
540
+ * throw new Revert('Shift overflow detected');
589
541
  * }
590
542
  * ```
591
543
  *
@@ -598,23 +550,43 @@ export class SafeMath {
598
550
  * - Commonly used in bit manipulation and flag operations
599
551
  */
600
552
  public static shl(value: u256, shift: i32): u256 {
601
- if (shift <= 0) {
602
- return shift == 0 ? value.clone() : new u256();
603
- }
604
-
605
- if (shift >= 256) {
606
- return new u256();
607
- }
553
+ if (shift <= 0) return shift == 0 ? value.clone() : u256.Zero;
554
+ if (shift >= 256) return u256.Zero;
608
555
 
609
556
  shift &= 255;
610
557
 
611
- const bitsPerSegment = 64;
612
- const segmentShift = (shift / bitsPerSegment) | 0;
613
- const bitShift = shift % bitsPerSegment;
558
+ // GAS OPTIMIZATION: Unrolled manual shifting avoids array allocation of segments
559
+ const bits = 64;
560
+ const segShift = (shift / bits) | 0;
561
+ const bitShift = shift % bits;
562
+ const invShift = bits - bitShift;
563
+
564
+ let r0: u64 = 0,
565
+ r1: u64 = 0,
566
+ r2: u64 = 0,
567
+ r3: u64 = 0;
568
+ const i0 = value.lo1,
569
+ i1 = value.lo2,
570
+ i2 = value.hi1,
571
+ i3 = value.hi2;
572
+
573
+ if (segShift == 0) {
574
+ r0 = i0 << bitShift;
575
+ r1 = (i1 << bitShift) | (bitShift == 0 ? 0 : i0 >>> invShift);
576
+ r2 = (i2 << bitShift) | (bitShift == 0 ? 0 : i1 >>> invShift);
577
+ r3 = (i3 << bitShift) | (bitShift == 0 ? 0 : i2 >>> invShift);
578
+ } else if (segShift == 1) {
579
+ r1 = i0 << bitShift;
580
+ r2 = (i1 << bitShift) | (bitShift == 0 ? 0 : i0 >>> invShift);
581
+ r3 = (i2 << bitShift) | (bitShift == 0 ? 0 : i1 >>> invShift);
582
+ } else if (segShift == 2) {
583
+ r2 = i0 << bitShift;
584
+ r3 = (i1 << bitShift) | (bitShift == 0 ? 0 : i0 >>> invShift);
585
+ } else if (segShift == 3) {
586
+ r3 = i0 << bitShift;
587
+ }
614
588
 
615
- const segments = [value.lo1, value.lo2, value.hi1, value.hi2];
616
- const result = SafeMath.shlSegment(segments, segmentShift, bitShift, bitsPerSegment, 4);
617
- return new u256(result[0], result[1], result[2], result[3]);
589
+ return new u256(r0, r1, r2, r3);
618
590
  }
619
591
 
620
592
  /**
@@ -631,23 +603,28 @@ export class SafeMath {
631
603
  * - Overflow bits are truncated without error
632
604
  */
633
605
  public static shl128(value: u128, shift: i32): u128 {
634
- if (shift <= 0) {
635
- return shift == 0 ? value.clone() : new u128();
636
- }
637
-
638
- if (shift >= 128) {
639
- return new u128();
640
- }
606
+ if (shift <= 0) return shift == 0 ? value.clone() : u128.Zero;
607
+ if (shift >= 128) return u128.Zero;
641
608
 
642
609
  shift &= 127;
610
+ const bits = 64;
611
+ const segShift = (shift / bits) | 0;
612
+ const bitShift = shift % bits;
613
+ const invShift = bits - bitShift;
614
+
615
+ let r0: u64 = 0,
616
+ r1: u64 = 0;
617
+ const i0 = value.lo,
618
+ i1 = value.hi;
619
+
620
+ if (segShift == 0) {
621
+ r0 = i0 << bitShift;
622
+ r1 = (i1 << bitShift) | (bitShift == 0 ? 0 : i0 >>> invShift);
623
+ } else if (segShift == 1) {
624
+ r1 = i0 << bitShift;
625
+ }
643
626
 
644
- const bitsPerSegment = 64;
645
- const segmentShift = (shift / bitsPerSegment) | 0;
646
- const bitShift = shift % bitsPerSegment;
647
-
648
- const segments = [value.lo, value.hi];
649
- const result = SafeMath.shlSegment(segments, segmentShift, bitShift, bitsPerSegment, 2);
650
- return new u128(result[0], result[1]);
627
+ return new u128(r0, r1);
651
628
  }
652
629
 
653
630
  /**
@@ -727,11 +704,11 @@ export class SafeMath {
727
704
  * ```
728
705
  *
729
706
  * @warning Returns 1 for inputs 1, 2, and 3 (not just 1). This is because
730
- * floor(√2) = floor(√3) = 1. Be aware of this when working with small values.
707
+ * floor(√2) = floor(√3) = 1. Be aware of this when working with small values.
731
708
  *
732
709
  * @security No overflow possible as sqrt(u256.Max) < 2^128. Used in various DeFi
733
- * protocols for computing prices from liquidity pools (e.g., Uniswap V2's
734
- * geometric mean price calculation).
710
+ * protocols for computing prices from liquidity pools (e.g., Uniswap V2's
711
+ * geometric mean price calculation).
735
712
  *
736
713
  * @remarks
737
714
  * - Uses Newton-Raphson method for values > 3
@@ -742,26 +719,22 @@ export class SafeMath {
742
719
  * - Converges in O(log log n) iterations
743
720
  */
744
721
  public static sqrt(y: u256): u256 {
745
- if (u256.gt(y, u256.fromU32(3))) {
722
+ if (u256.gt(y, SafeMath.CONST_3)) {
746
723
  let z = y;
747
724
 
748
- const u246_2 = u256.fromU32(2);
749
-
750
- const d = SafeMath.div(y, u246_2);
751
- let x = SafeMath.add(d, u256.One);
725
+ // Initial guess: y / 2 + 1
726
+ let x = u256.add(u256.div(y, SafeMath.CONST_2), SafeMath.ONE);
752
727
 
753
728
  while (u256.lt(x, z)) {
754
729
  z = x;
755
-
756
- const u = SafeMath.div(y, x);
757
- const y2 = u256.add(u, x);
758
-
759
- x = SafeMath.div(y2, u246_2);
730
+ const divResult = u256.div(y, x);
731
+ const sum = u256.add(divResult, x);
732
+ x = u256.div(sum, SafeMath.CONST_2);
760
733
  }
761
734
 
762
735
  return z;
763
- } else if (!u256.eq(y, u256.Zero)) {
764
- return u256.One;
736
+ } else if (!y.isZero()) {
737
+ return SafeMath.ONE;
765
738
  } else {
766
739
  return u256.Zero;
767
740
  }
@@ -782,12 +755,12 @@ export class SafeMath {
782
755
  * ```
783
756
  *
784
757
  * @warning Large bases with even small exponents can overflow. For example,
785
- * (2^128)^2 = 2^256 which overflows. Always consider the magnitude
786
- * of your inputs.
758
+ * (2^128)^2 = 2^256 which overflows. Always consider the magnitude
759
+ * of your inputs.
787
760
  *
788
761
  * @security Used in compound interest calculations and bonding curves. Be extremely
789
- * careful with user-supplied exponents as they can easily cause DoS through
790
- * gas exhaustion (large exponents) or overflows.
762
+ * careful with user-supplied exponents as they can easily cause DoS through
763
+ * gas exhaustion (large exponents) or overflows.
791
764
  *
792
765
  * @remarks
793
766
  * - Uses binary exponentiation (square-and-multiply) for O(log n) efficiency
@@ -796,21 +769,24 @@ export class SafeMath {
796
769
  * - Gas cost increases with exponent bit count
797
770
  */
798
771
  public static pow(base: u256, exponent: u256): u256 {
799
- if (exponent.isZero()) return u256.One;
772
+ if (exponent.isZero()) return SafeMath.ONE;
800
773
  if (base.isZero()) return u256.Zero;
801
- if (u256.eq(base, u256.One)) return u256.One;
774
+ if (u256.eq(base, SafeMath.ONE)) return SafeMath.ONE;
802
775
 
803
- let result: u256 = u256.One;
804
- while (u256.gt(exponent, u256.Zero)) {
805
- if (u256.ne(u256.and(exponent, u256.One), u256.Zero)) {
806
- result = SafeMath.mul(result, base);
776
+ let result: u256 = SafeMath.ONE;
777
+ let b = base;
778
+ let e = exponent;
779
+
780
+ while (u256.gt(e, u256.Zero)) {
781
+ // Check LSB using bitwise for speed
782
+ if ((e.lo1 & 1) != 0) {
783
+ result = SafeMath.mul(result, b);
807
784
  }
808
785
 
809
- exponent = u256.shr(exponent, 1);
786
+ e = u256.shr(e, 1);
810
787
 
811
- // Only square the base if there are more bits to process
812
- if (u256.gt(exponent, u256.Zero)) {
813
- base = SafeMath.mul(base, base);
788
+ if (u256.gt(e, u256.Zero)) {
789
+ b = SafeMath.mul(b, b);
814
790
  }
815
791
  }
816
792
  return result;
@@ -830,8 +806,8 @@ export class SafeMath {
830
806
  * ```
831
807
  *
832
808
  * @security Commonly used for token decimal scaling. Ensure exponent values
833
- * come from trusted sources (e.g., immutable token decimals) rather
834
- * than user input to prevent reverts.
809
+ * come from trusted sources (e.g., immutable token decimals) rather
810
+ * than user input to prevent reverts.
835
811
  *
836
812
  * @remarks
837
813
  * - Optimized specifically for base 10 calculations
@@ -844,11 +820,9 @@ export class SafeMath {
844
820
  throw new Revert('SafeMath: exponent too large, would overflow');
845
821
  }
846
822
 
847
- const ten = u256.fromU32(10);
848
-
849
- let result: u256 = u256.One;
823
+ let result: u256 = SafeMath.ONE;
850
824
  for (let i: u8 = 0; i < exponent; i++) {
851
- result = SafeMath.mul(result, ten);
825
+ result = SafeMath.mul(result, SafeMath.CONST_10);
852
826
  }
853
827
  return result;
854
828
  }
@@ -941,7 +915,7 @@ export class SafeMath {
941
915
  */
942
916
  @inline
943
917
  public static isEven(a: u256): bool {
944
- return u256.and(a, u256.One) == u256.Zero;
918
+ return (a.lo1 & 1) == 0;
945
919
  }
946
920
 
947
921
  /**
@@ -952,7 +926,7 @@ export class SafeMath {
952
926
  * @throws {Revert} When value equals u256.Max (would overflow)
953
927
  *
954
928
  * @warning At u256.Max, incrementing would wrap to 0. This function throws
955
- * instead to prevent silent wraparound errors.
929
+ * instead to prevent silent wraparound errors.
956
930
  *
957
931
  * @remarks
958
932
  * - Equivalent to add(value, 1) but potentially more efficient
@@ -973,14 +947,14 @@ export class SafeMath {
973
947
  * @throws {Revert} When value equals 0 (would underflow)
974
948
  *
975
949
  * @warning At 0, decrementing would wrap to u256.Max. This function throws
976
- * instead to prevent silent wraparound errors.
950
+ * instead to prevent silent wraparound errors.
977
951
  *
978
952
  * @remarks
979
953
  * - Equivalent to sub(value, 1) but potentially more efficient
980
954
  * - Safe against underflow at zero
981
955
  */
982
956
  public static dec(value: u256): u256 {
983
- if (u256.eq(value, u256.Zero)) {
957
+ if (value.isZero()) {
984
958
  throw new Revert('SafeMath: decrement underflow');
985
959
  }
986
960
  return value.preDec();
@@ -1002,15 +976,15 @@ export class SafeMath {
1002
976
  * ```
1003
977
  *
1004
978
  * @warning Returns 0 for both input 0 and input 1. While log2(0) is mathematically
1005
- * undefined and log2(1) = 0, this implementation returns 0 for both cases
1006
- * to avoid reverts and maintain gas efficiency in smart contracts. Callers
1007
- * requiring mathematical precision should handle these edge cases explicitly.
979
+ * undefined and log2(1) = 0, this implementation returns 0 for both cases
980
+ * to avoid reverts and maintain gas efficiency in smart contracts. Callers
981
+ * requiring mathematical precision should handle these edge cases explicitly.
1008
982
  *
1009
983
  * @security Extensively tested for monotonicity and consistency. Critical for:
1010
- * - Binary search algorithms in sorted data structures
1011
- * - Bit manipulation operations requiring position of highest bit
1012
- * - Rough categorization of value magnitudes in O(1) time
1013
- * - Efficient range checks in permission systems
984
+ * - Binary search algorithms in sorted data structures
985
+ * - Bit manipulation operations requiring position of highest bit
986
+ * - Rough categorization of value magnitudes in O(1) time
987
+ * - Efficient range checks in permission systems
1014
988
  *
1015
989
  * @remarks
1016
990
  * - Returns the position of the highest set bit (MSB)
@@ -1021,16 +995,9 @@ export class SafeMath {
1021
995
  * - More efficient than preciseLog when exact precision isn't needed
1022
996
  */
1023
997
  public static approximateLog2(x: u256): u256 {
1024
- // Count the position of the highest bit set
1025
- let n: u256 = u256.Zero;
1026
- let value = x;
1027
-
1028
- while (u256.gt(value, u256.One)) {
1029
- value = u256.shr(value, 1);
1030
- n = SafeMath.add(n, u256.One);
1031
- }
1032
-
1033
- return n;
998
+ const bitLen = SafeMath.bitLength256(x);
999
+ if (bitLen == 0) return u256.Zero;
1000
+ return u256.fromU32(bitLen - 1);
1034
1001
  }
1035
1002
 
1036
1003
  /**
@@ -1052,18 +1019,18 @@ export class SafeMath {
1052
1019
  * ```
1053
1020
  *
1054
1021
  * @warning This function has been extensively tested and validated for accuracy.
1055
- * The maximum error is bounded to 6 units (0.000006) across the entire
1056
- * input domain. While the implementation is production-ready, extreme
1057
- * values near u256 boundaries may experience precision degradation due
1058
- * to the limitations of integer arithmetic at such scales.
1022
+ * The maximum error is bounded to 6 units (0.000006) across the entire
1023
+ * input domain. While the implementation is production-ready, extreme
1024
+ * values near u256 boundaries may experience precision degradation due
1025
+ * to the limitations of integer arithmetic at such scales.
1059
1026
  *
1060
1027
  * @security Critical for DeFi applications including:
1061
- * - Automated Market Makers (AMMs) for price calculations
1062
- * - Interest rate models in lending protocols
1063
- * - Option pricing using Black-Scholes formulas
1064
- * - Bonding curve calculations
1065
- * Incorrect logarithm calculations can lead to severe mispricing,
1066
- * arbitrage opportunities, or protocol insolvency.
1028
+ * - Automated Market Makers (AMMs) for price calculations
1029
+ * - Interest rate models in lending protocols
1030
+ * - Option pricing using Black-Scholes formulas
1031
+ * - Bonding curve calculations
1032
+ * Incorrect logarithm calculations can lead to severe mispricing,
1033
+ * arbitrage opportunities, or protocol insolvency.
1067
1034
  *
1068
1035
  * @remarks
1069
1036
  * - Algorithm: Decomposes x = 2^k * (1 + r) where 0 ≤ r < 1
@@ -1076,7 +1043,7 @@ export class SafeMath {
1076
1043
  * - Monotonicity guaranteed across entire input range
1077
1044
  */
1078
1045
  public static preciseLog(x: u256): u256 {
1079
- if (x.isZero() || u256.eq(x, u256.One)) {
1046
+ if (x.isZero() || u256.eq(x, SafeMath.ONE)) {
1080
1047
  return u256.Zero;
1081
1048
  }
1082
1049
 
@@ -1085,28 +1052,23 @@ export class SafeMath {
1085
1052
  return u256.Zero;
1086
1053
  }
1087
1054
 
1088
- // integer part of log2(x)
1089
1055
  const k: u32 = bitLen - 1;
1090
- const LN2_SCALED = u256.fromU64(693147); // ln(2)*1e6
1091
- const base: u256 = SafeMath.mul(u256.fromU32(k), LN2_SCALED);
1056
+ const base: u256 = SafeMath.mul(u256.fromU32(k), u256.fromU64(SafeMath.LN2_SCALED));
1092
1057
 
1093
- // 2^k
1094
- const pow2k = SafeMath.shl(u256.One, <i32>k);
1058
+ const pow2k = SafeMath.shl(SafeMath.ONE, <i32>k);
1095
1059
  const xPrime = SafeMath.sub(x, pow2k);
1096
1060
 
1097
1061
  if (xPrime.isZero()) {
1098
1062
  return base;
1099
1063
  }
1100
1064
 
1101
- // rScaled = ((x - 2^k)*1e6)/2^k
1102
- const xPrimeTimes1e6 = SafeMath.mul(xPrime, u256.fromU64(1_000_000));
1065
+ const xPrimeTimes1e6 = SafeMath.mul(xPrime, u256.fromU64(SafeMath.SCALE_1E6));
1103
1066
  const rScaled = SafeMath.div(xPrimeTimes1e6, pow2k);
1104
1067
 
1105
1068
  if (u256.gt(rScaled, u256.fromU64(u64.MAX_VALUE))) {
1106
1069
  throw new Revert('SafeMath: rScaled overflow, input too large');
1107
1070
  }
1108
1071
 
1109
- // approximate ln(1 + r)
1110
1072
  const frac: u64 = SafeMath.polyLn1p3(rScaled.toU64());
1111
1073
 
1112
1074
  return SafeMath.add(base, u256.fromU64(frac));
@@ -1127,15 +1089,15 @@ export class SafeMath {
1127
1089
  * ```
1128
1090
  *
1129
1091
  * @warning Uses step-wise approximation based on bit length. The result has
1130
- * discrete jumps at powers of 2, with constant values between them.
1131
- * Maximum error is ln(2) ≈ 0.693 (scaled: 693,147). For smooth,
1132
- * continuous logarithm curves required in pricing models, use preciseLog.
1092
+ * discrete jumps at powers of 2, with constant values between them.
1093
+ * Maximum error is ln(2) ≈ 0.693 (scaled: 693,147). For smooth,
1094
+ * continuous logarithm curves required in pricing models, use preciseLog.
1133
1095
  *
1134
1096
  * @security Suitable for applications where monotonicity matters more than precision:
1135
- * - Rough categorization of token amounts
1136
- * - Tier-based reward systems
1137
- * - Quick magnitude comparisons
1138
- * Not recommended for precise financial calculations or smooth curves.
1097
+ * - Rough categorization of token amounts
1098
+ * - Tier-based reward systems
1099
+ * - Quick magnitude comparisons
1100
+ * Not recommended for precise financial calculations or smooth curves.
1139
1101
  *
1140
1102
  * @remarks
1141
1103
  * - Algorithm: ln(x) ≈ (bitLength(x) - 1) * ln(2)
@@ -1146,7 +1108,7 @@ export class SafeMath {
1146
1108
  * - Monotonically non-decreasing (required for security)
1147
1109
  */
1148
1110
  public static approxLog(x: u256): u256 {
1149
- if (x.isZero() || u256.eq(x, u256.One)) {
1111
+ if (x.isZero() || u256.eq(x, SafeMath.ONE)) {
1150
1112
  return u256.Zero;
1151
1113
  }
1152
1114
 
@@ -1155,10 +1117,9 @@ export class SafeMath {
1155
1117
  return u256.Zero;
1156
1118
  }
1157
1119
 
1158
- const LN2_SCALED: u64 = 693147; // ln(2)*1e6
1159
1120
  const log2Count: u64 = (bitLen - 1) as u64;
1160
1121
 
1161
- return SafeMath.mul(u256.fromU64(log2Count), u256.fromU64(LN2_SCALED));
1122
+ return SafeMath.mul(u256.fromU64(log2Count), u256.fromU64(SafeMath.LN2_SCALED));
1162
1123
  }
1163
1124
 
1164
1125
  /**
@@ -1176,13 +1137,13 @@ export class SafeMath {
1176
1137
  * ```
1177
1138
  *
1178
1139
  * @warning Returns 0 for input 0, which technically requires 0 bits to represent.
1179
- * This differs from some implementations that might return 1 for consistency.
1140
+ * This differs from some implementations that might return 1 for consistency.
1180
1141
  *
1181
1142
  * @security Validated across all u256 segment boundaries. Used internally for:
1182
- * - Logarithm calculations (bitLength = floor(log2(x)) + 1 for x > 0)
1183
- * - Efficient range determination in binary operations
1184
- * - Gas optimization by determining operation complexity
1185
- * - Overflow prediction in multiplication/exponentiation
1143
+ * - Logarithm calculations (bitLength = floor(log2(x)) + 1 for x > 0)
1144
+ * - Efficient range determination in binary operations
1145
+ * - Gas optimization by determining operation complexity
1146
+ * - Overflow prediction in multiplication/exponentiation
1186
1147
  *
1187
1148
  * @remarks
1188
1149
  * - Handles values across all four u64 segments of u256
@@ -1193,26 +1154,13 @@ export class SafeMath {
1193
1154
  * - Relationship: bitLength(x) = approximateLog2(x) + 1 for x > 1
1194
1155
  */
1195
1156
  public static bitLength256(x: u256): u32 {
1196
- if (u256.eq(x, u256.Zero)) {
1197
- return 0;
1198
- }
1199
-
1200
- if (x.hi2 != 0) {
1201
- const partial: u32 = SafeMath.bitLength64(x.hi2);
1202
- return 192 + partial;
1203
- }
1204
-
1205
- if (x.hi1 != 0) {
1206
- const partial: u32 = SafeMath.bitLength64(x.hi1);
1207
- return 128 + partial;
1208
- }
1209
-
1210
- if (x.lo2 != 0) {
1211
- const partial: u32 = SafeMath.bitLength64(x.lo2);
1212
- return 64 + partial;
1213
- }
1214
-
1215
- return SafeMath.bitLength64(x.lo1);
1157
+ // GAS OPTIMIZATION: Use clz intrinsic to find MSB in 1 instruction.
1158
+ // Must explicit cast result to u32 for subtraction logic.
1159
+ if (x.hi2 != 0) return 256 - <u32>clz(x.hi2);
1160
+ if (x.hi1 != 0) return 192 - <u32>clz(x.hi1);
1161
+ if (x.lo2 != 0) return 128 - <u32>clz(x.lo2);
1162
+ if (x.lo1 != 0) return 64 - <u32>clz(x.lo1);
1163
+ return 0;
1216
1164
  }
1217
1165
 
1218
1166
  /**
@@ -1236,18 +1184,18 @@ export class SafeMath {
1236
1184
  * ```
1237
1185
  *
1238
1186
  * @warning This function is optimized for internal use by preciseLog and requires
1239
- * understanding of fixed-point arithmetic. The input uses a scaling factor
1240
- * of 10^6, meaning rScaled=500000 represents z=0.5. Input must be strictly
1241
- * less than 1,000,000 to represent valid z values in [0,1). Direct usage
1242
- * outside of the logarithm calculation pipeline requires careful attention
1243
- * to scaling conventions.
1187
+ * understanding of fixed-point arithmetic. The input uses a scaling factor
1188
+ * of 10^6, meaning rScaled=500000 represents z=0.5. Input must be strictly
1189
+ * less than 1,000,000 to represent valid z values in [0,1). Direct usage
1190
+ * outside of the logarithm calculation pipeline requires careful attention
1191
+ * to scaling conventions.
1244
1192
  *
1245
1193
  * @security The algorithm uses integer arithmetic throughout to avoid
1246
- * floating-point vulnerabilities. All intermediate calculations
1247
- * are designed to prevent overflow: maximum intermediate value
1248
- * is approximately 1.11×10^11, well below u64.Max (≈1.84×10^19).
1249
- * This ensures deterministic, reproducible results critical for
1250
- * consensus in blockchain applications.
1194
+ * floating-point vulnerabilities. All intermediate calculations
1195
+ * are designed to prevent overflow: maximum intermediate value
1196
+ * is approximately 1.11×10^11, well below u64.Max (≈1.84×10^19).
1197
+ * This ensures deterministic, reproducible results critical for
1198
+ * consensus in blockchain applications.
1251
1199
  *
1252
1200
  * @remarks
1253
1201
  * Algorithm details:
@@ -1266,18 +1214,15 @@ export class SafeMath {
1266
1214
  * - All divisions use banker's rounding via (numerator + divisor/2) / divisor
1267
1215
  */
1268
1216
  public static polyLn1p3(rScaled: u64): u64 {
1269
- // Input validation: ensure we're in valid range [0, 1000000)
1270
- if (rScaled >= 1_000_000) {
1217
+ if (rScaled >= SafeMath.SCALE_1E6) {
1271
1218
  throw new Revert('SafeMath.polyLn1p3: input out of range');
1272
1219
  }
1273
1220
 
1274
- // Handle the zero case explicitly
1275
1221
  if (rScaled == 0) {
1276
1222
  return 0;
1277
1223
  }
1278
1224
 
1279
- // Constants
1280
- const SCALE: u64 = 1_000_000;
1225
+ const SCALE: u64 = SafeMath.SCALE_1E6;
1281
1226
  const HALF_SCALE: u64 = 500_000;
1282
1227
 
1283
1228
  // Compute w = z / (2 + z)
@@ -1306,56 +1251,14 @@ export class SafeMath {
1306
1251
 
1307
1252
  // ==================== Internal Helper Functions ====================
1308
1253
 
1309
- /**
1310
- * @internal
1311
- * Calculates bit length of a u64 value.
1312
- * Used internally by bitLength256 for individual segment processing.
1313
- */
1314
- private static bitLength64(value: u64): u32 {
1315
- if (value == 0) return 0;
1316
-
1317
- let count: u32 = 0;
1318
- let temp = value;
1319
- while (temp > 0) {
1320
- temp >>>= 1;
1321
- count++;
1322
- }
1323
- return count;
1324
- }
1325
-
1326
- /**
1327
- * @internal
1328
- * Helper for shift operations across word boundaries.
1329
- */
1330
- private static shlSegment(
1331
- segments: u64[],
1332
- segmentShift: i32,
1333
- bitShift: i32,
1334
- bitsPerSegment: i32,
1335
- fillCount: u8,
1336
- ): u64[] {
1337
- const result = new Array<u64>(fillCount).fill(0);
1338
-
1339
- for (let i = 0; i < segments.length; i++) {
1340
- if (i + segmentShift < segments.length) {
1341
- result[i + segmentShift] |= segments[i] << bitShift;
1342
- }
1343
- if (bitShift != 0 && i + segmentShift + 1 < segments.length) {
1344
- result[i + segmentShift + 1] |= segments[i] >>> (bitsPerSegment - bitShift);
1345
- }
1346
- }
1347
-
1348
- return result;
1349
- }
1350
-
1351
1254
  /**
1352
1255
  * @internal
1353
1256
  * Modular addition helper that prevents overflow.
1354
1257
  * Pre-condition: 0 <= x,y < m
1355
1258
  */
1356
1259
  private static addModNoCarry(x: u256, y: u256, m: u256): u256 {
1357
- const mMinusY = SafeMath.sub(m, y);
1358
- return u256.ge(x, mMinusY) ? SafeMath.sub(x, mMinusY) : SafeMath.add(x, y);
1260
+ const mMinusY = u256.sub(m, y);
1261
+ return u256.ge(x, mMinusY) ? u256.sub(x, mMinusY) : u256.add(x, y);
1359
1262
  }
1360
1263
 
1361
1264
  /**
@@ -1364,7 +1267,7 @@ export class SafeMath {
1364
1267
  * Pre-condition: 0 <= x < m
1365
1268
  */
1366
1269
  private static doubleModNoCarry(x: u256, m: u256): u256 {
1367
- const mMinusX = SafeMath.sub(m, x);
1368
- return u256.ge(x, mMinusX) ? SafeMath.sub(x, mMinusX) : SafeMath.add(x, x);
1270
+ const mMinusX = u256.sub(m, x);
1271
+ return u256.ge(x, mMinusX) ? u256.sub(x, mMinusX) : u256.add(x, x);
1369
1272
  }
1370
1273
  }