@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,786 @@
1
+ # OP_NET Base Contract
2
+
3
+ `OP_NET` is the base class for all OPNet smart contracts. It implements the `IBTC` interface and provides the foundational structure for contract lifecycle, method dispatching, event emission, and access control.
4
+
5
+ ## Overview
6
+
7
+ ```typescript
8
+ import { OP_NET, Calldata, BytesWriter, ABIDataTypes } from '@btc-vision/btc-runtime/runtime';
9
+
10
+ @final
11
+ export class MyContract extends OP_NET {
12
+ public constructor() {
13
+ super();
14
+ }
15
+
16
+ public override onDeployment(calldata: Calldata): void {
17
+ // One-time initialization
18
+ }
19
+
20
+ @method({ name: 'param', type: ABIDataTypes.UINT256 })
21
+ @returns({ name: 'result', type: ABIDataTypes.UINT256 })
22
+ public myMethod(calldata: Calldata): BytesWriter {
23
+ // Method implementation - routing is AUTOMATIC via @method decorator
24
+ return new BytesWriter(0);
25
+ }
26
+ }
27
+ ```
28
+
29
+ **Note:** Method routing is handled AUTOMATICALLY by the runtime via `@method` decorators. You do NOT need to override the `execute` method - the decorator system handles selector generation and call routing.
30
+
31
+ **Solidity Comparison:**
32
+
33
+ ```solidity
34
+ // Solidity: Automatic method routing via ABI
35
+ contract MyContract {
36
+ constructor() {
37
+ // Runs once at deployment
38
+ }
39
+
40
+ function myMethod() public returns (bytes memory) {
41
+ // Method implementation
42
+ }
43
+ }
44
+
45
+ // OPNet: AUTOMATIC method routing via @method decorators
46
+ // - Constructor runs on EVERY call
47
+ // - Routing is automatic via decorator system
48
+ // - One-time init in onDeployment()
49
+ ```
50
+
51
+ ## Contract Lifecycle
52
+
53
+ ### Inheritance Hierarchy
54
+
55
+ OPNet contracts follow a clear inheritance pattern:
56
+
57
+ ```mermaid
58
+ classDiagram
59
+ class OP_NET {
60
+ Base Contract
61
+ implements IBTC
62
+ +address Address (getter)
63
+ +contractDeployer Address (getter)
64
+ +onDeployment(_calldata: Calldata) void
65
+ +onExecutionStarted(_selector: Selector, _calldata: Calldata) void
66
+ +onExecutionCompleted(_selector: Selector, _calldata: Calldata) void
67
+ +execute(method: Selector, _calldata: Calldata) BytesWriter
68
+ #emitEvent(event: NetEvent) void
69
+ #onlyDeployer(caller: Address) void
70
+ #isSelf(address: Address) boolean
71
+ #_buildDomainSeparator() Uint8Array
72
+ Note: @method decorator handles routing
73
+ }
74
+
75
+ class MyContract {
76
+ Custom Contract
77
+ -balancesPointer: u16
78
+ -balances: AddressMemoryMap
79
+ +constructor()
80
+ +onDeployment(calldata: Calldata) void
81
+ +myMethod(calldata: Calldata) BytesWriter
82
+ Note: @method decorator handles routing
83
+ }
84
+
85
+ class ReentrancyGuard {
86
+ Reentrancy Protection
87
+ extends OP_NET
88
+ #_locked: StoredBoolean
89
+ #_reentrancyDepth: StoredU256
90
+ +nonReentrantBefore() void
91
+ +nonReentrantAfter() void
92
+ }
93
+
94
+ class OP20 {
95
+ Fungible Token Standard
96
+ extends ReentrancyGuard
97
+ -_totalSupply: StoredU256
98
+ -balanceOfMap: AddressMemoryMap
99
+ +transfer(calldata: Calldata) BytesWriter
100
+ +approve(calldata: Calldata) BytesWriter
101
+ }
102
+
103
+ class OP721 {
104
+ NFT Standard
105
+ extends ReentrancyGuard
106
+ -_owners: AddressMemoryMap
107
+ -_balances: AddressMemoryMap
108
+ +transferFrom(calldata: Calldata) BytesWriter
109
+ +mint(to: Address, tokenId: u256) void
110
+ }
111
+
112
+ OP_NET <|-- MyContract : extends
113
+ OP_NET <|-- ReentrancyGuard : extends
114
+ ReentrancyGuard <|-- OP20 : extends
115
+ ReentrancyGuard <|-- OP721 : extends
116
+ ```
117
+
118
+ ### Deployment and Execution Flow
119
+
120
+ The following diagram shows how contracts are deployed and executed on OPNet:
121
+
122
+ ```mermaid
123
+ ---
124
+ config:
125
+ theme: dark
126
+ ---
127
+ sequenceDiagram
128
+ participant User as User
129
+ participant Bitcoin as Bitcoin L1
130
+ participant WASM as WASM Runtime
131
+ participant Contract as Contract
132
+ participant Storage as Storage
133
+
134
+ Note over User,Storage: Deployment Phase (Once)
135
+ User->>Bitcoin: Submit deployment TX
136
+ Bitcoin->>WASM: Create contract
137
+ WASM->>Contract: constructor()
138
+ Contract->>Contract: Initialize storage pointers
139
+ Contract->>Contract: Create storage map instances
140
+ WASM->>Contract: onDeployment(calldata)
141
+ Contract->>Contract: Read deployment calldata
142
+ Contract->>Storage: Set initial state
143
+ Contract->>WASM: Emit deployment events
144
+ Note over Contract: Contract Ready
145
+
146
+ Note over User,Storage: Execution Phase (Every Transaction)
147
+ User->>Bitcoin: Submit transaction
148
+ Bitcoin->>WASM: Route to contract
149
+ WASM->>Contract: constructor() runs AGAIN
150
+ Contract->>Contract: Re-initialize storage maps
151
+ WASM->>Contract: onExecutionStarted(selector, calldata)
152
+ Contract->>Contract: Read method selector from TX
153
+ WASM->>Contract: execute(method, calldata)
154
+
155
+ alt Selector matches
156
+ Contract->>Contract: Call method handler
157
+ Contract->>Contract: Read calldata parameters
158
+ Contract->>Contract: Validate inputs
159
+ alt Valid inputs
160
+ Contract->>Storage: Read current state
161
+ Contract->>Contract: Execute business logic
162
+ Contract->>Storage: Write updated state
163
+ Contract->>WASM: emitEvent()
164
+ else Invalid inputs
165
+ Contract->>WASM: Revert transaction
166
+ end
167
+ else No match
168
+ Contract->>Contract: super.execute(parent)
169
+ alt Parent has method
170
+ Contract->>Storage: Execute parent method
171
+ Contract->>WASM: emitEvent()
172
+ else No handler
173
+ Contract->>WASM: Revert: Unknown selector
174
+ end
175
+ end
176
+
177
+ WASM->>Contract: onExecutionCompleted(selector, calldata)
178
+ Contract->>WASM: Return BytesWriter result
179
+ WASM->>Bitcoin: Commit state changes
180
+ Bitcoin->>User: Transaction complete
181
+ ```
182
+
183
+ ### 1. Construction
184
+
185
+ The constructor runs on **every** contract interaction:
186
+
187
+ ```typescript
188
+ public constructor() {
189
+ super(); // Always call parent constructor
190
+
191
+ // Initialize storage maps (these run every time)
192
+ this.balances = new AddressMemoryMap(this.balancesPointer);
193
+
194
+ // DON'T do one-time initialization here!
195
+ }
196
+ ```
197
+
198
+ **Key Difference from Solidity:**
199
+
200
+ | Solidity | OPNet |
201
+ |----------|-------|
202
+ | Constructor runs once at deployment | Constructor runs every call |
203
+ | Initialize state in constructor | Initialize state in `onDeployment` |
204
+
205
+ ### 2. Deployment (onDeployment)
206
+
207
+ Runs exactly **once** when the contract is first deployed:
208
+
209
+ ```typescript
210
+ public override onDeployment(calldata: Calldata): void {
211
+ // Read deployment parameters
212
+ const initialSupply = calldata.readU256();
213
+ const tokenName = calldata.readString();
214
+
215
+ // Set initial state
216
+ this._totalSupply.value = initialSupply;
217
+ this._name.value = tokenName;
218
+
219
+ // Mint initial tokens
220
+ this._mint(Blockchain.tx.origin, initialSupply);
221
+ }
222
+ ```
223
+
224
+ **Solidity Comparison:**
225
+
226
+ ```solidity
227
+ // Solidity: One-time init in constructor
228
+ constructor(uint256 initialSupply, string memory tokenName) {
229
+ _totalSupply = initialSupply;
230
+ _name = tokenName;
231
+ _mint(msg.sender, initialSupply);
232
+ }
233
+
234
+ // OPNet: One-time init in onDeployment()
235
+ // Constructor runs every call, onDeployment runs once
236
+ ```
237
+
238
+ ### 3. Method Execution
239
+
240
+ Methods are automatically routed via `@method` decorators:
241
+
242
+ ```typescript
243
+ @method(
244
+ { name: 'to', type: ABIDataTypes.ADDRESS },
245
+ { name: 'amount', type: ABIDataTypes.UINT256 },
246
+ )
247
+ @returns({ name: 'success', type: ABIDataTypes.BOOL })
248
+ public transfer(calldata: Calldata): BytesWriter {
249
+ const to = calldata.readAddress();
250
+ const amount = calldata.readU256();
251
+ // ... implementation
252
+ return new BytesWriter(1);
253
+ }
254
+
255
+ @method({ name: 'spender', type: ABIDataTypes.ADDRESS }, { name: 'amount', type: ABIDataTypes.UINT256 })
256
+ public approve(calldata: Calldata): BytesWriter {
257
+ // ... implementation
258
+ return new BytesWriter(0);
259
+ }
260
+
261
+ @method({ name: 'account', type: ABIDataTypes.ADDRESS })
262
+ @returns({ name: 'balance', type: ABIDataTypes.UINT256 })
263
+ public balanceOf(calldata: Calldata): BytesWriter {
264
+ const account = calldata.readAddress();
265
+ // ... implementation
266
+ const writer = new BytesWriter(32);
267
+ writer.writeU256(balance);
268
+ return writer;
269
+ }
270
+ ```
271
+
272
+ **Note:** The runtime automatically generates selectors and routes calls based on `@method` decorators. You do NOT need to override the `execute` method.
273
+
274
+ ### Transaction Sequence
275
+
276
+ The following sequence diagram shows the complete flow of a transaction through the system:
277
+
278
+ ```mermaid
279
+ sequenceDiagram
280
+ participant User as 👤 User Wallet
281
+ participant Blockchain as Bitcoin L1
282
+ participant TxPool as Transaction Pool
283
+ participant VM as WASM Runtime
284
+ participant Contract as OP_NET Contract
285
+ participant Storage as Storage Pointers
286
+ participant EventLog as Event Log
287
+
288
+ User->>TxPool: Submit signed transaction
289
+ Note over User,TxPool: Contains: contract address,<br/>method selector, calldata
290
+
291
+ TxPool->>Blockchain: Transaction confirmed
292
+ Blockchain->>VM: Route to contract address
293
+
294
+ VM->>Contract: Instantiate contract instance
295
+ activate Contract
296
+
297
+ Contract->>Contract: constructor()
298
+ Note over Contract: Runs EVERY call<br/>Initialize storage maps
299
+
300
+ Contract->>Storage: Allocate storage pointers
301
+ Storage-->>Contract: Pointer addresses
302
+
303
+ VM->>Contract: onExecutionStarted(selector, calldata)
304
+ Note over Contract: Pre-execution hook<br/>Can add logging/validation
305
+
306
+ VM->>Contract: execute(selector, calldata)
307
+
308
+ Contract->>Contract: switch(selector)
309
+ Note over Contract: Method routing logic
310
+
311
+ alt Known Method Selector
312
+ Contract->>Contract: methodHandler(calldata)
313
+
314
+ Contract->>Contract: calldata.readAddress()
315
+ Contract->>Contract: calldata.readU256()
316
+ Note over Contract: Parse parameters
317
+
318
+ Contract->>Storage: Read current state
319
+ Storage-->>Contract: Current values
320
+
321
+ Contract->>Contract: Business logic
322
+ Note over Contract: SafeMath operations,<br/>validations, state changes
323
+
324
+ Contract->>Storage: Write updated state
325
+ Note over Storage: Persistent storage<br/>committed on success
326
+
327
+ Contract->>EventLog: emitEvent(TransferEvent)
328
+ Note over EventLog: Events for indexing<br/>off-chain systems
329
+
330
+ else Unknown Method
331
+ Contract->>Contract: super.execute(selector, calldata)
332
+
333
+ alt Parent Has Method
334
+ Note over Contract: OP_NET parent<br/>or OP20/OP721 parent
335
+ Contract->>Storage: Parent method logic
336
+ Contract->>EventLog: Parent method events
337
+ else No Handler
338
+ Contract->>VM: throw Revert Unknown method
339
+ VM->>User: Transaction reverted
340
+ Note over User: No state changes,<br/>fees still consumed
341
+ end
342
+ end
343
+
344
+ Contract->>Contract: onExecutionCompleted(selector, calldata)
345
+ Note over Contract: Post-execution hook<br/>Cleanup, final checks
346
+
347
+ Contract->>VM: Return BytesWriter
348
+ deactivate Contract
349
+
350
+ VM->>Blockchain: Commit state changes
351
+ Blockchain->>User: Transaction receipt
352
+ Note over User: Success with events<br/>or revert with error
353
+ ```
354
+
355
+ ## Method Selectors
356
+
357
+ Methods are identified by selectors (4-byte identifiers). The `@method` decorator automatically generates and registers selectors:
358
+
359
+ ```typescript
360
+ // Selectors are generated AUTOMATICALLY from @method decorators
361
+ @method(
362
+ { name: 'to', type: ABIDataTypes.ADDRESS },
363
+ { name: 'amount', type: ABIDataTypes.UINT256 },
364
+ )
365
+ public transfer(calldata: Calldata): BytesWriter {
366
+ // Runtime automatically routes calls to this method
367
+ return new BytesWriter(0);
368
+ }
369
+ ```
370
+
371
+ ### Solidity Comparison
372
+
373
+ ```solidity
374
+ // Solidity: Automatic selector generation
375
+ function transfer(address to, uint256 amount) public { }
376
+ // Selector: keccak256("transfer(address,uint256)")[:4]
377
+
378
+ // OPNet: ALSO automatic via @method decorator
379
+ // @method({ name: 'to', type: ABIDataTypes.ADDRESS }, ...)
380
+ // public transfer(calldata: Calldata): BytesWriter { }
381
+ ```
382
+
383
+ **Note:** Both Solidity and OPNet handle selector generation automatically. In OPNet, use `@method` decorators and the runtime handles routing.
384
+
385
+ ### Built-in Methods
386
+
387
+ The base `OP_NET` class provides a built-in `deployer()` method that returns the contract deployer address:
388
+
389
+ ```typescript
390
+ // Built-in method handled by OP_NET.execute()
391
+ // Selector: encodeSelector('deployer()')
392
+ // Returns: Address (the contract deployer)
393
+ ```
394
+
395
+ This method is automatically available on all contracts that extend `OP_NET`. When called, it returns the `contractDeployer` address.
396
+
397
+ ## Access Control
398
+
399
+ ### onlyDeployer
400
+
401
+ Restrict function access to the contract deployer:
402
+
403
+ ```typescript
404
+ @method({ name: 'parameter', type: ABIDataTypes.UINT256 })
405
+ public adminFunction(calldata: Calldata): BytesWriter {
406
+ this.onlyDeployer(Blockchain.tx.sender);
407
+
408
+ // Only deployer reaches here
409
+ return new BytesWriter(0);
410
+ }
411
+ ```
412
+
413
+ **Solidity Comparison:**
414
+
415
+ ```solidity
416
+ // Solidity: Using OpenZeppelin Ownable
417
+ import "@openzeppelin/contracts/access/Ownable.sol";
418
+
419
+ contract MyContract is Ownable {
420
+ function adminFunction(uint256 parameter) public onlyOwner {
421
+ // Only owner reaches here
422
+ }
423
+ }
424
+
425
+ // OPNet: Built-in onlyDeployer check
426
+ // this.onlyDeployer(Blockchain.tx.sender);
427
+ ```
428
+
429
+ ### Custom Access Control
430
+
431
+ ```typescript
432
+ private adminPointer: u16 = Blockchain.nextPointer;
433
+ private admin: StoredAddress = new StoredAddress(this.adminPointer, Address.zero());
434
+
435
+ private onlyAdmin(): void {
436
+ if (!Blockchain.tx.sender.equals(this.admin.value)) {
437
+ throw new Revert('Caller is not admin');
438
+ }
439
+ }
440
+
441
+ @method({ name: 'value', type: ABIDataTypes.UINT256 })
442
+ @returns({ name: 'success', type: ABIDataTypes.BOOL })
443
+ public setParameter(calldata: Calldata): BytesWriter {
444
+ this.onlyAdmin();
445
+ // ...
446
+ }
447
+ ```
448
+
449
+ **Solidity Comparison:**
450
+
451
+ ```solidity
452
+ // Solidity: Custom access control
453
+ address private admin;
454
+
455
+ modifier onlyAdmin() {
456
+ require(msg.sender == admin, "Caller is not admin");
457
+ _;
458
+ }
459
+
460
+ function setParameter(uint256 value) public onlyAdmin {
461
+ // ...
462
+ }
463
+
464
+ // OPNet: Similar pattern but with explicit method call
465
+ // this.onlyAdmin(); at start of method
466
+ ```
467
+
468
+ ## Event Emission
469
+
470
+ Emit events to notify off-chain systems:
471
+
472
+ ```typescript
473
+ import { NetEvent, TransferEvent } from '@btc-vision/btc-runtime/runtime';
474
+
475
+ // Using built-in events
476
+ this.emitEvent(new TransferEvent(from, to, amount));
477
+
478
+ // Using custom events
479
+ this.emitEvent(new MyCustomEvent(data1, data2));
480
+ ```
481
+
482
+ **Solidity Comparison:**
483
+
484
+ ```solidity
485
+ // Solidity: Emit keyword
486
+ event Transfer(address indexed from, address indexed to, uint256 value);
487
+
488
+ emit Transfer(from, to, amount);
489
+
490
+ // OPNet: emitEvent method
491
+ this.emitEvent(new TransferEvent(from, to, amount));
492
+ ```
493
+
494
+ ## Protected Helper Methods
495
+
496
+ The `OP_NET` base class provides several protected helper methods:
497
+
498
+ ### isSelf
499
+
500
+ Checks if a given address is the contract's own address:
501
+
502
+ ```typescript
503
+ protected isSelf(address: Address): boolean {
504
+ return this.address === address;
505
+ }
506
+
507
+ // Usage example
508
+ if (this.isSelf(targetAddress)) {
509
+ // Handle self-call case
510
+ }
511
+ ```
512
+
513
+ ### _buildDomainSeparator
514
+
515
+ A method stub for building EIP-712 style domain separators. Must be overridden in derived classes that need signature verification:
516
+
517
+ ```typescript
518
+ protected _buildDomainSeparator(): Uint8Array {
519
+ // Override in derived class to provide domain separator
520
+ throw new Error('Method not implemented.');
521
+ }
522
+ ```
523
+
524
+ ## Storage Patterns
525
+
526
+ ### Pointer Allocation
527
+
528
+ ```typescript
529
+ export class MyContract extends OP_NET {
530
+ // Allocate storage pointers at class level
531
+ private counterPointer: u16 = Blockchain.nextPointer;
532
+ private ownerPointer: u16 = Blockchain.nextPointer;
533
+ private dataPointer: u16 = Blockchain.nextPointer;
534
+
535
+ // Create storage instances
536
+ private counter: StoredU256 = new StoredU256(this.counterPointer, EMPTY_POINTER);
537
+ private owner: StoredAddress = new StoredAddress(this.ownerPointer, Address.zero());
538
+ }
539
+ ```
540
+
541
+ **Solidity Comparison:**
542
+
543
+ ```solidity
544
+ // Solidity: Automatic storage slot allocation
545
+ contract MyContract {
546
+ uint256 private counter; // slot 0
547
+ address private owner; // slot 1
548
+ bytes private data; // slot 2
549
+ }
550
+
551
+ // OPNet: Explicit pointer allocation
552
+ // private counterPointer: u16 = Blockchain.nextPointer;
553
+ // private counter: StoredU256 = new StoredU256(this.counterPointer, EMPTY_POINTER);
554
+ ```
555
+
556
+ ### Storage Maps
557
+
558
+ ```typescript
559
+ export class MyContract extends OP_NET {
560
+ private balancesPointer: u16 = Blockchain.nextPointer;
561
+ private balances: AddressMemoryMap;
562
+
563
+ public constructor() {
564
+ super();
565
+ // Initialize maps in constructor (runs every time, but that's OK)
566
+ this.balances = new AddressMemoryMap(this.balancesPointer);
567
+ }
568
+ }
569
+ ```
570
+
571
+ **Solidity Comparison:**
572
+
573
+ ```solidity
574
+ // Solidity: mapping declaration
575
+ mapping(address => uint256) private balances;
576
+
577
+ // OPNet: AddressMemoryMap with pointer
578
+ // private balancesPointer: u16 = Blockchain.nextPointer;
579
+ // this.balances = new AddressMemoryMap(this.balancesPointer);
580
+ ```
581
+
582
+ ## Complete Example
583
+
584
+ ```typescript
585
+ import { u256 } from '@btc-vision/as-bignum/assembly';
586
+ import {
587
+ OP_NET,
588
+ Blockchain,
589
+ Address,
590
+ Calldata,
591
+ BytesWriter,
592
+ StoredU256,
593
+ AddressMemoryMap,
594
+ SafeMath,
595
+ Revert,
596
+ ABIDataTypes,
597
+ } from '@btc-vision/btc-runtime/runtime';
598
+
599
+ @final
600
+ export class SimpleToken extends OP_NET {
601
+ // Storage pointers
602
+ private totalSupplyPointer: u16 = Blockchain.nextPointer;
603
+ private balancesPointer: u16 = Blockchain.nextPointer;
604
+
605
+ // Storage
606
+ private _totalSupply: StoredU256 = new StoredU256(this.totalSupplyPointer, EMPTY_POINTER);
607
+ private balances: AddressMemoryMap;
608
+
609
+ public constructor() {
610
+ super();
611
+ this.balances = new AddressMemoryMap(this.balancesPointer);
612
+ }
613
+
614
+ public override onDeployment(calldata: Calldata): void {
615
+ const initialSupply = calldata.readU256();
616
+
617
+ this._totalSupply.value = initialSupply;
618
+ this.balances.set(Blockchain.tx.origin, initialSupply);
619
+ }
620
+
621
+ @method(
622
+ { name: 'to', type: ABIDataTypes.ADDRESS },
623
+ { name: 'amount', type: ABIDataTypes.UINT256 },
624
+ )
625
+ @returns({ name: 'success', type: ABIDataTypes.BOOL })
626
+ @emit('Transfer')
627
+ public transfer(calldata: Calldata): BytesWriter {
628
+ const to = calldata.readAddress();
629
+ const amount = calldata.readU256();
630
+ const from = Blockchain.tx.sender;
631
+
632
+ // Validation
633
+ if (to.equals(Address.zero())) {
634
+ throw new Revert('Cannot transfer to zero address');
635
+ }
636
+
637
+ // Get balances
638
+ const fromBalance = this.balances.get(from);
639
+ if (fromBalance < amount) {
640
+ throw new Revert('Insufficient balance');
641
+ }
642
+
643
+ // Update balances
644
+ this.balances.set(from, SafeMath.sub(fromBalance, amount));
645
+ this.balances.set(to, SafeMath.add(this.balances.get(to), amount));
646
+
647
+ return new BytesWriter(0);
648
+ }
649
+
650
+ @method({ name: 'account', type: ABIDataTypes.ADDRESS })
651
+ @returns({ name: 'balance', type: ABIDataTypes.UINT256 })
652
+ public balanceOf(calldata: Calldata): BytesWriter {
653
+ const address = calldata.readAddress();
654
+ const balance = this.balances.get(address);
655
+
656
+ const writer = new BytesWriter(32);
657
+ writer.writeU256(balance);
658
+ return writer;
659
+ }
660
+
661
+ @method()
662
+ @returns({ name: 'supply', type: ABIDataTypes.UINT256 })
663
+ public totalSupply(_calldata: Calldata): BytesWriter {
664
+ const writer = new BytesWriter(32);
665
+ writer.writeU256(this._totalSupply.value);
666
+ return writer;
667
+ }
668
+ }
669
+ ```
670
+
671
+ **Note:** Method routing is handled AUTOMATICALLY via `@method` decorators. No `execute` override is needed.
672
+
673
+ ## Inheritance
674
+
675
+ ### Extending OP_NET
676
+
677
+ ```typescript
678
+ // Direct extension
679
+ export class MyContract extends OP_NET { }
680
+
681
+ // Extend with reentrancy protection
682
+ export class MySecureContract extends ReentrancyGuard { }
683
+
684
+ // Extend with additional features (OP20/OP721 extend ReentrancyGuard which extends OP_NET)
685
+ export class MyToken extends OP20 { } // OP20 extends ReentrancyGuard extends OP_NET
686
+ export class MyNFT extends OP721 { } // OP721 extends ReentrancyGuard extends OP_NET
687
+ ```
688
+
689
+ ### Adding Functionality
690
+
691
+ ```typescript
692
+ // Create a base class with shared functionality
693
+ export abstract class Pausable extends OP_NET {
694
+ private pausedPointer: u16 = Blockchain.nextPointer;
695
+ protected _paused: StoredBoolean = new StoredBoolean(this.pausedPointer, false);
696
+
697
+ protected whenNotPaused(): void {
698
+ if (this._paused.value) {
699
+ throw new Revert('Contract is paused');
700
+ }
701
+ }
702
+ }
703
+
704
+ // Use in your contract
705
+ export class MyToken extends Pausable {
706
+ @method(
707
+ { name: 'to', type: ABIDataTypes.ADDRESS },
708
+ { name: 'amount', type: ABIDataTypes.UINT256 },
709
+ )
710
+ @returns({ name: 'success', type: ABIDataTypes.BOOL })
711
+ @emit('Transfer')
712
+ public transfer(calldata: Calldata): BytesWriter {
713
+ this.whenNotPaused();
714
+ // ...
715
+ }
716
+ }
717
+ ```
718
+
719
+ **Solidity Comparison:**
720
+
721
+ ```solidity
722
+ // Solidity: OpenZeppelin Pausable
723
+ import "@openzeppelin/contracts/security/Pausable.sol";
724
+
725
+ contract MyToken is ERC20, Pausable {
726
+ function transfer(address to, uint256 amount) public whenNotPaused {
727
+ // ...
728
+ }
729
+ }
730
+
731
+ // OPNet: Custom Pausable base class
732
+ // export abstract class Pausable extends OP_NET { ... }
733
+ // this.whenNotPaused(); at start of method
734
+ ```
735
+
736
+ ## Best Practices
737
+
738
+ ### 1. Always Use @final
739
+
740
+ ```typescript
741
+ @final // Prevents further inheritance, enables optimizations
742
+ export class MyContract extends OP_NET { }
743
+ ```
744
+
745
+ ### 2. Call super() in Constructor
746
+
747
+ ```typescript
748
+ public constructor() {
749
+ super(); // Always first!
750
+ // Then your initialization...
751
+ }
752
+ ```
753
+
754
+ ### 3. Use @method Decorators for Public Methods
755
+
756
+ ```typescript
757
+ // CORRECT: Use @method decorator for automatic routing
758
+ @method({ name: 'to', type: ABIDataTypes.ADDRESS }, { name: 'amount', type: ABIDataTypes.UINT256 })
759
+ @returns({ name: 'success', type: ABIDataTypes.BOOL })
760
+ @emit('Transfer')
761
+ public transfer(calldata: Calldata): BytesWriter {
762
+ // Implementation...
763
+ }
764
+
765
+ // DO NOT manually override execute() - routing is automatic
766
+ ```
767
+
768
+ ### 4. Document Your Methods
769
+
770
+ ```typescript
771
+ /**
772
+ * Transfers tokens from sender to recipient.
773
+ * @param calldata Contains: to (Address), amount (u256)
774
+ * @returns Empty BytesWriter on success
775
+ * @throws Revert if insufficient balance or zero address
776
+ */
777
+ private transfer(calldata: Calldata): BytesWriter {
778
+ // ...
779
+ }
780
+ ```
781
+
782
+ ---
783
+
784
+ **Navigation:**
785
+ - Previous: [Security](../core-concepts/security.md)
786
+ - Next: [OP20 Token](./op20-token.md)