@btc-vision/btc-runtime 1.10.10 → 1.10.12

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.
Files changed (45) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +258 -137
  3. package/SECURITY.md +226 -0
  4. package/docs/README.md +614 -0
  5. package/docs/advanced/bitcoin-scripts.md +939 -0
  6. package/docs/advanced/cross-contract-calls.md +579 -0
  7. package/docs/advanced/plugins.md +1006 -0
  8. package/docs/advanced/quantum-resistance.md +660 -0
  9. package/docs/advanced/signature-verification.md +715 -0
  10. package/docs/api-reference/blockchain.md +729 -0
  11. package/docs/api-reference/events.md +642 -0
  12. package/docs/api-reference/op20.md +902 -0
  13. package/docs/api-reference/op721.md +819 -0
  14. package/docs/api-reference/safe-math.md +510 -0
  15. package/docs/api-reference/storage.md +731 -0
  16. package/docs/contracts/op-net-base.md +786 -0
  17. package/docs/contracts/op20-token.md +687 -0
  18. package/docs/contracts/op20s-signatures.md +614 -0
  19. package/docs/contracts/op721-nft.md +785 -0
  20. package/docs/contracts/reentrancy-guard.md +787 -0
  21. package/docs/core-concepts/blockchain-environment.md +724 -0
  22. package/docs/core-concepts/decorators.md +466 -0
  23. package/docs/core-concepts/events.md +652 -0
  24. package/docs/core-concepts/pointers.md +391 -0
  25. package/docs/core-concepts/security.md +370 -0
  26. package/docs/core-concepts/storage-system.md +938 -0
  27. package/docs/examples/basic-token.md +745 -0
  28. package/docs/examples/nft-with-reservations.md +1210 -0
  29. package/docs/examples/oracle-integration.md +1212 -0
  30. package/docs/examples/stablecoin.md +1180 -0
  31. package/docs/getting-started/first-contract.md +575 -0
  32. package/docs/getting-started/installation.md +384 -0
  33. package/docs/getting-started/project-structure.md +630 -0
  34. package/docs/storage/memory-maps.md +721 -0
  35. package/docs/storage/stored-arrays.md +714 -0
  36. package/docs/storage/stored-maps.md +686 -0
  37. package/docs/storage/stored-primitives.md +608 -0
  38. package/docs/types/address.md +773 -0
  39. package/docs/types/bytes-writer-reader.md +938 -0
  40. package/docs/types/calldata.md +744 -0
  41. package/docs/types/safe-math.md +403 -0
  42. package/package.json +51 -26
  43. package/runtime/memory/MapOfMap.ts +1 -0
  44. package/runtime/types/SafeMath.ts +121 -1
  45. package/LICENSE.md +0 -21
