@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,969 @@
1
+ # Storage System
2
+
3
+ OPNet uses a pointer-based storage system that provides deterministic, secure, and efficient data persistence on Bitcoin L1. This guide explains how storage works and how to use it effectively.
4
+
5
+ ## Overview
6
+
7
+ Unlike Solidity where storage is implicitly managed, OPNet requires explicit pointer allocation for all persistent data. This design provides:
8
+
9
+ - **Deterministic storage locations** via SHA256 hashing
10
+ - **Collision-free addressing** through unique pointer combinations
11
+ - **Efficient access** with optimized read/write patterns
12
+ - **Verifiable state proofs** for cross-chain validation
13
+
14
+ ## How Storage Works
15
+
16
+ ### Storage Keys
17
+
18
+ Every storage location is identified by a unique key generated from:
19
+
20
+ ```
21
+ StorageKey = SHA256(pointer || subPointer)
22
+ ```
23
+
24
+ Where:
25
+ - `pointer` is a `u16` (0-65535) identifying the storage slot type
26
+ - `subPointer` is a `u256` for sub-indexing (e.g., addresses in a mapping)
27
+
28
+ ```typescript
29
+ // Example: Balance storage for address 0xABC...
30
+ pointer = 3 // balances mapping pointer
31
+ subPointer = 0xABC... // the address
32
+ storageKey = SHA256(3 || 0xABC...)
33
+ ```
34
+
35
+ ### Storage Key Derivation Flow
36
+
37
+ ```mermaid
38
+ ---
39
+ config:
40
+ theme: dark
41
+ ---
42
+ flowchart LR
43
+ subgraph Input["Developer Input"]
44
+ DEV["Contract Code"]
45
+ DEV -->|"Declares"| VAR1["totalSupplyPointer: u16"]
46
+ DEV -->|"Declares"| VAR2["balancesPointer: u16"]
47
+ end
48
+
49
+ subgraph Allocation["Runtime Allocation"]
50
+ BC["Blockchain.nextPointer"]
51
+ VAR1 -->|"Calls"| BC
52
+ VAR2 -->|"Calls"| BC
53
+ BC -->|"Returns 0"| P0["Pointer 0"]
54
+ BC -->|"Returns 1"| P1["Pointer 1"]
55
+ end
56
+
57
+ subgraph SimpleKey["Simple Value Key"]
58
+ P0 --> EMPTY["EMPTY_POINTER<br/>(u256.Zero)"]
59
+ EMPTY --> HASH1["SHA256(0 || 0x00...00)"]
60
+ HASH1 --> KEY1[("Storage Key<br/>for totalSupply")]
61
+ end
62
+
63
+ subgraph MappingKey["Mapping Key (balances)"]
64
+ P1 --> ADDR["User Address<br/>0xABC..."]
65
+ ADDR --> HASH2["SHA256(1 || 0xABC...)"]
66
+ HASH2 --> KEY2[("Storage Key<br/>for balances[0xABC]")]
67
+ end
68
+ ```
69
+
70
+ ### Storage Layout
71
+
72
+ ```mermaid
73
+ ---
74
+ config:
75
+ theme: dark
76
+ ---
77
+ flowchart LR
78
+ CS[Contract Storage] --> P0["Pointer 0: totalSupply"]
79
+ CS --> P1["Pointer 1: name"]
80
+ CS --> P2["Pointer 2: symbol"]
81
+ CS --> P3["Pointer 3: balances mapping"]
82
+ CS --> P4["Pointer 4: allowances mapping"]
83
+
84
+ P3 --> S1["subPointer 0xAAA -> balance"]
85
+ P3 --> S2["subPointer 0xBBB -> balance"]
86
+ P3 --> S3["..."]
87
+
88
+ P4 --> N1["owner+spender hash -> allowance"]
89
+ ```
90
+
91
+ ## Solidity vs OPNet Storage Model
92
+
93
+ In Solidity, storage slots are assigned implicitly by the compiler. In OPNet, you explicitly allocate pointers at runtime.
94
+
95
+ ### Quick Reference Table
96
+
97
+ | Feature | Solidity | OPNet |
98
+ |---------|----------|-------|
99
+ | Storage slot assignment | Implicit (compiler) | Explicit (`Blockchain.nextPointer`) |
100
+ | Hash function | keccak256 | SHA256 |
101
+ | Mapping type | `mapping(K => V)` | `StoredMapU256`, `AddressMemoryMap`, `MapOfMap<T>` |
102
+ | Array type | `T[]` | `StoredU256Array`, `StoredAddressArray`, etc. |
103
+ | Simple value | `uint256 public x;` | `StoredU256` |
104
+ | String storage | `string public s;` | `StoredString` |
105
+ | Boolean storage | `bool public b;` | `StoredBoolean` |
106
+ | Address storage | `address public a;` | `StoredAddress` |
107
+ | Nested mapping | `mapping(a => mapping(b => c))` | `MapOfMap<T>` |
108
+ | Default uint value | `0` | `u256.Zero` |
109
+ | Maximum slots/pointers | ~2^256 | 65,535 (`u16`) |
110
+
111
+ ### Type Mapping Reference
112
+
113
+ | Solidity Type | OPNet Equivalent | Notes |
114
+ |---------------|------------------|-------|
115
+ | `uint256` | `StoredU256` | 32 bytes |
116
+ | `uint64` (packed) | `StoredU64` | Stores up to 4 u64 values in one slot |
117
+ | `uint32` (packed) | `StoredU32` | Stores up to 8 u32 values in one slot |
118
+ | `bool` | `StoredBoolean` | 1 byte |
119
+ | `string` | `StoredString` | Variable length |
120
+ | `address` | `StoredAddress` | 32 bytes |
121
+ | `uint256[]` | `StoredU256Array` | Dynamic array |
122
+ | `address[]` | `StoredAddressArray` | Dynamic array |
123
+ | `mapping(address => uint256)` | `AddressMemoryMap` | Address-keyed |
124
+ | `mapping(uint256 => uint256)` | `StoredMapU256` | u256-keyed |
125
+ | `mapping(address => mapping(address => uint256))` | `MapOfMap<u256>` | Two-level nesting |
126
+
127
+ ### Side-by-Side Code Comparison
128
+
129
+ ```solidity
130
+ // Solidity - Implicit slot assignment
131
+ contract Token {
132
+ uint256 public totalSupply; // slot 0 (assigned by compiler)
133
+ string public name; // slot 1 (assigned by compiler)
134
+ mapping(address => uint256) balances; // slot 2 (assigned by compiler)
135
+ }
136
+ ```
137
+
138
+ ```typescript
139
+ // OPNet - Explicit pointer allocation
140
+ export class Token extends OP_NET {
141
+ private readonly totalSupplyPointer: u16 = Blockchain.nextPointer; // ~0 (allocated at runtime)
142
+ private readonly namePointer: u16 = Blockchain.nextPointer; // ~1 (allocated at runtime)
143
+ private readonly balancesPointer: u16 = Blockchain.nextPointer; // ~2 (allocated at runtime)
144
+ }
145
+ ```
146
+
147
+ ```mermaid
148
+ ---
149
+ config:
150
+ theme: dark
151
+ ---
152
+ flowchart LR
153
+ subgraph SolidityFlow["Solidity (Ethereum)"]
154
+ S_USER[("👤 User")] -->|"Sends ETH + calldata"| S_TX["Ethereum Transaction"]
155
+ S_TX -->|"EVM executes"| S_CONTRACT["Smart Contract"]
156
+
157
+ subgraph S_Storage["Storage (Implicit)"]
158
+ S_COMPILER["Compiler assigns slots<br/>at compile time"]
159
+ S_SLOT0["Slot 0: totalSupply"]
160
+ S_SLOT1["Slot 1: balances"]
161
+ S_SLOT2["Slot 2: allowances"]
162
+ S_COMPILER -.->|"Hidden from dev"| S_SLOT0
163
+ S_COMPILER -.->|"Hidden from dev"| S_SLOT1
164
+ S_COMPILER -.->|"Hidden from dev"| S_SLOT2
165
+ end
166
+
167
+ S_CONTRACT -->|"keccak256(slot.key)"| S_Storage
168
+ S_CONTRACT -->|"CAN hold ETH"| S_CUSTODY["Contract Custody<br/>address(this).balance"]
169
+ end
170
+
171
+ subgraph OPNetFlow["OPNet (Bitcoin L1)"]
172
+ O_USER[("👤 User")] -->|"Signs Bitcoin TX"| O_TX["Bitcoin Transaction"]
173
+ O_TX -->|"WASM executes"| O_CONTRACT["Smart Contract"]
174
+
175
+ subgraph O_Storage["Storage (Explicit)"]
176
+ O_RUNTIME["Runtime allocates ptrs<br/>at execution time"]
177
+ O_PTR0["Pointer 0: totalSupplyPointer"]
178
+ O_PTR1["Pointer 1: balancesPointer"]
179
+ O_PTR2["Pointer 2: allowancesPointer"]
180
+ O_RUNTIME -->|"Dev controls"| O_PTR0
181
+ O_RUNTIME -->|"Dev controls"| O_PTR1
182
+ O_RUNTIME -->|"Dev controls"| O_PTR2
183
+ end
184
+
185
+ O_CONTRACT -->|"SHA256(ptr || subPtr)"| O_Storage
186
+ O_CONTRACT -->|"CANNOT hold BTC"| O_VERIFY["Verify-Only Pattern<br/>blockchain.tx.outputs"]
187
+ O_VERIFY -->|"Validates"| O_TX
188
+ end
189
+
190
+ subgraph KeyDiff["Critical Differences"]
191
+ DIFF1["Custody: Solidity holds funds,<br/>OPNet verifies outputs"]
192
+ DIFF2["Storage: Solidity implicit slots,<br/>OPNet explicit pointers"]
193
+ DIFF3["Hash: Solidity keccak256,<br/>OPNet SHA256"]
194
+ DIFF4["Execution: Solidity EVM,<br/>OPNet WASM"]
195
+ end
196
+ ```
197
+
198
+ ## CRITICAL: Map Implementation Warning
199
+
200
+ > **DO NOT USE AssemblyScript's Built-in Map**
201
+ >
202
+ > 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.
203
+ >
204
+ > **Why the AssemblyScript Map is broken for blockchain:**
205
+ > - NOT optimized for blockchain storage patterns
206
+ > - Does NOT handle Uint8Array buffers as keys correctly
207
+ > - Does NOT work properly with Address key comparisons
208
+ > - Will cause silent data corruption or key collisions
209
+ >
210
+ > **CORRECT:**
211
+ > ```typescript
212
+ > import { Map } from '@btc-vision/btc-runtime/runtime';
213
+ >
214
+ > export class MyCustomMap<V> extends Map<Address, V> {
215
+ > // Your implementation
216
+ > }
217
+ > ```
218
+ >
219
+ > **WRONG:**
220
+ > ```typescript
221
+ > // DO NOT DO THIS - will break!
222
+ > const map = new Map<Uint8Array, u256>(); // AssemblyScript Map
223
+ > ```
224
+ >
225
+ > The btc-runtime Map is specifically designed to:
226
+ > - Handle Address and Uint8Array key comparisons correctly
227
+ > - Optimize for blockchain storage access patterns
228
+ > - Support proper serialization for persistent storage
229
+ > - Prevent key collisions with custom equality logic
230
+
231
+ ## Pointer Allocation
232
+
233
+ ### Allocating Pointers
234
+
235
+ Use `Blockchain.nextPointer` to allocate unique pointers:
236
+
237
+ ```typescript
238
+ import { Blockchain } from '@btc-vision/btc-runtime/runtime';
239
+
240
+ @final
241
+ export class MyContract extends OP_NET {
242
+ // Each call to nextPointer returns a unique u16
243
+ private readonly totalSupplyPointer: u16 = Blockchain.nextPointer;
244
+ private readonly namePointer: u16 = Blockchain.nextPointer;
245
+ private readonly balancesPointer: u16 = Blockchain.nextPointer;
246
+ private readonly allowancesPointer: u16 = Blockchain.nextPointer;
247
+
248
+ // ...
249
+ }
250
+ ```
251
+
252
+ ```mermaid
253
+ ---
254
+ config:
255
+ theme: dark
256
+ ---
257
+ sequenceDiagram
258
+ participant User
259
+ participant Bitcoin as Bitcoin L1
260
+ participant OPNet as OPNet Node
261
+ participant WASM as WASM Runtime
262
+ participant Contract
263
+ participant Blockchain as Blockchain API
264
+ participant Storage as Storage System
265
+
266
+ User->>Bitcoin: Broadcast Transaction
267
+ Bitcoin->>OPNet: New Block with TX
268
+ OPNet->>WASM: Execute Contract
269
+ WASM->>Contract: Instantiate
270
+ Contract->>Blockchain: nextPointer
271
+ Blockchain-->>Contract: 0 (totalSupply)
272
+ Contract->>Blockchain: nextPointer
273
+ Blockchain-->>Contract: 1 (name)
274
+ Contract->>Blockchain: nextPointer
275
+ Blockchain-->>Contract: 2 (balances)
276
+ Contract->>Blockchain: nextPointer
277
+ Blockchain-->>Contract: 3 (allowances)
278
+ Contract->>Storage: Read/Write with pointers
279
+ Storage-->>Contract: Data
280
+ Contract-->>WASM: Execution Result
281
+ WASM-->>OPNet: State Changes
282
+ OPNet->>OPNet: Update State Root
283
+ ```
284
+
285
+ ## System Architecture
286
+
287
+ The following diagram shows how storage fits into the overall OPNet architecture:
288
+
289
+ ```mermaid
290
+ ---
291
+ config:
292
+ theme: dark
293
+ ---
294
+ flowchart TD
295
+ subgraph UserLayer["User Layer"]
296
+ USER[("👤 User")]
297
+ end
298
+
299
+ subgraph BitcoinL1["Bitcoin L1"]
300
+ BTC_TX["Bitcoin Transaction"]
301
+ BTC_BLOCK["Bitcoin Block"]
302
+ UTXO["UTXOs"]
303
+ end
304
+
305
+ subgraph OPNetConsensus["OPNet Consensus Layer"]
306
+ INDEXER["OPNet Nodes"]
307
+ WASM["WASM Runtime"]
308
+ EPOCH["Epoch Mining<br/>SHA1 PoW"]
309
+ CHECKPOINT["State Checksum<br/>Root Hash"]
310
+ end
311
+
312
+ subgraph Contract["Smart Contract"]
313
+ ENTRY["Contract Entry Point"]
314
+ LOGIC["Business Logic"]
315
+ VERIFY["Output Verification<br/>blockchain.tx.outputs"]
316
+ PTR_ALLOC["Pointer Allocation<br/>Blockchain.nextPointer"]
317
+ end
318
+
319
+ subgraph StorageSystem["Storage System"]
320
+ PTR["Pointer (u16)<br/>0-65535 slots"]
321
+ SUBPTR["SubPointer (u256)<br/>mapping keys"]
322
+ HASH["SHA256(ptr || subPtr)"]
323
+ STORAGE[("Persistent State<br/>Key-Value Store")]
324
+
325
+ PTR --> HASH
326
+ SUBPTR --> HASH
327
+ HASH --> STORAGE
328
+ end
329
+
330
+ USER -->|"Signs & Broadcasts"| BTC_TX
331
+ BTC_TX -->|"Included in"| BTC_BLOCK
332
+ BTC_BLOCK -->|"Parsed by"| INDEXER
333
+ INDEXER -->|"Executes in"| WASM
334
+ WASM -->|"Runs"| ENTRY
335
+ ENTRY --> LOGIC
336
+ LOGIC -->|"Non-custodial verify"| VERIFY
337
+ LOGIC -->|"Allocates"| PTR_ALLOC
338
+ PTR_ALLOC -->|"Returns u16"| PTR
339
+ LOGIC -->|"Read/Write"| STORAGE
340
+
341
+ INDEXER -->|"Every 20 blocks"| EPOCH
342
+ EPOCH -->|"Produces"| CHECKPOINT
343
+ CHECKPOINT -->|"Anchors to"| BTC_BLOCK
344
+
345
+ VERIFY -->|"Validates"| UTXO
346
+ BTC_TX -->|"Creates/Spends"| UTXO
347
+ ```
348
+
349
+ ## Storage Types
350
+
351
+ OPNet provides typed storage classes for common data types:
352
+
353
+ ### Primitive Storage
354
+
355
+ ```typescript
356
+ import {
357
+ StoredU256,
358
+ StoredU64,
359
+ StoredU32,
360
+ StoredBoolean,
361
+ StoredString,
362
+ StoredAddress,
363
+ EMPTY_POINTER,
364
+ } from '@btc-vision/btc-runtime/runtime';
365
+
366
+ // Usage
367
+ private readonly totalSupplyPointer: u16 = Blockchain.nextPointer;
368
+ private readonly _totalSupply: StoredU256 = new StoredU256(
369
+ this.totalSupplyPointer,
370
+ EMPTY_POINTER
371
+ );
372
+
373
+ // Read value
374
+ const supply = this._totalSupply.value;
375
+
376
+ // Write value
377
+ this._totalSupply.value = newSupply;
378
+ ```
379
+
380
+ ### Array Storage
381
+
382
+ ```typescript
383
+ import {
384
+ ABIDataTypes,
385
+ BytesWriter,
386
+ Calldata,
387
+ StoredU256Array,
388
+ StoredU128Array,
389
+ StoredAddressArray,
390
+ StoredBooleanArray,
391
+ } from '@btc-vision/btc-runtime/runtime';
392
+
393
+ // Usage
394
+ private readonly holdersPointer: u16 = Blockchain.nextPointer;
395
+ private readonly holders: StoredAddressArray = new StoredAddressArray(this.holdersPointer, EMPTY_POINTER);
396
+
397
+ // Operations
398
+ @method({ name: 'holder', type: ABIDataTypes.ADDRESS })
399
+ public addHolder(calldata: Calldata): BytesWriter {
400
+ const newHolder = calldata.readAddress();
401
+ this.holders.push(newHolder);
402
+ this.holders.save(); // Commit changes
403
+ return new BytesWriter(0);
404
+ }
405
+
406
+ const holder = this.holders.get(index);
407
+ const length = this.holders.getLength();
408
+ this.holders.deleteLast();
409
+ this.holders.save();
410
+ ```
411
+
412
+ ### Map Storage
413
+
414
+ ```typescript
415
+ import { u256 } from '@btc-vision/as-bignum/assembly';
416
+ import {
417
+ Address,
418
+ StoredMapU256,
419
+ AddressMemoryMap,
420
+ } from '@btc-vision/btc-runtime/runtime';
421
+
422
+ // Simple mapping
423
+ private readonly balancesPointer: u16 = Blockchain.nextPointer;
424
+ private readonly balances: StoredMapU256 = new StoredMapU256(this.balancesPointer);
425
+
426
+ // Address-keyed mapping (default value is u256.Zero)
427
+ private readonly balanceMap: AddressMemoryMap;
428
+
429
+ public constructor() {
430
+ super();
431
+ this.balanceMap = new AddressMemoryMap(this.balancesPointer);
432
+ }
433
+ ```
434
+
435
+ ## Storage Patterns
436
+
437
+ ### Common Patterns Comparison
438
+
439
+ | Pattern | Solidity | OPNet |
440
+ |---------|----------|-------|
441
+ | Increment counter | `counter++;` | `counter.value = SafeMath.add(counter.value, u256.One);` |
442
+ | Read balance | `balances[addr]` | `balanceOf.get(addr)` |
443
+ | Write balance | `balances[addr] = x` | `balanceOf.set(addr, x)` |
444
+ | Check approval | `allowances[owner][spender]` | `allowances.get(owner).get(spender)` |
445
+ | Set approval | `allowances[owner][spender] = x` | `ownerMap = allowances.get(owner); ownerMap.set(spender, x); allowances.set(owner, ownerMap);` |
446
+ | Array push | `arr.push(x)` | `arr.push(x); arr.save()` |
447
+ | Array length | `arr.length` | `arr.getLength()` |
448
+ | Array access | `arr[i]` | `arr.get(i)` |
449
+ | Require/revert | `require(cond, "msg")` | `if (!cond) throw new Revert("msg")` |
450
+ | Get sender | `msg.sender` | `Blockchain.tx.sender` |
451
+ | Get origin | `tx.origin` | `Blockchain.tx.origin` |
452
+ | Block number | `block.number` | `Blockchain.block.number` |
453
+ | Block timestamp | `block.timestamp` | `Blockchain.block.medianTime` |
454
+
455
+ ### Simple Value
456
+
457
+ ```typescript
458
+ import { u256 } from '@btc-vision/as-bignum/assembly';
459
+ import {
460
+ Blockchain,
461
+ SafeMath,
462
+ StoredU256,
463
+ } from '@btc-vision/btc-runtime/runtime';
464
+
465
+ // Solidity: uint256 public counter;
466
+ private readonly counterPointer: u16 = Blockchain.nextPointer;
467
+ private readonly counter: StoredU256 = new StoredU256(this.counterPointer, EMPTY_POINTER);
468
+
469
+ // Increment
470
+ this.counter.value = SafeMath.add(this.counter.value, u256.One);
471
+ ```
472
+
473
+ ### Mapping (address => uint256)
474
+
475
+ ```typescript
476
+ import { u256 } from '@btc-vision/as-bignum/assembly';
477
+ import {
478
+ ABIDataTypes,
479
+ Address,
480
+ AddressMemoryMap,
481
+ SafeMath,
482
+ } from '@btc-vision/btc-runtime/runtime';
483
+
484
+ // Solidity: mapping(address => uint256) public balances;
485
+ private readonly balancesPointer: u16 = Blockchain.nextPointer;
486
+ private readonly balanceOf: AddressMemoryMap;
487
+
488
+ public constructor() {
489
+ super();
490
+ this.balanceOf = new AddressMemoryMap(this.balancesPointer);
491
+ }
492
+
493
+ // Get balance
494
+ @method({ name: 'address', type: ABIDataTypes.ADDRESS })
495
+ @returns({ name: 'balance', type: ABIDataTypes.UINT256 })
496
+ public getBalance(address: Address): u256 {
497
+ return this.balanceOf.get(address);
498
+ }
499
+
500
+ // Set balance (using SafeMath for operations)
501
+ @method(
502
+ { name: 'address', type: ABIDataTypes.ADDRESS },
503
+ { name: 'amount', type: ABIDataTypes.UINT256 }
504
+ )
505
+ public setBalance(address: Address, amount: u256): void {
506
+ this.balanceOf.set(address, amount);
507
+ }
508
+ ```
509
+
510
+ ### Nested Mapping (address => address => uint256)
511
+
512
+ For nested mappings like allowances, use `MapOfMap<T>` which provides a two-step get/set pattern:
513
+
514
+ ```typescript
515
+ import { u256 } from '@btc-vision/as-bignum/assembly';
516
+ import {
517
+ ABIDataTypes,
518
+ Address,
519
+ MapOfMap,
520
+ Nested,
521
+ } from '@btc-vision/btc-runtime/runtime';
522
+
523
+ // Solidity: mapping(address => mapping(address => uint256)) public allowances;
524
+ private readonly allowancesPointer: u16 = Blockchain.nextPointer;
525
+ private readonly allowances: MapOfMap<u256>;
526
+
527
+ public constructor() {
528
+ super();
529
+ this.allowances = new MapOfMap<u256>(this.allowancesPointer);
530
+ }
531
+
532
+ // Getting nested value - two-step process
533
+ @method(
534
+ { name: 'owner', type: ABIDataTypes.ADDRESS },
535
+ { name: 'spender', type: ABIDataTypes.ADDRESS }
536
+ )
537
+ @returns({ name: 'allowance', type: ABIDataTypes.UINT256 })
538
+ public getAllowance(owner: Address, spender: Address): u256 {
539
+ const ownerMap = this.allowances.get(owner); // Returns Nested<u256>
540
+ return ownerMap.get(spender); // Returns u256
541
+ }
542
+
543
+ // Setting nested value - get, modify, commit back
544
+ protected setAllowance(owner: Address, spender: Address, amount: u256): void {
545
+ const ownerMap = this.allowances.get(owner);
546
+ ownerMap.set(spender, amount);
547
+ this.allowances.set(owner, ownerMap); // Commit back
548
+ }
549
+ ```
550
+
551
+ ### Struct-like Storage
552
+
553
+ ```typescript
554
+ // Solidity:
555
+ // struct User { address addr; uint256 balance; bool active; }
556
+ // mapping(uint256 => User) public users;
557
+
558
+ // OPNet: Use multiple pointers or encode into u256
559
+ private readonly userAddressPointer: u16 = Blockchain.nextPointer;
560
+ private readonly userBalancePointer: u16 = Blockchain.nextPointer;
561
+ private readonly userActivePointer: u16 = Blockchain.nextPointer;
562
+
563
+ private readonly userAddresses: StoredMapU256 = new StoredMapU256(this.userAddressPointer);
564
+ private readonly userBalances: StoredMapU256 = new StoredMapU256(this.userBalancePointer);
565
+ private readonly userActives: StoredMapU256 = new StoredMapU256(this.userActivePointer);
566
+ ```
567
+
568
+ ### Complete ERC-20 Style Comparison
569
+
570
+ Here's a side-by-side comparison of a complete token contract:
571
+
572
+ **Solidity:**
573
+ ```solidity
574
+ // SPDX-License-Identifier: MIT
575
+ pragma solidity ^0.8.0;
576
+
577
+ contract SimpleToken {
578
+ string public name;
579
+ string public symbol;
580
+ uint8 public decimals = 18;
581
+ uint256 public totalSupply;
582
+
583
+ mapping(address => uint256) public balanceOf;
584
+ mapping(address => mapping(address => uint256)) public allowance;
585
+
586
+ event Transfer(address indexed from, address indexed to, uint256 value);
587
+ event Approval(address indexed owner, address indexed spender, uint256 value);
588
+
589
+ constructor(string memory _name, string memory _symbol, uint256 _initialSupply) {
590
+ name = _name;
591
+ symbol = _symbol;
592
+ totalSupply = _initialSupply;
593
+ balanceOf[msg.sender] = _initialSupply;
594
+ }
595
+
596
+ function transfer(address to, uint256 amount) external returns (bool) {
597
+ require(balanceOf[msg.sender] >= amount, "Insufficient balance");
598
+ balanceOf[msg.sender] -= amount;
599
+ balanceOf[to] += amount;
600
+ emit Transfer(msg.sender, to, amount);
601
+ return true;
602
+ }
603
+
604
+ function approve(address spender, uint256 amount) external returns (bool) {
605
+ allowance[msg.sender][spender] = amount;
606
+ emit Approval(msg.sender, spender, amount);
607
+ return true;
608
+ }
609
+
610
+ function transferFrom(address from, address to, uint256 amount) external returns (bool) {
611
+ require(allowance[from][msg.sender] >= amount, "Insufficient allowance");
612
+ require(balanceOf[from] >= amount, "Insufficient balance");
613
+ allowance[from][msg.sender] -= amount;
614
+ balanceOf[from] -= amount;
615
+ balanceOf[to] += amount;
616
+ emit Transfer(from, to, amount);
617
+ return true;
618
+ }
619
+ }
620
+ ```
621
+
622
+ **OPNet:**
623
+ ```typescript
624
+ import { u256 } from '@btc-vision/as-bignum/assembly';
625
+ import {
626
+ Address,
627
+ AddressMemoryMap,
628
+ Blockchain,
629
+ BytesWriter,
630
+ Calldata,
631
+ MapOfMap,
632
+ OP_NET,
633
+ Revert,
634
+ SafeMath,
635
+ StoredString,
636
+ StoredU256,
637
+ EMPTY_POINTER,
638
+ } from '@btc-vision/btc-runtime/runtime';
639
+
640
+ @final
641
+ export class SimpleToken extends OP_NET {
642
+ // Pointer allocation (equivalent to slot assignment)
643
+ private readonly namePointer: u16 = Blockchain.nextPointer;
644
+ private readonly symbolPointer: u16 = Blockchain.nextPointer;
645
+ private readonly totalSupplyPointer: u16 = Blockchain.nextPointer;
646
+ private readonly balancesPointer: u16 = Blockchain.nextPointer;
647
+ private readonly allowancesPointer: u16 = Blockchain.nextPointer;
648
+
649
+ // Storage variables
650
+ private readonly _name: StoredString = new StoredString(this.namePointer, 0);
651
+ private readonly _symbol: StoredString = new StoredString(this.symbolPointer, 0);
652
+ private readonly _decimals: u8 = 18; // Constant value, no storage needed
653
+ private readonly _totalSupply: StoredU256 = new StoredU256(this.totalSupplyPointer, EMPTY_POINTER);
654
+ private readonly _balanceOf: AddressMemoryMap;
655
+ private readonly _allowance: MapOfMap<u256>;
656
+
657
+ public constructor() {
658
+ super();
659
+ this._balanceOf = new AddressMemoryMap(this.balancesPointer);
660
+ this._allowance = new MapOfMap<u256>(this.allowancesPointer);
661
+ }
662
+
663
+ // Equivalent to Solidity constructor
664
+ public override onDeployment(calldata: Calldata): void {
665
+ this._name.value = calldata.readString();
666
+ this._symbol.value = calldata.readString();
667
+ const initialSupply = calldata.readU256();
668
+ this._totalSupply.value = initialSupply;
669
+ this._balanceOf.set(Blockchain.tx.origin, initialSupply);
670
+ }
671
+
672
+ // function transfer(address to, uint256 amount) external returns (bool)
673
+ public transfer(calldata: Calldata): BytesWriter {
674
+ const to = calldata.readAddress();
675
+ const amount = calldata.readU256();
676
+ const sender = Blockchain.tx.sender;
677
+
678
+ const senderBalance = this._balanceOf.get(sender);
679
+ if (senderBalance < amount) {
680
+ throw new Revert('Insufficient balance');
681
+ }
682
+
683
+ this._balanceOf.set(sender, SafeMath.sub(senderBalance, amount));
684
+ this._balanceOf.set(to, SafeMath.add(this._balanceOf.get(to), amount));
685
+
686
+ // Emit Transfer event (implementation depends on event system)
687
+
688
+ const writer = new BytesWriter(1);
689
+ writer.writeBoolean(true);
690
+ return writer;
691
+ }
692
+
693
+ // function approve(address spender, uint256 amount) external returns (bool)
694
+ public approve(calldata: Calldata): BytesWriter {
695
+ const spender = calldata.readAddress();
696
+ const amount = calldata.readU256();
697
+ const sender = Blockchain.tx.sender;
698
+
699
+ // MapOfMap pattern: get nested, modify, commit back
700
+ const senderAllowances = this._allowance.get(sender);
701
+ senderAllowances.set(spender, amount);
702
+ this._allowance.set(sender, senderAllowances);
703
+
704
+ const writer = new BytesWriter(1);
705
+ writer.writeBoolean(true);
706
+ return writer;
707
+ }
708
+
709
+ // function transferFrom(address from, address to, uint256 amount) external returns (bool)
710
+ public transferFrom(calldata: Calldata): BytesWriter {
711
+ const from = calldata.readAddress();
712
+ const to = calldata.readAddress();
713
+ const amount = calldata.readU256();
714
+ const sender = Blockchain.tx.sender;
715
+
716
+ // Check allowance
717
+ const fromAllowances = this._allowance.get(from);
718
+ const currentAllowance = fromAllowances.get(sender);
719
+ if (currentAllowance < amount) {
720
+ throw new Revert('Insufficient allowance');
721
+ }
722
+
723
+ // Check balance
724
+ const fromBalance = this._balanceOf.get(from);
725
+ if (fromBalance < amount) {
726
+ throw new Revert('Insufficient balance');
727
+ }
728
+
729
+ // Update allowance
730
+ fromAllowances.set(sender, SafeMath.sub(currentAllowance, amount));
731
+ this._allowance.set(from, fromAllowances);
732
+
733
+ // Update balances
734
+ this._balanceOf.set(from, SafeMath.sub(fromBalance, amount));
735
+ this._balanceOf.set(to, SafeMath.add(this._balanceOf.get(to), amount));
736
+
737
+ const writer = new BytesWriter(1);
738
+ writer.writeBoolean(true);
739
+ return writer;
740
+ }
741
+ }
742
+ ```
743
+
744
+ ## Reading and Writing
745
+
746
+ ```mermaid
747
+ ---
748
+ config:
749
+ theme: dark
750
+ ---
751
+ sequenceDiagram
752
+ participant TX as Bitcoin Transaction
753
+ participant Contract as Contract
754
+ participant Logic as Business Logic
755
+ participant Storage as Storage System
756
+ participant Buffer as Memory Buffer
757
+ participant State as Persistent State
758
+
759
+ Note over TX,State: Read Operation
760
+ TX->>Contract: TX Input (User Signs)
761
+ Contract->>Logic: Method Call
762
+ Logic->>Storage: Get pointer + subPointer
763
+ Storage->>Storage: Compute SHA256 key
764
+ Storage->>Storage: Blockchain.getStorageAt
765
+ Storage-->>Logic: Decode to typed value
766
+
767
+ Note over TX,State: Write Operation
768
+ Logic->>Storage: Get pointer + subPointer
769
+ Storage->>Storage: Compute SHA256 key
770
+ Storage->>Storage: Encode typed value
771
+ Storage->>Buffer: Buffer in memory
772
+
773
+ Note over TX,State: Verification & Commit
774
+ Logic->>TX: Verify TX Outputs (UTXOs)
775
+ TX-->>Contract: TX Complete
776
+ Buffer->>State: Commit on TX Success
777
+ State->>State: Update State Checksum
778
+ Note over State: Every 20 blocks: Epoch Root
779
+ ```
780
+
781
+ ### Read Operations
782
+
783
+ ```typescript
784
+ // Read primitive
785
+ const value = this._totalSupply.value;
786
+
787
+ // Read from map
788
+ const balance = this.balanceOf.get(address);
789
+
790
+ // Read from array
791
+ const holder = this.holders.get(index);
792
+ const length = this.holders.getLength();
793
+ ```
794
+
795
+ ### Write Operations
796
+
797
+ ```typescript
798
+ // Write primitive
799
+ this._totalSupply.value = newValue;
800
+
801
+ // Write to map
802
+ this.balanceOf.set(address, newBalance);
803
+
804
+ // Write to array
805
+ this.holders.push(newAddress);
806
+ this.holders.set(index, address);
807
+ this.holders.save(); // Commit changes
808
+ ```
809
+
810
+ ### Commit Optimization
811
+
812
+ For complex operations, delay commits until necessary:
813
+
814
+ ```typescript
815
+ import { SafeMath } from '@btc-vision/btc-runtime/runtime';
816
+
817
+ // Multiple operations without intermediate commits
818
+ const currentBalance = this.balanceOf.get(from);
819
+ const newBalance = SafeMath.sub(currentBalance, amount);
820
+ this.balanceOf.set(from, newBalance); // Value is buffered
821
+
822
+ // Changes are committed when transaction completes
823
+ ```
824
+
825
+ ## Default Values
826
+
827
+ Always provide sensible defaults:
828
+
829
+ ```typescript
830
+ // u256 with EMPTY_POINTER
831
+ private balance: StoredU256 = new StoredU256(pointer, EMPTY_POINTER);
832
+
833
+ // String with index 0
834
+ private name: StoredString = new StoredString(pointer, 0);
835
+
836
+ // Boolean with false default
837
+ private paused: StoredBoolean = new StoredBoolean(pointer, false);
838
+ ```
839
+
840
+ ## Storage Limits
841
+
842
+ | Limit | Value | Notes |
843
+ |-------|-------|-------|
844
+ | Pointers per contract | 65,535 | `u16` range |
845
+ | Array length | ~4 billion | `u32` range (default maxLength configurable) |
846
+ | String length | 65,535 bytes | Encoded in storage |
847
+ | Sub-pointers | `u256` range | Effectively unlimited |
848
+
849
+ ## Best Practices
850
+
851
+ ### 1. Allocate Pointers in Order
852
+
853
+ ```typescript
854
+ // Good: Sequential allocation
855
+ private ptr1: u16 = Blockchain.nextPointer;
856
+ private ptr2: u16 = Blockchain.nextPointer;
857
+ private ptr3: u16 = Blockchain.nextPointer;
858
+
859
+ // Bad: Gaps or manual assignment
860
+ private ptr1: u16 = 0;
861
+ private ptr2: u16 = 5; // Gap!
862
+ ```
863
+
864
+ ### 2. Use Typed Storage
865
+
866
+ ```typescript
867
+ // Good: Type-safe storage
868
+ private balance: StoredU256 = new StoredU256(ptr, EMPTY_POINTER);
869
+
870
+ // Avoid: Raw storage access (only for special cases)
871
+ import { encodePointer } from '@btc-vision/btc-runtime/runtime';
872
+ const pointerHash = encodePointer(ptr, subPtr);
873
+ Blockchain.setStorageAt(pointerHash, value);
874
+ ```
875
+
876
+ ### 3. Initialize in Constructor or onDeployment
877
+
878
+ ```typescript
879
+ import { u256 } from '@btc-vision/as-bignum/assembly';
880
+ import {
881
+ Address,
882
+ AddressMemoryMap,
883
+ Blockchain,
884
+ Calldata,
885
+ OP_NET,
886
+ } from '@btc-vision/btc-runtime/runtime';
887
+
888
+ export class MyContract extends OP_NET {
889
+ private readonly balancesPointer: u16 = Blockchain.nextPointer;
890
+ private readonly balanceOf: AddressMemoryMap;
891
+
892
+ public constructor() {
893
+ super();
894
+ // Initialize storage maps in constructor
895
+ this.balanceOf = new AddressMemoryMap(this.balancesPointer);
896
+ }
897
+
898
+ public override onDeployment(calldata: Calldata): void {
899
+ // Set initial values here
900
+ this._totalSupply.value = initialSupply;
901
+ }
902
+ }
903
+ ```
904
+
905
+ ### 4. Optimize Storage Access
906
+
907
+ ```mermaid
908
+ ---
909
+ config:
910
+ theme: dark
911
+ ---
912
+ flowchart TD
913
+ subgraph Bad["Expensive: Multiple Storage Reads"]
914
+ LOOP1["for (i = 0; i < 100; i++)"]
915
+ LOOP1 --> READ1["Storage Read #1"]
916
+ LOOP1 --> READ2["Storage Read #2"]
917
+ LOOP1 --> READ3["Storage Read #..."]
918
+ LOOP1 --> READ100["Storage Read #100"]
919
+ READ1 --> COST1["100x Storage I/O"]
920
+ READ2 --> COST1
921
+ READ3 --> COST1
922
+ READ100 --> COST1
923
+ end
924
+
925
+ subgraph Good["Optimized: Cache and Batch"]
926
+ CACHE["Single Storage Read"]
927
+ CACHE --> MEM["Cache in Memory"]
928
+ MEM --> LOOP2["for (i = 0; i < 100; i++)<br/>Use cached value"]
929
+ LOOP2 --> WRITE["Single Storage Write"]
930
+ WRITE --> COST2["1x Read + 1x Write"]
931
+ end
932
+
933
+ COST1 -->|"vs"| COST2
934
+ ```
935
+
936
+ ```typescript
937
+ import { SafeMath } from '@btc-vision/btc-runtime/runtime';
938
+
939
+ // Inefficient: Reading same value multiple times
940
+ for (let i = 0; i < 100; i++) {
941
+ const balance = this.balanceOf.get(address); // Storage read each time
942
+ // ...
943
+ }
944
+
945
+ // Better: Cache the value
946
+ const balance = this.balanceOf.get(address); // One storage read
947
+ for (let i = 0; i < 100; i++) {
948
+ // Use cached balance
949
+ // When modifying, use SafeMath
950
+ const updated = SafeMath.add(balance, u256.One);
951
+ }
952
+ ```
953
+
954
+ ## Transient Storage
955
+
956
+ For temporary data that doesn't persist between transactions:
957
+
958
+ ```typescript
959
+ // Transient storage is cleared after each transaction
960
+ // Useful for reentrancy guards, temporary calculations, etc.
961
+ ```
962
+
963
+ See [Advanced Storage](../storage/stored-primitives.md) for transient storage details.
964
+
965
+ ---
966
+
967
+ **Navigation:**
968
+ - Previous: [Blockchain Environment](./blockchain-environment.md)
969
+ - Next: [Pointers](./pointers.md)