@btc-vision/btc-runtime 1.5.3 → 1.5.6

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.5.3",
3
+ "version": "1.5.6",
4
4
  "description": "Bitcoin Smart Contract Runtime",
5
5
  "main": "btc/index.ts",
6
6
  "scripts": {},
@@ -148,6 +148,7 @@ export class BlockchainEnvironment {
148
148
  const blockHash = reader.readBytes(32);
149
149
  const blockNumber = reader.readU64();
150
150
  const blockMedianTime = reader.readU64();
151
+ const txId = reader.readBytes(32);
151
152
  const txHash = reader.readBytes(32);
152
153
  const contractAddress = reader.readAddress();
153
154
  const contractDeployer = reader.readAddress();
@@ -157,6 +158,7 @@ export class BlockchainEnvironment {
157
158
  this._tx = new Transaction(
158
159
  caller,
159
160
  origin,
161
+ txId,
160
162
  txHash,
161
163
  );
162
164
 
@@ -9,6 +9,7 @@ export class Transaction {
9
9
  public constructor(
10
10
  public readonly sender: Address, // "immediate caller"
11
11
  public readonly origin: Address, // "leftmost thing in the call chain"
12
+ public readonly txId: Uint8Array,
12
13
  public readonly hash: Uint8Array,
13
14
  ) {
14
15
  }
@@ -5,7 +5,7 @@ import { Selector } from '../math/abi';
5
5
  import { Calldata } from '../types';
6
6
  import { env_exit, getCalldata, getEnvironmentVariables } from '../env/global';
7
7
 
8
- const ENVIRONMENT_VARIABLES_BYTE_LENGTH: u32 = 208;
8
+ const ENVIRONMENT_VARIABLES_BYTE_LENGTH: u32 = 240;
9
9
 
10
10
  export function execute(calldataLength: u32): u32 {
11
11
  const environmentVariablesBuffer = new ArrayBuffer(ENVIRONMENT_VARIABLES_BYTE_LENGTH);
@@ -171,23 +171,7 @@ export function encodeBasePointer(pointer: u16, subPointer: Uint8Array): Uint8Ar
171
171
 
172
172
  @inline
173
173
  export function bigEndianAdd(base: Uint8Array, increment: u64): Uint8Array {
174
- const out = new Uint8Array(32);
174
+ const add = u64ToBE32Bytes(increment);
175
175
 
176
- // Copy the base pointer
177
- for (let i = 0; i < 32; i++) {
178
- out[i] = base[i];
179
- }
180
-
181
- // If this is truly big-endian, out[0] is the MSB, out[31] is the LSB.
182
- // So to add `increment`, we start from out[31] backward.
183
- let carry: u64 = increment;
184
- for (let i = 31; i >= 0; i--) {
185
- const sum = <u64>out[i] + (carry & 0xFF);
186
- out[i] = <u8>(sum & 0xFF);
187
- carry = sum >> 8;
188
- if (carry == 0 || i == 0) {
189
- break;
190
- }
191
- }
192
- return out;
176
+ return addUint8ArraysBE(base, add);
193
177
  }
@@ -1,7 +1,12 @@
1
- import { BytesReader } from '../../buffer/BytesReader';
2
1
  import { BytesWriter } from '../../buffer/BytesWriter';
3
2
  import { Blockchain } from '../../env';
4
- import { addUint8ArraysBE, u64ToBE32Bytes } from '../../math/bytes';
3
+ import {
4
+ addUint8ArraysBE,
5
+ bigEndianAdd,
6
+ encodeBasePointer,
7
+ readLengthAndStartIndex,
8
+ u64ToBE32Bytes,
9
+ } from '../../math/bytes';
5
10
  import { Address } from '../../types/Address';
6
11
  import { Revert } from '../../types/Revert';
7
12
 
@@ -38,34 +43,32 @@ export class StoredAddressArray {
38
43
  `You must pass a 30 bytes sub-pointer. (AddressArray, got ${subPointer.length})`,
39
44
  );
40
45
 
41
- // Construct base pointer as a 32-byte array
42
- const writer = new BytesWriter(32);
43
- writer.writeU16(pointer);
44
- writer.writeBytes(subPointer);
46
+ const basePointer = encodeBasePointer(pointer, subPointer);
47
+ this.lengthPointer = Uint8Array.wrap(basePointer.buffer);
48
+ this.baseU256Pointer = bigEndianAdd(basePointer, 1);
45
49
 
46
- // We'll reuse the same bytes as the "base pointer" for offsets
47
- const baseU256Pointer = writer.getBuffer(); // 32 bytes
48
- // For length+startIndex, we'll use the same pointer
49
- const lengthPointer = Uint8Array.wrap(baseU256Pointer.buffer);
50
+ const storedLenStart = Blockchain.getStorageAt(basePointer);
51
+ const data = readLengthAndStartIndex(storedLenStart);
50
52
 
51
- // Load length + startIndex from storage (16 bytes: 8 for length, 8 for startIndex).
52
- const storedLengthAndStartIndex: Uint8Array = Blockchain.getStorageAt(lengthPointer);
53
-
54
- const reader = new BytesReader(storedLengthAndStartIndex);
55
- this._length = reader.readU64();
56
- this._startIndex = reader.readU64();
53
+ this._length = data[0];
54
+ this._startIndex = data[1];
55
+ }
57
56
 
58
- this.lengthPointer = lengthPointer;
59
- this.baseU256Pointer = baseU256Pointer;
57
+ @inline
58
+ public has(index: u64): bool {
59
+ return index < this._length;
60
60
  }
61
61
 
62
62
  /** Get an element by its global index. */
63
63
  @inline
64
+ @operator('[]')
64
65
  public get(index: u64): Address {
65
- if (index > this.MAX_LENGTH) {
66
- throw new Revert('Operation failed: Index exceeds maximum allowed value.');
66
+ if (index >= this._length) {
67
+ throw new Revert('get: index out of range (address array)');
67
68
  }
68
- const slotIndex: u32 = <u32>index;
69
+
70
+ const physicalIndex = (this._startIndex + index) % this.MAX_LENGTH;
71
+ const slotIndex: u32 = <u32>physicalIndex;
69
72
  this.ensureValues(slotIndex);
70
73
 
71
74
  return this._values.get(slotIndex);
@@ -73,11 +76,14 @@ export class StoredAddressArray {
73
76
 
74
77
  /** Set an element by its global index. */
75
78
  @inline
79
+ @operator('[]=')
76
80
  public set(index: u64, value: Address): void {
77
- if (index > this.MAX_LENGTH) {
78
- throw new Revert('Set failed: Index exceeds maximum allowed value.');
81
+ if (index >= this._length) {
82
+ throw new Revert('set: index out of range (address array)');
79
83
  }
80
- const slotIndex: u32 = <u32>index;
84
+
85
+ const physicalIndex = (this._startIndex + index) % this.MAX_LENGTH;
86
+ const slotIndex: u32 = <u32>physicalIndex;
81
87
  this.ensureValues(slotIndex);
82
88
 
83
89
  const currentValue = this._values.get(slotIndex);
@@ -87,33 +93,16 @@ export class StoredAddressArray {
87
93
  }
88
94
  }
89
95
 
90
- /** Find the first index containing `value`. Returns -1 if not found. */
91
- @inline
92
- public indexOf(value: Address): i64 {
93
- for (let i: u64 = 0; i < this._length; i++) {
94
- if (this.get(i) == value) {
95
- return i64(i);
96
- }
97
- }
98
- return -1;
99
- }
100
-
101
- /** Check if the array contains `value`. */
102
- @inline
103
- public contains(value: Address): boolean {
104
- return this.indexOf(value) !== -1;
105
- }
106
-
107
96
  /** Append an address at the end of the array. */
97
+ @inline
108
98
  public push(value: Address): void {
109
99
  if (this._length >= this.MAX_LENGTH) {
110
- throw new Revert('Push failed: Array has reached its maximum length.');
100
+ throw new Revert('push: array reached maximum length (address array)');
111
101
  }
112
102
 
113
- const newIndex: u64 = this._length;
114
- const wrappedIndex: u64 =
115
- newIndex < this.MAX_LENGTH ? newIndex : newIndex % this.MAX_LENGTH;
116
- const slotIndex: u32 = <u32>wrappedIndex;
103
+ const newLogicalIndex: u64 = this._length;
104
+ const physicalIndex: u64 = (this._startIndex + newLogicalIndex) % this.MAX_LENGTH;
105
+ const slotIndex: u32 = <u32>physicalIndex;
117
106
 
118
107
  this.ensureValues(slotIndex);
119
108
  this._values.set(slotIndex, value);
@@ -126,11 +115,12 @@ export class StoredAddressArray {
126
115
  /** Delete the last element. */
127
116
  public deleteLast(): void {
128
117
  if (this._length === 0) {
129
- throw new Revert('Delete failed: Array is empty.');
118
+ throw new Revert('deleteLast: array is empty (address array)');
130
119
  }
131
120
 
132
- const lastIndex: u64 = this._length - 1;
133
- const slotIndex: u32 = <u32>(this._startIndex + lastIndex);
121
+ const lastLogicalIndex: u64 = this._length - 1;
122
+ const physicalIndex: u64 = (this._startIndex + lastLogicalIndex) % this.MAX_LENGTH;
123
+ const slotIndex: u32 = <u32>physicalIndex;
134
124
  this.ensureValues(slotIndex);
135
125
 
136
126
  const currentValue = this._values.get(slotIndex);
@@ -150,12 +140,14 @@ export class StoredAddressArray {
150
140
  }
151
141
 
152
142
  /** Delete a specific element by setting it to `defaultValue`. */
143
+ @inline
153
144
  public delete(index: u64): void {
154
145
  if (index > this.MAX_LENGTH) {
155
- throw new Revert('Operation failed: Index exceeds maximum allowed value.');
146
+ throw new Revert('delete: index out of range (address array)');
156
147
  }
157
148
 
158
- const slotIndex: u32 = <u32>index;
149
+ const physicalIndex: u64 = (this._startIndex + index) % this.MAX_LENGTH;
150
+ const slotIndex: u32 = <u32>physicalIndex;
159
151
  this.ensureValues(slotIndex);
160
152
 
161
153
  const currentValue = this._values.get(slotIndex);
@@ -170,6 +162,7 @@ export class StoredAddressArray {
170
162
  * - Store any changed slotIndex -> Address
171
163
  * - Store updated length and startIndex if changed
172
164
  */
165
+ @inline
173
166
  public save(): void {
174
167
  // 1) Save changed slots
175
168
  const changed = this._isChanged.values();
@@ -196,7 +189,6 @@ export class StoredAddressArray {
196
189
 
197
190
  /** Clear entire array content from storage, reset length and startIndex. */
198
191
  public deleteAll(): void {
199
- // Clear all loaded slots
200
192
  const keys = this._values.keys();
201
193
  for (let i = 0; i < keys.length; i++) {
202
194
  const slotIndex = keys[i];
@@ -204,14 +196,13 @@ export class StoredAddressArray {
204
196
  Blockchain.setStorageAt(storagePointer, this.defaultValue);
205
197
  }
206
198
 
207
- // Reset length and startIndex in storage
208
- Blockchain.setStorageAt(this.lengthPointer, new Uint8Array(0)); // or a 16-byte zero array if preferred
199
+ Blockchain.setStorageAt(this.lengthPointer, new Uint8Array(32));
200
+
209
201
  this._length = 0;
210
202
  this._startIndex = 0;
211
203
  this._isChangedLength = false;
212
204
  this._isChangedStartIndex = false;
213
205
 
214
- // Clear internal caches
215
206
  this._values.clear();
216
207
  this._isChanged.clear();
217
208
  }
@@ -228,7 +219,7 @@ export class StoredAddressArray {
228
219
  @inline
229
220
  public getAll(startIndex: u32, count: u32): Address[] {
230
221
  if (startIndex + count > this._length) {
231
- throw new Revert('Requested range exceeds array length');
222
+ throw new Revert('getAll: index out of range (address array)');
232
223
  }
233
224
 
234
225
  const result = new Array<Address>(count);
@@ -260,6 +251,10 @@ export class StoredAddressArray {
260
251
  this._startIndex = 0;
261
252
  this._isChangedLength = true;
262
253
  this._isChangedStartIndex = true;
254
+
255
+ this._values.clear();
256
+ this._isChanged.clear();
257
+
263
258
  this.save();
264
259
  }
265
260
 
@@ -295,8 +290,8 @@ export class StoredAddressArray {
295
290
  * Compute a 32-byte storage pointer = basePointer + (slotIndex + 1) big-endian.
296
291
  */
297
292
  private calculateStoragePointer(slotIndex: u64): Uint8Array {
298
- // Convert (slotIndex + 1) to a 32-byte big-endian offset
299
- const offset = u64ToBE32Bytes(slotIndex + 1);
293
+ // Convert (slotIndex) to a 32-byte big-endian offset
294
+ const offset = u64ToBE32Bytes(slotIndex);
300
295
 
301
296
  return addUint8ArraysBE(this.baseU256Pointer, offset);
302
297
  }
@@ -1,8 +1,16 @@
1
1
  import { BytesWriter } from '../../buffer/BytesWriter';
2
- import { BytesReader } from '../../buffer/BytesReader';
3
2
  import { Blockchain } from '../../env';
4
3
  import { Revert } from '../../types/Revert';
5
- import { addUint8ArraysBE, GET_EMPTY_BUFFER, getBit, setBit, u64ToBE32Bytes } from '../../math/bytes';
4
+ import {
5
+ addUint8ArraysBE,
6
+ bigEndianAdd,
7
+ encodeBasePointer,
8
+ GET_EMPTY_BUFFER,
9
+ getBit,
10
+ readLengthAndStartIndex,
11
+ setBit,
12
+ u64ToBE32Bytes,
13
+ } from '../../math/bytes';
6
14
 
7
15
  /**
8
16
  * @class StoredBooleanArray
@@ -42,36 +50,41 @@ export class StoredBooleanArray {
42
50
  ) {
43
51
  assert(subPtr.length <= 30, `You must pass a 30 bytes sub-pointer. (StoredBooleanArray, got ${subPtr.length})`);
44
52
 
45
- const writer = new BytesWriter(32);
46
- writer.writeU16(pointer);
47
- writer.writeBytes(subPtr);
48
-
49
- const basePtr = writer.getBuffer();
50
-
51
- this.basePointer = basePtr;
52
- this.lengthPointer = basePtr;
53
+ const basePointer = encodeBasePointer(pointer, subPtr);
54
+ this.lengthPointer = Uint8Array.wrap(basePointer.buffer);
55
+ this.basePointer = bigEndianAdd(basePointer, 1);
53
56
 
54
- const storedLenStart: Uint8Array = Blockchain.getStorageAt(
55
- this.lengthPointer,
56
- );
57
+ const storedLenStart = Blockchain.getStorageAt(basePointer);
58
+ const data = readLengthAndStartIndex(storedLenStart);
57
59
 
58
- const r = new BytesReader(storedLenStart);
59
- this._length = r.readU64();
60
- this._startIndex = r.readU64();
60
+ this._length = data[0];
61
+ this._startIndex = data[1];
61
62
  }
62
63
 
63
64
  // -------------- Public Accessors -------------- //
64
65
 
66
+ @inline
67
+ public has(index: u64): bool {
68
+ return index < this._length;
69
+ }
70
+
65
71
  /**
66
72
  * Retrieve boolean at `index`.
67
73
  */
74
+ @operator('[]')
68
75
  @inline
69
76
  public get(index: u64): bool {
70
- if (index > this.MAX_LENGTH) {
71
- throw new Revert('get: Index exceeds maximum allowed value.');
77
+ if (index >= this._length) {
78
+ throw new Revert(`get: index out of range (${index} >= ${this._length}, boolean array)`);
72
79
  }
73
- const slotIndex = index / 256; // which 32-byte slot
74
- const bitIndex = <u16>(index % 256);
80
+
81
+ const effectiveIndex = this._startIndex + index;
82
+ const wrappedIndex = effectiveIndex < this.MAX_LENGTH
83
+ ? effectiveIndex
84
+ : effectiveIndex % this.MAX_LENGTH;
85
+
86
+ const slotIndex = wrappedIndex / 256;
87
+ const bitIndex = <u16>(wrappedIndex % 256);
75
88
 
76
89
  this.ensureSlotLoaded(slotIndex);
77
90
 
@@ -82,13 +95,20 @@ export class StoredBooleanArray {
82
95
  /**
83
96
  * Set boolean at `index`.
84
97
  */
98
+ @operator('[]=')
85
99
  @inline
86
100
  public set(index: u64, value: bool): void {
87
- if (index > this.MAX_LENGTH) {
88
- throw new Revert('set: Index exceeds maximum allowed value.');
101
+ if (index >= this._length) {
102
+ throw new Revert(`set: index out of range (${index} >= ${this._length}, boolean array)`);
89
103
  }
90
- const slotIndex = index / 256;
91
- const bitIndex = <u16>(index % 256);
104
+
105
+ const effectiveIndex = this._startIndex + index;
106
+ const wrappedIndex = effectiveIndex < this.MAX_LENGTH
107
+ ? effectiveIndex
108
+ : effectiveIndex % this.MAX_LENGTH;
109
+
110
+ const slotIndex = wrappedIndex / 256;
111
+ const bitIndex = <u16>(wrappedIndex % 256);
92
112
 
93
113
  this.ensureSlotLoaded(slotIndex);
94
114
 
@@ -108,7 +128,7 @@ export class StoredBooleanArray {
108
128
  @inline
109
129
  public push(value: bool): void {
110
130
  if (this._length >= this.MAX_LENGTH) {
111
- throw new Revert('push: Reached max allowed length');
131
+ throw new Revert('push: reached max allowed length (boolean array)');
112
132
  }
113
133
 
114
134
  const newIndex = this._length;
@@ -137,11 +157,17 @@ export class StoredBooleanArray {
137
157
  */
138
158
  @inline
139
159
  public delete(index: u64): void {
140
- if (index > this.MAX_LENGTH) {
141
- throw new Revert('delete: Index exceeds maximum allowed value.');
160
+ if (index >= this._length) {
161
+ throw new Revert('delete: index out of range (boolean array)');
142
162
  }
143
- const slotIndex = index / 256;
144
- const bitIndex = <u16>(index % 256);
163
+
164
+ const effectiveIndex = this._startIndex + index;
165
+ const wrappedIndex = effectiveIndex < this.MAX_LENGTH
166
+ ? effectiveIndex
167
+ : effectiveIndex % this.MAX_LENGTH;
168
+
169
+ const slotIndex = wrappedIndex / 256;
170
+ const bitIndex = <u16>(wrappedIndex % 256);
145
171
 
146
172
  this.ensureSlotLoaded(slotIndex);
147
173
 
@@ -161,8 +187,9 @@ export class StoredBooleanArray {
161
187
  @inline
162
188
  public deleteLast(): void {
163
189
  if (this._length === 0) {
164
- throw new Revert('deleteLast: Array is empty');
190
+ throw new Revert('deleteLast: array is empty');
165
191
  }
192
+
166
193
  const lastIndex = this._length - 1;
167
194
  this.delete(lastIndex);
168
195
 
@@ -187,12 +214,11 @@ export class StoredBooleanArray {
187
214
 
188
215
  // 2) If length or startIndex changed, store them
189
216
  if (this._isChangedLength || this._isChangedStartIndex) {
190
- const w = new BytesWriter(16);
217
+ const w = new BytesWriter(32);
191
218
  w.writeU64(this._length);
192
219
  w.writeU64(this._startIndex);
193
220
 
194
- const data = w.getBuffer(); // 16 bytes
195
- // You could store 32 bytes if you prefer; the leftover bytes can be 0
221
+ const data = w.getBuffer();
196
222
  Blockchain.setStorageAt(this.lengthPointer, data);
197
223
 
198
224
  this._isChangedLength = false;
@@ -201,11 +227,10 @@ export class StoredBooleanArray {
201
227
  }
202
228
 
203
229
  /**
204
- * Delete all slots in storage (that are loaded) + reset length + startIndex.
230
+ * Delete all slots in storage (that are loaded) + reset length + _startIndex.
205
231
  */
206
232
  @inline
207
233
  public deleteAll(): void {
208
- // clear all loaded slots
209
234
  const keys = this._values.keys();
210
235
  const zeroArr = GET_EMPTY_BUFFER();
211
236
  for (let i = 0; i < keys.length; i++) {
@@ -214,17 +239,14 @@ export class StoredBooleanArray {
214
239
  Blockchain.setStorageAt(storagePointer, zeroArr);
215
240
  }
216
241
 
217
- // also reset length + startIndex in storage
218
- const writer = new BytesWriter(16);
219
- writer.writeU64(0);
220
- writer.writeU64(0);
242
+ const writer = new BytesWriter(32);
221
243
  Blockchain.setStorageAt(this.lengthPointer, writer.getBuffer());
222
244
 
223
- // reset in memory
224
245
  this._length = 0;
225
246
  this._startIndex = 0;
226
247
  this._isChangedLength = false;
227
248
  this._isChangedStartIndex = false;
249
+
228
250
  this._values.clear();
229
251
  this._isChanged.clear();
230
252
  }
@@ -243,18 +265,20 @@ export class StoredBooleanArray {
243
265
  * Retrieve a batch of bools.
244
266
  */
245
267
  @inline
246
- public getAll(startIndex: u64, count: u64): bool[] {
247
- if (startIndex + count > this._length) {
248
- throw new Revert('getAll: range exceeds array length');
268
+ public getAll(start: u64, count: u64): bool[] {
269
+ if (start + count > this._length) {
270
+ throw new Revert('getAll: range exceeds array length (boolean array)');
249
271
  }
272
+
250
273
  if (count > u64(u32.MAX_VALUE)) {
251
- throw new Revert('getAll: range exceeds max allowed');
274
+ throw new Revert('getAll: range exceeds max allowed (boolean array)');
252
275
  }
253
276
 
254
277
  const result = new Array<bool>(<i32>count);
255
278
  for (let i: u64 = 0; i < count; i++) {
256
- result[<i32>i] = this.get(startIndex + i);
279
+ result[<i32>i] = this.get(start + i);
257
280
  }
281
+
258
282
  return result;
259
283
  }
260
284
 
@@ -298,6 +322,12 @@ export class StoredBooleanArray {
298
322
  return this._length;
299
323
  }
300
324
 
325
+ @inline
326
+ public setStartingIndex(index: u64): void {
327
+ this._startIndex = index;
328
+ this._isChangedStartIndex = true;
329
+ }
330
+
301
331
  /**
302
332
  * Current starting index for the array.
303
333
  */
@@ -306,7 +336,6 @@ export class StoredBooleanArray {
306
336
  return this._startIndex;
307
337
  }
308
338
 
309
-
310
339
  /**
311
340
  * Ensure the 32-byte slot for `slotIndex` is loaded into _values.
312
341
  */
@@ -322,7 +351,7 @@ export class StoredBooleanArray {
322
351
  * Convert `slotIndex` -> pointer = basePointer + (slotIndex + 1), as big-endian addition.
323
352
  */
324
353
  private calculateStoragePointer(slotIndex: u64): Uint8Array {
325
- const offset = u64ToBE32Bytes(slotIndex + 1);
354
+ const offset = u64ToBE32Bytes(slotIndex);
326
355
  return addUint8ArraysBE(this.basePointer, offset);
327
356
  }
328
357
  }
@@ -1,4 +1,5 @@
1
1
  import {
2
+ bigEndianAdd,
2
3
  encodeBasePointer,
3
4
  GET_EMPTY_BUFFER,
4
5
  readLengthAndStartIndex,
@@ -53,7 +54,7 @@ export abstract class StoredPackedArray<T> {
53
54
 
54
55
  const basePointer = encodeBasePointer(pointer, subPointer);
55
56
  this.lengthPointer = Uint8Array.wrap(basePointer.buffer);
56
- this.basePointer = basePointer;
57
+ this.basePointer = bigEndianAdd(basePointer, 1);
57
58
 
58
59
  const storedLenStart = Blockchain.getStorageAt(basePointer);
59
60
  const data = readLengthAndStartIndex(storedLenStart);
@@ -62,9 +63,11 @@ export abstract class StoredPackedArray<T> {
62
63
  this._startIndex = data[1];
63
64
  }
64
65
 
66
+ @inline
67
+ @operator('[]')
65
68
  public get(index: u64): T {
66
69
  if (index > this.MAX_LENGTH) {
67
- throw new Revert('get: index exceeds MAX_LENGTH');
70
+ throw new Revert('get: index exceeds MAX_LENGTH (packed array)');
68
71
  }
69
72
 
70
73
  const cap = this.getSlotCapacity();
@@ -79,7 +82,13 @@ export abstract class StoredPackedArray<T> {
79
82
  return arr[subIndex];
80
83
  }
81
84
 
85
+ @inline
86
+ @operator('[]=')
82
87
  public set(index: u64, value: T): void {
88
+ if (index > this.MAX_LENGTH) {
89
+ throw new Revert('set: index exceeds MAX_LENGTH (packed array)');
90
+ }
91
+
83
92
  const cap = this.getSlotCapacity();
84
93
  const slotIndex = index / cap;
85
94
  const subIndex = <u32>(index % cap);
@@ -95,9 +104,10 @@ export abstract class StoredPackedArray<T> {
95
104
  }
96
105
  }
97
106
 
107
+ @inline
98
108
  public push(value: T): void {
99
109
  if (this._length >= this.MAX_LENGTH) {
100
- throw new Revert('push: array has reached MAX_LENGTH');
110
+ throw new Revert('push: array has reached MAX_LENGTH (packed array)');
101
111
  }
102
112
 
103
113
  const newIndex = this._length;
@@ -123,6 +133,7 @@ export abstract class StoredPackedArray<T> {
123
133
  * "Delete" by zeroing out the element at `index`,
124
134
  * but does not reduce the length.
125
135
  */
136
+ @inline
126
137
  public delete(index: u64): void {
127
138
  const cap = this.getSlotCapacity();
128
139
  const slotIndex = index / cap;
@@ -143,9 +154,10 @@ export abstract class StoredPackedArray<T> {
143
154
  /**
144
155
  * Remove the last element by zeroing it and decrementing length by 1.
145
156
  */
157
+ @inline
146
158
  public deleteLast(): void {
147
159
  if (this._length == 0) {
148
- throw new Revert('deleteLast: array is empty');
160
+ throw new Revert('deleteLast: array is empty (packed array)');
149
161
  }
150
162
 
151
163
  const lastIndex = this._length - 1;
@@ -155,11 +167,13 @@ export abstract class StoredPackedArray<T> {
155
167
  this._isChangedLength = true;
156
168
  }
157
169
 
170
+ @inline
158
171
  public setMultiple(startIndex: u64, values: T[]): void {
159
172
  const end = startIndex + <u64>values.length;
160
173
  if (end > this._length) {
161
- throw new Revert('setMultiple: out of range');
174
+ throw new Revert('setMultiple: out of range (packed array)');
162
175
  }
176
+
163
177
  for (let i = 0; i < values.length; i++) {
164
178
  this.set(startIndex + <u64>i, values[i]);
165
179
  }
@@ -168,10 +182,10 @@ export abstract class StoredPackedArray<T> {
168
182
  // -----------------------------------------------------------
169
183
  // Public Array-Like Methods
170
184
  // -----------------------------------------------------------
171
-
185
+ @inline
172
186
  public getAll(startIndex: u64, count: u64): T[] {
173
187
  if (count > <u64>u32.MAX_VALUE) {
174
- throw new Revert('getAll: count too large');
188
+ throw new Revert('getAll: count too large (packed array)');
175
189
  }
176
190
 
177
191
  const out = new Array<T>(<i32>count);
@@ -182,14 +196,17 @@ export abstract class StoredPackedArray<T> {
182
196
  return out;
183
197
  }
184
198
 
199
+ @inline
185
200
  public getLength(): u64 {
186
201
  return this._length;
187
202
  }
188
203
 
204
+ @inline
189
205
  public startingIndex(): u64 {
190
206
  return this._startIndex;
191
207
  }
192
208
 
209
+ @inline
193
210
  public setStartingIndex(index: u64): void {
194
211
  this._startIndex = index;
195
212
  this._isChangedStartIndex = true;
@@ -250,6 +267,9 @@ export abstract class StoredPackedArray<T> {
250
267
  this._isChanged.clear();
251
268
  }
252
269
 
270
+ /**
271
+ * Reset the array to its initial state.
272
+ */
253
273
  public reset(): void {
254
274
  this._length = 0;
255
275
  this._startIndex = 0;
@@ -59,6 +59,6 @@ export class StoredU128Array extends StoredPackedArray<u128> {
59
59
  }
60
60
 
61
61
  protected calculateStoragePointer(slotIndex: u64): Uint8Array {
62
- return bigEndianAdd(this.basePointer, slotIndex + 1);
62
+ return bigEndianAdd(this.basePointer, slotIndex);
63
63
  }
64
64
  }
@@ -53,6 +53,6 @@ export class StoredU16Array extends StoredPackedArray<u16> {
53
53
  }
54
54
 
55
55
  protected calculateStoragePointer(slotIndex: u64): Uint8Array {
56
- return bigEndianAdd(this.basePointer, slotIndex + 1);
56
+ return bigEndianAdd(this.basePointer, slotIndex);
57
57
  }
58
58
  }
@@ -34,6 +34,6 @@ export class StoredU256Array extends StoredPackedArray<u256> {
34
34
  }
35
35
 
36
36
  protected calculateStoragePointer(slotIndex: u64): Uint8Array {
37
- return bigEndianAdd(this.basePointer, slotIndex + 1);
37
+ return bigEndianAdd(this.basePointer, slotIndex);
38
38
  }
39
39
  }
@@ -52,6 +52,6 @@ export class StoredU32Array extends StoredPackedArray<u32> {
52
52
  }
53
53
 
54
54
  protected calculateStoragePointer(slotIndex: u64): Uint8Array {
55
- return bigEndianAdd(this.basePointer, slotIndex + 1);
55
+ return bigEndianAdd(this.basePointer, slotIndex);
56
56
  }
57
57
  }
@@ -61,6 +61,6 @@ export class StoredU64Array extends StoredPackedArray<u64> {
61
61
  }
62
62
 
63
63
  protected calculateStoragePointer(slotIndex: u64): Uint8Array {
64
- return bigEndianAdd(this.basePointer, slotIndex + 1);
64
+ return bigEndianAdd(this.basePointer, slotIndex);
65
65
  }
66
66
  }
@@ -43,6 +43,6 @@ export class StoredU8Array extends StoredPackedArray<u8> {
43
43
 
44
44
  protected calculateStoragePointer(slotIndex: u64): Uint8Array {
45
45
  // basePointer + (slotIndex+1) in big-endian
46
- return bigEndianAdd(this.basePointer, slotIndex + 1);
46
+ return bigEndianAdd(this.basePointer, slotIndex);
47
47
  }
48
48
  }