@btc-vision/btc-runtime 1.3.8 → 1.3.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,382 @@
1
+ import { u256 } from 'as-bignum/assembly';
2
+ import { Blockchain } from '../env';
3
+ import { BytesWriter } from '../buffer/BytesWriter';
4
+ import { SafeMath } from '../types/SafeMath';
5
+ import { Revert } from '../types/Revert';
6
+
7
+ /**
8
+ * @class StoredU256Array
9
+ * @description Manages an array of u256 values across multiple storage slots. Each slot holds one u256 value.
10
+ */
11
+ @final
12
+ export class StoredU256Array {
13
+ private readonly baseU256Pointer: u256;
14
+ private readonly lengthPointer: u256;
15
+
16
+ // Internal cache for storage slots
17
+ private _values: Map<u64, u256> = new Map(); // Map from slotIndex to u256 value
18
+ private _isLoaded: Set<u64> = new Set(); // Set of slotIndexes that are loaded
19
+ private _isChanged: Set<u64> = new Set(); // Set of slotIndexes that are modified
20
+
21
+ // Internal variables for length and startIndex management
22
+ private _length: u64 = 0; // Current length of the array
23
+ private _startIndex: u64 = 0; // Starting index of the array
24
+ private _isChangedLength: bool = false; // Indicates if the length has been modified
25
+ private _isChangedStartIndex: bool = false; // Indicates if the startIndex has been modified
26
+
27
+ // Define a maximum allowed length to prevent excessive storage usage
28
+ private readonly MAX_LENGTH: u64 = u64.MAX_VALUE - 1;
29
+
30
+ /**
31
+ * @constructor
32
+ * @param {u16} pointer - The primary pointer identifier.
33
+ * @param {Uint8Array} subPointer - The sub-pointer for memory slot addressing.
34
+ * @param {u256} defaultValue - The default u256 value if storage is uninitialized.
35
+ */
36
+ constructor(
37
+ public pointer: u16,
38
+ public subPointer: Uint8Array,
39
+ private defaultValue: u256,
40
+ ) {
41
+ // Initialize the base u256 pointer using the primary pointer and subPointer
42
+ const writer = new BytesWriter(32);
43
+ writer.writeU16(pointer);
44
+ writer.writeBytes(subPointer);
45
+
46
+ // Initialize the base and length pointers
47
+ const baseU256Pointer = u256.fromBytes(writer.getBuffer(), true);
48
+ const lengthPointer = baseU256Pointer.clone();
49
+
50
+ // Load the current length and startIndex from storage
51
+ const storedLengthAndStartIndex: u256 = Blockchain.getStorageAt(lengthPointer, u256.Zero);
52
+ this.lengthPointer = lengthPointer;
53
+ this.baseU256Pointer = baseU256Pointer;
54
+
55
+ this._length = storedLengthAndStartIndex.lo1; // Bytes 0-7: length
56
+ this._startIndex = storedLengthAndStartIndex.lo2; // Bytes 8-15: startIndex
57
+ }
58
+
59
+ /**
60
+ * @method get
61
+ * @description Retrieves the u256 value at the specified global index.
62
+ * @param {u64} index - The global index (0 to ∞) of the u256 value to retrieve.
63
+ * @returns {u256} - The u256 value at the specified index.
64
+ */
65
+ @inline
66
+ public get(index: u64): u256 {
67
+ assert(index < this._length, 'Index out of bounds');
68
+ const slotIndex: u32 = <u32>index;
69
+ this.ensureValues(slotIndex);
70
+ const value = this._values.get(slotIndex);
71
+ return value ? value : u256.Zero;
72
+ }
73
+
74
+ /**
75
+ * @method set
76
+ * @description Sets the u256 value at the specified global index.
77
+ * @param {u64} index - The global index (0 to ∞) of the u256 value to set.
78
+ * @param {u256} value - The u256 value to assign.
79
+ */
80
+ @inline
81
+ public set(index: u64, value: u256): void {
82
+ assert(index < this._length, 'Index exceeds current array length');
83
+ const slotIndex: u32 = <u32>index;
84
+ this.ensureValues(slotIndex);
85
+
86
+ const currentValue = this._values.get(slotIndex);
87
+ if (!u256.eq(currentValue, value)) {
88
+ this._values.set(slotIndex, value);
89
+ this._isChanged.add(slotIndex);
90
+ }
91
+ }
92
+
93
+ /**
94
+ * @method push
95
+ * @description Appends a new u256 value to the end of the array.
96
+ * @param {u256} value - The u256 value to append.
97
+ */
98
+ public push(value: u256): void {
99
+ if (this._length >= this.MAX_LENGTH) {
100
+ throw new Revert(
101
+ 'Push operation failed: Array has reached its maximum allowed length.',
102
+ );
103
+ }
104
+
105
+ const newIndex: u64 = this._length;
106
+ const effectiveIndex: u64 = this._startIndex + newIndex;
107
+ const wrappedIndex: u64 =
108
+ effectiveIndex < this.MAX_LENGTH ? effectiveIndex : effectiveIndex % this.MAX_LENGTH;
109
+ const slotIndex: u32 = <u32>wrappedIndex;
110
+
111
+ // Ensure the slot is loaded
112
+ this.ensureValues(slotIndex);
113
+
114
+ // Set the new value
115
+ this._values.set(slotIndex, value);
116
+ this._isChanged.add(slotIndex);
117
+
118
+ // Increment the length
119
+ this._length += 1;
120
+ this._isChangedLength = true;
121
+ }
122
+
123
+ /**
124
+ * @method delete
125
+ * @description Deletes the u256 value at the specified index by setting it to zero. Does not reorder the array.
126
+ * @param {u64} index - The global index of the u256 value to delete.
127
+ */
128
+ public delete(index: u64): void {
129
+ if (index >= this._length) {
130
+ throw new Revert('Delete operation failed: Index out of bounds.');
131
+ }
132
+
133
+ const slotIndex: u32 = <u32>index;
134
+ this.ensureValues(slotIndex);
135
+
136
+ const currentValue = this._values.get(slotIndex);
137
+ if (!u256.eq(currentValue, u256.Zero)) {
138
+ this._values.set(slotIndex, u256.Zero);
139
+ this._isChanged.add(slotIndex);
140
+ }
141
+ }
142
+
143
+ /**
144
+ * @method shift
145
+ * @description Removes the first element of the array by setting it to zero, decrementing the length, and incrementing the startIndex.
146
+ * If the startIndex reaches the maximum value of u64, it wraps around to 0.
147
+ */
148
+ public shift(): void {
149
+ if (this._length === 0) {
150
+ throw new Revert('Shift operation failed: Array is empty.');
151
+ }
152
+
153
+ const currentStartIndex: u64 = this._startIndex;
154
+ const slotIndex: u32 = <u32>currentStartIndex;
155
+ this.ensureValues(slotIndex);
156
+
157
+ const currentValue = this._values.get(slotIndex);
158
+ if (!u256.eq(currentValue, u256.Zero)) {
159
+ this._values.set(slotIndex, u256.Zero);
160
+ this._isChanged.add(slotIndex);
161
+ }
162
+
163
+ // Decrement the length
164
+ this._length -= 1;
165
+ this._isChangedLength = true;
166
+
167
+ // Increment the startIndex with wrap-around
168
+ if (this._startIndex < this.MAX_LENGTH - 1) {
169
+ this._startIndex += 1;
170
+ } else {
171
+ this._startIndex = 0;
172
+ }
173
+ this._isChangedStartIndex = true;
174
+ }
175
+
176
+ /**
177
+ * @method save
178
+ * @description Persists all cached u256 values, the length, and the startIndex to their respective storage slots if any have been modified.
179
+ */
180
+ public save(): void {
181
+ // Save all changed slots
182
+ const changed = this._isChanged.values();
183
+ for (let i = 0; i < changed.length; i++) {
184
+ const slotIndex = changed[i];
185
+ const storagePointer = this.calculateStoragePointer(slotIndex);
186
+ const value = this._values.get(slotIndex);
187
+ Blockchain.setStorageAt(storagePointer, value);
188
+ }
189
+ this._isChanged.clear();
190
+
191
+ // Save length and startIndex if changed
192
+ if (this._isChangedLength || this._isChangedStartIndex) {
193
+ const packedLengthAndStartIndex = new u256();
194
+ packedLengthAndStartIndex.lo1 = this._length;
195
+ packedLengthAndStartIndex.lo2 = this._startIndex;
196
+ Blockchain.setStorageAt(this.lengthPointer, packedLengthAndStartIndex);
197
+ this._isChangedLength = false;
198
+ this._isChangedStartIndex = false;
199
+ }
200
+ }
201
+
202
+ /**
203
+ * @method deleteAll
204
+ * @description Deletes all storage slots by setting them to zero, including the length and startIndex slots.
205
+ */
206
+ public deleteAll(): void {
207
+ // Iterate over all loaded slots and clear them
208
+ const keys = this._values.keys();
209
+ for (let i = 0; i < keys.length; i++) {
210
+ const slotIndex = keys[i];
211
+ const storagePointer = this.calculateStoragePointer(slotIndex);
212
+ Blockchain.setStorageAt(storagePointer, u256.Zero);
213
+ }
214
+
215
+ // Reset the length and startIndex to zero
216
+ const zeroLengthAndStartIndex = u256.Zero;
217
+ Blockchain.setStorageAt(this.lengthPointer, zeroLengthAndStartIndex);
218
+ this._length = 0;
219
+ this._startIndex = 0;
220
+ this._isChangedLength = false;
221
+ this._isChangedStartIndex = false;
222
+
223
+ // Clear internal caches
224
+ this._values.clear();
225
+ this._isLoaded.clear();
226
+ this._isChanged.clear();
227
+ }
228
+
229
+ /**
230
+ * @method setMultiple
231
+ * @description Sets multiple u256 values starting from a specific global index.
232
+ * @param {u32} startIndex - The starting global index.
233
+ * @param {u256[]} values - An array of u256 values to set.
234
+ */
235
+ @inline
236
+ public setMultiple(startIndex: u32, values: u256[]): void {
237
+ for (let i: u32 = 0; i < values.length; i++) {
238
+ this.set(<u64>(startIndex + i), values[i]);
239
+ }
240
+ }
241
+
242
+ /**
243
+ * @method getAll
244
+ * @description Retrieves a range of u256 values starting from a specific global index.
245
+ * @param {u32} startIndex - The starting global index.
246
+ * @param {u32} count - The number of u256 values to retrieve.
247
+ * @returns {u256[]} - An array containing the retrieved u256 values.
248
+ */
249
+ @inline
250
+ public getAll(startIndex: u32, count: u32): u256[] {
251
+ assert(startIndex + count <= this._length, 'Requested range exceeds array length');
252
+ const result: u256[] = new Array<u256>(count);
253
+ for (let i: u32 = 0; i < count; i++) {
254
+ result[i] = this.get(<u64>(startIndex + i));
255
+ }
256
+ return result;
257
+ }
258
+
259
+ /**
260
+ * @method toString
261
+ * @description Returns a string representation of all cached u256 values.
262
+ * @returns {string} - A string in the format "[value0, value1, ..., valueN]".
263
+ */
264
+ @inline
265
+ public toString(): string {
266
+ let str = '[';
267
+ for (let i: u32 = 0; i < this._length; i++) {
268
+ const value = this.get(<u64>i);
269
+ str += value.toString();
270
+ if (i !== this._length - 1) {
271
+ str += ', ';
272
+ }
273
+ }
274
+ str += ']';
275
+ return str;
276
+ }
277
+
278
+ /**
279
+ * @method toBytes
280
+ * @description Returns the packed u256 values as a byte array.
281
+ * @returns {u8[]} - The packed u256 values in byte form.
282
+ */
283
+ @inline
284
+ public toBytes(): u8[] {
285
+ const bytes: u8[] = new Array<u8>();
286
+ for (let i: u32 = 0; i < this._length; i++) {
287
+ this.ensureValues(i);
288
+ const value = this._values.get(i);
289
+ if (value) {
290
+ const valueBytes = value.toBytes();
291
+ for (let j: u32 = 0; j < valueBytes.length; j++) {
292
+ bytes.push(valueBytes[j]);
293
+ }
294
+ }
295
+ }
296
+ return bytes;
297
+ }
298
+
299
+ /**
300
+ * @method reset
301
+ * @description Resets all cached u256 values to zero and marks them as changed, including resetting the length and startIndex.
302
+ */
303
+ @inline
304
+ public reset(): void {
305
+ // Reset the length and startIndex to zero
306
+ this._length = 0;
307
+ this._startIndex = 0;
308
+ this._isChangedLength = true;
309
+ this._isChangedStartIndex = true;
310
+
311
+ this.save();
312
+ }
313
+
314
+ /**
315
+ * @method getLength
316
+ * @description Retrieves the current length of the array.
317
+ * @returns {u64} - The current length.
318
+ */
319
+ @inline
320
+ public getLength(): u64 {
321
+ return this._length;
322
+ }
323
+
324
+ /**
325
+ * @method startingIndex
326
+ * @description Retrieves the current starting index of the array.
327
+ * @returns {u64} - The starting index.
328
+ */
329
+ public startingIndex(): u64 {
330
+ return this._startIndex;
331
+ }
332
+
333
+ /**
334
+ * @method setLength
335
+ * @description Sets the length of the array.
336
+ * @param {u64} newLength - The new length to set.
337
+ */
338
+ public setLength(newLength: u64): void {
339
+ if (newLength > this.MAX_LENGTH) {
340
+ throw new Revert('SetLength operation failed: Length exceeds maximum allowed value.');
341
+ }
342
+
343
+ if (newLength < this._length) {
344
+ // Truncate the array if newLength is smaller
345
+ for (let i: u64 = newLength; i < this._length; i++) {
346
+ this.delete(i);
347
+ }
348
+ }
349
+
350
+ this._length = newLength;
351
+ this._isChangedLength = true;
352
+ }
353
+
354
+ /**
355
+ * @private
356
+ * @method ensureValues
357
+ * @description Loads and caches the u256 value from the specified storage slot.
358
+ * @param {u32} slotIndex - The index of the storage slot.
359
+ */
360
+ private ensureValues(slotIndex: u32): void {
361
+ if (!this._isLoaded.has(slotIndex)) {
362
+ const storagePointer = this.calculateStoragePointer(slotIndex);
363
+ const storedU256: u256 = Blockchain.getStorageAt(storagePointer, this.defaultValue);
364
+ this._values.set(slotIndex, storedU256);
365
+ this._isLoaded.add(slotIndex);
366
+ }
367
+ }
368
+
369
+ /**
370
+ * @private
371
+ * @method calculateStoragePointer
372
+ * @description Calculates the storage pointer for a given slot index by incrementing the base pointer.
373
+ * @param {u32} slotIndex - The index of the storage slot.
374
+ * @returns {u256} - The calculated storage pointer.
375
+ */
376
+ private calculateStoragePointer(slotIndex: u64): u256 {
377
+ // Each slot is identified by baseU256Pointer + slotIndex + 1
378
+ // Slot 0: baseU256Pointer + 1 (first element)
379
+ // Slot 1: baseU256Pointer + 2, etc.
380
+ return SafeMath.add(this.baseU256Pointer, u256.fromU64(slotIndex + 1));
381
+ }
382
+ }
@@ -0,0 +1,179 @@
1
+ import { MemorySlotPointer } from '../memory/MemorySlotPointer';
2
+ import { Blockchain } from '../env';
3
+ import { encodePointer } from '../math/abi';
4
+ import { BytesWriter } from '../buffer/BytesWriter';
5
+ import { u256 } from 'as-bignum/assembly';
6
+
7
+ /**
8
+ * @class StoredU64
9
+ * @description Manages up to four u64 values within a single u256 storage slot.
10
+ */
11
+ @final
12
+ export class StoredU64 {
13
+ private readonly u256Pointer: u256;
14
+
15
+ // Internal cache for four u64 values: [lo1, lo2, hi1, hi2]
16
+ private _values: u64[] = [0, 0, 0, 0];
17
+
18
+ // Flag to indicate if values are loaded from storage
19
+ private isLoaded: bool = false;
20
+
21
+ // Flag to indicate if any value has been changed
22
+ private isChanged: bool = false;
23
+
24
+ /**
25
+ * @constructor
26
+ * @param {u16} pointer - The primary pointer identifier.
27
+ * @param {MemorySlotPointer} subPointer - The sub-pointer for memory slot addressing.
28
+ * @param {u256} defaultValue - The default u256 value if storage is uninitialized.
29
+ */
30
+ constructor(
31
+ public pointer: u16,
32
+ public subPointer: MemorySlotPointer,
33
+ private defaultValue: u256,
34
+ ) {
35
+ const writer = new BytesWriter(32);
36
+ writer.writeU256(subPointer);
37
+
38
+ this.u256Pointer = encodePointer(pointer, writer.getBuffer());
39
+ }
40
+
41
+ /**
42
+ * @method get
43
+ * @description Retrieves the u64 value at the specified offset.
44
+ * @param {u8} index - The index (0 to 3) of the u64 value to retrieve.
45
+ * @returns {u64} - The u64 value at the specified index.
46
+ */
47
+ @inline
48
+ public get(index: u8): u64 {
49
+ assert(index < 4, 'Index out of bounds for StoredU64 (0-3)');
50
+ this.ensureValues();
51
+ return this._values[index];
52
+ }
53
+
54
+ /**
55
+ * @method set
56
+ * @description Sets the u64 value at the specified offset.
57
+ * @param {u8} index - The index (0 to 3) of the u64 value to set.
58
+ * @param {u64} value - The u64 value to assign.
59
+ */
60
+ @inline
61
+ public set(index: u8, value: u64): void {
62
+ assert(index < 4, 'Index out of bounds for StoredU64 (0-3)');
63
+ this.ensureValues();
64
+ if (this._values[index] != value) {
65
+ this._values[index] = value;
66
+ this.isChanged = true;
67
+ }
68
+ }
69
+
70
+ /**
71
+ * @method save
72
+ * @description Persists the cached u64 values to storage if any have been modified.
73
+ */
74
+ public save(): void {
75
+ if (this.isChanged) {
76
+ const packed = this.packValues();
77
+ Blockchain.setStorageAt(this.u256Pointer, packed);
78
+ this.isChanged = false;
79
+ }
80
+ }
81
+
82
+ /**
83
+ * @method setMultiple
84
+ * @description Sets multiple u64 values at once.
85
+ * @param {[u64, u64, u64, u64]} values - An array of four u64 values to set.
86
+ */
87
+ @inline
88
+ public setMultiple(values: u64[]): void {
89
+ this.ensureValues();
90
+ let changed = false;
91
+ for (let i: u8 = 0; i < 4; i++) {
92
+ if (this._values[i] != values[i]) {
93
+ this._values[i] = values[i];
94
+ changed = true;
95
+ }
96
+ }
97
+ if (changed) {
98
+ this.isChanged = true;
99
+ }
100
+ }
101
+
102
+ /**
103
+ * @method getAll
104
+ * @description Retrieves all four u64 values as a tuple.
105
+ * @returns {[u64, u64, u64, u64]} - A tuple containing all four u64 values.
106
+ */
107
+ @inline
108
+ public getAll(): u64[] {
109
+ this.ensureValues();
110
+ return this._values;
111
+ }
112
+
113
+ /**
114
+ * @method toString
115
+ * @description Returns a string representation of all four u64 values.
116
+ * @returns {string} - A string in the format "[value0, value1, value2, value3]".
117
+ */
118
+ @inline
119
+ public toString(): string {
120
+ this.ensureValues();
121
+ return `[${this._values[0].toString()}, ${this._values[1].toString()}, ${this._values[2].toString()}, ${this._values[3].toString()}]`;
122
+ }
123
+
124
+ /**
125
+ * @method toBytes
126
+ * @description Returns the packed u256 value as a byte array.
127
+ * @returns {Uint8Array} - The packed u256 value in byte form.
128
+ */
129
+ @inline
130
+ public toBytes(): u8[] {
131
+ this.ensureValues();
132
+ const packed = this.packValues();
133
+ return packed.toBytes();
134
+ }
135
+
136
+ /**
137
+ * @method reset
138
+ * @description Resets the cached values to default and marks as changed.
139
+ */
140
+ @inline
141
+ public reset(): void {
142
+ this._values = [0, 0, 0, 0];
143
+ this.isChanged = true;
144
+ }
145
+
146
+ /**
147
+ * @private
148
+ * @method ensureValues
149
+ * @description Loads and unpacks the u256 value from storage into four u64 cache variables.
150
+ */
151
+ private ensureValues(): void {
152
+ if (!this.isLoaded) {
153
+ const storedU256: u256 = Blockchain.getStorageAt(this.u256Pointer, this.defaultValue);
154
+
155
+ // Unpack the stored u256 into four u64s
156
+ this._values[0] = storedU256.lo1;
157
+ this._values[1] = storedU256.lo2;
158
+ this._values[2] = storedU256.hi1;
159
+ this._values[3] = storedU256.hi2;
160
+
161
+ this.isLoaded = true;
162
+ }
163
+ }
164
+
165
+ /**
166
+ * @private
167
+ * @method packValues
168
+ * @description Packs the four cached u64 values into a single u256 for storage.
169
+ * @returns {u256} - The packed u256 value.
170
+ */
171
+ private packValues(): u256 {
172
+ return new u256(
173
+ this._values[0], // lo1
174
+ this._values[1], // lo2
175
+ this._values[2], // hi1
176
+ this._values[3], // hi2
177
+ );
178
+ }
179
+ }
@@ -1,4 +1,4 @@
1
- import { u256 } from 'as-bignum/assembly';
1
+ import { u128, u256 } from 'as-bignum/assembly';
2
2
 