@@ -0,0 +1,731 @@
1
+ # Storage API Reference
2
+
3
+ Storage classes provide persistent state management for OPNet smart contracts.
4
+
5
+ ## Import
6
+
7
+ ```typescript
8
+ import {
9
+ StoredU256,
10
+ StoredU64,
11
+ StoredU32,
12
+ StoredBoolean,
13
+ StoredString,
14
+ StoredAddress,
15
+ StoredU256Array,
16
+ StoredAddressArray,
17
+ AddressMemoryMap,
18
+ StoredMapU256,
19
+ Blockchain,
20
+ EMPTY_POINTER,
21
+ } from '@btc-vision/btc-runtime/runtime';
22
+ ```
23
+
24
+ > **Important:** Do NOT use AssemblyScript's built-in Map for blockchain storage. See [CRITICAL: Map Implementation Warning](../storage/stored-maps.md#critical-map-implementation-warning) for details.
25
+
26
+ ## Storage Type Hierarchy
27
+
28
+ The following diagram shows the complete hierarchy of storage types available in the runtime:
29
+
30
+ ```mermaid
31
+ graph LR
32
+ A[Storage Types]
33
+
34
+ subgraph "Primitive Types"
35
+ B1[StoredU256<br/>StoredU64<br/>StoredU32]
36
+ B2[StoredBoolean<br/>StoredString<br/>StoredAddress]
37
+ end
38
+
39
+ subgraph "Array Types"
40
+ C1[StoredU256Array<br/>StoredU64Array]
41
+ C2[StoredU32Array<br/>StoredAddressArray]
42
+ C3[StoredBooleanArray]
43
+ end
44
+
45
+ subgraph "Map Types"
46
+ D1[AddressMemoryMap<br/>Address -> u256]
47
+ D2[StoredMapU256<br/>u256 -> u256]
48
+ D3[MapOfMap<br/>Nested maps]
49
+ end
50
+
51
+ subgraph "Backend Storage"
52
+ E1[encodePointer<br/>Generate hash]
53
+ E2[getStorageAt<br/>Read value]
54
+ E3[setStorageAt<br/>Write value]
55
+ end
56
+
57
+ A --> B1 & B2
58
+ A --> C1 & C2 & C3
59
+ A --> D1 & D2 & D3
60
+
61
+ B1 & B2 --> E1
62
+ C1 & C2 & C3 --> E1
63
+ D1 & D2 & D3 --> E1
64
+
65
+ E1 --> E2 & E3
66
+ ```
67
+
68
+ ```mermaid
69
+ classDiagram
70
+ class StoredU256 {
71
+ -pointer: u16
72
+ -subPointer: Uint8Array
73
+ +get value() u256
74
+ +set value(v: u256)
75
+ }
76
+
77
+ class StoredString {
78
+ -pointer: u16
79
+ -index: u64
80
+ +get value() string
81
+ +set value(v: string)
82
+ }
83
+
84
+ class AddressMemoryMap {
85
+ -pointer: u16
86
+ +get(key: Address) u256
87
+ +set(key: Address, value: u256)
88
+ +has(key: Address) bool
89
+ +delete(key: Address) bool
90
+ }
91
+
92
+ class StoredU256Array {
93
+ -pointer: u16
94
+ +getLength() u32
95
+ +push(value: u256) u32
96
+ +shift() u256
97
+ +get(index: u32) u256
98
+ +set(index: u32, value: u256)
99
+ +save()
100
+ }
101
+
102
+ class MapOfMap {
103
+ -pointer: u16
104
+ +get(key: Address) Nested~u256~
105
+ +set(key: Address, value: Nested~u256~)
106
+ +has(key: Address) bool
107
+ +delete(key: Address) bool
108
+ }
109
+
110
+ class Blockchain {
111
+ +getStorageAt(hash: Uint8Array) Uint8Array
112
+ +setStorageAt(hash: Uint8Array, value: Uint8Array)
113
+ +encodePointer(pointer: u16, subPointer: Uint8Array) Uint8Array
114
+ }
115
+
116
+ StoredU256 ..> Blockchain
117
+ StoredString ..> Blockchain
118
+ AddressMemoryMap ..> Blockchain
119
+ StoredU256Array ..> Blockchain
120
+ MapOfMap ..> Blockchain
121
+
122
+ note for Blockchain "All storage types use\nBlockchain as backend"
123
+ ```
124
+
125
+ ## Primitive Storage
126
+
127
+ ### StoredU256
128
+
129
+ Stores a 256-bit unsigned integer.
130
+
131
+ ```typescript
132
+ class StoredU256 {
133
+ constructor(pointer: u16, subPointer: Uint8Array)
134
+ public get value(): u256
135
+ public set value(v: u256)
136
+ public get toBytes(): Uint8Array
137
+ public toString(): string
138
+ public set(value: u256): this
139
+ public add(value: u256): this // operator +
140
+ public sub(value: u256): this // operator -
141
+ public mul(value: u256): this // operator *
142
+ public addNoCommit(value: u256): this
143
+ public subNoCommit(value: u256): this
144
+ public commit(): this
145
+ public toUint8Array(): Uint8Array
146
+ }
147
+ ```
148
+
149
+ ```typescript
150
+ private balancePointer: u16 = Blockchain.nextPointer;
151
+ private _balance: StoredU256 = new StoredU256(this.balancePointer, EMPTY_POINTER);
152
+
153
+ // Usage
154
+ this._balance.value = u256.fromU64(1000);
155
+ const balance = this._balance.value;
156
+ ```
157
+
158
+ The following sequence diagram shows how storage read and write operations work:
159
+
160
+ ```mermaid
161
+ sequenceDiagram
162
+ participant Contract
163
+ participant Storage as Storage Object
164
+ participant Encode as encodePointer
165
+ participant BC as Blockchain
166
+
167
+ Note over Contract,BC: Write Operation
168
+
169
+ Contract->>Contract: private balance: StoredU256
170
+ Contract->>Contract: balance.value = u256.fromU64(1000)
171
+
172
+ Storage->>Encode: encodePointer(pointer, subPointer)
173
+ Encode->>Encode: SHA-256(pointer + subPointer)
174
+ Encode->>Storage: Return 32-byte hash
175
+
176
+ Storage->>Storage: value.toUint8Array(true)
177
+ Storage->>BC: setStorageAt(hash, bytes)
178
+ BC->>BC: Write to persistent storage
179
+
180
+ Note over Contract,BC: Read Operation
181
+
182
+ Contract->>Contract: const amount = balance.value
183
+
184
+ Storage->>Encode: encodePointer(pointer, subPointer)
185
+ Encode->>Storage: Return 32-byte hash
186
+
187
+ Storage->>BC: getStorageAt(hash)
188
+ BC->>Storage: Return 32-byte value
189
+ Storage->>Storage: u256.fromUint8ArrayBE(bytes)
190
+ Storage->>Contract: Return u256 value
191
+ ```
192
+
193
+ ### StoredU64
194
+
195
+ Stores up to four 64-bit unsigned integers within a single u256 storage slot.
196
+
197
+ ```typescript
198
+ class StoredU64 {
199
+ constructor(pointer: u16, subPointer: Uint8Array)
200
+ public get(index: u8): u64 // index 0-3
201
+ public set(index: u8, value: u64): void
202
+ public save(): void
203
+ public getAll(): u64[]
204
+ public setMultiple(values: u64[]): void
205
+ public reset(): void
206
+ public toString(): string
207
+ }
208
+ ```
209
+
210
+ ```typescript
211
+ private timestampPointer: u16 = Blockchain.nextPointer;
212
+ private _timestamps: StoredU64 = new StoredU64(this.timestampPointer, EMPTY_POINTER);
213
+
214
+ // Usage - stores up to 4 u64 values in one storage slot
215
+ this._timestamps.set(0, Blockchain.block.medianTime); // First u64
216
+ this._timestamps.set(1, someOtherTimestamp); // Second u64
217
+ this._timestamps.save(); // Commit to storage
218
+
219
+ const firstTimestamp = this._timestamps.get(0);
220
+ ```
221
+
222
+ ### StoredU32
223
+
224
+ Stores up to eight 32-bit unsigned integers within a single u256 storage slot.
225
+
226
+ ```typescript
227
+ class StoredU32 {
228
+ constructor(pointer: u16, subPointer: Uint8Array)
229
+ public get(index: u8): u32 // index 0-7
230
+ public set(index: u8, value: u32): void
231
+ public save(): void
232
+ public getAll(): u32[]
233
+ public setMultiple(values: u32[]): void
234
+ public reset(): void
235
+ public toString(): string
236
+ }
237
+ ```
238
+
239
+ ```typescript
240
+ private configPointer: u16 = Blockchain.nextPointer;
241
+ private _config: StoredU32 = new StoredU32(this.configPointer, EMPTY_POINTER);
242
+
243
+ // Usage - stores up to 8 u32 values in one storage slot
244
+ this._config.set(0, 100); // First u32
245
+ this._config.set(1, 200); // Second u32
246
+ this._config.save(); // Commit to storage
247
+
248
+ const firstValue = this._config.get(0);
249
+ ```
250
+
251
+ ### StoredBoolean
252
+
253
+ Stores a boolean value.
254
+
255
+ ```typescript
256
+ class StoredBoolean {
257
+ constructor(pointer: u16, defaultValue: bool)
258
+ public get value(): bool
259
+ public set value(v: bool)
260
+ public toUint8Array(): Uint8Array
261
+ }
262
+ ```
263
+
264
+ ```typescript
265
+ private pausedPointer: u16 = Blockchain.nextPointer;
266
+ private _paused: StoredBoolean = new StoredBoolean(this.pausedPointer, false);
267
+
268
+ // Usage
269
+ this._paused.value = true;
270
+ if (this._paused.value) {
271
+ throw new Revert('Contract is paused');
272
+ }
273
+ ```
274
+
275
+ ### StoredString
276
+
277
+ Stores a string value.
278
+
279
+ ```typescript
280
+ class StoredString {
281
+ constructor(pointer: u16, index: u64 = 0)
282
+ public get value(): string
283
+ public set value(v: string)
284
+ }
285
+ ```
286
+
287
+ ```typescript
288
+ private namePointer: u16 = Blockchain.nextPointer;
289
+ private _name: StoredString = new StoredString(this.namePointer, 0);
290
+
291
+ // Usage
292
+ this._name.value = 'My Token';
293
+ const name = this._name.value;
294
+ ```
295
+
296
+ ### StoredAddress
297
+
298
+ Stores an Address value. Default value is Address.zero().
299
+
300
+ ```typescript
301
+ class StoredAddress {
302
+ constructor(pointer: u16)
303
+ public get value(): Address
304
+ public set value(v: Address)
305
+ public isDead(): bool // Note: checks if address equals Address.zero(), not ExtendedAddress.dead()
306
+ }
307
+ ```
308
+
309
+ ```typescript
310
+ private ownerPointer: u16 = Blockchain.nextPointer;
311
+ private _owner: StoredAddress = new StoredAddress(this.ownerPointer);
312
+
313
+ // Usage
314
+ this._owner.value = Blockchain.tx.origin;
315
+ const owner = this._owner.value;
316
+ ```
317
+
318
+ ## Array Storage
319
+
320
+ ### StoredU256Array
321
+
322
+ Dynamic array of u256 values. Elements are packed into 32-byte storage slots.
323
+
324
+ ```typescript
325
+ class StoredU256Array {
326
+ constructor(pointer: u16, subPointer: Uint8Array, maxLength: u32 = DEFAULT_MAX_LENGTH)
327
+ public getLength(): u32
328
+ public push(value: u256, isPhysical?: bool): u32
329
+ public deleteLast(): void
330
+ public delete(index: u32): void
331
+ public shift(): u256
332
+ public get(index: u32): u256
333
+ public set(index: u32, value: u256): void
334
+ public getAll(startIndex: u32, count: u32): u256[]
335
+ public setMultiple(startIndex: u32, values: u256[]): void
336
+ public save(): void
337
+ public reset(): void
338
+ public deleteAll(): void
339
+ public startingIndex(): u32
340
+ public setStartingIndex(index: u32): void
341
+ }
342
+ ```
343
+
344
+ ```typescript
345
+ private tokenIdsPointer: u16 = Blockchain.nextPointer;
346
+ private tokenIds: StoredU256Array = new StoredU256Array(this.tokenIdsPointer, EMPTY_POINTER);
347
+
348
+ // Usage
349
+ this.tokenIds.push(u256.fromU64(1));
350
+ this.tokenIds.push(u256.fromU64(2));
351
+ this.tokenIds.save(); // Commit changes to storage
352
+
353
+ const first = this.tokenIds.get(0); // u256.fromU64(1)
354
+ const len = this.tokenIds.getLength(); // 2
355
+ ```
356
+
357
+ The following diagram shows the array operation flow:
358
+
359
+ ```mermaid
360
+ flowchart LR
361
+ A[StoredU256Array] --> B{Operation<br/>Type}
362
+
363
+ subgraph "push Operation"
364
+ B -->|push| C[Get current length]
365
+ C --> D[Encode pointer<br/>with index]
366
+ D --> E[Write value at index]
367
+ E --> F[Increment length]
368
+ end
369
+
370
+ subgraph "get Operation"
371
+ B -->|get| G[Validate<br/>index < length]
372
+ G --> H[Encode pointer<br/>with index]
373
+ H --> I[Read value<br/>from storage]
374
+ I --> J[Return u256]
375
+ end
376
+
377
+ subgraph "shift Operation"
378
+ B -->|shift| K[Validate<br/>length > 0]
379
+ K --> L[Read first element]
380
+ L --> M[Decrement length]
381
+ M --> N[Return value]
382
+ end
383
+
384
+ subgraph "set Operation"
385
+ B -->|set| O[Validate<br/>index < length]
386
+ O --> P[Encode pointer<br/>with index]
387
+ P --> Q[Write new value]
388
+ end
389
+ ```
390
+
391
+ ### StoredAddressArray
392
+
393
+ Dynamic array of Address values. Each address takes one 32-byte storage slot.
394
+
395
+ ```typescript
396
+ class StoredAddressArray {
397
+ constructor(pointer: u16, subPointer: Uint8Array, maxLength: u32 = DEFAULT_MAX_LENGTH)
398
+ public getLength(): u32
399
+ public push(value: Address, isPhysical?: bool): u32
400
+ public deleteLast(): void
401
+ public delete(index: u32): void
402
+ public shift(): Address
403
+ public get(index: u32): Address
404
+ public set(index: u32, value: Address): void
405
+ public getAll(startIndex: u32, count: u32): Address[]
406
+ public setMultiple(startIndex: u32, values: Address[]): void
407
+ public save(): void
408
+ public reset(): void
409
+ public deleteAll(): void
410
+ public startingIndex(): u32
411
+ public setStartingIndex(index: u32): void
412
+ }
413
+ ```
414
+
415
+ ```typescript
416
+ private oraclesPointer: u16 = Blockchain.nextPointer;
417
+ private oracles: StoredAddressArray = new StoredAddressArray(this.oraclesPointer, EMPTY_POINTER);
418
+
419
+ // Add oracle
420
+ this.oracles.push(oracleAddress);
421
+ this.oracles.save(); // Commit changes
422
+
423
+ // Iterate
424
+ for (let i: u32 = 0; i < this.oracles.getLength(); i++) {
425
+ const oracle = this.oracles.get(i);
426
+ // Process oracle
427
+ }
428
+ ```
429
+
430
+ ### Other Array Types
431
+
432
+ All array types share the same base API as `StoredU256Array` (extending `StoredPackedArray<T>`) with their respective element types:
433
+
434
+ - `StoredU128Array` - 2 u128 values per 32-byte slot
435
+ - `StoredU64Array` - 4 u64 values per 32-byte slot
436
+ - `StoredU32Array` - 8 u32 values per 32-byte slot
437
+ - `StoredU16Array` - 16 u16 values per 32-byte slot
438
+ - `StoredU8Array` - 32 u8 values per 32-byte slot
439
+ - `StoredBooleanArray` - 256 boolean values per 32-byte slot (bit-packed)
440
+
441
+ > **Note:** These are array types only. There are no standalone `StoredU128`, `StoredU16`, or `StoredU8` primitive classes. For storing single small values, use `StoredU64` (which packs 4 u64 values) or `StoredU32` (which packs 8 u32 values) in a single storage slot.
442
+
443
+ ## Map Storage
444
+
445
+ ### AddressMemoryMap
446
+
447
+ Maps addresses to u256 values. Always returns u256.Zero for unset addresses.
448
+
449
+ ```typescript
450
+ class AddressMemoryMap {
451
+ constructor(pointer: u16)
452
+ public get(key: Address): u256
453
+ public set(key: Address, value: u256): this
454
+ public getAsUint8Array(key: Address): Uint8Array
455
+ public setAsUint8Array(key: Address, value: Uint8Array): this
456
+ public has(key: Address): bool
457
+ public delete(key: Address): bool
458
+ }
459
+ ```
460
+
461
+ The following diagram shows how map keys are converted to storage hashes:
462
+
463
+ ```mermaid
464
+ flowchart LR
465
+ subgraph "Key to Hash"
466
+ A[AddressMemoryMap] --> B[Address Key]
467
+ B --> C[encodePointer<br/>pointer, address.toBytes]
468
+ C --> D[32-byte storage hash]
469
+ end
470
+
471
+ subgraph "Operations"
472
+ D --> E{get or set?}
473
+ E -->|get| F[Blockchain.getStorageAt<br/>hash]
474
+ F --> G[Convert to u256]
475
+ G --> H[Return value<br/>or u256.Zero]
476
+ E -->|set| I[value.toUint8Array]
477
+ I --> J[Blockchain.setStorageAt<br/>hash, bytes]
478
+ end
479
+ ```
480
+
481
+ #### Usage Example
482
+
483
+ ```typescript
484
+ // mapping(address => uint256)
485
+ private balancesPointer: u16 = Blockchain.nextPointer;
486
+ private balances: AddressMemoryMap;
487
+
488
+ constructor() {
489
+ super();
490
+ this.balances = new AddressMemoryMap(this.balancesPointer);
491
+ }
492
+
493
+ // Usage
494
+ const balance = this.balances.get(userAddress); // Returns u256
495
+ this.balances.set(userAddress, u256.fromU64(1000));
496
+ ```
497
+
498
+ The following sequence diagram shows the complete balance mapping flow:
499
+
500
+ ```mermaid
501
+ sequenceDiagram
502
+ participant Contract
503
+ participant Map as AddressMemoryMap
504
+ participant BC as Blockchain
505
+
506
+ Note over Contract,BC: Balance Mapping Example
507
+
508
+ Contract->>Contract: balances = new AddressMemoryMap(pointer)
509
+
510
+ Note over Contract,BC: Set Balance
511
+
512
+ Contract->>Map: balances.set(userAddress, u256.fromU64(1000))
513
+ Map->>Map: hash = encodePointer(pointer, userAddress.toBytes())
514
+ Map->>BC: setStorageAt(hash, 1000.toUint8Array())
515
+ BC->>Map: Storage updated
516
+
517
+ Note over Contract,BC: Get Balance
518
+
519
+ Contract->>Map: balances.get(userAddress)
520
+ Map->>Map: hash = encodePointer(pointer, userAddress.toBytes())
521
+ Map->>BC: getStorageAt(hash)
522
+ BC->>Map: Return bytes
523
+ Map->>Map: u256.fromUint8ArrayBE(bytes)
524
+ Map->>Contract: Return u256.fromU64(1000)
525
+
526
+ Note over Contract,BC: Get Non-Existent Balance
527
+
528
+ Contract->>Map: balances.get(unknownAddress)
529
+ Map->>BC: getStorageAt(hash)
530
+ BC->>Map: Return zeros
531
+ Map->>Contract: Return u256.Zero
532
+ ```
533
+
534
+ ### StoredMapU256
535
+
536
+ Maps u256 keys to u256 values.
537
+
538
+ ```typescript
539
+ class StoredMapU256 {
540
+ constructor(pointer: u16, subPointer: Uint8Array = new Uint8Array(30))
541
+ public get(key: u256): u256
542
+ public set(key: u256, value: u256): void
543
+ public delete(key: u256): void // Sets value to zero
544
+ }
545
+ ```
546
+
547
+ Note: `StoredMapU256` does not have a `has()` method. To check if a key exists, compare the returned value with `u256.Zero`.
548
+
549
+ ```typescript
550
+ private dataPointer: u16 = Blockchain.nextPointer;
551
+ private data: StoredMapU256 = new StoredMapU256(this.dataPointer);
552
+
553
+ // Usage
554
+ const key = u256.fromU64(123);
555
+ this.data.set(key, u256.fromU64(456));
556
+ const value = this.data.get(key);
557
+ ```
558
+
559
+ ### Nested Maps (MapOfMap)
560
+
561
+ For allowances pattern (owner => spender => amount):
562
+
563
+ ```typescript
564
+ class MapOfMap<T> {
565
+ constructor(pointer: u16)
566
+ public get(key: Address): Nested<T>
567
+ public set(key: Address, value: Nested<T>): this
568
+ public has(key: Address): bool
569
+ public delete(key: Address): bool
570
+ public clear(): void
571
+ }
572
+
573
+ class Nested<T> {
574
+ constructor(parent: Uint8Array, pointer: u16)
575
+ public get(key: Uint8Array): T
576
+ public set(key: Uint8Array, value: T): this
577
+ public has(key: Uint8Array): bool
578
+ }
579
+ ```
580
+
581
+ > **Important:** `MapOfMap.get(key)` returns a `Nested<T>` object, not the final value. You must call `.get()` on the nested object to retrieve the actual value.
582
+
583
+ See [Stored Maps - MapOfMap](../storage/stored-maps.md#mapofmap) for detailed usage patterns and diagrams.
584
+
585
+ ## Storage Patterns
586
+
587
+ ### Lazy Initialization
588
+
589
+ ```typescript
590
+ private _data: StoredU256 | null = null;
591
+ private dataPointer: u16 = Blockchain.nextPointer;
592
+
593
+ private get data(): StoredU256 {
594
+ if (!this._data) {
595
+ this._data = new StoredU256(this.dataPointer, EMPTY_POINTER);
596
+ }
597
+ return this._data;
598
+ }
599
+ ```
600
+
601
+ ### Computed Storage Keys
602
+
603
+ ```typescript
604
+ import { encodePointer } from '@btc-vision/btc-runtime/runtime';
605
+
606
+ private storagePointer: u16 = Blockchain.nextPointer;
607
+
608
+ private getKey(addr: Address, slot: u256): u256 {
609
+ const combined = new Uint8Array(64);
610
+ combined.set(addr.toBytes(), 0);
611
+ combined.set(slot.toUint8Array(), 32);
612
+ return u256.fromBytes(Blockchain.sha256(combined));
613
+ }
614
+
615
+ public getValue(addr: Address, slot: u256): u256 {
616
+ const key = this.getKey(addr, slot);
617
+ const pointerHash = encodePointer(this.storagePointer, key.toUint8Array(true));
618
+ const stored = Blockchain.getStorageAt(pointerHash);
619
+ return u256.fromUint8ArrayBE(stored);
620
+ }
621
+ ```
622
+
623
+ ### Counter Pattern
624
+
625
+ ```typescript
626
+ private counterPointer: u16 = Blockchain.nextPointer;
627
+ private _counter: StoredU256 = new StoredU256(this.counterPointer, EMPTY_POINTER);
628
+
629
+ public getNextId(): u256 {
630
+ const current = this._counter.value;
631
+ this._counter.value = SafeMath.add(current, u256.One);
632
+ return current;
633
+ }
634
+ ```
635
+
636
+ ## Low-Level Storage
637
+
638
+ Direct storage access via Blockchain:
639
+
640
+ ```typescript
641
+ import { encodePointer } from '@btc-vision/btc-runtime/runtime';
642
+
643
+ // Generate storage key hash
644
+ public encodePointer(pointer: u16, subPointer: Uint8Array): Uint8Array
645
+
646
+ // Write to storage
647
+ public setStorageAt(pointerHash: Uint8Array, value: Uint8Array): void
648
+
649
+ // Read from storage
650
+ public getStorageAt(pointerHash: Uint8Array): Uint8Array
651
+
652
+ // Check existence
653
+ public hasStorageAt(pointerHash: Uint8Array): bool
654
+ ```
655
+
656
+ ```typescript
657
+ // Direct storage example
658
+ const pointer: u16 = Blockchain.nextPointer;
659
+ const subPointer = u256.fromU64(1).toUint8Array(true);
660
+
661
+ // Create storage key
662
+ const pointerHash = encodePointer(pointer, subPointer);
663
+
664
+ // Write value
665
+ Blockchain.setStorageAt(pointerHash, u256.fromU64(100).toUint8Array(true));
666
+
667
+ // Read value
668
+ const stored = Blockchain.getStorageAt(pointerHash);
669
+ const value = u256.fromUint8ArrayBE(stored);
670
+ ```
671
+
672
+ ## Best Practices
673
+
674
+ ### 1. Declare Pointers First
675
+
676
+ ```typescript
677
+ class MyContract extends OP_NET {
678
+ // Declare all pointers before any stored values
679
+ private ptr1: u16 = Blockchain.nextPointer;
680
+ private ptr2: u16 = Blockchain.nextPointer;
681
+ private ptr3: u16 = Blockchain.nextPointer;
682
+
683
+ // Then declare stored values
684
+ private _value1: StoredU256;
685
+ private _value2: StoredBoolean;
686
+
687
+ public constructor() {
688
+ super();
689
+ this._value1 = new StoredU256(this.ptr1, EMPTY_POINTER);
690
+ this._value2 = new StoredBoolean(this.ptr2, false);
691
+ }
692
+ }
693
+ ```
694
+
695
+ ### 2. Use Appropriate Types
696
+
697
+ ```typescript
698
+ // Good - use smallest type that fits
699
+ private _count: StoredU32; // If count < 4 billion
700
+
701
+ // Less efficient
702
+ private _count: StoredU256; // Uses more storage
703
+ ```
704
+
705
+ ### 3. Batch Updates
706
+
707
+ ```typescript
708
+ // Multiple related updates in one call
709
+ public updateBoth(a: u256, b: u256): void {
710
+ this._valueA.value = a;
711
+ this._valueB.value = b;
712
+ }
713
+ ```
714
+
715
+ ## Solidity Comparison
716
+
717
+ | Solidity | OPNet Storage |
718
+ |----------|---------------|
719
+ | `uint256 public value` | `StoredU256` |
720
+ | `mapping(address => uint256)` | `AddressMemoryMap` |
721
+ | `mapping(address => mapping(address => uint256))` | `MapOfMap<u256>` |
722
+ | `uint256[] public array` | `StoredU256Array` |
723
+ | `bool public paused` | `StoredBoolean` |
724
+ | `string public name` | `StoredString` |
725
+ | `address public owner` | `StoredAddress` |
726
+
727
+ ---
728
+
729
+ **Navigation:**
730
+ - Previous: [SafeMath API](./safe-math.md)
731
+ - Next: [Events API](./events.md)