@btc-vision/btc-runtime 1.3.11 → 1.3.13

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@btc-vision/btc-runtime",
3
- "version": "1.3.11",
3
+ "version": "1.3.13",
4
4
  "description": "Bitcoin Smart Contract Runtime",
5
5
  "main": "btc/index.ts",
6
6
  "scripts": {
@@ -86,7 +86,7 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
86
86
  throw new Revert('Already initialized');
87
87
  }
88
88
 
89
- if (!skipOwnerVerification) this.onlyOwner(Blockchain.tx.sender);
89
+ if (!skipOwnerVerification) this.onlyDeployer(Blockchain.tx.sender);
90
90
 
91
91
  if (params.decimals > 32) {
92
92
  throw new Revert('Decimals can not be more than 32');
@@ -237,12 +237,12 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
237
237
  return this.balanceOfMap.get(owner);
238
238
  }
239
239
 
240
- protected _burn(value: u256, onlyOwner: boolean = true): boolean {
240
+ protected _burn(value: u256, onlyDeployer: boolean = true): boolean {
241
241
  if (u256.eq(value, u256.Zero)) {
242
242
  throw new Revert(`No tokens`);
243
243
  }
244
244
 
245
- if (onlyOwner) this.onlyOwner(Blockchain.tx.sender);
245
+ if (onlyDeployer) this.onlyDeployer(Blockchain.tx.sender);
246
246
 
247
247
  if (this._totalSupply.value < value) throw new Revert(`Insufficient total supply.`);
248
248
  if (!this.balanceOfMap.has(Blockchain.tx.sender)) throw new Revert('No balance');
@@ -260,8 +260,8 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
260
260
  return true;
261
261
  }
262
262
 
263
- protected _mint(to: Address, value: u256, onlyOwner: boolean = true): boolean {
264
- if (onlyOwner) this.onlyOwner(Blockchain.tx.sender);
263
+ protected _mint(to: Address, value: u256, onlyDeployer: boolean = true): boolean {
264
+ if (onlyDeployer) this.onlyDeployer(Blockchain.tx.sender);
265
265
 
266
266
  if (!this.balanceOfMap.has(to)) {
267
267
  this.balanceOfMap.set(to, value);
@@ -331,7 +331,7 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
331
331
  }
332
332
 
333
333
  protected _transferFrom(from: Address, to: Address, value: u256): boolean {
334
- if (to === Blockchain.DEAD_ADDRESS || from === Blockchain.DEAD_ADDRESS) {
334
+ if (from === Blockchain.DEAD_ADDRESS) {
335
335
  throw new Revert('Cannot transfer to or from dead address');
336
336
  }
337
337
 
@@ -12,8 +12,8 @@ export class OP_NET implements IBTC {
12
12
  return Blockchain.contractAddress;
13
13
  }
14
14
 
15
- public get owner(): Address {
16
- return Blockchain.owner;
15
+ public get contractDeployer(): Address {
16
+ return Blockchain.contractDeployer;
17
17
  }
18
18
 
19
19
  public onDeployment(_calldata: Calldata): void {}
@@ -26,7 +26,7 @@ export class OP_NET implements IBTC {
26
26
  switch (method) {
27
27
  case encodeSelector('owner'):
28
28
  response = new BytesWriter(ADDRESS_BYTE_LENGTH);
29
- response.writeAddress(this.owner);
29
+ response.writeAddress(this.contractDeployer);
30
30
  break;
31
31
  default:
32
32
  throw new Revert('Method not found');
@@ -47,8 +47,8 @@ export class OP_NET implements IBTC {
47
47
  return this.address === address;
48
48
  }
49
49
 
50
- protected onlyOwner(caller: Address): void {
51
- if (this.owner !== caller) {
50
+ protected onlyDeployer(caller: Address): void {
51
+ if (this.contractDeployer !== caller) {
52
52
  throw new Revert('Only owner can call this method');
53
53
  }
54
54
  }
@@ -87,14 +87,14 @@ export class BlockchainEnvironment {
87
87
  return this._nextPointer;
88
88
  }
89
89
 
90
- public _owner: Potential<Address> = null;
90
+ public _contractDeployer: Potential<Address> = null;
91
91
 
92
- public get owner(): Address {
93
- if (!this._owner) {
94
- throw this.error('Owner is required');
92
+ public get contractDeployer(): Address {
93
+ if (!this._contractDeployer) {
94
+ throw this.error('Deployer is required');
95
95
  }
96
96
 
97
- return this._owner as Address;
97
+ return this._contractDeployer as Address;
98
98
  }
99
99
 
100
100
  public _contractAddress: Potential<Address> = null;
@@ -118,7 +118,7 @@ export class BlockchainEnvironment {
118
118
 
119
119
  const currentBlock = reader.readU256();
120
120
 
121
- this._owner = reader.readAddress();
121
+ this._contractDeployer = reader.readAddress();
122
122
  this._contractAddress = reader.readAddress();
123
123
 
124
124
  const medianTimestamp = reader.readU64();
package/runtime/index.ts CHANGED
@@ -63,6 +63,7 @@ export * from './memory/Uint8ArrayMerger';
63
63
  export * from './storage/StoredU256';
64
64
  export * from './storage/StoredU64';
65
65
  export * from './storage/StoredU16Array';
66
+ export * from './storage/StoredBooleanArray';
66
67
  export * from './storage/StoredU128Array';
67
68
  export * from './storage/StoredU256Array';
68
69
  export * from './storage/StoredString';
@@ -1,6 +1,6 @@
1
1
  import { Address } from '../types/Address';
2
2
 
3
3
  export interface IBTC {
4
- readonly owner: Address;
4
+ readonly contractDeployer: Address;
5
5
  readonly address: Address;
6
6
  }
@@ -6,6 +6,7 @@ import { BytesReader } from '../buffer/BytesReader';
6
6
  import { Revert } from '../types/Revert';
7
7
  import { encodePointer } from '../math/abi';
8
8
 
9
+ // Similar to a struct in Solidity. (Use in worst case scenario, consume a lot of gas)
9
10
  export abstract class Serializable {
10
11
  protected pointer: u16;
11
12
  protected subPointer: MemorySlotPointer;
@@ -58,7 +59,7 @@ export abstract class Serializable {
58
59
  );
59
60
  }
60
61
 
61
- for (let index: i32 = 0; index < chunks.length; index++) {
62
+ for (let index: u8 = 0; index < u8(chunks.length); index++) {
62
63
  Blockchain.setStorageAt(this.getPointer(this.subPointer, index), chunks[index]);
63
64
  }
64
65
  }
@@ -79,7 +80,8 @@ export abstract class Serializable {
79
80
  }
80
81
 
81
82
  protected chunksToBytes(chunks: u256[]): BytesReader {
82
- if (this.chunkCount >= 67108863) {
83
+ if (this.chunkCount >= u8(255)) {
84
+ //67108863
83
85
  throw new Revert('Too many chunks received');
84
86
  }
85
87
 
@@ -0,0 +1,497 @@
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 StoredBooleanArray
9
+ * @description Manages an array of boolean values across multiple storage slots. Each slot holds 256 booleans packed into a u256.
10
+ */
11
+ @final
12
+ export class StoredBooleanArray {
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
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 = u32.MAX_VALUE;
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 boolean value at the specified global index.
62
+ * @param {u64} index - The global index (0 to ∞) of the boolean value to retrieve.
63
+ * @returns {bool} - The boolean value at the specified index.
64
+ */
65
+ @inline
66
+ public get(index: u64): bool {
67
+ assert(index < this._length, 'Index out of bounds');
68
+
69
+ const slotIndex: u64 = index / 256; // Each slot holds 256 bits
70
+ const bitIndex: u16 = <u16>(index % 256); // 0 to 255
71
+ this.ensureValues(slotIndex);
72
+ const slotValue = this._values.get(slotIndex);
73
+ if (slotValue) {
74
+ return this.getBit(slotValue, bitIndex);
75
+ } else {
76
+ return false;
77
+ }
78
+ }
79
+
80
+ /**
81
+ * @method set
82
+ * @description Sets the boolean value at the specified global index.
83
+ * @param {u64} index - The global index (0 to ∞) of the boolean value to set.
84
+ * @param {bool} value - The boolean value to assign.
85
+ */
86
+ @inline
87
+ public set(index: u64, value: bool): void {
88
+ assert(index < this._length, 'Index exceeds current array length');
89
+ const slotIndex: u64 = index / 256;
90
+ const bitIndex: u16 = <u16>(index % 256);
91
+ this.ensureValues(slotIndex);
92
+
93
+ const slotValue = this._values.get(slotIndex);
94
+ if (slotValue) {
95
+ const oldValue = this.getBit(slotValue, bitIndex);
96
+ if (oldValue != value) {
97
+ this.setBit(slotValue, bitIndex, value);
98
+ this._isChanged.add(slotIndex);
99
+ }
100
+ }
101
+ }
102
+
103
+ /**
104
+ * @method push
105
+ * @description Appends a new boolean value to the end of the array.
106
+ * @param {bool} value - The boolean value to append.
107
+ */
108
+ public push(value: bool): void {
109
+ if (this._length >= this.MAX_LENGTH) {
110
+ throw new Revert(
111
+ 'Push operation failed: Array has reached its maximum allowed length.',
112
+ );
113
+ }
114
+
115
+ const newIndex: u64 = this._length;
116
+ const effectiveIndex: u64 = this._startIndex + newIndex;
117
+ const wrappedIndex: u64 =
118
+ effectiveIndex < this.MAX_LENGTH ? effectiveIndex : effectiveIndex % this.MAX_LENGTH;
119
+ const slotIndex: u64 = wrappedIndex / 256;
120
+ const bitIndex: u8 = <u8>(wrappedIndex % 256);
121
+
122
+ // Ensure the slot is loaded
123
+ this.ensureValues(slotIndex);
124
+
125
+ // Set the new value
126
+ const slotValue = this._values.get(slotIndex);
127
+ if (slotValue) {
128
+ this.setBit(slotValue, bitIndex, value);
129
+ this._isChanged.add(slotIndex);
130
+ }
131
+
132
+ // Increment the length
133
+ this._length += 1;
134
+ this._isChangedLength = true;
135
+ }
136
+
137
+ /**
138
+ * @method delete
139
+ * @description Deletes the boolean value at the specified index by setting it to false. Does not reorder the array.
140
+ * @param {u64} index - The global index of the boolean value to delete.
141
+ */
142
+ public delete(index: u64): void {
143
+ if (index >= this._length) {
144
+ throw new Revert('Delete operation failed: Index out of bounds.');
145
+ }
146
+
147
+ const slotIndex: u64 = index / 256;
148
+ const bitIndex: u16 = <u16>(index % 256);
149
+ this.ensureValues(slotIndex);
150
+
151
+ const slotValue = this._values.get(slotIndex);
152
+ if (slotValue) {
153
+ const oldValue = this.getBit(slotValue, bitIndex);
154
+ if (oldValue != false) {
155
+ this.setBit(slotValue, bitIndex, false);
156
+ this._isChanged.add(slotIndex);
157
+ }
158
+ }
159
+ }
160
+
161
+ /**
162
+ * @method shift
163
+ * @description Removes the first element of the array by setting it to false, decrementing the length, and incrementing the startIndex.
164
+ * If the startIndex reaches the maximum value of u64, it wraps around to 0.
165
+ */
166
+ public shift(): void {
167
+ if (this._length === 0) {
168
+ throw new Revert('Shift operation failed: Array is empty.');
169
+ }
170
+
171
+ const currentStartIndex: u64 = this._startIndex;
172
+ const slotIndex: u64 = currentStartIndex / 256;
173
+ const bitIndex: u16 = <u16>(currentStartIndex % 256);
174
+ this.ensureValues(slotIndex);
175
+
176
+ const slotValue = this._values.get(slotIndex);
177
+ if (slotValue) {
178
+ const oldValue = this.getBit(slotValue, bitIndex);
179
+ if (oldValue != false) {
180
+ this.setBit(slotValue, bitIndex, false);
181
+ this._isChanged.add(slotIndex);
182
+ }
183
+ }
184
+
185
+ // Decrement the length
186
+ this._length -= 1;
187
+ this._isChangedLength = true;
188
+
189
+ // Increment the startIndex with wrap-around
190
+ if (this._startIndex < this.MAX_LENGTH - 1) {
191
+ this._startIndex += 1;
192
+ } else {
193
+ this._startIndex = 0;
194
+ }
195
+ this._isChangedStartIndex = true;
196
+ }
197
+
198
+ /**
199
+ * @method save
200
+ * @description Persists all cached boolean values, the length, and the startIndex to their respective storage slots if any have been modified.
201
+ */
202
+ public save(): void {
203
+ // Save all changed slots
204
+ const values = this._isChanged.values();
205
+ for (let i = 0; i < values.length; i++) {
206
+ const slotIndex = values[i];
207
+ const slotValue = this._values.get(slotIndex);
208
+ if (slotValue) {
209
+ const storagePointer = this.calculateStoragePointer(slotIndex);
210
+ Blockchain.setStorageAt(storagePointer, slotValue);
211
+ }
212
+ }
213
+ this._isChanged.clear();
214
+
215
+ // Save length and startIndex if changed
216
+ if (this._isChangedLength || this._isChangedStartIndex) {
217
+ const packedLengthAndStartIndex = new u256();
218
+ packedLengthAndStartIndex.lo1 = this._length;
219
+ packedLengthAndStartIndex.lo2 = this._startIndex;
220
+ Blockchain.setStorageAt(this.lengthPointer, packedLengthAndStartIndex);
221
+ this._isChangedLength = false;
222
+ this._isChangedStartIndex = false;
223
+ }
224
+ }
225
+
226
+ /**
227
+ * @method deleteAll
228
+ * @description Deletes all storage slots by setting them to zero, including the length and startIndex slots.
229
+ */
230
+ public deleteAll(): void {
231
+ // Iterate over all loaded slots and clear them
232
+ const keys = this._values.keys();
233
+ for (let i = 0; i < keys.length; i++) {
234
+ const slotIndex = keys[i];
235
+ const storagePointer = this.calculateStoragePointer(slotIndex);
236
+ Blockchain.setStorageAt(storagePointer, u256.Zero);
237
+ }
238
+
239
+ // Reset the length and startIndex to zero
240
+ const zeroLengthAndStartIndex = u256.Zero;
241
+ Blockchain.setStorageAt(this.lengthPointer, zeroLengthAndStartIndex);
242
+ this._length = 0;
243
+ this._startIndex = 0;
244
+ this._isChangedLength = false;
245
+ this._isChangedStartIndex = false;
246
+
247
+ // Clear internal caches
248
+ this._values.clear();
249
+ this._isLoaded.clear();
250
+ this._isChanged.clear();
251
+ }
252
+
253
+ /**
254
+ * @method setMultiple
255
+ * @description Sets multiple boolean values starting from a specific global index.
256
+ * @param {u64} startIndex - The starting global index.
257
+ * @param {bool[]} values - An array of boolean values to set.
258
+ */
259
+ @inline
260
+ public setMultiple(startIndex: u64, values: bool[]): void {
261
+ for (let i: u64 = 0; i < values.length; i++) {
262
+ this.set(startIndex + i, values[i]);
263
+ }
264
+ }
265
+
266
+ /**
267
+ * @method getAll
268
+ * @description Retrieves a range of boolean values starting from a specific global index.
269
+ * @param {u64} startIndex - The starting global index.
270
+ * @param {u64} count - The number of boolean values to retrieve.
271
+ * @returns {bool[]} - An array containing the retrieved boolean values.
272
+ */
273
+ @inline
274
+ public getAll(startIndex: u64, count: u64): bool[] {
275
+ assert(startIndex + count <= this._length, 'Requested range exceeds array length');
276
+
277
+ if (u32.MAX_VALUE < count) {
278
+ throw new Revert('Requested range exceeds maximum allowed value.');
279
+ }
280
+
281
+ const result: bool[] = new Array<bool>(count as u32);
282
+ for (let i: u64 = 0; i < count; i++) {
283
+ result[i as u32] = this.get(startIndex + i);
284
+ }
285
+ return result;
286
+ }
287
+
288
+ /**
289
+ * @method toString
290
+ * @description Returns a string representation of all cached boolean values.
291
+ * @returns {string} - A string in the format "[true, false, ..., true]".
292
+ */
293
+ @inline
294
+ public toString(): string {
295
+ let str = '[';
296
+ for (let i: u64 = 0; i < this._length; i++) {
297
+ const value = this.get(i);
298
+ str += value.toString();
299
+ if (i !== this._length - 1) {
300
+ str += ', ';
301
+ }
302
+ }
303
+ str += ']';
304
+ return str;
305
+ }
306
+
307
+ /**
308
+ * @method toBytes
309
+ * @description Returns the packed u256 values as a byte array.
310
+ * @returns {u8[]} - The packed u256 values in byte form.
311
+ */
312
+ @inline
313
+ public toBytes(): u8[] {
314
+ const bytes: u8[] = new Array<u8>();
315
+ const slotCount: u64 = (this._length + 255) / 256;
316
+
317
+ for (let slotIndex: u64 = 0; slotIndex < slotCount; slotIndex++) {
318
+ this.ensureValues(slotIndex);
319
+ const slotValue = this._values.get(slotIndex);
320
+ if (slotValue) {
321
+ const slotBytes = slotValue.toBytes();
322
+ for (let i: u32 = 0; i < slotBytes.length; i++) {
323
+ bytes.push(slotBytes[i]);
324
+ }
325
+ }
326
+ }
327
+ return bytes;
328
+ }
329
+
330
+ /**
331
+ * @method reset
332
+ * @description Resets all cached boolean values to false and marks them as changed, including resetting the length and startIndex.
333
+ */
334
+ @inline
335
+ public reset(): void {
336
+ // Reset the length and startIndex to zero
337
+ this._length = 0;
338
+ this._startIndex = 0;
339
+ this._isChangedLength = true;
340
+ this._isChangedStartIndex = true;
341
+
342
+ // Clear internal caches
343
+ this._values.clear();
344
+ this._isLoaded.clear();
345
+ this._isChanged.clear();
346
+
347
+ this.save();
348
+ }
349
+
350
+ /**
351
+ * @method getLength
352
+ * @description Retrieves the current length of the array.
353
+ * @returns {u64} - The current length.
354
+ */
355
+ @inline
356
+ public getLength(): u64 {
357
+ return this._length;
358
+ }
359
+
360
+ /**
361
+ * @method startingIndex
362
+ * @description Retrieves the current starting index of the array.
363
+ * @returns {u64} - The starting index.
364
+ */
365
+ public startingIndex(): u64 {
366
+ return this._startIndex;
367
+ }
368
+
369
+ /**
370
+ * @method setLength
371
+ * @description Sets the length of the array.
372
+ * @param {u64} newLength - The new length to set.
373
+ */
374
+ public setLength(newLength: u64): void {
375
+ if (newLength > this.MAX_LENGTH) {
376
+ throw new Revert('SetLength operation failed: Length exceeds maximum allowed value.');
377
+ }
378
+
379
+ if (newLength < this._length) {
380
+ // Truncate the array if newLength is smaller
381
+ for (let i: u64 = newLength; i < this._length; i++) {
382
+ this.delete(i);
383
+ }
384
+ }
385
+
386
+ this._length = newLength;
387
+ this._isChangedLength = true;
388
+ }
389
+
390
+ /**
391
+ * @method deleteLast
392
+ * @description Deletes the last element of the array by setting it to false and decrementing the length.
393
+ */
394
+ public deleteLast(): void {
395
+ if (this._length === 0) {
396
+ throw new Revert('DeleteLast operation failed: Array is empty.');
397
+ }
398
+
399
+ const index = this._length - 1;
400
+ this.delete(index);
401
+
402
+ // Decrement the length
403
+ this._length -= 1;
404
+ this._isChangedLength = true;
405
+ }
406
+
407
+ /**
408
+ * @private
409
+ * @method ensureValues
410
+ * @description Loads and caches the u256 value from the specified storage slot.
411
+ * @param {u64} slotIndex - The index of the storage slot.
412
+ */
413
+ private ensureValues(slotIndex: u64): void {
414
+ if (!this._isLoaded.has(slotIndex)) {
415
+ const storagePointer = this.calculateStoragePointer(slotIndex);
416
+ const storedU256: u256 = Blockchain.getStorageAt(storagePointer, this.defaultValue);
417
+ this._values.set(slotIndex, storedU256);
418
+ this._isLoaded.add(slotIndex);
419
+ }
420
+ }
421
+
422
+ /**
423
+ * @private
424
+ * @method calculateStoragePointer
425
+ * @description Calculates the storage pointer for a given slot index by incrementing the base pointer.
426
+ * @param {u64} slotIndex - The index of the storage slot.
427
+ * @returns {u256} - The calculated storage pointer.
428
+ */
429
+ private calculateStoragePointer(slotIndex: u64): u256 {
430
+ // Each slot is identified by baseU256Pointer + slotIndex + 1
431
+ return SafeMath.add(this.baseU256Pointer, u256.fromU64(slotIndex + 1));
432
+ }
433
+
434
+ /**
435
+ * @private
436
+ * @method getBit
437
+ * @description Retrieves the bit value at the specified bit index from the u256 value.
438
+ * @param {u256} value - The u256 value containing the bits.
439
+ * @param {u16} bitIndex - The index of the bit to retrieve (0-255).
440
+ * @returns {bool} - The value of the bit at the specified index.
441
+ */
442
+ private getBit(value: u256, bitIndex: u16): bool {
443
+ assert(bitIndex < 256, 'Bit index out of range');
444
+
445
+ if (bitIndex < 64) {
446
+ return ((value.lo1 >> bitIndex) & 0b1) == 1;
447
+ } else if (bitIndex < 128) {
448
+ return ((value.lo2 >> (bitIndex - 64)) & 0b1) == 1;
449
+ } else if (bitIndex < 192) {
450
+ return ((value.hi1 >> (bitIndex - 128)) & 0b1) == 1;
451
+ } else {
452
+ return ((value.hi2 >> (bitIndex - 192)) & 0b1) == 1;
453
+ }
454
+ }
455
+
456
+ /**
457
+ * @private
458
+ * @method setBit
459
+ * @description Sets the bit value at the specified bit index in the u256 value.
460
+ * @param {u256} value - The u256 value containing the bits.
461
+ * @param {u16} bitIndex - The index of the bit to set (0-255).
462
+ * @param {bool} bitValue - The value to set (true or false).
463
+ */
464
+ private setBit(value: u256, bitIndex: u16, bitValue: bool): void {
465
+ assert(bitIndex < 256, 'Bit index out of range');
466
+
467
+ if (bitIndex < 64) {
468
+ const mask = u64(1) << bitIndex;
469
+ if (bitValue) {
470
+ value.lo1 |= mask;
471
+ } else {
472
+ value.lo1 &= ~mask;
473
+ }
474
+ } else if (bitIndex < 128) {
475
+ const mask = u64(1) << (bitIndex - 64);
476
+ if (bitValue) {
477
+ value.lo2 |= mask;
478
+ } else {
479
+ value.lo2 &= ~mask;
480
+ }
481
+ } else if (bitIndex < 192) {
482
+ const mask = u64(1) << (bitIndex - 128);
483
+ if (bitValue) {
484
+ value.hi1 |= mask;
485
+ } else {
486
+ value.hi1 &= ~mask;
487
+ }
488
+ } else {
489
+ const mask = u64(1) << (bitIndex - 192);
490
+ if (bitValue) {
491
+ value.hi2 |= mask;
492
+ } else {
493
+ value.hi2 &= ~mask;
494
+ }
495
+ }
496
+ }
497
+ }
@@ -25,7 +25,7 @@ export class StoredU128Array {
25
25
  private _isChangedStartIndex: bool = false; // Indicates if the startIndex has been modified
26
26
 
27
27
  // Define a maximum allowed length to prevent excessive storage usage
28
- private readonly MAX_LENGTH: u64 = u64.MAX_VALUE - 1;
28
+ private readonly MAX_LENGTH: u64 = u64(u32.MAX_VALUE - 1); // we need to check what happen in overflow situation to be able to set it to u64.MAX_VALUE
29
29
 
30
30
  /**
31
31
  * @constructor
@@ -246,6 +246,19 @@ export class StoredU128Array {
246
246
  this._isChanged.clear();
247
247
  }
248
248
 
249
+ public deleteLast(): void {
250
+ if (this._length === 0) {
251
+ throw new Revert('DeleteLast operation failed: Array is empty.');
252
+ }
253
+
254
+ const lastIndex = this._length - 1;
255
+ this.delete(lastIndex);
256
+
257
+ // Decrement the length
258
+ this._length -= 1;
259
+ this._isChangedLength = true;
260
+ }
261
+
249
262
  /**
250
263
  * @method setMultiple
251
264
  * @description Sets multiple u128 values starting from a specific global index.
@@ -25,7 +25,7 @@ export class StoredU16Array {
25
25
  private _isChangedStartIndex: bool = false; // Indicates if the startIndex has been modified
26
26
 
27
27
  // Define a maximum allowed length to prevent excessive storage usage
28
- private readonly MAX_LENGTH: u64 = u64.MAX_VALUE - 1;
28
+ private readonly MAX_LENGTH: u64 = u64(u32.MAX_VALUE - 1); // we need to check what happen in overflow situation to be able to set it to u64.MAX_VALUE
29
29
 
30
30
  /**
31
31
  * @constructor
@@ -258,9 +258,14 @@ export class StoredU16Array {
258
258
  @inline
259
259
  public getAll(startIndex: u64, count: u64): u16[] {
260
260
  assert(startIndex + count <= this._length, 'Requested range exceeds array length');
261
- const result: u16[] = new Array<u16>(count);
261
+
262
+ if (u32.MAX_VALUE < count) {
263
+ throw new Revert('Requested range exceeds maximum allowed value.');
264
+ }
265
+
266
+ const result: u16[] = new Array<u16>(count as u32);
262
267
  for (let i: u64 = 0; i < count; i++) {
263
- result[i] = this.get(startIndex + i);
268
+ result[i as u32] = this.get(startIndex + i);
264
269
  }
265
270
  return result;
266
271
  }
@@ -360,6 +365,19 @@ export class StoredU16Array {
360
365
  this._isChangedLength = true;
361
366
  }
362
367
 
368
+ public deleteLast(): void {
369
+ if (this._length === 0) {
370
+ throw new Revert('DeleteLast operation failed: Array is empty.');
371
+ }
372
+
373
+ const index = this._length - 1;
374
+ this.delete(index);
375
+
376
+ // Decrement the length
377
+ this._length -= 1;
378
+ this._isChangedLength = true;
379
+ }
380
+
363
381
  /**
364
382
  * @private
365
383
  * @method ensureValues
@@ -25,7 +25,7 @@ export class StoredU256Array {
25
25
  private _isChangedStartIndex: bool = false; // Indicates if the startIndex has been modified
26
26
 
27
27
  // Define a maximum allowed length to prevent excessive storage usage
28
- private readonly MAX_LENGTH: u64 = u64.MAX_VALUE - 1;
28
+ private readonly MAX_LENGTH: u64 = u64(u32.MAX_VALUE - 1); // we need to check what happen in overflow situation to be able to set it to u64.MAX_VALUE
29
29
 
30
30
  /**
31
31
  * @constructor
@@ -120,6 +120,31 @@ export class StoredU256Array {
120
120
  this._isChangedLength = true;
121
121
  }
122
122
 
123
+ public deleteLast(): void {
124
+ if (this._length === 0) {
125
+ throw new Revert('Delete operation failed: Array is empty.');
126
+ }
127
+
128
+ const lastIndex: u64 = this._length - 1;
129
+ const slotIndex: u32 = <u32>(this._startIndex + lastIndex);
130
+ this.ensureValues(slotIndex);
131
+
132
+ const currentValue = this._values.get(slotIndex);
133
+ if (!u256.eq(currentValue, u256.Zero)) {
134
+ this._values.set(slotIndex, u256.Zero);
135
+ this._isChanged.add(slotIndex);
136
+ }
137
+
138
+ // Decrement the length
139
+ this._length -= 1;
140
+ this._isChangedLength = true;
141
+ }
142
+
143
+ public setStartingIndex(index: u64): void {
144
+ this._startIndex = index;
145
+ this._isChangedStartIndex = true;
146
+ }
147
+
123
148
  /**
124
149
  * @method delete
125
150
  * @description Deletes the u256 value at the specified index by setting it to zero. Does not reorder the array.