3
3
  export class SafeMath {
4
4
  public static ZERO: u256 = u256.fromU32(0);
@@ -11,6 +11,23 @@ export class SafeMath {
11
11
  return c;
12
12
  }
13
13
 
14
+ public static add128(a: u128, b: u128): u128 {
15
+ const c: u128 = u128.add(a, b);
16
+ if (c < a) {
17
+ throw new Error('SafeMath: addition overflow');
18
+ }
19
+ return c;
20
+ }
21
+
22
+ public static add64(a: u64, b: u64): u64 {
23
+ const c: u64 = a + b;
24
+
25
+ if (c < a) {
26
+ throw new Error('SafeMath: addition overflow');
27
+ }
28
+ return c;
29
+ }
30
+
14
31
  public static sub(a: u256, b: u256): u256 {
15
32
  if (a < b) {
16
33
  throw new Error('SafeMath: subtraction overflow');
@@ -19,6 +36,22 @@ export class SafeMath {
19
36
  return u256.sub(a, b);
20
37
  }
21
38
 
39
+ public static sub128(a: u128, b: u128): u128 {
40
+ if (a < b) {
41
+ throw new Error('SafeMath: subtraction overflow');
42
+ }
43
+
44
+ return u128.sub(a, b);
45
+ }
46
+
47
+ public static sub64(a: u64, b: u64): u64 {
48
+ if (a < b) {
49
+ throw new Error('SafeMath: subtraction overflow');
50
+ }
51
+
52
+ return a - b;
53
+ }
54
+
22
55
  // Computes (a * b) % modulus with full precision
23
56
  public static mulmod(a: u256, b: u256, modulus: u256): u256 {
24
57
  if (u256.eq(modulus, u256.Zero)) throw new Error('SafeMath: modulo by zero');
@@ -91,6 +124,56 @@ export class SafeMath {
91
124
  return c;
92
125
  }
93
126
 
127
+ public static mul128(a: u128, b: u128): u128 {
128
+ if (a === u128.Zero || b === u128.Zero) {
129
+ return u128.Zero;
130
+ }
131
+
132
+ const c: u128 = u128.mul(a, b);
133
+ const d: u128 = SafeMath.div128(c, a);
134
+
135
+ if (u128.ne(d, b)) {
136
+ throw new Error('SafeMath: multiplication overflow');
137
+ }
138
+
139
+ return c;
140
+ }
141
+
142
+ public static div128(a: u128, b: u128): u128 {
143
+ if (b.isZero()) {
144
+ throw new Error('Division by zero');
145
+ }
146
+
147
+ if (a.isZero()) {
148
+ return new u128();
149
+ }
150
+
151
+ if (u128.lt(a, b)) {
152
+ return new u128(); // Return 0 if a < b
153
+ }
154
+
155
+ if (u128.eq(a, b)) {
156
+ return new u128(1); // Return 1 if a == b
157
+ }
158
+
159
+ let n = a.clone();
160
+ let d = b.clone();
161
+ let result = new u128();
162
+
163
+ const shift = u128.clz(d) - u128.clz(n);
164
+ d = SafeMath.shl128(d, shift); // align d with n by shifting left
165
+
166
+ for (let i = shift; i >= 0; i--) {
167
+ if (u128.ge(n, d)) {
168
+ n = u128.sub(n, d);
169
+ result = u128.or(result, SafeMath.shl128(u128.One, i));
170
+ }
171
+ d = u128.shr(d, 1); // restore d to original by shifting right
172
+ }
173
+
174
+ return result;
175
+ }
176
+
94
177
  @unsafe
95
178
  @operator('/')
96
179
  public static div(a: u256, b: u256): u256 {
@@ -199,6 +282,41 @@ export class SafeMath {
199
282
  return new u256(result[0], result[1], result[2], result[3]);
200
283
  }
201
284
 
285
+ public static shl128(value: u128, shift: i32): u128 {
286
+ if (shift == 0) {
287
+ return value.clone();
288
+ }
289
+
290
+ const totalBits = 256;
291
+ const bitsPerSegment = 64;
292
+
293
+ // Normalize shift to be within 0-255 range
294
+ shift &= 255;
295
+
296
+ if (shift >= totalBits) {
297
+ return new u128(); // Shift size larger than width results in zero
298
+ }
299
+
300
+ // Determine how many full 64-bit segments we are shifting
301
+ const segmentShift = (shift / bitsPerSegment) | 0;
302
+ const bitShift = shift % bitsPerSegment;
303
+
304
+ const segments = [value.lo, value.hi];
305
+
306
+ const result = new Array<u64>(2).fill(0);
307
+
308
+ for (let i = 0; i < segments.length; i++) {
309
+ if (i + segmentShift < segments.length) {
310
+ result[i + segmentShift] |= segments[i] << bitShift;
311
+ }
312
+ if (bitShift != 0 && i + segmentShift + 1 < segments.length) {
313
+ result[i + segmentShift + 1] |= segments[i] >>> (bitsPerSegment - bitShift);
314
+ }
315
+ }
316
+
317
+ return new u128(result[0], result[1]);
318
+ }
319
+
202
320
  public static and(a: u256, b: u256): u256 {
203
321
  return u256.and(a, b);
204
322
  }