@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,687 @@
1
+ # OP20 Token Standard
2
+
3
+ OP20 is OPNet's fungible token standard, equivalent to Ethereum's ERC20. It provides a complete implementation for creating tokens with transfer, approval, and balance tracking capabilities.
4
+
5
+ ## Overview
6
+
7
+ ```typescript
8
+ import { u256 } from '@btc-vision/as-bignum/assembly';
9
+ import {
10
+ OP20,
11
+ OP20InitParameters,
12
+ Blockchain,
13
+ Calldata,
14
+ BytesWriter,
15
+ ABIDataTypes,
16
+ } from '@btc-vision/btc-runtime/runtime';
17
+
18
+ @final
19
+ export class MyToken extends OP20 {
20
+ public constructor() {
21
+ super();
22
+ }
23
+
24
+ public override onDeployment(_calldata: Calldata): void {
25
+ this.instantiate(new OP20InitParameters(
26
+ u256.fromString('1000000000000000000000000'), // maxSupply: 1M tokens
27
+ 18, // decimals
28
+ 'MyToken', // name
29
+ 'MTK', // symbol
30
+ 'https://example.com/icon.png' // icon (optional)
31
+ ));
32
+
33
+ // Mint initial supply to deployer
34
+ this._mint(Blockchain.tx.origin, this._maxSupply.value);
35
+ }
36
+ }
37
+ ```
38
+
39
+ ## ERC20 vs OP20 Comparison
40
+
41
+ | Feature | ERC20 (Solidity) | OP20 (OPNet) |
42
+ |---------|------------------|--------------|
43
+ | Language | Solidity | AssemblyScript |
44
+ | Runtime | EVM | WASM |
45
+ | Integer Type | `uint256` | `u256` |
46
+ | Max Decimals | 18 (convention) | 32 (hard limit) |
47
+ | Max Supply | Unlimited | Enforced at instantiation |
48
+ | Approval Pattern | `approve()` + `transferFrom()` | `increaseAllowance()`/`decreaseAllowance()` + `transferFrom()` |
49
+ | Unlimited Approval | Decremented on transfer | Optimized - not decremented |
50
+ | Events | Solidity events | `emitEvent()` system |
51
+ | Inheritance | Multiple inheritance | Single inheritance |
52
+
53
+ ## Initialization
54
+
55
+ ### OP20InitParameters
56
+
57
+ | Parameter | Type | Description |
58
+ |-----------|------|-------------|
59
+ | `maxSupply` | `u256` | Maximum token supply (cannot be exceeded) |
60
+ | `decimals` | `u8` | Decimal places (max 32) |
61
+ | `name` | `string` | Token name |
62
+ | `symbol` | `string` | Token symbol |
63
+ | `icon` | `string` | Token icon URL (optional, defaults to empty string) |
64
+
65
+ ```typescript
66
+ const params = new OP20InitParameters(
67
+ u256.fromString('1000000000000000000000000000'), // 1 billion with 18 decimals
68
+ 18,
69
+ 'My Token',
70
+ 'MTK',
71
+ 'https://example.com/icon.png' // optional
72
+ );
73
+
74
+ this.instantiate(params);
75
+ ```
76
+
77
+ ### Decimal Limit
78
+
79
+ **IMPORTANT:** Decimals cannot exceed 32.
80
+
81
+ ```typescript
82
+ // Valid
83
+ const params = new OP20InitParameters(maxSupply, 18, name, symbol);
84
+
85
+ // Invalid - will throw
86
+ const params = new OP20InitParameters(maxSupply, 33, name, symbol);
87
+ ```
88
+
89
+ ## Transfer Flow
90
+
91
+ The following diagram illustrates how a token transfer is processed:
92
+
93
+ ```mermaid
94
+ ---
95
+ config:
96
+ theme: dark
97
+ ---
98
+ flowchart LR
99
+ A[👤 User signs TX] --> B[Submit to blockchain]
100
+ B --> C[Contract.transfer called]
101
+ C --> D{Valid recipient?}
102
+ D -->|No| E[Revert]
103
+ D -->|Yes| F{Sufficient balance?}
104
+ F -->|No| G[Revert]
105
+ F -->|Yes| H[Subtract from sender]
106
+ H --> I[Add to recipient]
107
+ I --> J[Emit TransferredEvent]
108
+ J --> K[Return success]
109
+ ```
110
+
111
+ ### Detailed Transfer Sequence
112
+
113
+ ```mermaid
114
+ sequenceDiagram
115
+ participant User as 👤 User Wallet
116
+ participant Blockchain as Bitcoin L1
117
+ participant VM as WASM Runtime
118
+ participant OP20 as OP20 Contract
119
+ participant Calldata as Calldata Reader
120
+ participant Storage as Storage Pointers
121
+ participant BalanceMap as balanceOfMap<br/>(Pointer 5)
122
+ participant TotalSupply as _totalSupply<br/>(Pointer 4)
123
+ participant EventLog as Event Log System
124
+
125
+ User->>Blockchain: Submit transfer(to, amount) TX
126
+ Blockchain->>VM: Execute transaction
127
+ VM->>OP20: Call transfer method
128
+
129
+ activate OP20
130
+
131
+ OP20->>Calldata: readAddress()
132
+ Calldata-->>OP20: to address
133
+ OP20->>Calldata: readU256()
134
+ Calldata-->>OP20: amount
135
+
136
+ OP20->>OP20: Get sender = Blockchain.tx.sender
137
+ Note over OP20: sender is msg.sender equivalent
138
+
139
+ OP20->>OP20: Validate to != Address.zero()
140
+
141
+ alt to is zero address
142
+ OP20->>VM: Revert('Cannot transfer to zero address')
143
+ VM->>User: Transaction failed
144
+ else Valid recipient
145
+ OP20->>OP20: _transfer(sender, to, amount)
146
+
147
+ OP20->>BalanceMap: get(sender)
148
+ BalanceMap->>Storage: Read from storage slot
149
+ Storage-->>BalanceMap: Raw balance data
150
+ BalanceMap-->>OP20: senderBalance: u256
151
+
152
+ alt Insufficient balance
153
+ OP20->>VM: Revert('Insufficient balance')
154
+ VM->>User: Transaction failed
155
+ else Sufficient balance
156
+ OP20->>OP20: SafeMath.sub(senderBalance, amount)
157
+ Note over OP20: Underflow protection
158
+
159
+ OP20->>BalanceMap: set(sender, newSenderBalance)
160
+ BalanceMap->>Storage: Write to storage slot
161
+ Note over Storage: Persistent state change
162
+
163
+ OP20->>BalanceMap: get(to)
164
+ BalanceMap->>Storage: Read recipient balance
165
+ Storage-->>BalanceMap: Recipient balance
166
+ BalanceMap-->>OP20: recipientBalance: u256
167
+
168
+ OP20->>OP20: SafeMath.add(recipientBalance, amount)
169
+ Note over OP20: Overflow protection
170
+
171
+ OP20->>BalanceMap: set(to, newRecipientBalance)
172
+ BalanceMap->>Storage: Write updated balance
173
+ Note over Storage: Both balances now updated
174
+
175
+ OP20->>OP20: Create TransferredEvent(operator, from, to, amount)
176
+ OP20->>EventLog: emitEvent(transferEvent)
177
+ Note over EventLog: Indexed for off-chain queries
178
+
179
+ OP20->>VM: Return BytesWriter(0)
180
+ deactivate OP20
181
+
182
+ VM->>Blockchain: Commit state changes
183
+ Blockchain->>User: Transaction success + receipt
184
+ Note over User: Balance updated,<br/>event emitted
185
+ end
186
+ end
187
+ ```
188
+
189
+ ## Token Lifecycle
190
+
191
+ ```mermaid
192
+ stateDiagram-v2
193
+ [*] --> Undeployed
194
+ Undeployed --> Deployed: onDeployment(params)
195
+
196
+ state Deployed {
197
+ [*] --> ZeroSupply
198
+ ZeroSupply --> HasSupply: _mint()
199
+ HasSupply --> HasSupply: transfer()
200
+ HasSupply --> HasSupply: increaseAllowance()
201
+ HasSupply --> HasSupply: transferFrom()
202
+ HasSupply --> LowerSupply: _burn()
203
+ LowerSupply --> HasSupply: _mint()
204
+ LowerSupply --> ZeroSupply: _burn() all
205
+
206
+ state "Total Supply Management" as Supply {
207
+ [*] --> BelowMax
208
+ BelowMax --> BelowMax: _mint() within limit
209
+ BelowMax --> AtMax: _mint() to maxSupply
210
+ AtMax --> BelowMax: _burn()
211
+ BelowMax --> [*]: totalSupply = 0
212
+ }
213
+ }
214
+ ```
215
+
216
+ ## Built-in Methods
217
+
218
+ OP20 provides these methods automatically:
219
+
220
+ ### Query Methods
221
+
222
+ | Method | Returns | Description |
223
+ |--------|---------|-------------|
224
+ | `name()` | `string` | Token name |
225
+ | `symbol()` | `string` | Token symbol |
226
+ | `icon()` | `string` | Token icon URL |
227
+ | `decimals()` | `u8` | Decimal places |
228
+ | `totalSupply()` | `u256` | Current total supply |
229
+ | `maximumSupply()` | `u256` | Maximum possible supply |
230
+ | `balanceOf(owner)` | `u256` | Balance of address |
231
+ | `allowance(owner, spender)` | `u256` | Approved amount |
232
+ | `nonceOf(owner)` | `u256` | Nonce for signature verification |
233
+ | `domainSeparator()` | `bytes32` | EIP-712 domain separator |
234
+ | `metadata()` | `multiple` | All token metadata in one call |
235
+
236
+ ### Transfer Methods
237
+
238
+ | Method | Description |
239
+ |--------|-------------|
240
+ | `transfer(to, amount)` | Transfer tokens from sender |
241
+ | `transferFrom(from, to, amount)` | Transfer using approval |
242
+ | `safeTransfer(to, amount, data)` | Transfer with recipient callback |
243
+ | `safeTransferFrom(from, to, amount, data)` | TransferFrom with recipient callback |
244
+
245
+ ### Approval Methods
246
+
247
+ | Method | Description |
248
+ |--------|-------------|
249
+ | `increaseAllowance(spender, amount)` | Increase approval |
250
+ | `decreaseAllowance(spender, amount)` | Decrease approval |
251
+ | `increaseAllowanceBySignature(...)` | Gasless approval increase via signature |
252
+ | `decreaseAllowanceBySignature(...)` | Gasless approval decrease via signature |
253
+
254
+ ### Other Methods
255
+
256
+ | Method | Description |
257
+ |--------|-------------|
258
+ | `burn(amount)` | Burn tokens from sender's balance |
259
+
260
+ ## Approval Flow
261
+
262
+ The following diagram shows how the approval and transferFrom pattern works:
263
+
264
+ ```mermaid
265
+ ---
266
+ config:
267
+ theme: dark
268
+ ---
269
+ flowchart LR
270
+ A[👤 User increases allowance] --> B[Set allowance in storage]
271
+ B --> C[Emit ApprovedEvent]
272
+ C --> D[Spender calls transferFrom]
273
+ D --> E{Sufficient allowance?}
274
+ E -->|No| F[Revert]
275
+ E -->|Yes| G{Unlimited approval?}
276
+ G -->|Yes| H[Skip allowance update]
277
+ G -->|No| I[Decrease allowance]
278
+ H --> J[Execute transfer]
279
+ I --> J
280
+ J --> K[Update balances]
281
+ K --> L[Emit TransferredEvent]
282
+ ```
283
+
284
+ ## Solidity Comparison
285
+
286
+ <table>
287
+ <tr>
288
+ <th>ERC20 (Solidity)</th>
289
+ <th>OP20 (OPNet)</th>
290
+ </tr>
291
+ <tr>
292
+ <td>
293
+
294
+ ```solidity
295
+ contract MyToken is ERC20 {
296
+ constructor()
297
+ ERC20("MyToken", "MTK")
298
+ {
299
+ _mint(msg.sender, 1000000 * 10**18);
300
+ }
301
+ }
302
+ ```
303
+
304
+ </td>
305
+ <td>
306
+
307
+ ```typescript
308
+ @final
309
+ export class MyToken extends OP20 {
310
+ constructor() {
311
+ super();
312
+ }
313
+
314
+ public override onDeployment(_: Calldata): void {
315
+ this.instantiate(new OP20InitParameters(
316
+ u256.fromString('1000000000000000000000000'),
317
+ 18, 'MyToken', 'MTK', ''
318
+ ));
319
+ this._mint(Blockchain.tx.origin, this._maxSupply.value);
320
+ }
321
+ }
322
+ ```
323
+
324
+ </td>
325
+ </tr>
326
+ </table>
327
+
328
+ ## Storage Layout
329
+
330
+ OP20 uses the following storage pointers internally:
331
+
332
+ | Pointer | Storage | Description |
333
+ |---------|---------|-------------|
334
+ | 0 | `nonceMap` | Address -> nonce mapping (for signatures) |
335
+ | 1 | `maxSupply` | Maximum token supply |
336
+ | 2 | `decimals` | Decimal places |
337
+ | 3 | `stringPointer` | Shared pointer for name (sub 0), symbol (sub 1), icon (sub 2) |
338
+ | 4 | `totalSupply` | Current total supply |
339
+ | 5 | `allowanceMap` | Owner -> spender -> amount mapping |
340
+ | 6 | `balanceOfMap` | Address -> balance mapping |
341
+
342
+ **Note:** Your contract's pointers start after OP20's internal pointers (pointer 7+).
343
+
344
+ ## Extending OP20
345
+
346
+ ### Adding Custom Methods
347
+
348
+ ```typescript
349
+ @final
350
+ export class MyToken extends OP20 {
351
+ public constructor() {
352
+ super();
353
+ }
354
+
355
+ public override onDeployment(calldata: Calldata): void {
356
+ this.instantiate(new OP20InitParameters(
357
+ u256.fromString('1000000000000000000000000'),
358
+ 18, 'MyToken', 'MTK'
359
+ ));
360
+ }
361
+
362
+ // Custom mint function (OP20 does not have a built-in public mint)
363
+ @method(
364
+ { name: 'to', type: ABIDataTypes.ADDRESS },
365
+ { name: 'amount', type: ABIDataTypes.UINT256 },
366
+ )
367
+ @returns({ name: 'success', type: ABIDataTypes.BOOL })
368
+ @emit('Minted')
369
+ public mint(calldata: Calldata): BytesWriter {
370
+ this.onlyDeployer(Blockchain.tx.sender);
371
+
372
+ const to = calldata.readAddress();
373
+ const amount = calldata.readU256();
374
+
375
+ this._mint(to, amount);
376
+ // Note: _mint() already emits MintedEvent internally
377
+
378
+ return new BytesWriter(0);
379
+ }
380
+
381
+ // Note: OP20 already has a public burn(amount) method built-in
382
+ // You can override it if you need custom behavior:
383
+ // public override burn(calldata: Calldata): BytesWriter { ... }
384
+ }
385
+ ```
386
+
387
+ ### Internal Methods
388
+
389
+ OP20 provides protected methods for extending functionality:
390
+
391
+ | Method | Description |
392
+ |--------|-------------|
393
+ | `_mint(to, amount)` | Mint new tokens |
394
+ | `_burn(from, amount)` | Burn tokens |
395
+ | `_transfer(from, to, amount)` | Internal transfer |
396
+ | `_balanceOf(owner)` | Get balance of address |
397
+ | `_allowance(owner, spender)` | Get allowance amount |
398
+ | `_increaseAllowance(owner, spender, amount)` | Increase allowance with overflow protection |
399
+ | `_decreaseAllowance(owner, spender, amount)` | Decrease allowance with underflow protection |
400
+ | `_spendAllowance(owner, spender, amount)` | Spend from allowance (for transferFrom) |
401
+ | `_safeTransfer(from, to, amount, data)` | Transfer with receiver callback |
402
+
403
+ ```typescript
404
+ // Minting tokens
405
+ this._mint(recipient, amount);
406
+
407
+ // Burning tokens
408
+ this._burn(holder, amount);
409
+
410
+ // Internal transfer (no sender checks)
411
+ this._transfer(from, to, amount);
412
+ ```
413
+
414
+ ## Events
415
+
416
+ OP20 emits these events automatically:
417
+
418
+ ### TransferredEvent
419
+
420
+ ```typescript
421
+ // Emitted on transfer(), transferFrom(), safeTransfer(), safeTransferFrom()
422
+ TransferredEvent(operator: Address, from: Address, to: Address, amount: u256)
423
+
424
+ // operator: the address that initiated the transfer (Blockchain.tx.sender)
425
+ // from: the address tokens are transferred from
426
+ // to: the address tokens are transferred to
427
+ // amount: the number of tokens transferred
428
+ ```
429
+
430
+ ### ApprovedEvent
431
+
432
+ ```typescript
433
+ // Emitted on increaseAllowance(), decreaseAllowance()
434
+ ApprovedEvent(owner: Address, spender: Address, amount: u256)
435
+ ```
436
+
437
+ ### MintedEvent
438
+
439
+ ```typescript
440
+ // Emitted when new tokens are minted via _mint()
441
+ MintedEvent(to: Address, amount: u256)
442
+ ```
443
+
444
+ ### BurnedEvent
445
+
446
+ ```typescript
447
+ // Emitted when tokens are burned via burn() or _burn()
448
+ BurnedEvent(from: Address, amount: u256)
449
+ ```
450
+
451
+ ## Approval Patterns
452
+
453
+ The following state diagram shows how an allowance transitions between different states:
454
+
455
+ ```mermaid
456
+ ---
457
+ config:
458
+ theme: dark
459
+ ---
460
+ stateDiagram-v2
461
+ [*] --> NoAllowance
462
+
463
+ NoAllowance --> LimitedAllowance: increaseAllowance(amount)
464
+ NoAllowance --> UnlimitedAllowance: increaseAllowance(u256.Max)
465
+
466
+ LimitedAllowance --> LimitedAllowance: increaseAllowance(delta)
467
+ LimitedAllowance --> LimitedAllowance: decreaseAllowance(delta)
468
+ LimitedAllowance --> LimitedAllowance: transferFrom (decrements)
469
+ LimitedAllowance --> NoAllowance: transferFrom (exhausted)
470
+ LimitedAllowance --> NoAllowance: decreaseAllowance(all)
471
+ LimitedAllowance --> UnlimitedAllowance: increaseAllowance (overflow)
472
+
473
+ UnlimitedAllowance --> UnlimitedAllowance: transferFrom (no change)
474
+ UnlimitedAllowance --> LimitedAllowance: decreaseAllowance(amount)
475
+ UnlimitedAllowance --> NoAllowance: decreaseAllowance(all)
476
+ ```
477
+
478
+ ### Standard Approval
479
+
480
+ ```typescript
481
+ // User increases allowance for spender
482
+ increaseAllowance(spender, 1000);
483
+
484
+ // Spender can transfer up to 1000 tokens
485
+ transferFrom(user, recipient, 500); // Allowance now 500
486
+ transferFrom(user, recipient, 500); // Allowance now 0
487
+ ```
488
+
489
+ ### Unlimited Approval
490
+
491
+ ```typescript
492
+ // Increase allowance to maximum - overflows to u256.Max
493
+ increaseAllowance(spender, u256.Max);
494
+
495
+ // Transfers don't reduce unlimited allowance
496
+ transferFrom(user, recipient, 1000); // Allowance still u256.Max
497
+ ```
498
+
499
+ **Note:** OP20 optimizes unlimited approvals (u256.Max) - they're not decremented on transfer.
500
+
501
+ ### Increase/Decrease Pattern
502
+
503
+ ```typescript
504
+ // Safe pattern using increase/decrease (prevents front-running)
505
+ increaseAllowance(spender, 100); // Add 100 to current allowance
506
+ decreaseAllowance(spender, 50); // Remove 50 from current allowance
507
+
508
+ // Note: If decrease amount > current allowance, it sets to zero (no underflow)
509
+ // If increase would overflow, it sets to u256.Max (unlimited)
510
+ ```
511
+
512
+ ## Edge Cases
513
+
514
+ The following state diagram shows how token balances transition for an individual address:
515
+
516
+ ```mermaid
517
+ ---
518
+ config:
519
+ theme: dark
520
+ ---
521
+ stateDiagram-v2
522
+ [*] --> ZeroBalance
523
+
524
+ ZeroBalance --> HasBalance: receive tokens
525
+
526
+ HasBalance --> HasBalance: transfer (partial)
527
+ HasBalance --> HasBalance: receive more
528
+ HasBalance --> ZeroBalance: transfer (all)
529
+ HasBalance --> ZeroBalance: burn (all)
530
+
531
+ note right of HasBalance
532
+ Balance can increase via:
533
+ - _mint()
534
+ - transfer()
535
+ - transferFrom()
536
+ end note
537
+ ```
538
+
539
+ ### Zero Address
540
+
541
+ ```typescript
542
+ // Transfer to zero address reverts
543
+ transfer(Address.zero(), amount); // Throws: "Cannot transfer to zero address"
544
+
545
+ // Minting to zero address reverts
546
+ _mint(Address.zero(), amount); // Throws
547
+
548
+ // Burning from zero address reverts
549
+ _burn(Address.zero(), amount); // Throws
550
+ ```
551
+
552
+ ### Overflow Protection
553
+
554
+ ```typescript
555
+ // Minting beyond maxSupply reverts
556
+ _mint(to, amount); // Throws if totalSupply + amount > maxSupply
557
+
558
+ // All arithmetic uses SafeMath
559
+ // Overflow/underflow automatically reverts
560
+ ```
561
+
562
+ ### Self-Approval
563
+
564
+ ```typescript
565
+ // Approving yourself is valid but pointless
566
+ increaseAllowance(Blockchain.tx.sender, amount); // Works, but why?
567
+ ```
568
+
569
+ ## Complete Token Example
570
+
571
+ ```typescript
572
+ import { u256 } from '@btc-vision/as-bignum/assembly';
573
+ import {
574
+ OP20,
575
+ OP20InitParameters,
576
+ Blockchain,
577
+ Address,
578
+ Calldata,
579
+ BytesWriter,
580
+ Selector,
581
+ SafeMath,
582
+ Revert,
583
+ StoredBoolean,
584
+ AddressMemoryMap,
585
+ ABIDataTypes,
586
+ } from '@btc-vision/btc-runtime/runtime';
587
+
588
+ @final
589
+ export class AdvancedToken extends OP20 {
590
+ // Additional storage
591
+ private pausedPointer: u16 = Blockchain.nextPointer;
592
+ private blacklistPointer: u16 = Blockchain.nextPointer;
593
+
594
+ private _paused: StoredBoolean = new StoredBoolean(this.pausedPointer, false);
595
+ private _blacklist: AddressMemoryMap;
596
+
597
+ public constructor() {
598
+ super();
599
+ this._blacklist = new AddressMemoryMap(this.blacklistPointer);
600
+ }
601
+
602
+ public override onDeployment(calldata: Calldata): void {
603
+ const maxSupply = calldata.readU256();
604
+ const decimals = calldata.readU8();
605
+ const name = calldata.readString();
606
+ const symbol = calldata.readString();
607
+
608
+ this.instantiate(new OP20InitParameters(maxSupply, decimals, name, symbol));
609
+ }
610
+
611
+ // Override transfer to add checks
612
+ public override transfer(calldata: Calldata): BytesWriter {
613
+ this.whenNotPaused();
614
+ this.checkBlacklist(Blockchain.tx.sender);
615
+
616
+ return super.transfer(calldata);
617
+ }
618
+
619
+ // Admin: Mint tokens
620
+ @method(
621
+ { name: 'to', type: ABIDataTypes.ADDRESS },
622
+ { name: 'amount', type: ABIDataTypes.UINT256 },
623
+ )
624
+ @returns({ name: 'success', type: ABIDataTypes.BOOL })
625
+ @emit('Minted')
626
+ public mint(calldata: Calldata): BytesWriter {
627
+ this.onlyDeployer(Blockchain.tx.sender);
628
+ this._mint(calldata.readAddress(), calldata.readU256());
629
+ // Note: _mint() already emits MintedEvent internally
630
+ return new BytesWriter(0);
631
+ }
632
+
633
+ // Admin: Pause/unpause
634
+ @method()
635
+ @returns({ name: 'success', type: ABIDataTypes.BOOL })
636
+ public pause(_calldata: Calldata): BytesWriter {
637
+ this.onlyDeployer(Blockchain.tx.sender);
638
+ this._paused.value = true;
639
+ return new BytesWriter(0);
640
+ }
641
+
642
+ @method()
643
+ @returns({ name: 'success', type: ABIDataTypes.BOOL })
644
+ public unpause(_calldata: Calldata): BytesWriter {
645
+ this.onlyDeployer(Blockchain.tx.sender);
646
+ this._paused.value = false;
647
+ return new BytesWriter(0);
648
+ }
649
+
650
+ // Admin: Blacklist management
651
+ @method({ name: 'address', type: ABIDataTypes.ADDRESS })
652
+ @returns({ name: 'success', type: ABIDataTypes.BOOL })
653
+ public blacklist(calldata: Calldata): BytesWriter {
654
+ this.onlyDeployer(Blockchain.tx.sender);
655
+ this._blacklist.set(calldata.readAddress(), true);
656
+ return new BytesWriter(0);
657
+ }
658
+
659
+ // Internal helpers
660
+ private whenNotPaused(): void {
661
+ if (this._paused.value) {
662
+ throw new Revert('Token is paused');
663
+ }
664
+ }
665
+
666
+ private checkBlacklist(address: Address): void {
667
+ if (this._blacklist.get(address)) {
668
+ throw new Revert('Address is blacklisted');
669
+ }
670
+ }
671
+ }
672
+ ```
673
+
674
+ ## Best Practices
675
+
676
+ 1. **Always call `instantiate()` in `onDeployment`**
677
+ 2. **Use SafeMath for any custom arithmetic**
678
+ 3. **Emit events for custom state changes**
679
+ 4. **Validate all inputs before processing**
680
+ 5. **Use `_mint`/`_burn` for supply changes**
681
+ 6. **Override `transfer` carefully (call `super`)**
682
+
683
+ ---
684
+
685
+ **Navigation:**
686
+ - Previous: [OP_NET Base](./op-net-base.md)
687
+ - Next: [OP20S Signatures](./op20s-signatures.md)