@btc-vision/btc-runtime 1.10.10 → 1.10.11

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