@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,902 @@
1
+ # OP20 API Reference
2
+
3
+ The `OP20` class implements the fungible token standard, equivalent to ERC20 on Ethereum.
4
+
5
+ ## Import
6
+
7
+ ```typescript
8
+ import { OP20, OP20InitParameters } from '@btc-vision/btc-runtime/runtime';
9
+ ```
10
+
11
+ ## OP20 Architecture
12
+
13
+ ```mermaid
14
+ classDiagram
15
+ class OP20 {
16
+ <<abstract>>
17
+ +_totalSupply: StoredU256
18
+ #_maxSupply: StoredU256
19
+ #_decimals: StoredU256
20
+ #_name: StoredString
21
+ #_symbol: StoredString
22
+ #_icon: StoredString
23
+ #balanceOfMap: AddressMemoryMap
24
+ #allowanceMap: MapOfMap~u256~
25
+ #_nonceMap: AddressMemoryMap
26
+ +instantiate(params, skipDeployerVerification) void
27
+ +name(calldata) BytesWriter
28
+ +symbol(calldata) BytesWriter
29
+ +icon(calldata) BytesWriter
30
+ +decimals(calldata) BytesWriter
31
+ +totalSupply(calldata) BytesWriter
32
+ +maximumSupply(calldata) BytesWriter
33
+ +balanceOf(calldata) BytesWriter
34
+ +allowance(calldata) BytesWriter
35
+ +nonceOf(calldata) BytesWriter
36
+ +domainSeparator(calldata) BytesWriter
37
+ +metadata(calldata) BytesWriter
38
+ +transfer(calldata) BytesWriter
39
+ +transferFrom(calldata) BytesWriter
40
+ +safeTransfer(calldata) BytesWriter
41
+ +safeTransferFrom(calldata) BytesWriter
42
+ +increaseAllowance(calldata) BytesWriter
43
+ +decreaseAllowance(calldata) BytesWriter
44
+ +increaseAllowanceBySignature(calldata) BytesWriter
45
+ +decreaseAllowanceBySignature(calldata) BytesWriter
46
+ +burn(calldata) BytesWriter
47
+ #_mint(to, amount) void
48
+ #_burn(from, amount) void
49
+ #_transfer(from, to, amount) void
50
+ #_balanceOf(owner) u256
51
+ #_allowance(owner, spender) u256
52
+ #_increaseAllowance(owner, spender, amount) void
53
+ #_decreaseAllowance(owner, spender, amount) void
54
+ #_spendAllowance(owner, spender, amount) void
55
+ #_safeTransfer(from, to, amount, data) void
56
+ }
57
+
58
+ class ReentrancyGuard {
59
+ <<abstract>>
60
+ #reentrancyLevel: ReentrancyLevel
61
+ #isSelectorExcluded(selector) bool
62
+ }
63
+
64
+ class OP_NET {
65
+ <<abstract>>
66
+ +address: Address
67
+ +contractDeployer: Address
68
+ #emitEvent(event) void
69
+ #onlyDeployer(caller) void
70
+ Note: @method decorator handles routing
71
+ }
72
+
73
+ class MyToken {
74
+ +constructor()
75
+ +onDeployment(calldata) void
76
+ +mint(calldata) BytesWriter
77
+ Note: @method decorator handles routing
78
+ }
79
+
80
+ OP_NET <|-- ReentrancyGuard
81
+ ReentrancyGuard <|-- OP20
82
+ OP20 <|-- MyToken
83
+
84
+ note for OP20 "Fungible token standard\nwith safe math and\nreentrancy protection"
85
+ note for MyToken "Your implementation\nextends OP20"
86
+ ```
87
+
88
+ ## Class Definition
89
+
90
+ ```typescript
91
+ @final
92
+ export class MyToken extends OP20 {
93
+ public constructor() {
94
+ super();
95
+ }
96
+
97
+ public override onDeployment(calldata: Calldata): void {
98
+ this.instantiate(new OP20InitParameters(
99
+ maxSupply,
100
+ decimals,
101
+ name,
102
+ symbol,
103
+ icon
104
+ ));
105
+ }
106
+ }
107
+ ```
108
+
109
+ ## Initialization
110
+
111
+ ### OP20InitParameters
112
+
113
+ ```typescript
114
+ class OP20InitParameters {
115
+ constructor(
116
+ maxSupply: u256,
117
+ decimals: u8,
118
+ name: string,
119
+ symbol: string,
120
+ icon: string = ''
121
+ )
122
+ }
123
+ ```
124
+
125
+ | Parameter | Type | Description |
126
+ |-----------|------|-------------|
127
+ | `maxSupply` | `u256` | Maximum token supply (use `u256.Max` for unlimited) |
128
+ | `decimals` | `u8` | Token decimals (max 32) |
129
+ | `name` | `string` | Token name |
130
+ | `symbol` | `string` | Token symbol |
131
+ | `icon` | `string` | Token icon URL (optional, default empty string) |
132
+
133
+ ### instantiate
134
+
135
+ Initializes the OP20 token. Must be called in `onDeployment`.
136
+
137
+ ```typescript
138
+ public instantiate(params: OP20InitParameters, skipDeployerVerification: boolean = false): void
139
+ ```
140
+
141
+ ```typescript
142
+ public override onDeployment(calldata: Calldata): void {
143
+ this.instantiate(new OP20InitParameters(
144
+ u256.fromU64(1000000), // Max supply: 1M
145
+ 18, // 18 decimals
146
+ 'My Token', // Name
147
+ 'MTK', // Symbol
148
+ '' // Icon URL (optional)
149
+ ));
150
+ }
151
+ ```
152
+
153
+ **Solidity Comparison:**
154
+ | Solidity (ERC20) | OPNet (OP20) |
155
+ |------------------|--------------|
156
+ | `constructor(string name, string symbol)` | `onDeployment(calldata)` + `instantiate()` |
157
+
158
+ ## View Methods
159
+
160
+ All view methods in OP20 take `Calldata` and return `BytesWriter`. The `@method` decorator handles ABI encoding/decoding.
161
+
162
+ ### name
163
+
164
+ Returns the token name.
165
+
166
+ ```typescript
167
+ @method()
168
+ @returns({ name: 'name', type: ABIDataTypes.STRING })
169
+ public name(calldata: Calldata): BytesWriter
170
+ ```
171
+
172
+ ### symbol
173
+
174
+ Returns the token symbol.
175
+
176
+ ```typescript
177
+ @method()
178
+ @returns({ name: 'symbol', type: ABIDataTypes.STRING })
179
+ public symbol(calldata: Calldata): BytesWriter
180
+ ```
181
+
182
+ ### icon
183
+
184
+ Returns the token icon URL.
185
+
186
+ ```typescript
187
+ @method()
188
+ @returns({ name: 'icon', type: ABIDataTypes.STRING })
189
+ public icon(calldata: Calldata): BytesWriter
190
+ ```
191
+
192
+ ### decimals
193
+
194
+ Returns the number of decimals.
195
+
196
+ ```typescript
197
+ @method()
198
+ @returns({ name: 'decimals', type: ABIDataTypes.UINT8 })
199
+ public decimals(calldata: Calldata): BytesWriter
200
+ ```
201
+
202
+ ### nonceOf
203
+
204
+ Returns the current nonce for an address (used for signature verification).
205
+
206
+ ```typescript
207
+ @method({ name: 'owner', type: ABIDataTypes.ADDRESS })
208
+ @returns({ name: 'nonce', type: ABIDataTypes.UINT256 })
209
+ public nonceOf(calldata: Calldata): BytesWriter
210
+ ```
211
+
212
+ ### domainSeparator
213
+
214
+ Returns the EIP-712 domain separator for signature verification.
215
+
216
+ ```typescript
217
+ @method()
218
+ @returns({ name: 'domainSeparator', type: ABIDataTypes.BYTES32 })
219
+ public domainSeparator(calldata: Calldata): BytesWriter
220
+ ```
221
+
222
+ ### metadata
223
+
224
+ Returns all token metadata in a single call (name, symbol, icon, decimals, totalSupply, domainSeparator).
225
+
226
+ ```typescript
227
+ @method()
228
+ @returns(
229
+ { name: 'name', type: ABIDataTypes.STRING },
230
+ { name: 'symbol', type: ABIDataTypes.STRING },
231
+ { name: 'icon', type: ABIDataTypes.STRING },
232
+ { name: 'decimals', type: ABIDataTypes.UINT8 },
233
+ { name: 'totalSupply', type: ABIDataTypes.UINT256 },
234
+ { name: 'domainSeparator', type: ABIDataTypes.BYTES32 },
235
+ )
236
+ public metadata(calldata: Calldata): BytesWriter
237
+ ```
238
+
239
+ ### totalSupply
240
+
241
+ Returns current total supply.
242
+
243
+ ```typescript
244
+ @method()
245
+ @returns({ name: 'totalSupply', type: ABIDataTypes.UINT256 })
246
+ public totalSupply(calldata: Calldata): BytesWriter
247
+ ```
248
+
249
+ ### maximumSupply
250
+
251
+ Returns maximum possible supply.
252
+
253
+ ```typescript
254
+ @method()
255
+ @returns({ name: 'maximumSupply', type: ABIDataTypes.UINT256 })
256
+ public maximumSupply(calldata: Calldata): BytesWriter
257
+ ```
258
+
259
+ ### balanceOf
260
+
261
+ Returns balance of an address.
262
+
263
+ ```typescript
264
+ @method({ name: 'owner', type: ABIDataTypes.ADDRESS })
265
+ @returns({ name: 'balance', type: ABIDataTypes.UINT256 })
266
+ public balanceOf(calldata: Calldata): BytesWriter
267
+ ```
268
+
269
+ Internal helper (for use within your contract):
270
+
271
+ ```typescript
272
+ protected _balanceOf(owner: Address): u256
273
+ ```
274
+
275
+ ### allowance
276
+
277
+ Returns spending allowance.
278
+
279
+ ```typescript
280
+ @method(
281
+ { name: 'owner', type: ABIDataTypes.ADDRESS },
282
+ { name: 'spender', type: ABIDataTypes.ADDRESS },
283
+ )
284
+ @returns({ name: 'remaining', type: ABIDataTypes.UINT256 })
285
+ public allowance(calldata: Calldata): BytesWriter
286
+ ```
287
+
288
+ Internal helper (for use within your contract):
289
+
290
+ ```typescript
291
+ protected _allowance(owner: Address, spender: Address): u256
292
+ ```
293
+
294
+ **Solidity Comparison:**
295
+ | Solidity (ERC20) | OPNet (OP20) |
296
+ |------------------|--------------|
297
+ | `function name() view returns (string)` | `name(calldata): BytesWriter` |
298
+ | `function balanceOf(address) view returns (uint256)` | `balanceOf(calldata): BytesWriter` |
299
+ | `function allowance(address, address) view returns (uint256)` | `allowance(calldata): BytesWriter` |
300
+
301
+ ## Transfer Methods
302
+
303
+ ### transfer (Calldata)
304
+
305
+ Transfers tokens from caller to recipient.
306
+
307
+ ```typescript
308
+ public transfer(calldata: Calldata): BytesWriter
309
+ ```
310
+
311
+ **Calldata format:**
312
+ | Field | Type | Size |
313
+ |-------|------|------|
314
+ | to | Address | 32 bytes |
315
+ | amount | u256 | 32 bytes |
316
+
317
+ **Returns:** Boolean (success)
318
+
319
+ The following diagram shows the complete transfer validation and execution flow:
320
+
321
+ ```mermaid
322
+ flowchart LR
323
+ subgraph "Validation"
324
+ Start([Transfer Request]) --> CheckFrom{from == zero?}
325
+ CheckFrom -->|Yes| Revert1[Revert: Invalid sender]
326
+ CheckFrom -->|No| CheckTo{to == zero?}
327
+ CheckTo -->|Yes| Revert2[Revert: Invalid receiver]
328
+ CheckTo -->|No| GetBal[Get balance of from]
329
+ end
330
+
331
+ subgraph "Balance Check"
332
+ GetBal --> CheckBal{balance >= amount?}
333
+ CheckBal -->|No| Revert3[Revert: Insufficient balance]
334
+ CheckBal -->|Yes| SubBal[balance from -= amount]
335
+ end
336
+
337
+ subgraph "Transfer Execution"
338
+ SubBal --> AddBal[balance to += amount]
339
+ AddBal --> Emit[Emit TransferredEvent]
340
+ Emit --> Success([Complete])
341
+ end
342
+ ```
343
+
344
+ ### transferFrom (Calldata)
345
+
346
+ Transfers tokens using allowance.
347
+
348
+ ```typescript
349
+ public transferFrom(calldata: Calldata): BytesWriter
350
+ ```
351
+
352
+ **Calldata format:**
353
+ | Field | Type | Size |
354
+ |-------|------|------|
355
+ | from | Address | 32 bytes |
356
+ | to | Address | 32 bytes |
357
+ | amount | u256 | 32 bytes |
358
+
359
+ **Returns:** Boolean (success)
360
+
361
+ The following sequence diagram illustrates both standard transfer and transferFrom with allowance:
362
+
363
+ ```mermaid
364
+ sequenceDiagram
365
+ participant User as 👤 User
366
+ participant Contract as OP20 Contract
367
+ participant BalMap as Balance Map
368
+ participant Events as Event System
369
+
370
+ Note over User,Events: Standard Transfer Flow
371
+
372
+ User->>Contract: transfer(to, amount)
373
+ Contract->>Contract: _transfer(tx.sender, to, amount)
374
+
375
+ Contract->>Contract: Validate from != zero
376
+ Contract->>Contract: Validate to != zero
377
+
378
+ Contract->>BalMap: Get balance[from]
379
+ BalMap->>Contract: Return balance
380
+
381
+ Contract->>Contract: Check balance >= amount
382
+
383
+ Contract->>BalMap: Set balance[from] -= amount
384
+ Contract->>BalMap: Set balance[to] += amount
385
+
386
+ Contract->>Events: Emit TransferredEvent(operator, from, to, amount)
387
+
388
+ Contract->>User: Return success
389
+
390
+ Note over User,Events: TransferFrom with Allowance
391
+
392
+ User->>Contract: transferFrom(from, to, amount)
393
+ Contract->>Contract: _spendAllowance(from, tx.sender, amount)
394
+
395
+ alt Allowance is u256.Max
396
+ Contract->>Contract: Skip allowance deduction
397
+ else Normal allowance
398
+ Contract->>Contract: Check allowance >= amount
399
+ Contract->>Contract: Deduct from allowance
400
+ end
401
+
402
+ Contract->>Contract: _transfer(from, to, amount)
403
+ Contract->>Events: Emit TransferredEvent
404
+ Contract->>User: Return success
405
+ ```
406
+
407
+ **Solidity Comparison:**
408
+ | Solidity (ERC20) | OPNet (OP20) |
409
+ |------------------|--------------|
410
+ | `function transfer(address to, uint256 amount) returns (bool)` | `transfer(calldata): BytesWriter` |
411
+ | `function transferFrom(address from, address to, uint256 amount) returns (bool)` | `transferFrom(calldata): BytesWriter` |
412
+
413
+ ## Approval Methods
414
+
415
+ ### increaseAllowance (Calldata)
416
+
417
+ Increases the allowance granted to a spender.
418
+
419
+ ```typescript
420
+ public increaseAllowance(calldata: Calldata): BytesWriter
421
+ ```
422
+
423
+ **Calldata format:**
424
+ | Field | Type | Size |
425
+ |-------|------|------|
426
+ | spender | Address | 32 bytes |
427
+ | amount | u256 | 32 bytes |
428
+
429
+ **Returns:** Empty BytesWriter (emits ApprovedEvent)
430
+
431
+ **Note:** If overflow would occur, sets allowance to u256.Max (unlimited).
432
+
433
+ ### decreaseAllowance (Calldata)
434
+
435
+ Decreases the allowance granted to a spender.
436
+
437
+ ```typescript
438
+ public decreaseAllowance(calldata: Calldata): BytesWriter
439
+ ```
440
+
441
+ **Calldata format:**
442
+ | Field | Type | Size |
443
+ |-------|------|------|
444
+ | spender | Address | 32 bytes |
445
+ | amount | u256 | 32 bytes |
446
+
447
+ **Returns:** Empty BytesWriter (emits ApprovedEvent)
448
+
449
+ **Note:** If underflow would occur, sets allowance to zero.
450
+
451
+ ### increaseAllowanceBySignature (Calldata)
452
+
453
+ Increases allowance using an EIP-712 typed signature (gasless approval).
454
+
455
+ ```typescript
456
+ public increaseAllowanceBySignature(calldata: Calldata): BytesWriter
457
+ ```
458
+
459
+ **Calldata format:**
460
+ | Field | Type | Size |
461
+ |-------|------|------|
462
+ | owner | bytes32 | 32 bytes |
463
+ | ownerTweakedPublicKey | bytes32 | 32 bytes |
464
+ | spender | Address | 32 bytes |
465
+ | amount | u256 | 32 bytes |
466
+ | deadline | u64 | 8 bytes |
467
+ | signature | bytes | variable |
468
+
469
+ ### decreaseAllowanceBySignature (Calldata)
470
+
471
+ Decreases allowance using an EIP-712 typed signature.
472
+
473
+ ```typescript
474
+ public decreaseAllowanceBySignature(calldata: Calldata): BytesWriter
475
+ ```
476
+
477
+ **Calldata format:**
478
+ | Field | Type | Size |
479
+ |-------|------|------|
480
+ | owner | bytes32 | 32 bytes |
481
+ | ownerTweakedPublicKey | bytes32 | 32 bytes |
482
+ | spender | Address | 32 bytes |
483
+ | amount | u256 | 32 bytes |
484
+ | deadline | u64 | 8 bytes |
485
+ | signature | bytes | variable |
486
+
487
+ The following diagram shows the allowance management flow including both direct and signature-based methods:
488
+
489
+ ```mermaid
490
+ flowchart LR
491
+ subgraph "Allowance Method Selection"
492
+ A[User Allows Spender] --> B{Choose<br/>Method}
493
+ B -->|Direct| C[increaseAllowance]
494
+ B -->|Off-chain| D[increaseAllowanceBySignature]
495
+ end
496
+
497
+ subgraph "Direct Allowance Path"
498
+ C --> E[Get allowanceMap owner]
499
+ E --> F[Get current for spender]
500
+ F --> G[new = current + amount]
501
+ G --> H{Overflow?}
502
+ H -->|Yes| I[Set to u256.Max<br/>unlimited]
503
+ H -->|No| J[Set new allowance]
504
+ end
505
+
506
+ subgraph "Signature Path"
507
+ D --> L[Verify EIP-712<br/>signature]
508
+ L --> M{Valid?}
509
+ M -->|No| N[Revert:<br/>Invalid signature]
510
+ M -->|Yes| O[Increment nonce]
511
+ O --> E
512
+ end
513
+
514
+ I --> K[Emit ApprovedEvent]
515
+ J --> K
516
+ ```
517
+
518
+ The complete allowance lifecycle from approval to spending:
519
+
520
+ ```mermaid
521
+ sequenceDiagram
522
+ participant Owner
523
+ participant Contract as OP20
524
+ participant AllowMap as Allowance Map
525
+ participant Spender
526
+
527
+ Note over Owner,Spender: Standard Approval Flow
528
+
529
+ Owner->>Contract: increaseAllowance(spender, 1000)
530
+ Contract->>AllowMap: Get allowanceMap[owner]
531
+ AllowMap->>Contract: Return owner's map
532
+ Contract->>Contract: Get current allowance for spender
533
+ Contract->>Contract: newAllowance = current + 1000
534
+ Contract->>AllowMap: Set allowance[owner][spender] = newAllowance
535
+ Contract->>Contract: Emit ApprovedEvent
536
+ Contract->>Owner: Success
537
+
538
+ Note over Owner,Spender: Spender Uses Allowance
539
+
540
+ Spender->>Contract: transferFrom(owner, recipient, 500)
541
+ Contract->>AllowMap: Get allowance[owner][spender]
542
+ AllowMap->>Contract: Returns 1000
543
+
544
+ alt Allowance is u256.Max
545
+ Contract->>Contract: Skip deduction (infinite approval)
546
+ else Normal allowance
547
+ Contract->>Contract: Check 1000 >= 500
548
+ Contract->>AllowMap: Set allowance to 1000 - 500 = 500
549
+ end
550
+
551
+ Contract->>Contract: Execute transfer
552
+ Contract->>Spender: Success
553
+ ```
554
+
555
+ **Solidity Comparison:**
556
+ | Solidity (ERC20) | OPNet (OP20) |
557
+ |------------------|--------------|
558
+ | `function approve(address, uint256) returns (bool)` | N/A (use increaseAllowance/decreaseAllowance) |
559
+ | `function increaseAllowance(address, uint256) returns (bool)` | `increaseAllowance(calldata): BytesWriter` |
560
+ | `function decreaseAllowance(address, uint256) returns (bool)` | `decreaseAllowance(calldata): BytesWriter` |
561
+
562
+ ## Protected Methods
563
+
564
+ For internal contract use:
565
+
566
+ ### _mint
567
+
568
+ Mints new tokens.
569
+
570
+ ```typescript
571
+ protected _mint(to: Address, amount: u256): void
572
+ ```
573
+
574
+ ```typescript
575
+ @method(
576
+ { name: 'to', type: ABIDataTypes.ADDRESS },
577
+ { name: 'amount', type: ABIDataTypes.UINT256 },
578
+ )
579
+ @returns({ name: 'success', type: ABIDataTypes.BOOL })
580
+ @emit('Mint')
581
+ public mint(calldata: Calldata): BytesWriter {
582
+ this.onlyDeployer(Blockchain.tx.sender);
583
+ const to: Address = calldata.readAddress();
584
+ const amount: u256 = calldata.readU256();
585
+ this._mint(to, amount);
586
+ return new BytesWriter(0);
587
+ }
588
+ ```
589
+
590
+ ### _burn
591
+
592
+ Burns tokens.
593
+
594
+ ```typescript
595
+ protected _burn(from: Address, amount: u256): void
596
+ ```
597
+
598
+ ```typescript
599
+ @method({ name: 'amount', type: ABIDataTypes.UINT256 })
600
+ @returns({ name: 'success', type: ABIDataTypes.BOOL })
601
+ @emit('Burn')
602
+ public burn(calldata: Calldata): BytesWriter {
603
+ const amount: u256 = calldata.readU256();
604
+ this._burn(Blockchain.tx.sender, amount);
605
+ return new BytesWriter(0);
606
+ }
607
+ ```
608
+
609
+ ### _transfer
610
+
611
+ Internal transfer.
612
+
613
+ ```typescript
614
+ protected _transfer(from: Address, to: Address, amount: u256): void
615
+ ```
616
+
617
+ ### _increaseAllowance
618
+
619
+ Internal method to increase allowance with overflow protection.
620
+
621
+ ```typescript
622
+ protected _increaseAllowance(owner: Address, spender: Address, amount: u256): void
623
+ ```
624
+
625
+ ### _decreaseAllowance
626
+
627
+ Internal method to decrease allowance with underflow protection.
628
+
629
+ ```typescript
630
+ protected _decreaseAllowance(owner: Address, spender: Address, amount: u256): void
631
+ ```
632
+
633
+ ### _spendAllowance
634
+
635
+ Decrements allowance.
636
+
637
+ ```typescript
638
+ protected _spendAllowance(owner: Address, spender: Address, amount: u256): void
639
+ ```
640
+
641
+ **Solidity Comparison:**
642
+ | Solidity (ERC20) | OPNet (OP20) |
643
+ |------------------|--------------|
644
+ | `function _mint(address, uint256) internal` | `_mint(Address, u256): void` |
645
+ | `function _burn(address, uint256) internal` | `_burn(Address, u256): void` |
646
+ | `function _transfer(address, address, uint256) internal` | `_transfer(Address, Address, u256): void` |
647
+
648
+ ## Safe Transfer Methods
649
+
650
+ ### safeTransfer
651
+
652
+ Transfer with recipient callback.
653
+
654
+ ```typescript
655
+ public safeTransfer(calldata: Calldata): BytesWriter
656
+ ```
657
+
658
+ **Calldata format:**
659
+ | Field | Type | Size |
660
+ |-------|------|------|
661
+ | to | Address | 32 bytes |
662
+ | amount | u256 | 32 bytes |
663
+ | data | bytes | variable (with length prefix) |
664
+
665
+ Calls `onOP20Received` on recipient if it's a contract.
666
+
667
+ ### safeTransferFrom
668
+
669
+ TransferFrom with callback.
670
+
671
+ ```typescript
672
+ public safeTransferFrom(calldata: Calldata): BytesWriter
673
+ ```
674
+
675
+ **Calldata format:**
676
+ | Field | Type | Size |
677
+ |-------|------|------|
678
+ | from | Address | 32 bytes |
679
+ | to | Address | 32 bytes |
680
+ | amount | u256 | 32 bytes |
681
+ | data | bytes | variable (with length prefix) |
682
+
683
+ The following diagram shows the safe transfer flow with recipient callback verification:
684
+
685
+ ```mermaid
686
+ sequenceDiagram
687
+ participant User as 👤 User
688
+ participant Token as OP20 Contract
689
+ participant Target as Recipient Contract
690
+ participant Guard as ReentrancyGuard
691
+
692
+ Note over User,Guard: Safe Transfer Flow
693
+
694
+ User->>Token: safeTransfer(to, amount, data)
695
+ Token->>Guard: Check reentrancy depth
696
+ Guard->>Token: OK (depth allowed for callback)
697
+
698
+ Token->>Token: _transfer(from, to, amount)
699
+ Token->>Token: Update balances
700
+ Token->>Token: Emit TransferredEvent
701
+
702
+ Token->>Token: Check if recipient is contract
703
+ alt Recipient is Contract
704
+ Token->>Token: Build onOP20Received calldata
705
+ Token->>Target: call(onOP20Received(operator, from, amount, data))
706
+
707
+ alt Target implements onOP20Received
708
+ Target->>Target: Process receipt
709
+ Target->>Token: Return ON_OP20_RECEIVED_SELECTOR
710
+
711
+ Token->>Token: Verify return value
712
+ alt Return value matches selector
713
+ Token->>User: Success
714
+ else Invalid return value
715
+ Token->>User: Revert: Transfer rejected
716
+ end
717
+ else Target doesn't implement
718
+ Target->>Token: Error or wrong selector
719
+ Token->>User: Revert: Transfer rejected
720
+ end
721
+ else Recipient is EOA
722
+ Token->>User: Success (no callback needed)
723
+ end
724
+ ```
725
+
726
+ ## Events
727
+
728
+ ### TransferredEvent
729
+
730
+ Emitted on transfers (transfer, transferFrom, safeTransfer, safeTransferFrom).
731
+
732
+ ```typescript
733
+ class TransferredEvent extends NetEvent {
734
+ constructor(
735
+ operator: Address, // Address that initiated the transfer
736
+ from: Address, // Address tokens transferred from
737
+ to: Address, // Address tokens transferred to
738
+ amount: u256 // Amount transferred
739
+ )
740
+ }
741
+ ```
742
+
743
+ ### ApprovedEvent
744
+
745
+ Emitted on allowance changes (increaseAllowance, decreaseAllowance).
746
+
747
+ ```typescript
748
+ class ApprovedEvent extends NetEvent {
749
+ constructor(
750
+ owner: Address,
751
+ spender: Address,
752
+ amount: u256
753
+ )
754
+ }
755
+ ```
756
+
757
+ ### MintedEvent
758
+
759
+ Emitted on minting.
760
+
761
+ ```typescript
762
+ class MintedEvent extends NetEvent {
763
+ constructor(
764
+ to: Address,
765
+ amount: u256
766
+ )
767
+ }
768
+ ```
769
+
770
+ ### BurnedEvent
771
+
772
+ Emitted on burning.
773
+
774
+ ```typescript
775
+ class BurnedEvent extends NetEvent {
776
+ constructor(
777
+ from: Address,
778
+ amount: u256
779
+ )
780
+ }
781
+ ```
782
+
783
+ **Solidity Comparison:**
784
+ | Solidity (ERC20) | OPNet (OP20) |
785
+ |------------------|--------------|
786
+ | `event Transfer(address indexed from, address indexed to, uint256 value)` | `TransferredEvent(operator, from, to, amount)` |
787
+ | `event Approval(address indexed owner, address indexed spender, uint256 value)` | `ApprovedEvent(owner, spender, amount)` |
788
+ | `emit Transfer(from, to, amount)` | `emitEvent(new TransferredEvent(operator, from, to, amount))` |
789
+
790
+ ## Storage Layout
791
+
792
+ OP20 uses 7 storage pointers:
793
+
794
+ | Pointer | Purpose |
795
+ |---------|---------|
796
+ | 0 | Nonce mapping (for signatures) |
797
+ | 1 | Max supply |
798
+ | 2 | Decimals |
799
+ | 3 | String pointer (shared for name/symbol/icon) |
800
+ | 4 | Total supply |
801
+ | 5 | Allowances mapping |
802
+ | 6 | Balances mapping |
803
+
804
+ ## Method Selectors
805
+
806
+ | Selector | Method |
807
+ |----------|--------|
808
+ | `name` | Returns name |
809
+ | `symbol` | Returns symbol |
810
+ | `icon` | Returns icon URL |
811
+ | `decimals` | Returns decimals |
812
+ | `totalSupply` | Returns total supply |
813
+ | `maximumSupply` | Returns max supply |
814
+ | `balanceOf` | Returns balance |
815
+ | `allowance` | Returns allowance |
816
+ | `nonceOf` | Returns nonce for address |
817
+ | `domainSeparator` | Returns EIP-712 domain separator |
818
+ | `metadata` | Returns all metadata |
819
+ | `transfer` | Transfer tokens |
820
+ | `transferFrom` | Transfer with allowance |
821
+ | `safeTransfer` | Safe transfer with callback |
822
+ | `safeTransferFrom` | Safe transferFrom with callback |
823
+ | `increaseAllowance` | Increase spender allowance |
824
+ | `decreaseAllowance` | Decrease spender allowance |
825
+ | `increaseAllowanceBySignature` | Gasless allowance increase |
826
+ | `decreaseAllowanceBySignature` | Gasless allowance decrease |
827
+ | `burn` | Burn tokens from sender |
828
+
829
+ ## Complete Example
830
+
831
+ ```typescript
832
+ import { u256 } from '@btc-vision/as-bignum/assembly';
833
+ import {
834
+ OP20,
835
+ OP20InitParameters,
836
+ Blockchain,
837
+ Calldata,
838
+ BytesWriter,
839
+ ABIDataTypes,
840
+ } from '@btc-vision/btc-runtime/runtime';
841
+
842
+ @final
843
+ export class MyToken extends OP20 {
844
+ public constructor() {
845
+ super();
846
+ }
847
+
848
+ public override onDeployment(calldata: Calldata): void {
849
+ const maxSupply = calldata.readU256();
850
+ const decimals = calldata.readU8();
851
+ const name = calldata.readString();
852
+ const symbol = calldata.readString();
853
+ const initialMint = calldata.readU256();
854
+
855
+ this.instantiate(new OP20InitParameters(
856
+ maxSupply,
857
+ decimals,
858
+ name,
859
+ symbol,
860
+ '' // icon (optional)
861
+ ));
862
+
863
+ if (!initialMint.isZero()) {
864
+ this._mint(Blockchain.tx.origin, initialMint);
865
+ }
866
+ }
867
+
868
+ @method(
869
+ { name: 'to', type: ABIDataTypes.ADDRESS },
870
+ { name: 'amount', type: ABIDataTypes.UINT256 },
871
+ )
872
+ @returns({ name: 'success', type: ABIDataTypes.BOOL })
873
+ @emit('Minted')
874
+ public mint(calldata: Calldata): BytesWriter {
875
+ this.onlyDeployer(Blockchain.tx.sender);
876
+
877
+ const to = calldata.readAddress();
878
+ const amount = calldata.readU256();
879
+
880
+ this._mint(to, amount);
881
+ // Note: _mint already emits MintedEvent internally
882
+
883
+ return new BytesWriter(0);
884
+ }
885
+ }
886
+ ```
887
+
888
+ ## Solidity Comparison Summary
889
+
890
+ | Solidity (ERC20) | OPNet (OP20) |
891
+ |------------------|--------------|
892
+ | `constructor(...)` | `onDeployment(calldata)` |
893
+ | `function name()` | `name(): string` |
894
+ | `function balanceOf(address)` | `balanceOf(Address): u256` |
895
+ | `_mint(address, uint256)` | `_mint(Address, u256)` |
896
+ | `emit Transfer(...)` | `emitEvent(new TransferredEvent(...))` |
897
+
898
+ ---
899
+
900
+ **Navigation:**
901
+ - Previous: [Blockchain API](./blockchain.md)
902
+ - Next: [OP721 API](./op721.md)