@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.
- package/LICENSE +190 -0
- package/README.md +258 -137
- package/SECURITY.md +226 -0
- package/docs/README.md +614 -0
- package/docs/advanced/bitcoin-scripts.md +939 -0
- package/docs/advanced/cross-contract-calls.md +579 -0
- package/docs/advanced/plugins.md +1006 -0
- package/docs/advanced/quantum-resistance.md +660 -0
- package/docs/advanced/signature-verification.md +715 -0
- package/docs/api-reference/blockchain.md +729 -0
- package/docs/api-reference/events.md +642 -0
- package/docs/api-reference/op20.md +902 -0
- package/docs/api-reference/op721.md +819 -0
- package/docs/api-reference/safe-math.md +510 -0
- package/docs/api-reference/storage.md +840 -0
- package/docs/contracts/op-net-base.md +786 -0
- package/docs/contracts/op20-token.md +687 -0
- package/docs/contracts/op20s-signatures.md +614 -0
- package/docs/contracts/op721-nft.md +785 -0
- package/docs/contracts/reentrancy-guard.md +787 -0
- package/docs/core-concepts/blockchain-environment.md +724 -0
- package/docs/core-concepts/decorators.md +466 -0
- package/docs/core-concepts/events.md +652 -0
- package/docs/core-concepts/pointers.md +391 -0
- package/docs/core-concepts/security.md +473 -0
- package/docs/core-concepts/storage-system.md +969 -0
- package/docs/examples/basic-token.md +745 -0
- package/docs/examples/nft-with-reservations.md +1440 -0
- package/docs/examples/oracle-integration.md +1212 -0
- package/docs/examples/stablecoin.md +1180 -0
- package/docs/getting-started/first-contract.md +575 -0
- package/docs/getting-started/installation.md +384 -0
- package/docs/getting-started/project-structure.md +630 -0
- package/docs/storage/memory-maps.md +764 -0
- package/docs/storage/stored-arrays.md +778 -0
- package/docs/storage/stored-maps.md +758 -0
- package/docs/storage/stored-primitives.md +655 -0
- package/docs/types/address.md +773 -0
- package/docs/types/bytes-writer-reader.md +938 -0
- package/docs/types/calldata.md +744 -0
- package/docs/types/safe-math.md +446 -0
- package/package.json +52 -27
- package/runtime/memory/MapOfMap.ts +1 -0
- 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)
|