@btc-vision/btc-runtime 1.6.1 → 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.
@@ -8,6 +8,8 @@ import {
8
8
  import { Blockchain } from '../../env';
9
9
  import { Revert } from '../../types/Revert';
10
10
 
11
+ export const DEFAULT_MAX_LENGTH: u32 = u32.MAX_VALUE - 1;
12
+
11
13
  /**
12
14
  * Abstract base class for an array of T values that are packed
13
15
  * in 32-byte (Uint8Array) "slots" in storage.
@@ -16,6 +18,8 @@ import { Revert } from '../../types/Revert';
16
18
  * - Maps each global index to (slotIndex, subIndex).
17
19
  * - Child classes define how many T items fit per 32-byte slot
18
20
  * and how to pack/unpack them.
21
+ *
22
+ * Note: This is designed to wrap around.
19
23
  */
20
24
  export abstract class StoredPackedArray<T> {
21
25
  /** 32-byte base pointer (used to derive storage keys). */
@@ -25,10 +29,10 @@ export abstract class StoredPackedArray<T> {
25
29
  protected readonly lengthPointer: Uint8Array;
26
30
 
27
31
  /** Internal length of the array. */
28
- protected _length: u64 = 0;
32
+ protected _length: u32 = 0;
29
33
 
30
34
  /** Optional "start index" if needed by your logic. */
31
- protected _startIndex: u64 = 0;
35
+ protected _startIndex: u32 = 0;
32
36
 
33
37
  /** Whether length or startIndex changed. */
34
38
  protected _isChangedLength: bool = false;
@@ -38,19 +42,23 @@ export abstract class StoredPackedArray<T> {
38
42
  * A map from slotIndex => the 32-byte slot data in memory.
39
43
  * Child classes will parse that 32 bytes into an array of T, or vice versa.
40
44
  */
41
- protected _slots: Map<u64, Uint8Array> = new Map();
45
+ protected _slots: Map<u32, Uint8Array> = new Map();
42
46
 
43
47
  /** Track which slotIndexes are changed and need saving. */
44
- protected _isChanged: Set<u64> = new Set();
48
+ protected _isChanged: Set<u32> = new Set();
45
49
 
46
- /**
47
- * Maximum length to prevent unbounded usage.
48
- * Adjust to fit your constraints (e.g. `u64.MAX_VALUE`).
49
- */
50
- protected readonly MAX_LENGTH: u64 = <u64>(u32.MAX_VALUE - 1);
50
+ private nextItemOffset: u32 = 0;
51
51
 
52
- protected constructor(public pointer: u16, public subPointer: Uint8Array, protected defaultValue: T) {
53
- assert(subPointer.length <= 30, `You must pass a 30 bytes sub-pointer. (Array, got ${subPointer.length})`);
52
+ protected constructor(
53
+ public pointer: u16,
54
+ public subPointer: Uint8Array,
55
+ protected defaultValue: T,
56
+ protected MAX_LENGTH: u32 = DEFAULT_MAX_LENGTH,
57
+ ) {
58
+ assert(
59
+ subPointer.length <= 30,
60
+ `You must pass a 30 bytes sub-pointer. (Array, got ${subPointer.length})`,
61
+ );
54
62
 
55
63
  const basePointer = encodeBasePointer(pointer, subPointer);
56
64
  this.lengthPointer = Uint8Array.wrap(basePointer.buffer);
@@ -63,16 +71,38 @@ export abstract class StoredPackedArray<T> {
63
71
  this._startIndex = data[1];
64
72
  }
65
73
 
74
+ @inline
75
+ public get previousOffset(): u32 {
76
+ return <u32>(
77
+ ((this._startIndex +
78
+ <u64>(this.nextItemOffset === 0 ? this.nextItemOffset : this.nextItemOffset - 1)) %
79
+ <u64>this.MAX_LENGTH)
80
+ );
81
+ }
82
+
83
+ /**
84
+ * Set the maximum length of the array.
85
+ * This is a safety check to prevent unbounded usage.
86
+ */
87
+ @inline
88
+ public setMaxLength(maxLength: u32): void {
89
+ if (maxLength > this.MAX_LENGTH) {
90
+ throw new Revert('setMaxLength: maxLength exceeds MAX_LENGTH');
91
+ }
92
+
93
+ this.MAX_LENGTH = maxLength;
94
+ }
95
+
66
96
  @inline
67
97
  @operator('[]')
68
- public get(index: u64): T {
98
+ public get(index: u32): T {
69
99
  // max length used on purpose to prevent unbounded usage
70
100
  if (index > this.MAX_LENGTH) {
71
101
  throw new Revert('get: out of range');
72
102
  }
73
103
 
74
- const realIndex = (this._startIndex + index) % this.MAX_LENGTH;
75
- const cap = this.getSlotCapacity();
104
+ const realIndex: u32 = this.getRealIndex(index);
105
+ const cap: u32 = this.getSlotCapacity();
76
106
  const slotIndex = realIndex / cap;
77
107
  const subIndex = <u32>(realIndex % cap);
78
108
 
@@ -83,7 +113,7 @@ export abstract class StoredPackedArray<T> {
83
113
  }
84
114
 
85
115
  @inline
86
- public get_physical(index: u64): T {
116
+ public get_physical(index: u32): T {
87
117
  if (index > this.MAX_LENGTH) {
88
118
  throw new Revert('get: index exceeds MAX_LENGTH (packed array)');
89
119
  }
@@ -100,12 +130,12 @@ export abstract class StoredPackedArray<T> {
100
130
 
101
131
  @inline
102
132
  @operator('[]=')
103
- public set(index: u64, value: T): void {
133
+ public set(index: u32, value: T): void {
104
134
  if (index > this.MAX_LENGTH) {
105
135
  throw new Revert('set: index exceeds MAX_LENGTH (packed array)');
106
136
  }
107
137
 
108
- const realIndex = (this._startIndex + index) % this.MAX_LENGTH;
138
+ const realIndex: u32 = this.getRealIndex(index);
109
139
  const cap = this.getSlotCapacity();
110
140
  const slotIndex = realIndex / cap;
111
141
  const subIndex = <u32>(realIndex % cap);
@@ -122,7 +152,7 @@ export abstract class StoredPackedArray<T> {
122
152
  }
123
153
 
124
154
  @inline
125
- public set_physical(index: u64, value: T): void {
155
+ public set_physical(index: u32, value: T): void {
126
156
  if (index > this.MAX_LENGTH) {
127
157
  throw new Revert('set: index exceeds MAX_LENGTH (packed array)');
128
158
  }
@@ -142,13 +172,48 @@ export abstract class StoredPackedArray<T> {
142
172
  }
143
173
  }
144
174
 
175
+ /**
176
+ * Get the next item in the array, starting from the current offset.
177
+ * This is useful for iterating through the array.
178
+ */
179
+ @inline
180
+ public next(): T {
181
+ const value = this.get(this.nextItemOffset);
182
+ this.nextItemOffset += 1;
183
+
184
+ return value;
185
+ }
186
+
145
187
  @inline
146
- public push(value: T, isPhysical: bool = false): void {
188
+ public incrementStartingIndex(): void {
189
+ if (this._startIndex >= this.MAX_LENGTH) {
190
+ this._startIndex = 0;
191
+ } else {
192
+ this._startIndex += 1;
193
+ }
194
+
195
+ this._isChangedStartIndex = true;
196
+ }
197
+
198
+ /**
199
+ * Apply the starting index with n offset.
200
+ */
201
+ @inline
202
+ public applyNextOffsetToStartingIndex(): void {
203
+ if (!this.nextItemOffset) return;
204
+
205
+ this._startIndex += this.nextItemOffset;
206
+ this._isChangedStartIndex = true;
207
+ this.nextItemOffset = 0;
208
+ }
209
+
210
+ @inline
211
+ public push(value: T, isPhysical: bool = false): u32 {
147
212
  if (this._length >= this.MAX_LENGTH) {
148
213
  throw new Revert('push: array has reached MAX_LENGTH');
149
214
  }
150
215
 
151
- const realIndex = ((isPhysical ? 0 : this._startIndex) + this._length) % this.MAX_LENGTH;
216
+ const realIndex: u32 = this.getRealIndex(this._length, isPhysical);
152
217
  const cap = this.getSlotCapacity();
153
218
  const slotIndex = realIndex / cap;
154
219
  const subIndex = <u32>(realIndex % cap);
@@ -165,6 +230,8 @@ export abstract class StoredPackedArray<T> {
165
230
 
166
231
  this._length += 1;
167
232
  this._isChangedLength = true;
233
+
234
+ return realIndex;
168
235
  }
169
236
 
170
237
  /**
@@ -207,8 +274,8 @@ export abstract class StoredPackedArray<T> {
207
274
  * but does not reduce the length.
208
275
  */
209
276
  @inline
210
- public delete(index: u64): void {
211
- const realIndex = (this._startIndex + index) % this.MAX_LENGTH;
277
+ public delete(index: u32): void {
278
+ const realIndex = this.getRealIndex(index);
212
279
  const cap = this.getSlotCapacity();
213
280
  const slotIndex = realIndex / cap;
214
281
  const subIndex = <u32>(realIndex % cap);
@@ -225,12 +292,22 @@ export abstract class StoredPackedArray<T> {
225
292
  }
226
293
  }
227
294
 
295
+ @inline
296
+ public removeItemFromLength(): void {
297
+ if (this._length == 0) {
298
+ throw new Revert('delete: array is empty');
299
+ }
300
+
301
+ this._length -= 1;
302
+ this._isChangedLength = true;
303
+ }
304
+
228
305
  /**
229
306
  * "Delete" by zeroing out the element at `index`,
230
307
  * but does not reduce the length.
231
308
  */
232
309
  @inline
233
- public delete_physical(index: u64): void {
310
+ public delete_physical(index: u32): void {
234
311
  const cap = this.getSlotCapacity();
235
312
  const slotIndex = index / cap;
236
313
  const subIndex = <u32>(index % cap);
@@ -264,28 +341,25 @@ export abstract class StoredPackedArray<T> {
264
341
  }
265
342
 
266
343
  @inline
267
- public setMultiple(startIndex: u64, values: T[]): void {
268
- const end = startIndex + <u64>values.length;
344
+ public setMultiple(startIndex: u32, values: T[]): void {
345
+ const end = startIndex + <u32>values.length;
269
346
  if (end > this._length) {
270
347
  throw new Revert('setMultiple: out of range (packed array)');
271
348
  }
272
349
 
273
350
  for (let i = 0; i < values.length; i++) {
274
- this.set(startIndex + <u64>i, values[i]);
351
+ this.set(startIndex + <u32>i, values[i]);
275
352
  }
276
353
  }
277
354
 
278
- // -----------------------------------------------------------
279
- // Public Array-Like Methods
280
- // -----------------------------------------------------------
281
355
  @inline
282
- public getAll(startIndex: u64, count: u64): T[] {
283
- if (count > <u64>u32.MAX_VALUE) {
356
+ public getAll(startIndex: u32, count: u32): T[] {
357
+ if (count > <u32>u32.MAX_VALUE) {
284
358
  throw new Revert('getAll: count too large (packed array)');
285
359
  }
286
360
 
287
361
  const out = new Array<T>(<i32>count);
288
- for (let i: u64 = 0; i < count; i++) {
362
+ for (let i: u32 = 0; i < count; i++) {
289
363
  out[<i32>i] = this.get(startIndex + i);
290
364
  }
291
365
 
@@ -293,17 +367,17 @@ export abstract class StoredPackedArray<T> {
293
367
  }
294
368
 
295
369
  @inline
296
- public getLength(): u64 {
370
+ public getLength(): u32 {
297
371
  return this._length;
298
372
  }
299
373
 
300
374
  @inline
301
- public startingIndex(): u64 {
375
+ public startingIndex(): u32 {
302
376
  return this._startIndex;
303
377
  }
304
378
 
305
379
  @inline
306
- public setStartingIndex(index: u64): void {
380
+ public setStartingIndex(index: u32): void {
307
381
  this._startIndex = index;
308
382
  this._isChangedStartIndex = true;
309
383
  }
@@ -313,7 +387,7 @@ export abstract class StoredPackedArray<T> {
313
387
  */
314
388
  public toString(): string {
315
389
  let s = '[';
316
- for (let i: u64 = 0; i < this._length; i++) {
390
+ for (let i: u32 = 0; i < this._length; i++) {
317
391
  s += `${this.get(i)}`;
318
392
  if (i < this._length - 1) {
319
393
  s += ', ';
@@ -329,7 +403,7 @@ export abstract class StoredPackedArray<T> {
329
403
  const slotIndex = changed[i];
330
404
  const slotData = this._slots.get(slotIndex);
331
405
  if (slotData) {
332
- const ptr = this.calculateStoragePointer(slotIndex);
406
+ const ptr = this.calculateStoragePointer(<u64>slotIndex);
333
407
  Blockchain.setStorageAt(ptr, slotData);
334
408
  }
335
409
  }
@@ -348,7 +422,7 @@ export abstract class StoredPackedArray<T> {
348
422
  const keys = this._slots.keys();
349
423
  for (let i = 0; i < keys.length; i++) {
350
424
  const slotIndex = keys[i];
351
- const ptr = this.calculateStoragePointer(slotIndex);
425
+ const ptr = this.calculateStoragePointer(<u64>slotIndex);
352
426
  Blockchain.setStorageAt(ptr, GET_EMPTY_BUFFER()); // 32 bytes of zero
353
427
  }
354
428
 
@@ -379,7 +453,7 @@ export abstract class StoredPackedArray<T> {
379
453
  }
380
454
 
381
455
  /** Number of T items that fit in one 32-byte slot. */
382
- protected abstract getSlotCapacity(): u64;
456
+ protected abstract getSlotCapacity(): u32;
383
457
 
384
458
  /** Return the "zero" value of type T. */
385
459
  protected abstract zeroValue(): T;
@@ -387,10 +461,6 @@ export abstract class StoredPackedArray<T> {
387
461
  /** Compare two T values for equality. */
388
462
  protected abstract eq(a: T, b: T): bool;
389
463
 
390
- // -----------------------------------------------------------
391
- // Persistence (save, reset, etc.)
392
- // -----------------------------------------------------------
393
-
394
464
  /**
395
465
  * Pack an array of T (length = getSlotCapacity()) into a 32-byte buffer.
396
466
  */
@@ -408,16 +478,12 @@ export abstract class StoredPackedArray<T> {
408
478
  */
409
479
  protected abstract calculateStoragePointer(slotIndex: u64): Uint8Array;
410
480
 
411
- // -----------------------------------------------------------
412
- // Internal Slot-Loading Helpers
413
- // -----------------------------------------------------------
414
-
415
481
  /**
416
482
  * Ensure that slotIndex is loaded into _slots. If missing, read from storage.
417
483
  */
418
- protected ensureSlot(slotIndex: u64): Uint8Array {
484
+ protected ensureSlot(slotIndex: u32): Uint8Array {
419
485
  if (!this._slots.has(slotIndex)) {
420
- const ptr = this.calculateStoragePointer(slotIndex);
486
+ const ptr = this.calculateStoragePointer(<u64>slotIndex);
421
487
  const data = Blockchain.getStorageAt(ptr);
422
488
 
423
489
  // Must be exactly 32 bytes; if it's empty, you get 32 zero bytes from GET_EMPTY_BUFFER()
@@ -426,4 +492,14 @@ export abstract class StoredPackedArray<T> {
426
492
 
427
493
  return this._slots.get(slotIndex);
428
494
  }
495
+
496
+ private getRealIndex(index: u32, isPhysical: bool = false): u32 {
497
+ const maxLength: u64 = <u64>this.MAX_LENGTH;
498
+ let realIndex: u64 = (isPhysical ? <u64>0 : <u64>this._startIndex) + <u64>index;
499
+ if (!(realIndex < maxLength)) {
500
+ realIndex %= maxLength;
501
+ }
502
+
503
+ return <u32>realIndex;
504
+ }
429
505
  }
@@ -1,4 +1,4 @@
1
- import { StoredPackedArray } from './StoredPackedArray';
1
+ import { DEFAULT_MAX_LENGTH, StoredPackedArray } from './StoredPackedArray';
2
2
  import { u128 } from '@btc-vision/as-bignum/assembly';
3
3
  import { bigEndianAdd } from '../../math/bytes';
4
4
 
@@ -9,16 +9,16 @@ import { bigEndianAdd } from '../../math/bytes';
9
9
  */
10
10
  @final
11
11
  export class StoredU128Array extends StoredPackedArray<u128> {
12
- public constructor(pointer: u16, subPointer: Uint8Array) {
13
- super(pointer, subPointer, u128.Zero);
12
+ public constructor(pointer: u16, subPointer: Uint8Array, maxLength: u32 = DEFAULT_MAX_LENGTH) {
13
+ super(pointer, subPointer, u128.Zero, maxLength);
14
14
  }
15
15
 
16
- protected getSlotCapacity(): u64 {
16
+ protected getSlotCapacity(): u32 {
17
17
  return 2; // 2 x u128 => 32 bytes
18
18
  }
19
19
 
20
20
  protected zeroValue(): u128 {
21
- return u128.Zero; // from the as-bignum library
21
+ return u128.Zero; // from the as-bignum library
22
22
  }
23
23
 
24
24
  protected eq(a: u128, b: u128): bool {
@@ -61,4 +61,4 @@ export class StoredU128Array extends StoredPackedArray<u128> {
61
61
  protected calculateStoragePointer(slotIndex: u64): Uint8Array {
62
62
  return bigEndianAdd(this.basePointer, slotIndex);
63
63
  }
64
- }
64
+ }
@@ -1,4 +1,4 @@
1
- import { StoredPackedArray } from './StoredPackedArray';
1
+ import { DEFAULT_MAX_LENGTH, StoredPackedArray } from './StoredPackedArray';
2
2
  import { bigEndianAdd } from '../../math/bytes';
3
3
 
4
4
  /**
@@ -6,11 +6,11 @@ import { bigEndianAdd } from '../../math/bytes';
6
6
  */
7
7
  @final
8
8
  export class StoredU16Array extends StoredPackedArray<u16> {
9
- public constructor(pointer: u16, subPointer: Uint8Array) {
10
- super(pointer, subPointer, 0);
9
+ public constructor(pointer: u16, subPointer: Uint8Array, maxLength: u32 = DEFAULT_MAX_LENGTH) {
10
+ super(pointer, subPointer, 0, maxLength);
11
11
  }
12
12
 
13
- protected getSlotCapacity(): u64 {
13
+ protected getSlotCapacity(): u32 {
14
14
  return 16; // 16 x u16 = 32 bytes
15
15
  }
16
16
 
@@ -30,8 +30,8 @@ export class StoredU16Array extends StoredPackedArray<u16> {
30
30
  let offset = 0;
31
31
  for (let i = 0; i < 16; i++) {
32
32
  const v = values[i];
33
- out[offset] = <u8>((v >> 8) & 0xff); // high byte
34
- out[offset + 1] = <u8>(v & 0xff); // low byte
33
+ out[offset] = <u8>((v >> 8) & 0xff); // high byte
34
+ out[offset + 1] = <u8>(v & 0xff); // low byte
35
35
  offset += 2;
36
36
  }
37
37
  return out;
@@ -1,4 +1,4 @@
1
- import { StoredPackedArray } from './StoredPackedArray';
1
+ import { DEFAULT_MAX_LENGTH, StoredPackedArray } from './StoredPackedArray';
2
2
  import { u256 } from '@btc-vision/as-bignum/assembly';
3
3
  import { bigEndianAdd } from '../../math/bytes';
4
4
 
@@ -8,11 +8,11 @@ import { bigEndianAdd } from '../../math/bytes';
8
8
  */
9
9
  @final
10
10
  export class StoredU256Array extends StoredPackedArray<u256> {
11
- public constructor(pointer: u16, subPointer: Uint8Array) {
12
- super(pointer, subPointer, u256.Zero);
11
+ public constructor(pointer: u16, subPointer: Uint8Array, maxLength: u32 = DEFAULT_MAX_LENGTH) {
12
+ super(pointer, subPointer, u256.Zero, maxLength);
13
13
  }
14
14
 
15
- protected getSlotCapacity(): u64 {
15
+ protected getSlotCapacity(): u32 {
16
16
  return 1; // 1 x u256 => 32 bytes
17
17
  }
18
18
 
@@ -36,4 +36,4 @@ export class StoredU256Array extends StoredPackedArray<u256> {
36
36
  protected calculateStoragePointer(slotIndex: u64): Uint8Array {
37
37
  return bigEndianAdd(this.basePointer, slotIndex);
38
38
  }
39
- }
39
+ }
@@ -1,4 +1,4 @@
1
- import { StoredPackedArray } from './StoredPackedArray';
1
+ import { DEFAULT_MAX_LENGTH, StoredPackedArray } from './StoredPackedArray';
2
2
  import { bigEndianAdd } from '../../math/bytes';
3
3
 
4
4
  /**
@@ -7,11 +7,11 @@ import { bigEndianAdd } from '../../math/bytes';
7
7
  */
8
8
  @final
9
9
  export class StoredU32Array extends StoredPackedArray<u32> {
10
- public constructor(pointer: u16, subPointer: Uint8Array) {
11
- super(pointer, subPointer, 0);
10
+ public constructor(pointer: u16, subPointer: Uint8Array, maxLength: u32 = DEFAULT_MAX_LENGTH) {
11
+ super(pointer, subPointer, 0, maxLength);
12
12
  }
13
13
 
14
- protected getSlotCapacity(): u64 {
14
+ protected getSlotCapacity(): u32 {
15
15
  return 8; // 8 x u32 => 32 bytes
16
16
  }
17
17
 
@@ -45,7 +45,7 @@ export class StoredU32Array extends StoredPackedArray<u32> {
45
45
  const b1 = slotData[offset + 1];
46
46
  const b2 = slotData[offset + 2];
47
47
  const b3 = slotData[offset + 3];
48
- out[i] = ((<u32>b0 << 24) | (<u32>b1 << 16) | (<u32>b2 << 8) | b3) as u32;
48
+ out[i] = (((<u32>b0) << 24) | ((<u32>b1) << 16) | ((<u32>b2) << 8) | b3) as u32;
49
49
  offset += 4;
50
50
  }
51
51
  return out;
@@ -1,4 +1,4 @@
1
- import { StoredPackedArray } from './StoredPackedArray';
1
+ import { DEFAULT_MAX_LENGTH, StoredPackedArray } from './StoredPackedArray';
2
2
  import { bigEndianAdd } from '../../math/bytes';
3
3
 
4
4
  /**
@@ -7,11 +7,11 @@ import { bigEndianAdd } from '../../math/bytes';
7
7
  */
8
8
  @final
9
9
  export class StoredU64Array extends StoredPackedArray<u64> {
10
- public constructor(pointer: u16, subPointer: Uint8Array) {
11
- super(pointer, subPointer, 0);
10
+ public constructor(pointer: u16, subPointer: Uint8Array, maxLength: u32 = DEFAULT_MAX_LENGTH) {
11
+ super(pointer, subPointer, 0, maxLength);
12
12
  }
13
13
 
14
- protected getSlotCapacity(): u64 {
14
+ protected getSlotCapacity(): u32 {
15
15
  return 4; // 4 x u64 => 32 bytes
16
16
  }
17
17
 
@@ -53,8 +53,15 @@ export class StoredU64Array extends StoredPackedArray<u64> {
53
53
  const b5 = <u64>slotData[offset + 5];
54
54
  const b6 = <u64>slotData[offset + 6];
55
55
  const b7 = <u64>slotData[offset + 7];
56
- out[i] = (b0 << 56) | (b1 << 48) | (b2 << 40) | (b3 << 32) |
57
- (b4 << 24) | (b5 << 16) | (b6 << 8) | b7;
56
+ out[i] =
57
+ (b0 << 56) |
58
+ (b1 << 48) |
59
+ (b2 << 40) |
60
+ (b3 << 32) |
61
+ (b4 << 24) |
62
+ (b5 << 16) |
63
+ (b6 << 8) |
64
+ b7;
58
65
  offset += 8;
59
66
  }
60
67
  return out;
@@ -63,4 +70,4 @@ export class StoredU64Array extends StoredPackedArray<u64> {
63
70
  protected calculateStoragePointer(slotIndex: u64): Uint8Array {
64
71
  return bigEndianAdd(this.basePointer, slotIndex);
65
72
  }
66
- }
73
+ }
@@ -1,4 +1,4 @@
1
- import { StoredPackedArray } from './StoredPackedArray';
1
+ import { DEFAULT_MAX_LENGTH, StoredPackedArray } from './StoredPackedArray';
2
2
  import { bigEndianAdd } from '../../math/bytes';
3
3
 
4
4
  /**
@@ -7,11 +7,11 @@ import { bigEndianAdd } from '../../math/bytes';
7
7
  */
8
8
  @final
9
9
  export class StoredU8Array extends StoredPackedArray<u8> {
10
- public constructor(pointer: u16, subPointer: Uint8Array) {
11
- super(pointer, subPointer, 0);
10
+ public constructor(pointer: u16, subPointer: Uint8Array, maxLength: u32 = DEFAULT_MAX_LENGTH) {
11
+ super(pointer, subPointer, 0, maxLength);
12
12
  }
13
13
 
14
- protected getSlotCapacity(): u64 {
14
+ protected getSlotCapacity(): u32 {
15
15
  return 32; // 32 bytes => 32 x u8
16
16
  }
17
17
 
@@ -45,4 +45,4 @@ export class StoredU8Array extends StoredPackedArray<u8> {
45
45
  // basePointer + (slotIndex+1) in big-endian
46
46
  return bigEndianAdd(this.basePointer, slotIndex);
47
47
  }
48
- }
48
+ }
@@ -0,0 +1,62 @@
1
+ import {u256} from '@btc-vision/as-bignum/assembly';
2
+ import {Blockchain} from '../../env';
3
+ import {EMPTY_BUFFER} from "../../math/bytes";
4
+ import {BytesWriter} from "../../buffer/BytesWriter";
5
+ import {encodePointerUnknownLength} from "../../math/abi";
6
+
7
+ /**
8
+ * StoredMap<K, V> implementation using u256 as keys.
9
+ */
10
+ @final
11
+ export class StoredMapU256 {
12
+ private readonly pointer: u16;
13
+ private readonly subPointer: Uint8Array;
14
+
15
+ constructor(pointer: u16, subPointer: Uint8Array = new Uint8Array(30)) {
16
+ this.pointer = pointer;
17
+ this.subPointer = subPointer;
18
+ }
19
+
20
+ /**
21
+ * Sets the value for a given key.
22
+ * @param key - The key of type K.
23
+ * @param value - The value of type V.
24
+ */
25
+ public set(key: u256, value: u256): void {
26
+ const keyPointer = this.getKeyPointer(key);
27
+ Blockchain.setStorageAt(keyPointer, value.toUint8Array(true));
28
+ }
29
+
30
+ /**
31
+ * Retrieves the value for a given key.
32
+ * @param key - The key of type K.
33
+ * @returns The value of type V or null if the key does not exist.
34
+ */
35
+ public get(key: u256): u256 {
36
+ const keyPointer = this.getKeyPointer(key);
37
+ return u256.fromUint8ArrayBE(Blockchain.getStorageAt(keyPointer));
38
+ }
39
+
40
+ /**
41
+ * Deletes the value for a given key.
42
+ * @param key - The key of type K.
43
+ */
44
+ public delete(key: u256): void {
45
+ const keyPointer = this.getKeyPointer(key);
46
+ Blockchain.setStorageAt(keyPointer, EMPTY_BUFFER);
47
+ }
48
+
49
+ /**
50
+ * Generates the storage pointer for a given key.
51
+ * @param key - The key of type K.
52
+ * @returns The storage pointer as u256.
53
+ */
54
+ private getKeyPointer(key: u256): Uint8Array {
55
+ const writer = new BytesWriter(64);
56
+
57
+ writer.writeBytes(this.subPointer);
58
+ writer.writeU256(key);
59
+
60
+ return encodePointerUnknownLength(this.pointer, writer.getBuffer());
61
+ }
62
+ }
@@ -154,6 +154,40 @@ export class SafeMath {
154
154
  return c;
155
155
  }
156
156
 
157
+ public static mul64(a: u64, b: u64): u64 {
158
+ if (a === 0 || b === 0) {
159
+ return 0;
160
+ }
161
+
162
+ const c: u64 = a * b;
163
+
164
+ if (c / a !== b) {
165
+ throw new Error('SafeMath: multiplication overflow');
166
+ }
167
+
168
+ return c;
169
+ }
170
+
171
+ public static div64(a: u64, b: u64): u64 {
172
+ if (b === 0) {
173
+ throw new Error('Division by zero');
174
+ }
175
+
176
+ if (a === 0) {
177
+ return 0;
178
+ }
179
+
180
+ if (a < b) {
181
+ return 0; // Return 0 if a < b
182
+ }
183
+
184
+ if (a === b) {
185
+ return 1; // Return 1 if a == b
186
+ }
187
+
188
+ return a / b;
189
+ }
190
+
157
191
  public static div128(a: u128, b: u128): u128 {
158
192
  if (b.isZero()) {
159
193
  throw new Error('Division by zero');
@@ -226,6 +260,22 @@ export class SafeMath {
226
260
  return result;
227
261
  }
228
262
 
263
+ public static min64(a: u64, b: u64): u64 {
264
+ return a < b ? a : b;
265
+ }
266
+
267
+ public static max64(a: u64, b: u64): u64 {
268
+ return a > b ? a : b;
269
+ }
270
+
271
+ public static min128(a: u128, b: u128): u128 {
272
+ return u128.lt(a, b) ? a : b;
273
+ }
274
+
275
+ public static max128(a: u128, b: u128): u128 {
276
+ return u128.gt(a, b) ? a : b;
277
+ }
278
+
229
279
  public static min(a: u256, b: u256): u256 {
230
280
  return u256.lt(a, b) ? a : b;
231
281
  }