@btc-vision/btc-runtime 1.6.0 → 1.6.2

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.
@@ -1,298 +1,39 @@
1
- import { BytesWriter } from '../../buffer/BytesWriter';
2
- import { Blockchain } from '../../env';
3
- import {
4
- addUint8ArraysBE,
5
- bigEndianAdd,
6
- encodeBasePointer,
7
- readLengthAndStartIndex,
8
- u64ToBE32Bytes,
9
- } from '../../math/bytes';
1
+ import { DEFAULT_MAX_LENGTH, StoredPackedArray } from './StoredPackedArray';
2
+ import { bigEndianAdd } from '../../math/bytes';
10
3
  import { Address } from '../../types/Address';
11
- import { Revert } from '../../types/Revert';
12
4
 
13
5
  /**
14
- * @class StoredAddressArray
15
- * @description Manages an array of Address values across multiple storage slots.
16
- * Each slot holds one Address (stored as a raw Uint8Array in storage).
6
+ * StoredAddressArray
7
+ *
8
+ * Array of addresses.
17
9
  */
18
10
  @final
19
- export class StoredAddressArray {
20
- private readonly baseU256Pointer: Uint8Array;
21
- private readonly lengthPointer: Uint8Array;
22
-
23
- private _values: Map<u64, Address> = new Map(); // slotIndex -> Address
24
- private _isChanged: Set<u64> = new Set(); // track changed slotIndexes
25
-
26
- private _length: u64 = 0;
27
- private _startIndex: u64 = 0;
28
- private _isChangedLength: bool = false;
29
- private _isChangedStartIndex: bool = false;
30
-
31
- private readonly MAX_LENGTH: u64 = u64(u32.MAX_VALUE - 1);
32
-
33
- private readonly defaultValue: Address = Address.zero();
34
-
35
- /**
36
- * @constructor
37
- * @param {u16} pointer - The primary pointer identifier.
38
- * @param {Uint8Array} subPointer - The sub-pointer for memory slot addressing.
39
- */
40
- constructor(public pointer: u16, public subPointer: Uint8Array) {
41
- assert(
42
- subPointer.length <= 30,
43
- `You must pass a 30 bytes sub-pointer. (AddressArray, got ${subPointer.length})`,
44
- );
45
-
46
- const basePointer = encodeBasePointer(pointer, subPointer);
47
- this.lengthPointer = Uint8Array.wrap(basePointer.buffer);
48
- this.baseU256Pointer = bigEndianAdd(basePointer, 1);
49
-
50
- const storedLenStart = Blockchain.getStorageAt(basePointer);
51
- const data = readLengthAndStartIndex(storedLenStart);
52
-
53
- this._length = data[0];
54
- this._startIndex = data[1];
55
- }
56
-
57
- @inline
58
- public has(index: u64): bool {
59
- return index < this._length;
60
- }
61
-
62
- /** Get an element by its global index. */
63
- @inline
64
- @operator('[]')
65
- public get(index: u64): Address {
66
- if (index >= this._length) {
67
- throw new Revert('get: index out of range (address array)');
68
- }
69
-
70
- const physicalIndex = (this._startIndex + index) % this.MAX_LENGTH;
71
- const slotIndex: u32 = <u32>physicalIndex;
72
- this.ensureValues(slotIndex);
73
-
74
- return this._values.get(slotIndex);
11
+ export class StoredAddressArray extends StoredPackedArray<Address> {
12
+ public constructor(pointer: u16, subPointer: Uint8Array, maxLength: u32 = DEFAULT_MAX_LENGTH) {
13
+ super(pointer, subPointer, Address.zero(), maxLength);
75
14
  }
76
15
 
77
- /** Set an element by its global index. */
78
- @inline
79
- @operator('[]=')
80
- public set(index: u64, value: Address): void {
81
- if (index >= this._length) {
82
- throw new Revert('set: index out of range (address array)');
83
- }
84
-
85
- const physicalIndex = (this._startIndex + index) % this.MAX_LENGTH;
86
- const slotIndex: u32 = <u32>physicalIndex;
87
- this.ensureValues(slotIndex);
88
-
89
- const currentValue = this._values.get(slotIndex);
90
- if (currentValue != value) {
91
- this._values.set(slotIndex, value);
92
- this._isChanged.add(slotIndex);
93
- }
16
+ protected getSlotCapacity(): u32 {
17
+ return 1; // 1 x u256 => 32 bytes
94
18
  }
95
19
 
96
- /** Append an address at the end of the array. */
97
- @inline
98
- public push(value: Address): void {
99
- if (this._length >= this.MAX_LENGTH) {
100
- throw new Revert('push: array reached maximum length (address array)');
101
- }
102
-
103
- const newLogicalIndex: u64 = this._length;
104
- const physicalIndex: u64 = (this._startIndex + newLogicalIndex) % this.MAX_LENGTH;
105
- const slotIndex: u32 = <u32>physicalIndex;
106
-
107
- this.ensureValues(slotIndex);
108
- this._values.set(slotIndex, value);
109
- this._isChanged.add(slotIndex);
110
-
111
- this._length += 1;
112
- this._isChangedLength = true;
20
+ protected zeroValue(): Address {
21
+ return Address.zero();
113
22
  }
114
23
 
115
- /** Delete the last element. */
116
- public deleteLast(): void {
117
- if (this._length === 0) {
118
- throw new Revert('deleteLast: array is empty (address array)');
119
- }
120
-
121
- const lastLogicalIndex: u64 = this._length - 1;
122
- const physicalIndex: u64 = (this._startIndex + lastLogicalIndex) % this.MAX_LENGTH;
123
- const slotIndex: u32 = <u32>physicalIndex;
124
- this.ensureValues(slotIndex);
125
-
126
- const currentValue = this._values.get(slotIndex);
127
- if (currentValue != this.defaultValue) {
128
- this._values.set(slotIndex, this.defaultValue);
129
- this._isChanged.add(slotIndex);
130
- }
131
-
132
- this._length -= 1;
133
- this._isChangedLength = true;
24
+ protected eq(a: Address, b: Address): bool {
25
+ return a == b;
134
26
  }
135
27
 
136
- /** Adjust the starting index. */
137
- public setStartingIndex(index: u64): void {
138
- this._startIndex = index;
139
- this._isChangedStartIndex = true;
28
+ protected packSlot(values: Address[]): Uint8Array {
29
+ return values[0];
140
30
  }
141
31
 
142
- /** Delete a specific element by setting it to `defaultValue`. */
143
- @inline
144
- public delete(index: u64): void {
145
- if (index > this.MAX_LENGTH) {
146
- throw new Revert('delete: index out of range (address array)');
147
- }
148
-
149
- const physicalIndex: u64 = (this._startIndex + index) % this.MAX_LENGTH;
150
- const slotIndex: u32 = <u32>physicalIndex;
151
- this.ensureValues(slotIndex);
152
-
153
- const currentValue = this._values.get(slotIndex);
154
- if (currentValue != this.defaultValue) {
155
- this._values.set(slotIndex, this.defaultValue);
156
- this._isChanged.add(slotIndex);
157
- }
32
+ protected unpackSlot(slotData: Uint8Array): Address[] {
33
+ return [Address.fromUint8Array(slotData)];
158
34
  }
159
35
 
160
- /**
161
- * Persist changes to storage.
162
- * - Store any changed slotIndex -> Address
163
- * - Store updated length and startIndex if changed
164
- */
165
- @inline
166
- public save(): void {
167
- // 1) Save changed slots
168
- const changed = this._isChanged.values();
169
- for (let i = 0; i < changed.length; i++) {
170
- const slotIndex = changed[i];
171
- const storagePointer = this.calculateStoragePointer(slotIndex);
172
-
173
- const value = this._values.get(slotIndex);
174
- Blockchain.setStorageAt(storagePointer, value);
175
- }
176
- this._isChanged.clear();
177
-
178
- // 2) Save length and startIndex if changed
179
- if (this._isChangedLength || this._isChangedStartIndex) {
180
- const writer = new BytesWriter(16);
181
- writer.writeU64(this._length);
182
- writer.writeU64(this._startIndex);
183
-
184
- Blockchain.setStorageAt(this.lengthPointer, writer.getBuffer());
185
- this._isChangedLength = false;
186
- this._isChangedStartIndex = false;
187
- }
188
- }
189
-
190
- /** Clear entire array content from storage, reset length and startIndex. */
191
- public deleteAll(): void {
192
- const keys = this._values.keys();
193
- for (let i = 0; i < keys.length; i++) {
194
- const slotIndex = keys[i];
195
- const storagePointer = this.calculateStoragePointer(slotIndex);
196
- Blockchain.setStorageAt(storagePointer, this.defaultValue);
197
- }
198
-
199
- Blockchain.setStorageAt(this.lengthPointer, new Uint8Array(32));
200
-
201
- this._length = 0;
202
- this._startIndex = 0;
203
- this._isChangedLength = false;
204
- this._isChangedStartIndex = false;
205
-
206
- this._values.clear();
207
- this._isChanged.clear();
208
- }
209
-
210
- /** Bulk-set multiple addresses starting at `startIndex`. */
211
- @inline
212
- public setMultiple(startIndex: u32, values: Address[]): void {
213
- for (let i: u32 = 0; i < values.length; i++) {
214
- this.set(<u64>(startIndex + i), values[i]);
215
- }
216
- }
217
-
218
- /** Retrieve a batch of addresses (range). */
219
- @inline
220
- public getAll(startIndex: u32, count: u32): Address[] {
221
- if (startIndex + count > this._length) {
222
- throw new Revert('getAll: index out of range (address array)');
223
- }
224
-
225
- const result = new Array<Address>(count);
226
- for (let i: u32 = 0; i < count; i++) {
227
- result[i] = this.get(<u64>(startIndex + i));
228
- }
229
- return result;
230
- }
231
-
232
- /** Returns a string of the form "[addr0, addr1, ...]". */
233
- @inline
234
- public toString(): string {
235
- let str = '[';
236
- for (let i: u32 = 0; i < this._length; i++) {
237
- const value = this.get(<u64>i);
238
- str += value.toString();
239
- if (i !== this._length - 1) {
240
- str += ', ';
241
- }
242
- }
243
- str += ']';
244
- return str;
245
- }
246
-
247
- /** Reset in-memory and persist. */
248
- @inline
249
- public reset(): void {
250
- this._length = 0;
251
- this._startIndex = 0;
252
- this._isChangedLength = true;
253
- this._isChangedStartIndex = true;
254
-
255
- this._values.clear();
256
- this._isChanged.clear();
257
-
258
- this.save();
259
- }
260
-
261
- /** Current array length. */
262
- @inline
263
- public getLength(): u64 {
264
- return this._length;
265
- }
266
-
267
- /** Current starting index. */
268
- public startingIndex(): u64 {
269
- return this._startIndex;
270
- }
271
-
272
- /**
273
- * Ensure the given slot index is loaded into `_values`.
274
- */
275
- private ensureValues(slotIndex: u32): void {
276
- if (!this._values.has(slotIndex)) {
277
- const storagePointer = this.calculateStoragePointer(slotIndex);
278
-
279
- // Load raw bytes from storage
280
- const stored: Uint8Array = Blockchain.getStorageAt(storagePointer);
281
-
282
- const storedAddress: Address =
283
- stored.length == 0 ? this.defaultValue : Address.fromUint8Array(stored);
284
-
285
- this._values.set(slotIndex, storedAddress);
286
- }
287
- }
288
-
289
- /**
290
- * Compute a 32-byte storage pointer = basePointer + (slotIndex + 1) big-endian.
291
- */
292
- private calculateStoragePointer(slotIndex: u64): Uint8Array {
293
- // Convert (slotIndex) to a 32-byte big-endian offset
294
- const offset = u64ToBE32Bytes(slotIndex);
295
-
296
- return addUint8ArraysBE(this.baseU256Pointer, offset);
36
+ protected calculateStoragePointer(slotIndex: u64): Uint8Array {
37
+ return bigEndianAdd(this.basePointer, slotIndex);
297
38
  }
298
39
  }
@@ -1,6 +1,6 @@
1
- import { BytesWriter } from '../../buffer/BytesWriter';
2
- import { Blockchain } from '../../env';
3
- import { Revert } from '../../types/Revert';
1
+ import {BytesWriter} from '../../buffer/BytesWriter';
2
+ import {Blockchain} from '../../env';
3
+ import {Revert} from '../../types/Revert';
4
4
  import {
5
5
  addUint8ArraysBE,
6
6
  bigEndianAdd,
@@ -11,6 +11,7 @@ import {
11
11
  setBit,
12
12
  u64ToBE32Bytes,
13
13
  } from '../../math/bytes';
14
+ import {DEFAULT_MAX_LENGTH} from './StoredPackedArray';
14
15
 
15
16
  /**
16
17
  * @class StoredBooleanArray
@@ -21,34 +22,40 @@ import {
21
22
  * - slot 0 stores indexes [0..255]
22
23
  * - slot 1 stores indexes [256..511]
23
24
  * - ...
25
+ * Note: This is designed to wrap around.
24
26
  */
25
27
  @final
26
28
  export class StoredBooleanArray {
27
29
  private readonly basePointer: Uint8Array;
28
30
  private readonly lengthPointer: Uint8Array;
29
31
 
30
- private _values: Map<u64, Uint8Array> = new Map();
31
- private _isChanged: Set<u64> = new Set();
32
+ private _values: Map<u32, Uint8Array> = new Map();
33
+ private _isChanged: Set<u32> = new Set();
32
34
 
33
- private _length: u64 = 0;
34
- private _startIndex: u64 = 0;
35
+ private _length: u32 = 0;
36
+ private _startIndex: u32 = 0;
35
37
  private _isChangedLength: bool = false;
36
38
  private _isChangedStartIndex: bool = false;
37
39
 
38
- private readonly MAX_LENGTH: u64 = u32.MAX_VALUE;
40
+ private nextItemOffset: u32 = 0;
39
41
 
40
42
  /**
41
43
  * @constructor
42
44
  * @param {u16} pointer - The primary pointer identifier.
43
45
  * @param {Uint8Array} subPtr - The sub-pointer for memory slot addressing.
46
+ * @param {u32} [MAX_LENGTH=DEFAULT_MAX_LENGTH] - The maximum length of the array.
44
47
  *
45
48
  * The code below treats the first 16 bytes of `lengthPointer` as storing [length, startIndex].
46
49
  */
47
50
  constructor(
48
51
  public pointer: u16,
49
52
  public subPtr: Uint8Array,
53
+ protected MAX_LENGTH: u32 = DEFAULT_MAX_LENGTH,
50
54
  ) {
51
- assert(subPtr.length <= 30, `You must pass a 30 bytes sub-pointer. (StoredBooleanArray, got ${subPtr.length})`);
55
+ assert(
56
+ subPtr.length <= 30,
57
+ `You must pass a 30 bytes sub-pointer. (StoredBooleanArray, got ${subPtr.length})`,
58
+ );
52
59
 
53
60
  const basePointer = encodeBasePointer(pointer, subPtr);
54
61
  this.lengthPointer = Uint8Array.wrap(basePointer.buffer);
@@ -61,28 +68,81 @@ export class StoredBooleanArray {
61
68
  this._startIndex = data[1];
62
69
  }
63
70
 
64
- // -------------- Public Accessors -------------- //
71
+ @inline
72
+ public get previousOffset(): u32 {
73
+ return <u32>(
74
+ ((this._startIndex +
75
+ <u64>(this.nextItemOffset === 0 ? this.nextItemOffset : this.nextItemOffset - 1)) %
76
+ this.MAX_LENGTH)
77
+ );
78
+ }
65
79
 
80
+ /**
81
+ * Set the maximum length of the array.
82
+ * This is a safety check to prevent unbounded usage.
83
+ */
66
84
  @inline
67
- public has(index: u64): bool {
85
+ public setMaxLength(maxLength: u32): void {
86
+ if (maxLength > this.MAX_LENGTH) {
87
+ throw new Revert('setMaxLength: maxLength exceeds MAX_LENGTH');
88
+ }
89
+
90
+ this.MAX_LENGTH = maxLength;
91
+ }
92
+
93
+ @inline
94
+ public has(index: u32): bool {
68
95
  return index < this._length;
69
96
  }
70
97
 
98
+ /**
99
+ * Get the next item in the array, starting from the current offset.
100
+ * This is useful for iterating through the array.
101
+ */
102
+ @inline
103
+ public next(): bool {
104
+ const value = this.get(this.nextItemOffset);
105
+ this.nextItemOffset += 1;
106
+
107
+ return value;
108
+ }
109
+
110
+ /**
111
+ * Apply the starting index with n offset.
112
+ */
113
+ @inline
114
+ public applyNextOffsetToStartingIndex(): void {
115
+ if (!this.nextItemOffset) return;
116
+
117
+ this._startIndex += this.nextItemOffset - 1;
118
+ this._isChangedStartIndex = true;
119
+ this.nextItemOffset = 0;
120
+ }
121
+
122
+ @inline
123
+ public incrementStartingIndex(): void {
124
+ if (this._startIndex >= this.MAX_LENGTH) {
125
+ this._startIndex = 0;
126
+ } else {
127
+ this._startIndex += 1;
128
+ }
129
+
130
+ this._isChangedStartIndex = true;
131
+ }
132
+
71
133
  /**
72
134
  * Retrieve boolean at `index`.
73
135
  */
74
136
  @operator('[]')
75
137
  @inline
76
- public get(index: u64): bool {
138
+ public get(index: u32): bool {
77
139
  if (index >= this._length) {
78
- throw new Revert(`get: index out of range (${index} >= ${this._length}, boolean array)`);
140
+ throw new Revert(
141
+ `get: index out of range (${index} >= ${this._length}, boolean array)`,
142
+ );
79
143
  }
80
144
 
81
- const effectiveIndex = this._startIndex + index;
82
- const wrappedIndex = effectiveIndex < this.MAX_LENGTH
83
- ? effectiveIndex
84
- : effectiveIndex % this.MAX_LENGTH;
85
-
145
+ const wrappedIndex = this.getRealIndex(index);
86
146
  const slotIndex = wrappedIndex / 256;
87
147
  const bitIndex = <u16>(wrappedIndex % 256);
88
148
 
@@ -97,16 +157,14 @@ export class StoredBooleanArray {
97
157
  */
98
158
  @operator('[]=')
99
159
  @inline
100
- public set(index: u64, value: bool): void {
160
+ public set(index: u32, value: bool): void {
101
161
  if (index >= this._length) {
102
- throw new Revert(`set: index out of range (${index} >= ${this._length}, boolean array)`);
162
+ throw new Revert(
163
+ `set: index out of range (${index} >= ${this._length}, boolean array)`,
164
+ );
103
165
  }
104
166
 
105
- const effectiveIndex = this._startIndex + index;
106
- const wrappedIndex = effectiveIndex < this.MAX_LENGTH
107
- ? effectiveIndex
108
- : effectiveIndex % this.MAX_LENGTH;
109
-
167
+ const wrappedIndex = this.getRealIndex(index);
110
168
  const slotIndex = wrappedIndex / 256;
111
169
  const bitIndex = <u16>(wrappedIndex % 256);
112
170
 
@@ -126,17 +184,12 @@ export class StoredBooleanArray {
126
184
  * Push a new boolean at the "end" of the array.
127
185
  */
128
186
  @inline
129
- public push(value: bool): void {
187
+ public push(value: bool): u32 {
130
188
  if (this._length >= this.MAX_LENGTH) {
131
189
  throw new Revert('push: reached max allowed length (boolean array)');
132
190
  }
133
191
 
134
- const newIndex = this._length;
135
- const effectiveIndex = this._startIndex + newIndex;
136
- const wrappedIndex = effectiveIndex < this.MAX_LENGTH
137
- ? effectiveIndex
138
- : effectiveIndex % this.MAX_LENGTH;
139
-
192
+ const wrappedIndex = this.getRealIndex(this._length);
140
193
  const slotIndex = wrappedIndex / 256;
141
194
  const bitIndex = <u16>(wrappedIndex % 256);
142
195
 
@@ -150,22 +203,20 @@ export class StoredBooleanArray {
150
203
 
151
204
  this._length += 1;
152
205
  this._isChangedLength = true;
206
+
207
+ return wrappedIndex;
153
208
  }
154
209
 
155
210
  /**
156
211
  * Delete the boolean at `index` by setting it to false.
157
212
  */
158
213
  @inline
159
- public delete(index: u64): void {
214
+ public delete(index: u32): void {
160
215
  if (index >= this._length) {
161
216
  throw new Revert('delete: index out of range (boolean array)');
162
217
  }
163
218
 
164
- const effectiveIndex = this._startIndex + index;
165
- const wrappedIndex = effectiveIndex < this.MAX_LENGTH
166
- ? effectiveIndex
167
- : effectiveIndex % this.MAX_LENGTH;
168
-
219
+ const wrappedIndex = this.getRealIndex(index);
169
220
  const slotIndex = wrappedIndex / 256;
170
221
  const bitIndex = <u16>(wrappedIndex % 256);
171
222
 
@@ -181,6 +232,16 @@ export class StoredBooleanArray {
181
232
  }
182
233
  }
183
234
 
235
+ @inline
236
+ public removeItemFromLength(): void {
237
+ if (this._length == 0) {
238
+ throw new Revert('delete: array is empty');
239
+ }
240
+
241
+ this._length -= 1;
242
+ this._isChangedLength = true;
243
+ }
244
+
184
245
  /**
185
246
  * Remove the last element by setting it false and decrementing length.
186
247
  */
@@ -212,11 +273,10 @@ export class StoredBooleanArray {
212
273
 
213
274
  this._isChanged.clear();
214
275
 
215
- // 2) If length or startIndex changed, store them
216
276
  if (this._isChangedLength || this._isChangedStartIndex) {
217
277
  const w = new BytesWriter(32);
218
- w.writeU64(this._length);
219
- w.writeU64(this._startIndex);
278
+ w.writeU32(this._length);
279
+ w.writeU32(this._startIndex);
220
280
 
221
281
  const data = w.getBuffer();
222
282
  Blockchain.setStorageAt(this.lengthPointer, data);
@@ -255,8 +315,8 @@ export class StoredBooleanArray {
255
315
  * Set multiple bools starting at `startIndex`.
256
316
  */
257
317
  @inline
258
- public setMultiple(startIndex: u64, values: bool[]): void {
259
- for (let i: u64 = 0; i < values.length; i++) {
318
+ public setMultiple(startIndex: u32, values: bool[]): void {
319
+ for (let i: u32 = 0; i < values.length; i++) {
260
320
  this.set(startIndex + i, values[i]);
261
321
  }
262
322
  }
@@ -265,17 +325,17 @@ export class StoredBooleanArray {
265
325
  * Retrieve a batch of bools.
266
326
  */
267
327
  @inline
268
- public getAll(start: u64, count: u64): bool[] {
328
+ public getAll(start: u32, count: u32): bool[] {
269
329
  if (start + count > this._length) {
270
330
  throw new Revert('getAll: range exceeds array length (boolean array)');
271
331
  }
272
332
 
273
- if (count > u64(u32.MAX_VALUE)) {
333
+ if (count > u32(u32.MAX_VALUE)) {
274
334
  throw new Revert('getAll: range exceeds max allowed (boolean array)');
275
335
  }
276
336
 
277
337
  const result = new Array<bool>(<i32>count);
278
- for (let i: u64 = 0; i < count; i++) {
338
+ for (let i: u32 = 0; i < count; i++) {
279
339
  result[<i32>i] = this.get(start + i);
280
340
  }
281
341
 
@@ -288,7 +348,7 @@ export class StoredBooleanArray {
288
348
  @inline
289
349
  public toString(): string {
290
350
  let s = '[';
291
- for (let i: u64 = 0; i < this._length; i++) {
351
+ for (let i: u32 = 0; i < this._length; i++) {
292
352
  s += this.get(i).toString();
293
353
  if (i < this._length - 1) {
294
354
  s += ', ';
@@ -318,12 +378,12 @@ export class StoredBooleanArray {
318
378
  * Current array length (number of booleans stored).
319
379
  */
320
380
  @inline
321
- public getLength(): u64 {
381
+ public getLength(): u32 {
322
382
  return this._length;
323
383
  }
324
384
 
325
385
  @inline
326
- public setStartingIndex(index: u64): void {
386
+ public setStartingIndex(index: u32): void {
327
387
  this._startIndex = index;
328
388
  this._isChangedStartIndex = true;
329
389
  }
@@ -332,14 +392,24 @@ export class StoredBooleanArray {
332
392
  * Current starting index for the array.
333
393
  */
334
394
  @inline
335
- public startingIndex(): u64 {
395
+ public startingIndex(): u32 {
336
396
  return this._startIndex;
337
397
  }
338
398
 
399
+ private getRealIndex(index: u32): u32 {
400
+ const maxLength: u64 = <u64>this.MAX_LENGTH;
401
+ let realIndex: u64 = <u64>this._startIndex + <u64>index;
402
+ if (!(realIndex < maxLength)) {
403
+ realIndex %= maxLength;
404
+ }
405
+
406
+ return <u32>realIndex;
407
+ }
408
+
339
409
  /**
340
410
  * Ensure the 32-byte slot for `slotIndex` is loaded into _values.
341
411
  */
342
- private ensureSlotLoaded(slotIndex: u64): void {
412
+ private ensureSlotLoaded(slotIndex: u32): void {
343
413
  if (!this._values.has(slotIndex)) {
344
414
  const pointer = this.calculateStoragePointer(slotIndex);
345
415
  const stored = Blockchain.getStorageAt(pointer);
@@ -350,7 +420,7 @@ export class StoredBooleanArray {
350
420
  /**
351
421
  * Convert `slotIndex` -> pointer = basePointer + (slotIndex + 1), as big-endian addition.
352
422
  */
353
- private calculateStoragePointer(slotIndex: u64): Uint8Array {
423
+ private calculateStoragePointer(slotIndex: u32): Uint8Array {
354
424
  const offset = u64ToBE32Bytes(slotIndex);
355
425
  return addUint8ArraysBE(this.basePointer, offset);
356
426
  }