@btc-vision/btc-runtime 1.10.8 → 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 +52 -27
  43. package/runtime/memory/MapOfMap.ts +1 -0
  44. package/LICENSE.md +0 -21
@@ -0,0 +1,773 @@
1
+ # Address Type
2
+
3
+ The `Address` type represents a 32-byte Bitcoin/OPNet address. It provides methods for creating, comparing, and serializing addresses.
4
+
5
+ ## Overview
6
+
7
+ ```typescript
8
+ import { Address, Blockchain } from '@btc-vision/btc-runtime/runtime';
9
+
10
+ // Get current sender
11
+ const sender: Address = Blockchain.tx.sender;
12
+
13
+ // Create zero address
14
+ const zero: Address = Address.zero();
15
+
16
+ // Compare addresses
17
+ if (sender.equals(zero)) {
18
+ throw new Revert('Invalid sender');
19
+ }
20
+ ```
21
+
22
+ ### Address Type Architecture
23
+
24
+ ```mermaid
25
+ classDiagram
26
+ class Address {
27
+ +Uint8Array[32] bytes
28
+ -bool isDefined
29
+ -Uint8Array _mldsaPublicKey
30
+ +zero() Address$
31
+ +fromString(hex) Address$
32
+ +fromUint8Array(bytes) Address$
33
+ +equals(other) bool
34
+ +notEquals(other) bool
35
+ +lessThan(other) bool
36
+ +greaterThan(other) bool
37
+ +lessThanOrEqual(other) bool
38
+ +greaterThanOrEqual(other) bool
39
+ +isZero() bool
40
+ +clone() Address
41
+ +toHex() string
42
+ +toString() string
43
+ +mldsaPublicKey Uint8Array
44
+ }
45
+
46
+ class ExtendedAddress {
47
+ +Uint8Array[32] tweakedPublicKey
48
+ +fromStringPair(schnorr, mldsa) ExtendedAddress
49
+ +p2tr() string
50
+ +isDead() bool
51
+ +downCast() Address
52
+ +dead() ExtendedAddress
53
+ }
54
+
55
+ class Uint8Array {
56
+ <<built-in>>
57
+ +length: 32
58
+ }
59
+
60
+ Uint8Array <|-- Address : extends
61
+ Address <|-- ExtendedAddress : extends
62
+
63
+ note for Address "32-byte ML-DSA\npublic key hash"
64
+ note for ExtendedAddress "Dual-key support:\nSchnorr + ML-DSA"
65
+ ```
66
+
67
+ ## Creating Addresses
68
+
69
+ ### Address Creation Flow
70
+
71
+ ```mermaid
72
+ ---
73
+ config:
74
+ theme: dark
75
+ ---
76
+ flowchart LR
77
+ A["Address Creation"] --> B{"Source?"}
78
+ B -->|"Runtime"| C["Blockchain.tx.sender/origin"]
79
+ B -->|"Factory"| D["Address.zero()<br/>ExtendedAddress.dead()"]
80
+ B -->|"Parsing"| E["fromString/fromBytes"]
81
+ B -->|"Calldata"| F["calldata.readAddress"]
82
+ C --> G["32-byte Address"]
83
+ D --> G
84
+ E --> G
85
+ F --> G
86
+ G --> H{"Valid?"}
87
+ H -->|"Yes"| I["Address Instance"]
88
+ H -->|"No"| J["Revert"]
89
+ ```
90
+
91
+ ### From Runtime
92
+
93
+ ```typescript
94
+ // Current transaction sender
95
+ const sender: Address = Blockchain.tx.sender;
96
+
97
+ // Original transaction signer
98
+ const origin: Address = Blockchain.tx.origin;
99
+
100
+ // This contract's address
101
+ const self: Address = Blockchain.contract.address;
102
+
103
+ // Contract deployer
104
+ const deployer: Address = Blockchain.contract.deployer;
105
+ ```
106
+
107
+ ### Special Addresses
108
+
109
+ ```typescript
110
+ // Zero address (all zeros)
111
+ const zero: Address = Address.zero();
112
+ // Equivalent to address(0) in Solidity
113
+
114
+ // Note: For dead/burn addresses, use ExtendedAddress.dead()
115
+ // See ExtendedAddress section below
116
+ ```
117
+
118
+ ### From Bytes
119
+
120
+ ```typescript
121
+ // From Uint8Array (32 bytes) - uses efficient memory copy
122
+ const bytes = new Uint8Array(32);
123
+ bytes[31] = 0x01;
124
+ const addr = Address.fromUint8Array(bytes);
125
+
126
+ // From u8[] array (32 bytes)
127
+ const byteArray: u8[] = new Array<u8>(32);
128
+ byteArray[31] = 0x01;
129
+ const addr2 = new Address(byteArray);
130
+
131
+ // From hex string
132
+ const addr3 = Address.fromString('0x' + '00'.repeat(32));
133
+ ```
134
+
135
+ ### From Calldata
136
+
137
+ ```typescript
138
+ public myMethod(calldata: Calldata): BytesWriter {
139
+ // Read address from calldata (32 bytes)
140
+ const recipient: Address = calldata.readAddress();
141
+
142
+ // ...
143
+ }
144
+ ```
145
+
146
+ ## Comparing Addresses
147
+
148
+ ### Address Comparison Methods
149
+
150
+ ```mermaid
151
+ ---
152
+ config:
153
+ theme: dark
154
+ ---
155
+ flowchart LR
156
+ A["Address A"] --> C{"Comparison Method"}
157
+ B["Address B"] --> C
158
+ C -->|"equals()"| D["Byte-by-byte compare"]
159
+ C -->|"lessThan/greaterThan"| E["Integer compare"]
160
+ D --> F{"Result"}
161
+ E --> F
162
+ F --> G["true/false"]
163
+ ```
164
+
165
+ ### Equality
166
+
167
+ ```typescript
168
+ const addr1 = Blockchain.tx.sender;
169
+ const addr2 = Blockchain.tx.origin;
170
+
171
+ // Using equals()
172
+ if (addr1.equals(addr2)) {
173
+ // Same address
174
+ }
175
+
176
+ // Using == operator
177
+ if (addr1 == addr2) {
178
+ // Same address
179
+ }
180
+
181
+ // Not equal
182
+ if (!addr1.equals(Address.zero())) {
183
+ // Not zero address
184
+ }
185
+ ```
186
+
187
+ ### Common Checks
188
+
189
+ ```typescript
190
+ // Check for zero address
191
+ private validateAddress(addr: Address): void {
192
+ if (addr.equals(Address.zero())) {
193
+ throw new Revert('Cannot be zero address');
194
+ }
195
+ }
196
+
197
+ // Check if sender is deployer
198
+ private onlyDeployer(): void {
199
+ if (!Blockchain.tx.sender.equals(Blockchain.contract.deployer)) {
200
+ throw new Revert('Not deployer');
201
+ }
202
+ }
203
+
204
+ // Check if two addresses are the same
205
+ private preventSelfTransfer(from: Address, to: Address): void {
206
+ if (from.equals(to)) {
207
+ throw new Revert('Cannot transfer to self');
208
+ }
209
+ }
210
+ ```
211
+
212
+ ## Serialization
213
+
214
+ ### To Bytes
215
+
216
+ ```typescript
217
+ const addr: Address = Blockchain.tx.sender;
218
+
219
+ // Address extends Uint8Array, so it can be used directly as bytes
220
+ // Access the underlying buffer
221
+ const bytes: ArrayBuffer = addr.buffer;
222
+
223
+ // Get hex string representation
224
+ const hexString: string = addr.toHex();
225
+
226
+ // Clone the address
227
+ const cloned: Address = addr.clone();
228
+ ```
229
+
230
+ ### With BytesWriter
231
+
232
+ ```typescript
233
+ const writer = new BytesWriter(64);
234
+
235
+ // Write address (32 bytes)
236
+ writer.writeAddress(sender);
237
+ writer.writeAddress(recipient);
238
+ ```
239
+
240
+ ### From BytesReader
241
+
242
+ ```typescript
243
+ const reader = new BytesReader(data);
244
+
245
+ // Read address (32 bytes)
246
+ const sender: Address = reader.readAddress();
247
+ ```
248
+
249
+ ## Address Size
250
+
251
+ OPNet addresses are **32 bytes**, compared to Ethereum's 20 bytes:
252
+
253
+ | Platform | Address Size | Format |
254
+ |----------|-------------|--------|
255
+ | Ethereum | 20 bytes | 0x + 40 hex chars |
256
+ | OPNet | 32 bytes | 64 hex chars |
257
+
258
+ ```typescript
259
+ // Full 32-byte address (Address extends Uint8Array)
260
+ const addr: Address = Blockchain.tx.sender;
261
+ assert(addr.length === 32);
262
+ ```
263
+
264
+ ## Storage with Addresses
265
+
266
+ ### Storing Addresses
267
+
268
+ ```typescript
269
+ import { StoredAddress } from '@btc-vision/btc-runtime/runtime';
270
+
271
+ // Store a single address
272
+ private ownerPointer: u16 = Blockchain.nextPointer;
273
+ private _owner: StoredAddress;
274
+
275
+ constructor() {
276
+ super();
277
+ this._owner = new StoredAddress(this.ownerPointer);
278
+ }
279
+
280
+ // Set/get
281
+ this._owner.value = newOwner;
282
+ const owner: Address = this._owner.value;
283
+ ```
284
+
285
+ ### Address Mappings
286
+
287
+ ```typescript
288
+ import { AddressMemoryMap } from '@btc-vision/btc-runtime/runtime';
289
+
290
+ // mapping(address => uint256)
291
+ private balancesPointer: u16 = Blockchain.nextPointer;
292
+ private balances: AddressMemoryMap;
293
+
294
+ constructor() {
295
+ super();
296
+ this.balances = new AddressMemoryMap(this.balancesPointer);
297
+ }
298
+
299
+ // Usage
300
+ const balance: u256 = this.balances.get(userAddress);
301
+ this.balances.set(userAddress, newBalance);
302
+ ```
303
+
304
+ ## ML-DSA Public Key Access
305
+
306
+ Every `Address` in OPNet can access its ML-DSA (quantum-resistant) public key directly:
307
+
308
+ ```typescript
309
+ const sender: Address = Blockchain.tx.sender;
310
+
311
+ // Get the ML-DSA public key (1312 bytes for ML-DSA-44)
312
+ const mldsaKey: Uint8Array = sender.mldsaPublicKey;
313
+
314
+ // Key is lazily loaded and cached
315
+ const sameKey: Uint8Array = sender.mldsaPublicKey; // Returns cached key
316
+ ```
317
+
318
+ ### ML-DSA Key Loading Sequence
319
+
320
+ ```mermaid
321
+ sequenceDiagram
322
+ participant C as Contract
323
+ participant A as Address
324
+ participant L as Lazy Loader
325
+ participant S as Storage
326
+
327
+ C->>A: address.mldsaPublicKey
328
+ A->>A: Check cache (_mldsaPublicKey)
329
+
330
+ alt Cache Hit
331
+ A-->>C: Return cached key (1312 bytes)
332
+ else Cache Miss
333
+ A->>L: loadMLDSAPublicKey(address, Level2)
334
+ L->>S: Retrieve from blockchain storage
335
+ S-->>L: ML-DSA-44 public key
336
+ L-->>A: 1312-byte key
337
+ A->>A: Cache key (_mldsaPublicKey = key)
338
+ A-->>C: Return key (1312 bytes)
339
+ end
340
+
341
+ Note over A,S: Address = SHA256(ML-DSA Public Key)
342
+ Note over L: Security Level 2 (ML-DSA-44)
343
+ ```
344
+
345
+ **Key points:**
346
+ - The address itself is the SHA256 hash of the ML-DSA public key
347
+ - The full public key is loaded on-demand from the blockchain
348
+ - No custom storage needed - the runtime handles this
349
+
350
+ See [Quantum Resistance](../advanced/quantum-resistance.md) for signature verification details.
351
+
352
+ ## Extended Address
353
+
354
+ `ExtendedAddress` supports dual-key addresses (Schnorr + ML-DSA) for the quantum transition:
355
+
356
+ ```typescript
357
+ import { ExtendedAddress } from '@btc-vision/btc-runtime/runtime';
358
+
359
+ // Create from both key components
360
+ const extAddr = ExtendedAddress.fromStringPair(
361
+ '0x' + 'aa'.repeat(32), // Tweaked Schnorr key (for taproot)
362
+ '0x' + 'bb'.repeat(32) // ML-DSA key hash
363
+ );
364
+
365
+ // Access Schnorr tweaked key (32 bytes)
366
+ const schnorrKey: Uint8Array = extAddr.tweakedPublicKey;
367
+
368
+ // Access ML-DSA public key (inherited from Address)
369
+ const mldsaKey: Uint8Array = extAddr.mldsaPublicKey;
370
+
371
+ // Generate Bitcoin P2TR address
372
+ const p2trAddress: string = extAddr.p2tr(); // "bc1p..." or "tb1p..."
373
+
374
+ // Downcast to base Address
375
+ const addr: Address = extAddr.downCast();
376
+ ```
377
+
378
+ ### ExtendedAddress Memory Layout
379
+
380
+ ```mermaid
381
+ graph LR
382
+ subgraph ExtendedAddress["ExtendedAddress (64 bytes total)"]
383
+ direction TB
384
+
385
+ subgraph Inherited["Inherited from Address (32 bytes)"]
386
+ A1["Byte 0-31: ML-DSA Public Key Hash"]
387
+ A2["SHA256 of full ML-DSA key"]
388
+ end
389
+
390
+ subgraph Extended["Extended Fields (32 bytes)"]
391
+ B1["Byte 0-31: Tweaked Schnorr Public Key"]
392
+ B2["Used for P2TR address generation"]
393
+ end
394
+
395
+ subgraph Cached["Lazily Loaded"]
396
+ C1["ML-DSA Full Key: 1312 bytes"]
397
+ C2["Loaded on first access"]
398
+ end
399
+ end
400
+
401
+ A1 --> A2
402
+ B1 --> B2
403
+ A2 -.->|"derives"| C1
404
+ C1 --> C2
405
+ ```
406
+
407
+ ### Dead Address
408
+
409
+ The dead address is derived from the Bitcoin genesis block (block 0) public key:
410
+ - **Genesis block public key**: `04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f`
411
+ - **Resulting hash**: `284ae4acdb32a99ba3ebfa66a91ddb41a7b7a1d2fef415399922cd8a04485c02`
412
+
413
+ This address is commonly used as a burn address or null recipient in contracts.
414
+
415
+ ```typescript
416
+ // Dead address for burns (derived from Bitcoin block 0 pubkey)
417
+ const dead: ExtendedAddress = ExtendedAddress.dead();
418
+
419
+ // Check if address is dead
420
+ if (extAddr.isDead()) {
421
+ // Funds are being burned
422
+ }
423
+
424
+ // Also available via Blockchain singleton
425
+ const deadAddr: ExtendedAddress = Blockchain.DEAD_ADDRESS;
426
+ ```
427
+
428
+ **Note:** The `Address` base class does NOT have a `dead()` method. Only `ExtendedAddress` provides the dead address functionality.
429
+
430
+ See [Quantum Resistance](../advanced/quantum-resistance.md) for details.
431
+
432
+ ## Solidity vs OPNet Comparison
433
+
434
+ ### Address Type Comparison Table
435
+
436
+ | Feature | Solidity | OPNet |
437
+ |---------|----------|-------|
438
+ | **Type name** | `address` | `Address` |
439
+ | **Size** | 20 bytes (160 bits) | 32 bytes (256 bits) |
440
+ | **Format** | `0x` + 40 hex chars | 64 hex chars |
441
+ | **Zero address** | `address(0)` | `Address.zero()` |
442
+ | **Dead/burn address** | `0x000...dEaD` | `ExtendedAddress.dead()` (Bitcoin block 0 pubkey hash) |
443
+ | **Payable variant** | `address payable` | N/A (different model) |
444
+ | **Current sender** | `msg.sender` | `Blockchain.tx.sender` |
445
+ | **Original signer** | `tx.origin` | `Blockchain.tx.origin` |
446
+ | **Contract address** | `address(this)` | `Blockchain.contract.address` |
447
+ | **Deployer** | N/A (use constructor arg) | `Blockchain.contract.deployer` |
448
+ | **Equality check** | `addr1 == addr2` | `addr1.equals(addr2)` |
449
+ | **Zero check** | `addr == address(0)` | `addr.isZero()` or `addr.equals(Address.zero())` |
450
+ | **From bytes** | `address(bytes20(data))` | `Address.fromUint8Array(data)` |
451
+ | **To bytes** | `abi.encodePacked(addr)` | `addr` (extends Uint8Array) |
452
+ | **Checksum** | EIP-55 mixed-case | N/A |
453
+ | **Quantum-resistant key** | N/A | `addr.mldsaPublicKey` (1312 bytes) |
454
+
455
+ ### Side-by-Side Code Examples
456
+
457
+ #### Getting Addresses
458
+
459
+ ```solidity
460
+ // Solidity
461
+ address sender = msg.sender;
462
+ address origin = tx.origin;
463
+ address self = address(this);
464
+ ```
465
+
466
+ ```typescript
467
+ // OPNet
468
+ const sender: Address = Blockchain.tx.sender;
469
+ const origin: Address = Blockchain.tx.origin;
470
+ const self: Address = Blockchain.contract.address;
471
+ ```
472
+
473
+ #### Zero Address Checks
474
+
475
+ ```solidity
476
+ // Solidity
477
+ require(to != address(0), "Cannot send to zero address");
478
+ ```
479
+
480
+ ```typescript
481
+ // OPNet
482
+ if (to.equals(Address.zero())) {
483
+ throw new Revert('Cannot send to zero address');
484
+ }
485
+ // Or using isZero()
486
+ if (to.isZero()) {
487
+ throw new Revert('Cannot send to zero address');
488
+ }
489
+ ```
490
+
491
+ #### Address Comparison
492
+
493
+ ```solidity
494
+ // Solidity
495
+ require(msg.sender == owner, "Not owner");
496
+ require(from != to, "Cannot transfer to self");
497
+ ```
498
+
499
+ ```typescript
500
+ // OPNet
501
+ if (!Blockchain.tx.sender.equals(owner)) {
502
+ throw new Revert('Not owner');
503
+ }
504
+ if (from.equals(to)) {
505
+ throw new Revert('Cannot transfer to self');
506
+ }
507
+ ```
508
+
509
+ #### Storing Owner Address
510
+
511
+ ```solidity
512
+ // Solidity
513
+ address public owner;
514
+
515
+ constructor() {
516
+ owner = msg.sender;
517
+ }
518
+
519
+ modifier onlyOwner() {
520
+ require(msg.sender == owner, "Not owner");
521
+ _;
522
+ }
523
+
524
+ function transferOwnership(address newOwner) public onlyOwner {
525
+ require(newOwner != address(0), "Invalid address");
526
+ owner = newOwner;
527
+ }
528
+ ```
529
+
530
+ ```typescript
531
+ // OPNet
532
+ private ownerPointer: u16 = Blockchain.nextPointer;
533
+ private _owner: StoredAddress;
534
+
535
+ constructor() {
536
+ super();
537
+ this._owner = new StoredAddress(this.ownerPointer);
538
+ }
539
+
540
+ public override onDeployment(_calldata: Calldata): void {
541
+ this._owner.value = Blockchain.tx.origin;
542
+ }
543
+
544
+ private onlyOwner(): void {
545
+ if (!Blockchain.tx.sender.equals(this._owner.value)) {
546
+ throw new Revert('Not owner');
547
+ }
548
+ }
549
+
550
+ public transferOwnership(calldata: Calldata): BytesWriter {
551
+ this.onlyOwner();
552
+ const newOwner = calldata.readAddress();
553
+ if (newOwner.equals(Address.zero())) {
554
+ throw new Revert('Invalid address');
555
+ }
556
+ this._owner.value = newOwner;
557
+ return new BytesWriter(0);
558
+ }
559
+ ```
560
+
561
+ #### Balance Mappings
562
+
563
+ ```solidity
564
+ // Solidity
565
+ mapping(address => uint256) public balances;
566
+
567
+ function balanceOf(address account) public view returns (uint256) {
568
+ return balances[account];
569
+ }
570
+ ```
571
+
572
+ ```typescript
573
+ // OPNet
574
+ private balancesPointer: u16 = Blockchain.nextPointer;
575
+ private balances: AddressMemoryMap;
576
+
577
+ constructor() {
578
+ super();
579
+ this.balances = new AddressMemoryMap(this.balancesPointer);
580
+ }
581
+
582
+ public balanceOf(calldata: Calldata): BytesWriter {
583
+ const account = calldata.readAddress();
584
+ const balance: u256 = this.balances.get(account);
585
+
586
+ const writer = new BytesWriter(32);
587
+ writer.writeU256(balance);
588
+ return writer;
589
+ }
590
+ ```
591
+
592
+ #### Sending Value (Key Difference)
593
+
594
+ ```solidity
595
+ // Solidity - Native ETH transfers
596
+ address payable recipient = payable(to);
597
+ recipient.transfer(amount); // Reverts on failure
598
+ bool success = recipient.send(amount); // Returns false on failure
599
+ (bool ok, ) = recipient.call{value: amount}(""); // Low-level call
600
+ ```
601
+
602
+ ```typescript
603
+ // OPNet - No native value transfers on addresses
604
+ // Bitcoin UTXO model is fundamentally different
605
+ // Token transfers are done via contract calls instead:
606
+ this._transfer(from, to, amount); // Internal token transfer logic
607
+ ```
608
+
609
+ ### Key Differences Explained
610
+
611
+ | Aspect | Solidity/Ethereum | OPNet/Bitcoin |
612
+ |--------|-------------------|---------------|
613
+ | **Address derivation** | Keccak256 hash of public key (last 20 bytes) | SHA256 of ML-DSA public key (32 bytes) |
614
+ | **Native currency** | ETH handled via `payable` | Bitcoin UTXOs handled separately |
615
+ | **Transfer mechanism** | `addr.transfer()`, `addr.send()`, `addr.call()` | Contract method calls only |
616
+ | **Balance query** | `addr.balance` (native ETH) | N/A for native; use contract mappings for tokens |
617
+ | **Code size** | `addr.code.length` | N/A |
618
+ | **Code hash** | `addr.codehash` | N/A |
619
+ | **Quantum resistance** | None | ML-DSA public key accessible via `mldsaPublicKey` |
620
+
621
+ ## Common Patterns
622
+
623
+ ### Transfer Validation
624
+
625
+ ```typescript
626
+ public transfer(calldata: Calldata): BytesWriter {
627
+ const to = calldata.readAddress();
628
+ const amount = calldata.readU256();
629
+
630
+ // Validate recipient
631
+ if (to.equals(Address.zero())) {
632
+ throw new Revert('Cannot transfer to zero address');
633
+ }
634
+
635
+ // Prevent self-transfer (optional)
636
+ if (to.equals(Blockchain.tx.sender)) {
637
+ throw new Revert('Cannot transfer to self');
638
+ }
639
+
640
+ // ... execute transfer
641
+ }
642
+ ```
643
+
644
+ ### Access Control
645
+
646
+ ```typescript
647
+ // Store owner/admin
648
+ private ownerPointer: u16 = Blockchain.nextPointer;
649
+ private _owner: StoredAddress;
650
+
651
+ constructor() {
652
+ super();
653
+ this._owner = new StoredAddress(this.ownerPointer);
654
+ }
655
+
656
+ public override onDeployment(calldata: Calldata): void {
657
+ this._owner.value = Blockchain.tx.origin;
658
+ }
659
+
660
+ private onlyOwner(): void {
661
+ if (!Blockchain.tx.sender.equals(this._owner.value)) {
662
+ throw new Revert('Not owner');
663
+ }
664
+ }
665
+
666
+ public transferOwnership(calldata: Calldata): BytesWriter {
667
+ this.onlyOwner();
668
+
669
+ const newOwner = calldata.readAddress();
670
+ if (newOwner.equals(Address.zero())) {
671
+ throw new Revert('New owner is zero address');
672
+ }
673
+
674
+ this._owner.value = newOwner;
675
+ return new BytesWriter(0);
676
+ }
677
+ ```
678
+
679
+ ### Address as Map Key
680
+
681
+ ```typescript
682
+ // Using address as map key
683
+ private userDataPointer: u16 = Blockchain.nextPointer;
684
+ private userData: AddressMemoryMap;
685
+
686
+ constructor() {
687
+ super();
688
+ this.userData = new AddressMemoryMap(this.userDataPointer);
689
+ }
690
+
691
+ // Store data by address
692
+ public setUserData(calldata: Calldata): BytesWriter {
693
+ const data = calldata.readU256();
694
+ this.userData.set(Blockchain.tx.sender, data);
695
+ return new BytesWriter(0);
696
+ }
697
+
698
+ // Get data by address
699
+ public getUserData(calldata: Calldata): BytesWriter {
700
+ const user = calldata.readAddress();
701
+ const data: u256 = this.userData.get(user);
702
+
703
+ const writer = new BytesWriter(32);
704
+ writer.writeU256(data);
705
+ return writer;
706
+ }
707
+ ```
708
+
709
+ ## API Reference
710
+
711
+ ### Static Methods
712
+
713
+ | Method | Returns | Description |
714
+ |--------|---------|-------------|
715
+ | `Address.zero()` | `Address` | All-zero address (cloned from constant) |
716
+ | `Address.fromString(hex)` | `Address` | Create from hex string (with or without 0x prefix) |
717
+ | `Address.fromUint8Array(bytes)` | `Address` | Create from Uint8Array using direct memory copy |
718
+
719
+ ### Instance Methods
720
+
721
+ | Method | Returns | Description |
722
+ |--------|---------|-------------|
723
+ | `equals(other)` | `bool` | Compare addresses (operator `==`) |
724
+ | `notEquals(other)` | `bool` | Check inequality (operator `!=`) |
725
+ | `lessThan(other)` | `bool` | Compare as big-endian integers (operator `<`) |
726
+ | `greaterThan(other)` | `bool` | Compare as big-endian integers (operator `>`) |
727
+ | `lessThanOrEqual(other)` | `bool` | Compare addresses (operator `<=`) |
728
+ | `greaterThanOrEqual(other)` | `bool` | Compare addresses (operator `>=`) |
729
+ | `isZero()` | `bool` | Check if zero address |
730
+ | `clone()` | `Address` | Create a deep copy |
731
+ | `toHex()` | `string` | Convert to hex string (no 0x prefix) |
732
+ | `toString()` | `string` | Same as toHex() |
733
+
734
+ ### Instance Properties
735
+
736
+ | Property | Type | Description |
737
+ |----------|------|-------------|
738
+ | `mldsaPublicKey` | `Uint8Array` | ML-DSA public key (lazily loaded, 1312 bytes) |
739
+
740
+ ### ExtendedAddress Static Methods
741
+
742
+ | Method | Returns | Description |
743
+ |--------|---------|-------------|
744
+ | `ExtendedAddress.dead()` | `ExtendedAddress` | Bitcoin block 0 pubkey-derived burn address |
745
+ | `ExtendedAddress.zero()` | `ExtendedAddress` | All-zero address (cloned from constant) |
746
+ | `ExtendedAddress.fromStringPair(schnorr, mldsa)` | `ExtendedAddress` | Create from two hex strings |
747
+ | `ExtendedAddress.fromUint8Array(bytes)` | `ExtendedAddress` | Create from 64-byte array |
748
+ | `ExtendedAddress.toCSV(address, blocks)` | `string` | Generate CSV timelocked P2WSH address |
749
+ | `ExtendedAddress.p2wpkh(address)` | `string` | Generate P2WPKH address |
750
+
751
+ ### ExtendedAddress Instance Methods
752
+
753
+ | Method | Returns | Description |
754
+ |--------|---------|-------------|
755
+ | `p2tr()` | `string` | Generate P2TR (taproot) address |
756
+ | `isDead()` | `bool` | Check if this is the dead address |
757
+ | `isZero()` | `bool` | Check if ML-DSA key hash is all zeros |
758
+ | `downCast()` | `Address` | Cast to base Address type |
759
+ | `clone()` | `ExtendedAddress` | Create a deep copy |
760
+ | `toString()` | `string` | Returns P2TR address (overrides base) |
761
+
762
+ ### ExtendedAddress Properties
763
+
764
+ | Property | Type | Description |
765
+ |----------|------|-------------|
766
+ | `tweakedPublicKey` | `Uint8Array` | Schnorr tweaked key (32 bytes) |
767
+ | `mldsaPublicKey` | `Uint8Array` | ML-DSA public key (inherited, 1312 bytes) |
768
+
769
+ ---
770
+
771
+ **Navigation:**
772
+ - Previous: [ReentrancyGuard](../contracts/reentrancy-guard.md)
773
+ - Next: [SafeMath](./safe-math.md)