@mimicprotocol/lib-ts 0.0.1-rc.10

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.
@@ -0,0 +1,781 @@
1
+ // eslint-disable-next-line no-secrets/no-secrets
2
+ // This file is based on code from "The Graph Tooling" (https://github.com/graphprotocol/graph-tooling/tree/7faa3098b2e6c61f09fc81b8b2d333e66b0080d1).
3
+ // Licensed under the MIT License.
4
+ // Copyright (c) 2018 Graph Protocol, Inc. and contributors.
5
+ // Modified by Mimic Protocol, 2025.
6
+
7
+ import { areAllZeros, bytesToHexString, isHex, normalizeScientificNotation } from '../helpers'
8
+
9
+ import { ByteArray } from './ByteArray'
10
+ import { Bytes } from './Bytes'
11
+
12
+ const ZERO_ASCII = '0'.charCodeAt(0)
13
+
14
+ /**
15
+ * Represents an arbitrary-precision integer stored as a byte array.
16
+ */
17
+ export class BigInt extends Uint8Array {
18
+ /**
19
+ * Returns a BigInt initialized to zero.
20
+ */
21
+ static zero(): BigInt {
22
+ return BigInt.fromI32(0)
23
+ }
24
+
25
+ /**
26
+ * Creates a BigInt from a signed 8-bit integer (i8).
27
+ */
28
+ static fromI8(x: i8): BigInt {
29
+ return BigInt.fromByteArray(ByteArray.fromI8(x))
30
+ }
31
+
32
+ /**
33
+ * Creates a BigInt from an unsigned 8-bit integer (u8).
34
+ */
35
+ static fromU8(x: u8): BigInt {
36
+ return BigInt.fromByteArray(ByteArray.fromU8(x))
37
+ }
38
+
39
+ /**
40
+ * Creates a BigInt from a signed 16-bit integer (i16).
41
+ */
42
+ static fromI16(x: i16): BigInt {
43
+ return BigInt.fromByteArray(ByteArray.fromI16(x))
44
+ }
45
+
46
+ /**
47
+ * Creates a BigInt from an unsigned 16-bit integer (u16).
48
+ */
49
+ static fromU16(x: u16): BigInt {
50
+ return BigInt.fromByteArray(ByteArray.fromU16(x))
51
+ }
52
+
53
+ /**
54
+ * Creates a BigInt from a signed 32-bit integer (i32).
55
+ */
56
+ static fromI32(x: i32): BigInt {
57
+ return BigInt.fromByteArray(ByteArray.fromI32(x))
58
+ }
59
+
60
+ /**
61
+ * Creates a BigInt from an unsigned 32-bit integer (u32).
62
+ */
63
+ static fromU32(x: u32): BigInt {
64
+ return BigInt.fromUnsignedBytes(ByteArray.fromU32(x))
65
+ }
66
+
67
+ /**
68
+ * Creates a BigInt from a signed 64-bit integer (i64).
69
+ */
70
+ static fromI64(x: i64): BigInt {
71
+ return BigInt.fromByteArray(ByteArray.fromI64(x))
72
+ }
73
+
74
+ /**
75
+ * Creates a BigInt from an unsigned 64-bit integer (u64).
76
+ */
77
+ static fromU64(x: u64): BigInt {
78
+ return BigInt.fromUnsignedBytes(ByteArray.fromU64(x))
79
+ }
80
+
81
+ /**
82
+ * Converts a Bytes instance containing a signed integer to a BigInt.
83
+ */
84
+ static fromSignedBytes(bytes: Bytes): BigInt {
85
+ return BigInt.fromByteArray(changetype<ByteArray>(bytes))
86
+ }
87
+
88
+ /**
89
+ * Converts a ByteArray to a BigInt.
90
+ */
91
+ static fromByteArray(byteArray: ByteArray): BigInt {
92
+ return changetype<BigInt>(byteArray)
93
+ }
94
+
95
+ /**
96
+ * Converts an unsigned ByteArray to a BigInt, appending a zero byte for sign extension.
97
+ */
98
+ static fromUnsignedBytes(bytes: ByteArray): BigInt {
99
+ const signedBytes = new BigInt(bytes.length + 1)
100
+ for (let i = 0; i < bytes.length; i++) signedBytes[i] = bytes[i]
101
+ signedBytes[bytes.length] = 0
102
+ return signedBytes
103
+ }
104
+
105
+ /**
106
+ * Parses a string representation of a number and converts it to a BigInt.
107
+ * Supports decimal and scientific notation formats.
108
+ */
109
+ static fromString(str: string): BigInt {
110
+ const hexIndex = str.toLowerCase().indexOf('0x')
111
+ if (hexIndex == 0 || hexIndex == 1) return BigInt.fromHexString(str)
112
+ return this.fromStringDecimal(str, 0)
113
+ }
114
+
115
+ /**
116
+ * Parses a string representation of a number with precision and converts it to a BigInt.
117
+ * Supports decimal and scientific notation formats.
118
+ */
119
+ static fromStringDecimal(str: string, precision: u8): BigInt {
120
+ if (str === '0') return this.zero()
121
+ str = normalizeScientificNotation(str)
122
+
123
+ const parts = str.split('.')
124
+ if (parts.length > 2) throw new Error(`Invalid string. Received: ${str}`)
125
+
126
+ const isNegative = parts[0].startsWith('-')
127
+ const wholePart = isNegative ? parts[0].substring(1) : parts[0]
128
+ let result = BigInt.fromStringRaw(wholePart).upscale(precision)
129
+
130
+ if (parts.length > 1 && parts[1].length > 0) {
131
+ const decimalDigits = parts[1]
132
+ if (decimalDigits.length > <i32>precision && !areAllZeros(decimalDigits)) {
133
+ throw new Error(`Too many decimal places. Max allowed: ${precision}, found: ${decimalDigits.length}`)
134
+ }
135
+ const decimalPart = parts[1].padEnd(precision, '0')
136
+ result = result.plus(BigInt.fromString(decimalPart))
137
+ }
138
+
139
+ return isNegative ? result.neg() : result
140
+ }
141
+
142
+ /**
143
+ * Parses a hexadecimal string representation of a number and converts it to a BigInt.
144
+ */
145
+ static fromHexString(hex: string): BigInt {
146
+ hex = hex.toLowerCase()
147
+ if (!hex || hex === '0x' || hex === '-0x') return BigInt.zero()
148
+
149
+ // Remove prefix
150
+ const isNegative = hex.startsWith('-')
151
+ if (isNegative) hex = hex.substring(1)
152
+ else if (hex.startsWith('+')) hex = hex.substring(1)
153
+ if (hex.startsWith('0x')) hex = hex.substring(2)
154
+
155
+ if (!isHex(hex)) throw new Error(`Invalid hex string: '${hex}'`)
156
+
157
+ // ByteArray will still assert even length
158
+ const bytes = ByteArray.fromHexString(hex)
159
+ const result = BigInt.fromUnsignedBytes(bytes)
160
+ return isNegative && !result.isZero() ? result.neg() : result
161
+ }
162
+
163
+ private static fromStringRaw(str: string): BigInt {
164
+ if (str.length == 0) throw new Error('Received invalid empty string')
165
+ let result = BigInt.zero()
166
+ for (let i = 0; i < str.length; i++) {
167
+ const c = str.charCodeAt(i)
168
+ if (c < 48 || c > 57) throw new Error(`Invalid digit '${str.charAt(i)}'`)
169
+ result = result.times(BigInt.fromI32(10)).plus(BigInt.fromI32(c - 48))
170
+ }
171
+ return result
172
+ }
173
+
174
+ private static addUnsigned(a: BigInt, b: BigInt): BigInt {
175
+ if (a.isZero()) return b.clone()
176
+ if (b.isZero()) return a.clone()
177
+
178
+ const maxLen = max(a.length, b.length)
179
+ const result = new BigInt(maxLen + 1)
180
+ let carry: u32 = 0
181
+ for (let i = 0; i < maxLen; i++) {
182
+ const ai = i < a.length ? a[i] : 0
183
+ const bi = i < b.length ? b[i] : 0
184
+ const sum = <u32>ai + <u32>bi + carry
185
+ result[i] = <u8>(sum & 0xff)
186
+ carry = sum >> 8
187
+ }
188
+ if (carry != 0) result[maxLen] = <u8>carry
189
+ return result
190
+ }
191
+
192
+ private static subUnsigned(a: BigInt, b: BigInt): BigInt {
193
+ const result = new BigInt(a.length)
194
+ let borrow: i32 = 0
195
+ for (let i = 0; i < a.length; i++) {
196
+ const ai = <i32>(i < a.length ? a[i] : 0)
197
+ const bi = <i32>(i < b.length ? b[i] : 0)
198
+ let diff = ai - bi - borrow
199
+ borrow = 0
200
+ if (diff < 0) {
201
+ diff += 256
202
+ borrow = 1
203
+ }
204
+ result[i] = <u8>(diff & 0xff)
205
+ }
206
+ return result
207
+ }
208
+
209
+ private static mulUnsigned(a: BigInt, b: BigInt): BigInt {
210
+ if (a.length < b.length) return BigInt.mulUnsigned(b, a)
211
+ if (b.length === 1 && b[0] === 2) return a.leftShift(1)
212
+
213
+ const result = new BigInt(a.length + b.length)
214
+ for (let i = 0; i < a.length; i++) {
215
+ let carry: u32 = 0
216
+ for (let j = 0; j < b.length || carry != 0; j++) {
217
+ const bj = j < b.length ? b[j] : 0
218
+ const sum = <u32>result[i + j] + <u32>a[i] * <u32>bj + carry
219
+ result[i + j] = <u8>(sum & 0xff)
220
+ carry = sum >> 8
221
+ }
222
+ }
223
+ return result
224
+ }
225
+
226
+ private static divUnsigned(a: BigInt, b: BigInt): BigInt {
227
+ assert(!b.isZero(), 'Trying to divide by zero')
228
+ if (BigInt.compare(a, b) < 0) return BigInt.zero()
229
+
230
+ let quotient = BigInt.zero()
231
+ let remainder = BigInt.zero()
232
+
233
+ for (let i = a.length - 1; i >= 0; i--) {
234
+ remainder = remainder.leftShift(8)
235
+ remainder = BigInt.addUnsigned(remainder, BigInt.fromI32(a[i]))
236
+
237
+ let digit = BigInt.zero()
238
+ while (BigInt.compare(remainder, b) >= 0) {
239
+ remainder = BigInt.subUnsigned(remainder, b)
240
+ digit = BigInt.addUnsigned(digit, BigInt.fromI32(1))
241
+ }
242
+
243
+ quotient = quotient.leftShift(8)
244
+ quotient = BigInt.addUnsigned(quotient, digit)
245
+ }
246
+ return quotient
247
+ }
248
+
249
+ /**
250
+ * Compares two BigInt values.
251
+ * @param a - The first BigInt to compare
252
+ * @param b - The second BigInt to compare
253
+ * @returns -1 if a < b, 0 if a == b, 1 if a > b
254
+ */
255
+ static compare(a: BigInt, b: BigInt): i32 {
256
+ const aIsNeg = a.length > 0 && a[a.length - 1] >> 7 == 1
257
+ const bIsNeg = b.length > 0 && b[b.length - 1] >> 7 == 1
258
+ if (!aIsNeg && bIsNeg) return 1
259
+ if (aIsNeg && !bIsNeg) return -1
260
+
261
+ let aRelevantBytes = a.length
262
+ while (
263
+ aRelevantBytes > 0 &&
264
+ ((!aIsNeg && a[aRelevantBytes - 1] == 0) || (aIsNeg && a[aRelevantBytes - 1] == 255))
265
+ ) {
266
+ aRelevantBytes -= 1
267
+ }
268
+
269
+ let bRelevantBytes = b.length
270
+ while (
271
+ bRelevantBytes > 0 &&
272
+ ((!bIsNeg && b[bRelevantBytes - 1] == 0) || (bIsNeg && b[bRelevantBytes - 1] == 255))
273
+ ) {
274
+ bRelevantBytes -= 1
275
+ }
276
+
277
+ if (aRelevantBytes > bRelevantBytes) return aIsNeg ? -1 : 1
278
+ if (bRelevantBytes > aRelevantBytes) return aIsNeg ? 1 : -1
279
+
280
+ const relevantBytes = aRelevantBytes
281
+ for (let i = 1; i <= relevantBytes; i++) {
282
+ if (a[relevantBytes - i] < b[relevantBytes - i]) return -1
283
+ if (a[relevantBytes - i] > b[relevantBytes - i]) return 1
284
+ }
285
+
286
+ return 0
287
+ }
288
+
289
+ /**
290
+ * Checks if this BigInt represents a negative number.
291
+ * @returns True if the number is negative, false otherwise
292
+ */
293
+ isNegative(): boolean {
294
+ return this.length > 0 && (this[this.length - 1] & 0x80) == 0x80
295
+ }
296
+
297
+ /**
298
+ * Checks if this BigInt represents zero.
299
+ * @returns True if the number is zero, false otherwise
300
+ */
301
+ isZero(): boolean {
302
+ if (this.length == 0) return true
303
+ for (let i = 0; i < this.length; i++) if (this[i] !== 0) return false
304
+ return true
305
+ }
306
+
307
+ /**
308
+ * Checks if this BigInt can fit within the range of a signed 32-bit integer.
309
+ * @returns True if the value fits in i32 range, false otherwise
310
+ */
311
+ isI32(): boolean {
312
+ return BigInt.fromI32(i32.MIN_VALUE) <= this && this <= BigInt.fromI32(i32.MAX_VALUE)
313
+ }
314
+
315
+ /**
316
+ * Tells the absolute value of this BigInt.
317
+ * @returns A new BigInt representing the absolute value
318
+ */
319
+ abs(): BigInt {
320
+ return this.isNegative() ? this.neg() : this
321
+ }
322
+
323
+ /**
324
+ * Raises this BigInt to the power of the given exponent.
325
+ * @param exp - The exponent (unsigned 8-bit integer)
326
+ * @returns A new BigInt representing this^exp
327
+ */
328
+ pow(exp: u8): BigInt {
329
+ if (exp === 0) return BigInt.fromI32(1)
330
+ if (exp === 1) return this.clone()
331
+ if (exp === 2) return this.times(this)
332
+
333
+ let base = this.clone()
334
+ let e = exp
335
+ let result = BigInt.fromI32(1)
336
+
337
+ while (e > 0) {
338
+ if ((e & 1) == 1) result = result.times(base)
339
+ base = base.times(base)
340
+ e >>= 1
341
+ }
342
+ return result
343
+ }
344
+
345
+ /**
346
+ * Multiplies this BigInt by 10^precision (moves decimal point right).
347
+ * @param precision - Number of decimal places to scale up
348
+ * @returns A new BigInt scaled up by the specified precision
349
+ */
350
+ upscale(precision: u8): BigInt {
351
+ return this.times(BigInt.fromI32(10).pow(precision))
352
+ }
353
+
354
+ /**
355
+ * Divides this BigInt by 10^precision (moves decimal point left).
356
+ * @param precision - Number of decimal places to scale down
357
+ * @returns A new BigInt scaled down by the specified precision
358
+ */
359
+ downscale(precision: u8): BigInt {
360
+ return this.div(BigInt.fromI32(10).pow(precision))
361
+ }
362
+
363
+ /**
364
+ * Creates a deep copy of this BigInt.
365
+ * @returns A new BigInt instance with the same value
366
+ */
367
+ clone(): BigInt {
368
+ const clone = new BigInt(this.length)
369
+ memory.copy(clone.dataStart, this.dataStart, this.length)
370
+ return clone
371
+ }
372
+
373
+ /**
374
+ * Creates a new BigInt from a portion of this BigInt's byte array.
375
+ * @param start - Starting index (inclusive)
376
+ * @param end - Ending index (exclusive)
377
+ * @returns A new BigInt containing the specified byte range
378
+ */
379
+ subarray(start: i32, end: i32): BigInt {
380
+ const length = end - start
381
+ const result = new BigInt(length)
382
+ memory.copy(result.dataStart, this.dataStart + start, length)
383
+ return result
384
+ }
385
+
386
+ /**
387
+ * Adds another BigInt to this BigInt.
388
+ * @param other - The BigInt to add
389
+ * @returns A new BigInt representing the sum
390
+ */
391
+ @operator('+')
392
+ plus(other: BigInt): BigInt {
393
+ const aIsNeg = this.isNegative()
394
+ const bIsNeg = other.isNegative()
395
+ if (aIsNeg === bIsNeg) {
396
+ const resultAbs = BigInt.addUnsigned(this.abs(), other.abs())
397
+ return aIsNeg ? resultAbs.neg() : resultAbs
398
+ }
399
+ const cmp = BigInt.compare(this.abs(), other.abs())
400
+ if (cmp === 0) {
401
+ return BigInt.zero()
402
+ } else if (cmp > 0) {
403
+ const resultAbs = BigInt.subUnsigned(this.abs(), other.abs())
404
+ return this.isNegative() ? resultAbs.neg() : resultAbs
405
+ } else {
406
+ const resultAbs = BigInt.subUnsigned(other.abs(), this.abs())
407
+ return other.isNegative() ? resultAbs.neg() : resultAbs
408
+ }
409
+ }
410
+
411
+ /**
412
+ * Subtracts another BigInt from this BigInt.
413
+ * @param other - The BigInt to subtract
414
+ * @returns A new BigInt representing the difference
415
+ */
416
+ @operator('-')
417
+ minus(other: BigInt): BigInt {
418
+ return this.plus(other.neg())
419
+ }
420
+
421
+ /**
422
+ * Multiplies this BigInt by another BigInt.
423
+ * @param other - The BigInt to multiply by
424
+ * @returns A new BigInt representing the product
425
+ */
426
+ @operator('*')
427
+ times(other: BigInt): BigInt {
428
+ if (other.isZero() || this.isZero()) return BigInt.zero()
429
+
430
+ if (other.length == 1) {
431
+ if (other[0] == 1) return this.clone()
432
+ if (other[0] == 2) return this.leftShift(1)
433
+ if (other[0] == 4) return this.leftShift(2)
434
+ if (other[0] == 8) return this.leftShift(3)
435
+ }
436
+
437
+ const aIsNeg = this.isNegative()
438
+ const bIsNeg = other.isNegative()
439
+ const signIsNeg = (aIsNeg && !bIsNeg) || (!aIsNeg && bIsNeg)
440
+ const resultAbs = BigInt.mulUnsigned(this.abs(), other.abs())
441
+ return signIsNeg ? resultAbs.neg() : resultAbs
442
+ }
443
+
444
+ /**
445
+ * Divides this BigInt by another BigInt.
446
+ * @param other - The BigInt to divide by (cannot be zero)
447
+ * @returns A new BigInt representing the quotient
448
+ */
449
+ @operator('/')
450
+ div(other: BigInt): BigInt {
451
+ assert(!other.isZero(), 'Trying to divide by zero')
452
+ const aIsNeg = this.isNegative()
453
+ const bIsNeg = other.isNegative()
454
+ const signIsNeg = (aIsNeg && !bIsNeg) || (!aIsNeg && bIsNeg)
455
+ const resultAbs = BigInt.divUnsigned(this.abs(), other.abs())
456
+ return signIsNeg ? resultAbs.neg() : resultAbs
457
+ }
458
+
459
+ /**
460
+ * Calculates the remainder when dividing this BigInt by another BigInt.
461
+ * @param other - The BigInt to divide by (cannot be zero)
462
+ * @returns A new BigInt representing the remainder
463
+ */
464
+ @operator('%')
465
+ mod(other: BigInt): BigInt {
466
+ assert(!other.isZero(), '')
467
+ return this.minus(this.div(other).times(other))
468
+ }
469
+
470
+ /**
471
+ * Checks if this BigInt is equal to another BigInt.
472
+ * @param other - The BigInt to compare with
473
+ * @returns True if both BigInts have the same value
474
+ */
475
+ @operator('==')
476
+ equals(other: BigInt): boolean {
477
+ return BigInt.compare(this, other) == 0
478
+ }
479
+
480
+ /**
481
+ * Checks if this BigInt is not equal to another BigInt.
482
+ * @param other - The BigInt to compare with
483
+ * @returns True if the BigInts have different values
484
+ */
485
+ @operator('!=')
486
+ notEqual(other: BigInt): boolean {
487
+ return !(this == other)
488
+ }
489
+
490
+ /**
491
+ * Checks if this BigInt is less than another BigInt.
492
+ * @param other - The BigInt to compare with
493
+ * @returns True if this BigInt is smaller
494
+ */
495
+ @operator('<')
496
+ lt(other: BigInt): boolean {
497
+ return BigInt.compare(this, other) == -1
498
+ }
499
+
500
+ /**
501
+ * Checks if this BigInt is greater than another BigInt.
502
+ * @param other - The BigInt to compare with
503
+ * @returns True if this BigInt is larger
504
+ */
505
+ @operator('>')
506
+ gt(other: BigInt): boolean {
507
+ return BigInt.compare(this, other) == 1
508
+ }
509
+
510
+ /**
511
+ * Checks if this BigInt is less than or equal to another BigInt.
512
+ * @param other - The BigInt to compare with
513
+ * @returns True if this BigInt is smaller or equal
514
+ */
515
+ @operator('<=')
516
+ le(other: BigInt): boolean {
517
+ return !(this > other)
518
+ }
519
+
520
+ /**
521
+ * Checks if this BigInt is greater than or equal to another BigInt.
522
+ * @param other - The BigInt to compare with
523
+ * @returns True if this BigInt is larger or equal
524
+ */
525
+ @operator('>=')
526
+ ge(other: BigInt): boolean {
527
+ return !(this < other)
528
+ }
529
+
530
+ /**
531
+ * Tells the negation (opposite sign) of this BigInt.
532
+ * @returns A new BigInt with the opposite sign
533
+ */
534
+ @operator.prefix('-')
535
+ neg(): BigInt {
536
+ const result = new BigInt(this.length)
537
+ for (let i = 0; i < this.length; i++) {
538
+ result[i] = ~this[i]
539
+ }
540
+ let carry: u32 = 1
541
+ for (let i = 0; i < result.length && carry != 0; i++) {
542
+ const sum = <u32>result[i] + carry
543
+ result[i] = <u8>(sum & 0xff)
544
+ carry = sum >> 8
545
+ }
546
+ return result
547
+ }
548
+
549
+ /**
550
+ * Performs a bitwise OR operation with another BigInt.
551
+ * @param other - The BigInt to perform bitwise OR with
552
+ * @returns A new BigInt representing the bitwise OR result
553
+ */
554
+ @operator('|')
555
+ bitOr(other: BigInt): BigInt {
556
+ const maxLen = max(this.length, other.length)
557
+ const result = new BigInt(maxLen)
558
+ for (let i = 0; i < maxLen; i++) {
559
+ const aByte = i < this.length ? this[i] : this.isNegative() ? 0xff : 0x00
560
+ const bByte = i < other.length ? other[i] : other.isNegative() ? 0xff : 0x00
561
+ result[i] = aByte | bByte
562
+ }
563
+ return result
564
+ }
565
+
566
+ /**
567
+ * Performs a bitwise AND operation with another BigInt.
568
+ * @param other - The BigInt to perform bitwise AND with
569
+ * @returns A new BigInt representing the bitwise AND result
570
+ */
571
+ @operator('&')
572
+ bitAnd(other: BigInt): BigInt {
573
+ const maxLen = max(this.length, other.length)
574
+ const result = new BigInt(maxLen)
575
+ for (let i = 0; i < maxLen; i++) {
576
+ const aByte = i < this.length ? this[i] : this.isNegative() ? 0xff : 0x00
577
+ const bByte = i < other.length ? other[i] : other.isNegative() ? 0xff : 0x00
578
+ result[i] = aByte & bByte
579
+ }
580
+ return result
581
+ }
582
+
583
+ /**
584
+ * Shifts this BigInt left by the specified number of bits.
585
+ * @param bits - Number of bits to shift left
586
+ * @returns A new BigInt shifted left (equivalent to multiplying by 2^bits)
587
+ */
588
+ @operator('<<')
589
+ leftShift(bits: u8): BigInt {
590
+ if (bits == 0) return this
591
+ const byteShift = bits / 8
592
+ const bitShift = bits % 8
593
+ const newLen = this.length + <i32>byteShift + 1
594
+ const result = new BigInt(newLen)
595
+ let carry = 0
596
+ for (let i = 0; i < this.length; i++) {
597
+ const cur = (this[i] << bitShift) | carry
598
+ result[i + byteShift] = <u8>(cur & 0xff)
599
+ carry = (cur >> 8) & 0xff
600
+ }
601
+ if (carry != 0) {
602
+ result[this.length + byteShift] = <u8>carry
603
+ }
604
+ return result
605
+ }
606
+
607
+ /**
608
+ * Shifts this BigInt right by the specified number of bits.
609
+ * @param bits - Number of bits to shift right
610
+ * @returns A new BigInt shifted right (equivalent to dividing by 2^bits)
611
+ */
612
+ @operator('>>')
613
+ rightShift(bits: u8): BigInt {
614
+ if (bits == 0) return this
615
+
616
+ const byteShift = (bits >> 3) as i32
617
+ const bitShift = bits & 0b111
618
+ const negative = this.isNegative()
619
+ const result = new BigInt(this.length)
620
+
621
+ if (byteShift >= this.length) {
622
+ for (let i = 0; i < this.length; i++) {
623
+ result[i] = negative ? 0xff : 0x00
624
+ }
625
+ return result
626
+ }
627
+
628
+ let carry: u16 = 0
629
+ for (let i = this.length - 1; i >= 0; i--) {
630
+ let cur = <u16>(this[i] & 0xff)
631
+
632
+ if (negative && i == this.length - 1) {
633
+ cur |= 0xff00
634
+ }
635
+
636
+ cur |= carry << 8
637
+ carry = cur & ((1 << bitShift) - 1)
638
+ cur = cur >> bitShift
639
+ const pos = i - byteShift
640
+
641
+ if (pos >= 0) {
642
+ result[pos] = <u8>(cur & 0xff)
643
+ }
644
+ if (i == 0) break
645
+ }
646
+
647
+ for (let i = this.length - 1; i >= this.length - byteShift; i--) {
648
+ if (i >= 0 && i < result.length) {
649
+ result[i] = negative ? 0xff : 0x00
650
+ }
651
+ if (i == 0) break
652
+ }
653
+
654
+ return result
655
+ }
656
+
657
+ /**
658
+ * Converts this BigInt to a Bytes representation.
659
+ * @returns A new Bytes instance containing the raw byte data
660
+ */
661
+ toBytes(): Bytes {
662
+ return Bytes.fromUint8Array(changetype<Uint8Array>(this))
663
+ }
664
+
665
+ /**
666
+ * Converts this BigInt to a signed 32-bit integer.
667
+ * @returns The i32 representation (behavior undefined if value doesn't fit)
668
+ */
669
+ toI32(): i32 {
670
+ const byteArray = this.toBytes()
671
+ return byteArray.toI32()
672
+ }
673
+
674
+ /**
675
+ * Converts this BigInt to an unsigned 32-bit integer.
676
+ * @returns The u32 representation (behavior undefined if value doesn't fit)
677
+ */
678
+ toU32(): u32 {
679
+ const byteArray = this.toBytes()
680
+ return byteArray.toU32()
681
+ }
682
+
683
+ /**
684
+ * Converts this BigInt to a signed 64-bit integer.
685
+ * @returns The i64 representation (behavior undefined if value doesn't fit)
686
+ */
687
+ toI64(): i64 {
688
+ const byteArray = this.toBytes()
689
+ return byteArray.toI64()
690
+ }
691
+
692
+ /**
693
+ * Converts this BigInt to an unsigned 64-bit integer.
694
+ * @returns The u64 representation (behavior undefined if value doesn't fit)
695
+ */
696
+ toU64(): u64 {
697
+ const byteArray = this.toBytes()
698
+ return byteArray.toU64()
699
+ }
700
+
701
+ /**
702
+ * Converts this BigInt to a hexadecimal string representation.
703
+ * @returns Hex string with '0x' prefix, or '0x' for zero
704
+ */
705
+ toHexString(): string {
706
+ if (this.isZero()) return '0x'
707
+ const abs = this.isNegative() ? this.neg() : this
708
+ let end = abs.length
709
+ while (end > 1 && abs[end - 1] === 0) end--
710
+ const trimmed = abs.subarray(0, end)
711
+ const hex = bytesToHexString(trimmed)
712
+ return this.isNegative() ? '-' + hex : hex
713
+ }
714
+
715
+ /**
716
+ * Converts this BigInt to a decimal string representation.
717
+ * @returns Decimal string representation of the number
718
+ */
719
+ toString(): string {
720
+ if (this.isZero()) return '0'
721
+
722
+ let absValue = this.isNegative() ? this.neg() : this.clone()
723
+ const digits = new Array<i32>()
724
+ while (!absValue.isZero()) {
725
+ let carry = 0
726
+
727
+ for (let i = absValue.length - 1; i >= 0; i--) {
728
+ const current = (carry << 8) + absValue[i]
729
+ absValue[i] = <u8>(current / 10)
730
+ carry = current % 10
731
+ }
732
+
733
+ digits.push(ZERO_ASCII + carry)
734
+
735
+ while (absValue.length > 1 && absValue[absValue.length - 1] == 0) {
736
+ absValue = absValue.subarray(0, absValue.length - 1)
737
+ }
738
+ }
739
+
740
+ if (this.isNegative()) digits.push('-'.charCodeAt(0))
741
+ digits.reverse()
742
+ return String.fromCharCodes(digits)
743
+ }
744
+
745
+ /**
746
+ * Converts this BigInt to a decimal string with specified precision.
747
+ * @param precision - Number of decimal places to format
748
+ * @returns Decimal string with the specified precision, trailing zeros removed
749
+ */
750
+ toStringDecimal(precision: u8): string {
751
+ if (this.isZero()) return '0'
752
+
753
+ const isNegative = this.isNegative()
754
+ const absAmount = this.abs()
755
+
756
+ const str = absAmount.toString()
757
+ if (str.length <= (precision as i32)) {
758
+ let decimalPart = str.padStart(precision, '0')
759
+
760
+ let lastNonZero = decimalPart.length - 1
761
+ while (lastNonZero >= 0 && decimalPart.charCodeAt(lastNonZero) === ZERO_ASCII) lastNonZero--
762
+
763
+ decimalPart = decimalPart.substring(0, lastNonZero + 1)
764
+ return (isNegative ? '-' : '') + '0' + (decimalPart.length ? '.' + decimalPart : '')
765
+ }
766
+
767
+ const wholePart = str.slice(0, str.length - precision)
768
+ let decimalPart = str.slice(str.length - precision)
769
+
770
+ // Remove trailing zeros manually
771
+ let lastNonZero = decimalPart.length - 1
772
+ while (lastNonZero >= 0 && decimalPart.charCodeAt(lastNonZero) === ZERO_ASCII) lastNonZero--
773
+
774
+ decimalPart = decimalPart.substring(0, lastNonZero + 1)
775
+
776
+ // If the decimal part is empty, return only the whole part
777
+ return decimalPart.length > 0
778
+ ? `${isNegative ? '-' : ''}${wholePart}.${decimalPart}`
779
+ : `${isNegative ? '-' : ''}${wholePart}`
780
+ }
781
+ }