@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,819 @@
1
+ # OP721 API Reference
2
+
3
+ The `OP721` class implements the non-fungible token (NFT) standard, equivalent to ERC721 on Ethereum.
4
+
5
+ ## Import
6
+
7
+ ```typescript
8
+ import { OP721, OP721InitParameters } from '@btc-vision/btc-runtime/runtime';
9
+ ```
10
+
11
+ ## OP721 Architecture
12
+
13
+ ```mermaid
14
+ classDiagram
15
+ class OP721 {
16
+ <<abstract>>
17
+ +_name: StoredString
18
+ +_symbol: StoredString
19
+ +_baseURI: StoredString
20
+ +_totalSupply: StoredU256
21
+ +_maxSupply: StoredU256
22
+ +ownerOfMap: StoredMapU256
23
+ +balanceOfMap: AddressMemoryMap
24
+ +tokenApprovalMap: StoredMapU256
25
+ +operatorApprovalMap: MapOfMap~u256~
26
+ +instantiate(params) void
27
+ +ownerOf(tokenId) Address
28
+ +balanceOf(owner) u256
29
+ +approve(operator, tokenId) void
30
+ +safeTransfer(to, tokenId, data) void
31
+ +safeTransferFrom(from, to, tokenId, data) void
32
+ #_mint(to, tokenId) void
33
+ #_burn(tokenId) void
34
+ #_transfer(from, to, tokenId, data) void
35
+ }
36
+
37
+ class ReentrancyGuard {
38
+ <<abstract>>
39
+ #reentrancyLevel: ReentrancyLevel
40
+ }
41
+
42
+ class OP_NET {
43
+ <<abstract>>
44
+ +address: Address
45
+ #emitEvent(event) void
46
+ Note: @method decorator handles routing
47
+ }
48
+
49
+ class MyNFT {
50
+ +_nextTokenId: StoredU256
51
+ +_baseURI: StoredString
52
+ +constructor()
53
+ +onDeployment(calldata) void
54
+ +mint(calldata) BytesWriter
55
+ +tokenURI(tokenId) string
56
+ Note: @method decorator handles routing
57
+ }
58
+
59
+ OP_NET <|-- ReentrancyGuard
60
+ ReentrancyGuard <|-- OP721
61
+ OP721 <|-- MyNFT
62
+
63
+ note for OP721 "NFT standard with\nenumeration support\nand approval management"
64
+ note for MyNFT "Your NFT collection\nextends OP721"
65
+ ```
66
+
67
+ ## Class Definition
68
+
69
+ ```typescript
70
+ @final
71
+ export class MyNFT extends OP721 {
72
+ public constructor() {
73
+ super();
74
+ }
75
+
76
+ public override onDeployment(calldata: Calldata): void {
77
+ const name = calldata.readString();
78
+ const symbol = calldata.readString();
79
+ const baseURI = calldata.readString();
80
+ const maxSupply = calldata.readU256();
81
+
82
+ this.instantiate(new OP721InitParameters(
83
+ name,
84
+ symbol,
85
+ baseURI,
86
+ maxSupply
87
+ ));
88
+ }
89
+ }
90
+ ```
91
+
92
+ ## Initialization
93
+
94
+ ### OP721InitParameters
95
+
96
+ ```typescript
97
+ class OP721InitParameters {
98
+ constructor(
99
+ name: string,
100
+ symbol: string,
101
+ baseURI: string,
102
+ maxSupply: u256,
103
+ collectionBanner: string = '',
104
+ collectionIcon: string = '',
105
+ collectionWebsite: string = '',
106
+ collectionDescription: string = ''
107
+ )
108
+ }
109
+ ```
110
+
111
+ | Parameter | Type | Required | Description |
112
+ |-----------|------|----------|-------------|
113
+ | `name` | `string` | Yes | Collection name |
114
+ | `symbol` | `string` | Yes | Collection symbol |
115
+ | `baseURI` | `string` | Yes | Base URI for token metadata |
116
+ | `maxSupply` | `u256` | Yes | Maximum number of tokens that can be minted |
117
+ | `collectionBanner` | `string` | No | Collection banner URL (default: '') |
118
+ | `collectionIcon` | `string` | No | Collection icon URL (default: '') |
119
+ | `collectionWebsite` | `string` | No | Collection website URL (default: '') |
120
+ | `collectionDescription` | `string` | No | Collection description (default: '') |
121
+
122
+ ### instantiate
123
+
124
+ Initializes the OP721 NFT. Must be called in `onDeployment`.
125
+
126
+ ```typescript
127
+ public instantiate(
128
+ params: OP721InitParameters,
129
+ skipDeployerVerification: boolean = false
130
+ ): void
131
+ ```
132
+
133
+ | Parameter | Type | Description |
134
+ |-----------|------|-------------|
135
+ | `params` | `OP721InitParameters` | Initialization parameters |
136
+ | `skipDeployerVerification` | `boolean` | Skip deployer check (default: false) |
137
+
138
+ **Solidity Comparison:**
139
+ | Solidity (ERC721) | OPNet (OP721) |
140
+ |-------------------|---------------|
141
+ | `constructor(string name, string symbol)` | `onDeployment(calldata)` + `instantiate()` |
142
+
143
+ ## View Methods
144
+
145
+ ### name
146
+
147
+ Returns the collection name.
148
+
149
+ ```typescript
150
+ public name(): string
151
+ ```
152
+
153
+ ### symbol
154
+
155
+ Returns the collection symbol.
156
+
157
+ ```typescript
158
+ public symbol(): string
159
+ ```
160
+
161
+ ### totalSupply
162
+
163
+ Returns total minted tokens.
164
+
165
+ ```typescript
166
+ public get totalSupply(): u256
167
+ ```
168
+
169
+ ### maxSupply
170
+
171
+ Returns maximum supply limit.
172
+
173
+ ```typescript
174
+ public get maxSupply(): u256
175
+ ```
176
+
177
+ ### balanceOf
178
+
179
+ Returns number of tokens owned by address.
180
+
181
+ ```typescript
182
+ public balanceOf(owner: Address): u256
183
+ ```
184
+
185
+ ### ownerOf
186
+
187
+ Returns owner of a token.
188
+
189
+ ```typescript
190
+ public ownerOf(tokenId: u256): Address
191
+ ```
192
+
193
+ ### tokenURI
194
+
195
+ Returns metadata URI for a token.
196
+
197
+ ```typescript
198
+ public tokenURI(tokenId: u256): string
199
+ ```
200
+
201
+ Override to customize metadata:
202
+
203
+ ```typescript
204
+ public override tokenURI(tokenId: u256): string {
205
+ return this._baseURI.value + tokenId.toString() + '.json';
206
+ }
207
+ ```
208
+
209
+ ### getApproved
210
+
211
+ Returns approved address for a token.
212
+
213
+ ```typescript
214
+ public getApproved(tokenId: u256): Address
215
+ ```
216
+
217
+ ### isApprovedForAll
218
+
219
+ Checks if operator is approved for all tokens.
220
+
221
+ ```typescript
222
+ public isApprovedForAll(owner: Address, operator: Address): bool
223
+ ```
224
+
225
+ **Solidity Comparison:**
226
+ | Solidity (ERC721) | OPNet (OP721) |
227
+ |-------------------|---------------|
228
+ | `function ownerOf(uint256) view returns (address)` | `ownerOf(u256): Address` |
229
+ | `function balanceOf(address) view returns (uint256)` | `balanceOf(Address): u256` |
230
+ | `function tokenURI(uint256) view returns (string)` | `tokenURI(u256): string` |
231
+
232
+ ## Transfer Methods
233
+
234
+ ### safeTransfer (Calldata)
235
+
236
+ Transfers an NFT from the sender to a recipient.
237
+
238
+ ```typescript
239
+ public safeTransfer(calldata: Calldata): BytesWriter
240
+ ```
241
+
242
+ **Calldata format:**
243
+ | Field | Type | Size |
244
+ |-------|------|------|
245
+ | to | Address | 32 bytes |
246
+ | tokenId | u256 | 32 bytes |
247
+ | data | bytes | variable (length-prefixed) |
248
+
249
+ The following diagram shows the complete NFT transfer validation and execution flow:
250
+
251
+ ```mermaid
252
+ flowchart LR
253
+ subgraph "Validation"
254
+ Start([NFT Transfer]) --> CheckOwner{Verify owner}
255
+ CheckOwner -->|Invalid| Revert1[Revert: Wrong owner]
256
+ CheckOwner -->|Valid| CheckTo{to != zero?}
257
+ CheckTo -->|to == zero| Revert2[Revert: Invalid recipient]
258
+ CheckTo -->|Valid| CheckAuth{Authorization}
259
+ end
260
+
261
+ subgraph "Authorization"
262
+ CheckAuth -->|Not authorized| Revert3[Revert: Not authorized]
263
+ CheckAuth -->|Owner| DoTransfer[Execute transfer]
264
+ CheckAuth -->|Approved operator| DoTransfer
265
+ CheckAuth -->|Token approved| DoTransfer
266
+ end
267
+
268
+ subgraph "Transfer Execution"
269
+ DoTransfer --> ClearApproval[Clear approval]
270
+ ClearApproval --> UpdateEnum[Update enumerations]
271
+ UpdateEnum --> UpdateBal[Update balances]
272
+ UpdateBal --> UpdateOwner[Set new owner]
273
+ UpdateOwner --> EmitEvent[Emit TransferEvent]
274
+ end
275
+
276
+ subgraph "Callback Handling"
277
+ EmitEvent --> IsContract{Recipient<br/>is contract?}
278
+ IsContract -->|No| Success([Complete])
279
+ IsContract -->|Yes| Callback[Call onOP721Received]
280
+ Callback --> CheckResponse{Valid<br/>response?}
281
+ CheckResponse -->|Yes| Success
282
+ CheckResponse -->|No| Revert4[Revert: Rejected]
283
+ end
284
+ ```
285
+
286
+ ### safeTransferFrom (Calldata)
287
+
288
+ Safe transfer with recipient callback.
289
+
290
+ ```typescript
291
+ public safeTransferFrom(calldata: Calldata): BytesWriter
292
+ ```
293
+
294
+ **Calldata format:**
295
+ | Field | Type | Size |
296
+ |-------|------|------|
297
+ | from | Address | 32 bytes |
298
+ | to | Address | 32 bytes |
299
+ | tokenId | u256 | 32 bytes |
300
+ | data | bytes | variable (length-prefixed) |
301
+
302
+ Calls `onOP721Received` on recipient if it's a contract.
303
+
304
+ ### burn (Calldata)
305
+
306
+ Burns a token. Only owner or approved addresses can burn.
307
+
308
+ ```typescript
309
+ public burn(calldata: Calldata): BytesWriter
310
+ ```
311
+
312
+ **Calldata format:**
313
+ | Field | Type | Size |
314
+ |-------|------|------|
315
+ | tokenId | u256 | 32 bytes |
316
+
317
+ The following sequence diagram illustrates the complete mint and transfer flow:
318
+
319
+ ```mermaid
320
+ sequenceDiagram
321
+ participant Owner
322
+ participant NFT as OP721 Contract
323
+ participant Maps as Storage Maps
324
+ participant Recipient
325
+
326
+ Note over Owner,Recipient: Mint Flow
327
+
328
+ Owner->>NFT: _mint(to, tokenId)
329
+ NFT->>NFT: Check tokenId doesn't exist
330
+ NFT->>NFT: Check max supply not reached
331
+
332
+ NFT->>Maps: ownerOfMap.set(tokenId, to)
333
+ NFT->>Maps: balanceOfMap[to] += 1
334
+ NFT->>Maps: Add to owner enumeration
335
+ NFT->>NFT: totalSupply += 1
336
+
337
+ NFT->>NFT: Emit TransferEvent(zero, to, tokenId)
338
+
339
+ Note over Owner,Recipient: Transfer Flow
340
+
341
+ Owner->>NFT: approve(operator, tokenId)
342
+ NFT->>NFT: Check caller is owner or approved for all
343
+ NFT->>Maps: tokenApprovalMap.set(tokenId, operator)
344
+ NFT->>NFT: Emit ApprovalEvent
345
+
346
+ Recipient->>NFT: safeTransferFrom(owner, recipient, tokenId, data)
347
+ NFT->>NFT: Check authorization
348
+ NFT->>NFT: _transfer(owner, recipient, tokenId, data)
349
+
350
+ NFT->>Maps: Clear token approval
351
+ NFT->>Maps: Remove from old owner enumeration
352
+ NFT->>Maps: Add to new owner enumeration
353
+ NFT->>Maps: balanceOfMap[owner] -= 1
354
+ NFT->>Maps: balanceOfMap[recipient] += 1
355
+ NFT->>Maps: ownerOfMap.set(tokenId, recipient)
356
+
357
+ NFT->>NFT: Emit TransferEvent
358
+
359
+ alt Recipient is Contract
360
+ NFT->>Recipient: call onOP721Received
361
+ Recipient->>NFT: Return selector
362
+ NFT->>NFT: Verify response
363
+ end
364
+
365
+ NFT->>Recipient: Success
366
+ ```
367
+
368
+ **Solidity Comparison:**
369
+ | Solidity (ERC721) | OPNet (OP721) |
370
+ |-------------------|---------------|
371
+ | `function transferFrom(address, address, uint256)` | `safeTransferFrom(calldata): BytesWriter` |
372
+ | `function safeTransferFrom(address, address, uint256, bytes)` | `safeTransferFrom(calldata): BytesWriter` |
373
+ | N/A | `safeTransfer(calldata): BytesWriter` (from sender) |
374
+
375
+ ## Approval Methods
376
+
377
+ ### approve (Calldata)
378
+
379
+ Approves an address for a single token.
380
+
381
+ ```typescript
382
+ public approve(calldata: Calldata): BytesWriter
383
+ ```
384
+
385
+ **Calldata format:**
386
+ | Field | Type | Size |
387
+ |-------|------|------|
388
+ | operator | Address | 32 bytes |
389
+ | tokenId | u256 | 32 bytes |
390
+
391
+ ### setApprovalForAll (Calldata)
392
+
393
+ Sets operator approval for all tokens.
394
+
395
+ ```typescript
396
+ public setApprovalForAll(calldata: Calldata): BytesWriter
397
+ ```
398
+
399
+ **Calldata format:**
400
+ | Field | Type | Size |
401
+ |-------|------|------|
402
+ | operator | Address | 32 bytes |
403
+ | approved | bool | 1 byte |
404
+
405
+ **Solidity Comparison:**
406
+ | Solidity (ERC721) | OPNet (OP721) |
407
+ |-------------------|---------------|
408
+ | `function approve(address, uint256)` | `approve(calldata): BytesWriter` |
409
+ | `function setApprovalForAll(address, bool)` | `setApprovalForAll(calldata): BytesWriter` |
410
+
411
+ ## Protected Methods
412
+
413
+ ### _mint
414
+
415
+ Mints a new token.
416
+
417
+ ```typescript
418
+ protected _mint(to: Address, tokenId: u256): void
419
+ ```
420
+
421
+ ```typescript
422
+ @method(
423
+ { name: 'to', type: ABIDataTypes.ADDRESS },
424
+ { name: 'tokenId', type: ABIDataTypes.UINT256 },
425
+ )
426
+ @returns({ name: 'success', type: ABIDataTypes.BOOL })
427
+ @emit('Transfer')
428
+ public mint(calldata: Calldata): BytesWriter {
429
+ this.onlyDeployer(Blockchain.tx.sender);
430
+ const to: Address = calldata.readAddress();
431
+ const tokenId: u256 = calldata.readU256();
432
+ this._mint(to, tokenId);
433
+ return new BytesWriter(0);
434
+ }
435
+ ```
436
+
437
+ ### _burn
438
+
439
+ Burns a token.
440
+
441
+ ```typescript
442
+ protected _burn(tokenId: u256): void
443
+ ```
444
+
445
+ ### _transfer
446
+
447
+ Internal transfer with data for safe transfer callbacks.
448
+
449
+ ```typescript
450
+ protected _transfer(from: Address, to: Address, tokenId: u256, data: Uint8Array): void
451
+ ```
452
+
453
+ ### _approve
454
+
455
+ Internal approval.
456
+
457
+ ```typescript
458
+ protected _approve(operator: Address, tokenId: u256): void
459
+ ```
460
+
461
+ ### _setApprovalForAll
462
+
463
+ Internal operator approval.
464
+
465
+ ```typescript
466
+ protected _setApprovalForAll(owner: Address, operator: Address, approved: bool): void
467
+ ```
468
+
469
+ ### _setTokenURI
470
+
471
+ Sets a custom URI for a specific token.
472
+
473
+ ```typescript
474
+ protected _setTokenURI(tokenId: u256, uri: string): void
475
+ ```
476
+
477
+ ### _setBaseURI
478
+
479
+ Sets the base URI for all tokens.
480
+
481
+ ```typescript
482
+ protected _setBaseURI(baseURI: string): void
483
+ ```
484
+
485
+ ### _exists
486
+
487
+ Checks if a token exists.
488
+
489
+ ```typescript
490
+ protected _exists(tokenId: u256): bool
491
+ ```
492
+
493
+ ### _ownerOf
494
+
495
+ Returns the owner of a token. Throws if token doesn't exist.
496
+
497
+ ```typescript
498
+ protected _ownerOf(tokenId: u256): Address
499
+ ```
500
+
501
+ ### _balanceOf
502
+
503
+ Returns the balance of an address. Throws if zero address.
504
+
505
+ ```typescript
506
+ protected _balanceOf(owner: Address): u256
507
+ ```
508
+
509
+ ### _isApprovedForAll
510
+
511
+ Checks if an operator is approved for all tokens.
512
+
513
+ ```typescript
514
+ protected _isApprovedForAll(owner: Address, operator: Address): boolean
515
+ ```
516
+
517
+ **Solidity Comparison:**
518
+ | Solidity (ERC721) | OPNet (OP721) |
519
+ |-------------------|---------------|
520
+ | `function _mint(address, uint256) internal` | `_mint(Address, u256): void` |
521
+ | `function _burn(uint256) internal` | `_burn(u256): void` |
522
+ | `function _safeMint(address, uint256)` | `_mint()` (automatically checks recipient) |
523
+
524
+ ## Enumeration System
525
+
526
+ ### tokenOfOwnerByIndex
527
+
528
+ Returns token ID at index for owner.
529
+
530
+ ```typescript
531
+ public tokenOfOwnerByIndex(owner: Address, index: u256): u256
532
+ ```
533
+
534
+ The enumeration system allows efficient iteration over tokens owned by an address:
535
+
536
+ ```mermaid
537
+ graph LR
538
+ A[NFT Collection]
539
+
540
+ subgraph "Owner Enumeration"
541
+ B[ownerTokensMap<br/>Address -> StoredU256Array]
542
+ C[Owner's Token Array]
543
+ D[Token IDs owned<br/>by address]
544
+ B --> C
545
+ C --> D
546
+ end
547
+
548
+ subgraph "Index Tracking"
549
+ E[tokenIndexMap<br/>tokenId -> u256]
550
+ F[Index in owner's array]
551
+ E --> F
552
+ end
553
+
554
+ subgraph "Add Token Operations"
555
+ G[_addTokenToOwnerEnumeration<br/>On mint/transfer in]
556
+ H[Push tokenId to array]
557
+ I[Set tokenIndexMap]
558
+ G --> H --> I
559
+ end
560
+
561
+ subgraph "Remove Token Operations"
562
+ J[_removeTokenFromOwnerEnumeration<br/>On burn/transfer out]
563
+ K[Get last token in array]
564
+ L[Move last to removed position]
565
+ M[Delete last element]
566
+ N[Update tokenIndexMap]
567
+ J --> K --> L --> M --> N
568
+ end
569
+
570
+ A --> B
571
+ A --> E
572
+ G -.->|modifies| C
573
+ J -.->|modifies| C
574
+ ```
575
+
576
+ The following sequence diagram shows how enumeration queries work:
577
+
578
+ ```mermaid
579
+ sequenceDiagram
580
+ participant User as 👤 User
581
+ participant NFT as OP721
582
+ participant OwnerMap as ownerTokensMap
583
+ participant IndexMap as tokenIndexMap
584
+
585
+ Note over User,IndexMap: Query Owner's Tokens
586
+
587
+ User->>NFT: balanceOf(owner)
588
+ NFT->>NFT: Return balance (e.g., 5 tokens)
589
+
590
+ User->>NFT: tokenOfOwnerByIndex(owner, 0)
591
+ NFT->>OwnerMap: Get owner's token array
592
+ OwnerMap->>NFT: Return StoredU256Array
593
+ NFT->>NFT: array.get(0)
594
+ NFT->>User: Return tokenId (e.g., 42)
595
+
596
+ User->>NFT: tokenOfOwnerByIndex(owner, 1)
597
+ NFT->>NFT: array.get(1)
598
+ NFT->>User: Return tokenId (e.g., 137)
599
+
600
+ Note over User,IndexMap: Iterate All Tokens
601
+
602
+ loop For each index from 0 to balance-1
603
+ User->>NFT: tokenOfOwnerByIndex(owner, index)
604
+ NFT->>User: Return tokenId
605
+ end
606
+
607
+ Note over User,IndexMap: Internal: Add Token to Enumeration
608
+
609
+ NFT->>NFT: _addTokenToOwnerEnumeration(owner, tokenId)
610
+ NFT->>OwnerMap: Get owner's array
611
+ NFT->>NFT: newIndex = array.length
612
+ NFT->>OwnerMap: array.push(tokenId)
613
+ NFT->>IndexMap: tokenIndexMap.set(tokenId, newIndex)
614
+
615
+ Note over User,IndexMap: Internal: Remove Token from Enumeration
616
+
617
+ NFT->>NFT: _removeTokenFromOwnerEnumeration(owner, tokenId)
618
+ NFT->>IndexMap: Get tokenIndex for tokenId
619
+ NFT->>OwnerMap: Get last token in array
620
+ NFT->>OwnerMap: Move last token to removed position
621
+ NFT->>IndexMap: Update index for moved token
622
+ NFT->>OwnerMap: Delete last element
623
+ NFT->>IndexMap: Delete tokenId mapping
624
+ ```
625
+
626
+ ```typescript
627
+ // Get all tokens owned by address
628
+ const balance = this.balanceOf(owner);
629
+ for (let i = u256.Zero; i < balance; i = SafeMath.add(i, u256.One)) {
630
+ const tokenId = this.tokenOfOwnerByIndex(owner, i);
631
+ // Process tokenId
632
+ }
633
+ ```
634
+
635
+ **Note:** Unlike ERC721Enumerable, OP721 does not include a global `tokenByIndex` method. It only provides `tokenOfOwnerByIndex` for per-owner enumeration.
636
+
637
+ ## Events
638
+
639
+ ### TransferredEvent
640
+
641
+ Emitted on transfers, mints, and burns.
642
+
643
+ ```typescript
644
+ class TransferredEvent extends NetEvent {
645
+ constructor(
646
+ operator: Address, // The address that initiated the transfer (Blockchain.tx.sender)
647
+ from: Address, // Previous owner (Address.zero() for mint)
648
+ to: Address, // New owner (Address.zero() for burn)
649
+ tokenId: u256 // The token being transferred
650
+ )
651
+ }
652
+ ```
653
+
654
+ ### ApprovedEvent
655
+
656
+ Emitted on token approvals.
657
+
658
+ ```typescript
659
+ class ApprovedEvent extends NetEvent {
660
+ constructor(
661
+ owner: Address, // Token owner
662
+ spender: Address, // Approved address
663
+ tokenId: u256 // The token being approved
664
+ )
665
+ }
666
+ ```
667
+
668
+ ### ApprovedForAllEvent
669
+
670
+ Emitted on operator approvals.
671
+
672
+ ```typescript
673
+ class ApprovedForAllEvent extends NetEvent {
674
+ constructor(
675
+ owner: Address, // Token owner
676
+ operator: Address, // Operator address
677
+ approved: bool // Approval status
678
+ )
679
+ }
680
+ ```
681
+
682
+ ### URIEvent
683
+
684
+ Emitted when a token URI is set or changed.
685
+
686
+ ```typescript
687
+ class URIEvent extends NetEvent {
688
+ constructor(
689
+ value: string, // The new URI
690
+ id: u256 // The token ID
691
+ )
692
+ }
693
+ ```
694
+
695
+ **Solidity Comparison:**
696
+ | Solidity (ERC721) | OPNet (OP721) |
697
+ |-------------------|---------------|
698
+ | `event Transfer(address indexed, address indexed, uint256 indexed)` | `TransferredEvent(operator, from, to, tokenId)` |
699
+ | `event Approval(address indexed, address indexed, uint256 indexed)` | `ApprovedEvent(owner, spender, tokenId)` |
700
+ | `emit Transfer(from, to, tokenId)` | `emitEvent(new TransferredEvent(sender, from, to, tokenId))` |
701
+
702
+ ## Storage Layout
703
+
704
+ OP721 uses multiple storage pointers:
705
+
706
+ | Purpose | Description |
707
+ |---------|-------------|
708
+ | Token owners | Maps tokenId -> owner |
709
+ | Balances | Maps owner -> count |
710
+ | Approvals | Maps tokenId -> approved |
711
+ | Operator approvals | Maps owner+operator -> bool |
712
+ | Owned tokens | Maps owner+index -> tokenId |
713
+ | Owned token index | Maps tokenId -> index |
714
+ | All tokens | Maps index -> tokenId |
715
+ | All tokens index | Maps tokenId -> index |
716
+ | Name | Collection name |
717
+ | Symbol | Collection symbol |
718
+ | Total supply | Current count |
719
+
720
+ ## Method Selectors
721
+
722
+ | Selector | Method |
723
+ |----------|--------|
724
+ | `name` | Returns name |
725
+ | `symbol` | Returns symbol |
726
+ | `totalSupply` | Returns total supply |
727
+ | `maxSupply` | Returns maximum supply |
728
+ | `balanceOf` | Returns balance |
729
+ | `ownerOf` | Returns owner |
730
+ | `tokenURI` | Returns metadata URI |
731
+ | `approve` | Approve address |
732
+ | `getApproved` | Get approved address |
733
+ | `setApprovalForAll` | Set operator approval |
734
+ | `isApprovedForAll` | Check operator approval |
735
+ | `safeTransfer` | Transfer from sender |
736
+ | `safeTransferFrom` | Safe transfer with from address |
737
+ | `burn` | Burn token |
738
+ | `tokenOfOwnerByIndex` | Enumerable: owner token at index |
739
+ | `collectionInfo` | Returns collection metadata |
740
+ | `metadata` | Returns full collection metadata |
741
+ | `domainSeparator` | Returns EIP-712 domain separator |
742
+ | `getApproveNonce` | Returns signature nonce for address |
743
+ | `approveBySignature` | Approve via EIP-712 signature |
744
+ | `setApprovalForAllBySignature` | Set operator approval via signature |
745
+ | `setBaseURI` | Update base URI (deployer only) |
746
+ | `changeMetadata` | Update collection metadata (deployer only) |
747
+
748
+ ## Complete Example
749
+
750
+ ```typescript
751
+ import { u256 } from '@btc-vision/as-bignum/assembly';
752
+ import {
753
+ OP721,
754
+ OP721InitParameters,
755
+ Blockchain,
756
+ Calldata,
757
+ BytesWriter,
758
+ SafeMath,
759
+ Address,
760
+ ABIDataTypes,
761
+ } from '@btc-vision/btc-runtime/runtime';
762
+
763
+ @final
764
+ export class MyNFT extends OP721 {
765
+ public constructor() {
766
+ super();
767
+ }
768
+
769
+ public override onDeployment(calldata: Calldata): void {
770
+ const name = calldata.readStringWithLength();
771
+ const symbol = calldata.readStringWithLength();
772
+ const baseURI = calldata.readStringWithLength();
773
+ const maxSupply = calldata.readU256();
774
+
775
+ this.instantiate(new OP721InitParameters(
776
+ name,
777
+ symbol,
778
+ baseURI,
779
+ maxSupply
780
+ ));
781
+ }
782
+
783
+ @method({ name: 'to', type: ABIDataTypes.ADDRESS })
784
+ @returns({ name: 'tokenId', type: ABIDataTypes.UINT256 })
785
+ @emit('Transferred')
786
+ public mint(calldata: Calldata): BytesWriter {
787
+ this.onlyDeployer(Blockchain.tx.sender);
788
+
789
+ const to: Address = calldata.readAddress();
790
+
791
+ // Use internal _nextTokenId from OP721 base class
792
+ const tokenId = this._nextTokenId.value;
793
+ this._mint(to, tokenId);
794
+ this._nextTokenId.value = SafeMath.add(tokenId, u256.One);
795
+
796
+ const writer = new BytesWriter(32);
797
+ writer.writeU256(tokenId);
798
+ return writer;
799
+ }
800
+ }
801
+ ```
802
+
803
+ ## Solidity Comparison Summary
804
+
805
+ | Solidity (ERC721) | OPNet (OP721) |
806
+ |-------------------|---------------|
807
+ | `constructor(name, symbol)` | `instantiate(new OP721InitParameters(name, symbol, baseURI, maxSupply, ...))` |
808
+ | `function ownerOf(uint256)` | `ownerOf(u256): Address` |
809
+ | `_mint(address, uint256)` | `_mint(Address, u256)` |
810
+ | `_safeMint(address, uint256)` | `_mint()` (automatically emits TransferredEvent) |
811
+ | `emit Transfer(...)` | `emitEvent(new TransferredEvent(operator, from, to, tokenId))` |
812
+ | `transferFrom(from, to, tokenId)` | `safeTransferFrom(from, to, tokenId, data)` |
813
+ | N/A | `safeTransfer(to, tokenId, data)` (from sender) |
814
+
815
+ ---
816
+
817
+ **Navigation:**
818
+ - Previous: [OP20 API](./op20.md)
819
+ - Next: [SafeMath API](./safe-math.md)