@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,575 @@
1
+ # Your First Contract
2
+
3
+ This tutorial guides you through creating a complete OP20 token contract from scratch. By the end, you'll understand the core concepts of OPNet smart contract development.
4
+
5
+ ## What We're Building
6
+
7
+ A simple fungible token (like an ERC20 on Ethereum) with:
8
+ - Fixed maximum supply
9
+ - Minting capability (deployer only)
10
+ - Transfer functionality
11
+ - Balance queries
12
+
13
+ ## Step 1: Create the Contract File
14
+
15
+ Create `src/token/MyToken.ts`:
16
+
17
+ ```typescript
18
+ import { u256 } from '@btc-vision/as-bignum/assembly';
19
+ import {
20
+ Blockchain,
21
+ BytesWriter,
22
+ Calldata,
23
+ OP20,
24
+ OP20InitParameters,
25
+ } from '@btc-vision/btc-runtime/runtime';
26
+
27
+ @final
28
+ export class MyToken extends OP20 {
29
+ public constructor() {
30
+ super();
31
+ }
32
+
33
+ public override onDeployment(_calldata: Calldata): void {
34
+ const maxSupply: u256 = u256.fromString('1000000000000000000000000');
35
+ const decimals: u8 = 18;
36
+ const name: string = 'MyToken';
37
+ const symbol: string = 'MTK';
38
+
39
+ this.instantiate(new OP20InitParameters(maxSupply, decimals, name, symbol));
40
+ this._mint(Blockchain.tx.origin, maxSupply);
41
+ }
42
+
43
+ @method(
44
+ { name: 'address', type: ABIDataTypes.ADDRESS },
45
+ { name: 'amount', type: ABIDataTypes.UINT256 }
46
+ )
47
+ @emit('Minted')
48
+ public mint(calldata: Calldata): BytesWriter {
49
+ this.onlyDeployer(Blockchain.tx.sender);
50
+
51
+ this._mint(calldata.readAddress(), calldata.readU256());
52
+
53
+ return new BytesWriter(0);
54
+ }
55
+ }
56
+ ```
57
+
58
+ Let's break this down piece by piece.
59
+
60
+ ## Contract Lifecycle Overview
61
+
62
+ This diagram illustrates the complete lifecycle of an OPNet smart contract from deployment to execution:
63
+
64
+ ```mermaid
65
+ ---
66
+ config:
67
+ theme: dark
68
+ ---
69
+ flowchart LR
70
+ A["Deployment"] --> B["onDeployment()"]
71
+ B --> C["Initialize Parameters"]
72
+ C --> D["Contract Active"]
73
+ D --> E{"Transaction"}
74
+ E --> F["execute()"]
75
+ F -->|"transfer()"| G["Transfer Tokens"]
76
+ F -->|"mint()"| H["Mint Tokens"]
77
+ F -->|"balanceOf()"| I["Query Balance"]
78
+ F -->|"approve()"| J["Approve Spender"]
79
+ G --> K["Complete"]
80
+ H --> K
81
+ I --> K
82
+ J --> K
83
+ K --> E
84
+ ```
85
+
86
+ ## Step 2: Understanding the Code
87
+
88
+ ### Token Contract Architecture
89
+
90
+ This diagram shows how your MyToken contract inherits functionality from the OP20 base class:
91
+
92
+ ```mermaid
93
+ ---
94
+ config:
95
+ theme: dark
96
+ ---
97
+ classDiagram
98
+ class OP_NET {
99
+ +Address address
100
+ +Address contractDeployer
101
+ +onDeployment(calldata)
102
+ +execute(selector, calldata)
103
+ +onlyDeployer(caller)
104
+ +emitEvent(event)
105
+ }
106
+
107
+ class ReentrancyGuard {
108
+ -u8 reentrancyStatus
109
+ +nonReentrant()
110
+ +protected()
111
+ }
112
+
113
+ class OP20 {
114
+ +StoredU256 _totalSupply
115
+ +StoredString _name
116
+ +StoredString _symbol
117
+ +u8 _decimals
118
+ +u256 _maxSupply
119
+ +transfer(to, amount)
120
+ +approve(spender, amount)
121
+ +balanceOf(address)
122
+ +_mint(to, amount)
123
+ +_burn(from, amount)
124
+ }
125
+
126
+ class MyToken {
127
+ +onDeployment(calldata)
128
+ +mint(calldata)
129
+ }
130
+
131
+ OP_NET <|-- ReentrancyGuard
132
+ ReentrancyGuard <|-- OP20
133
+ OP20 <|-- MyToken
134
+
135
+ note for MyToken "Custom implementation:\n- Deployment logic\n- Additional mint function"
136
+ note for OP20 "Built-in methods:\n- transfer\n- approve\n- balanceOf\n- totalSupply"
137
+ note for OP_NET "Base contract:\n- Access control\n- Event system\n- Execution router"
138
+ ```
139
+
140
+ ### The Class Declaration
141
+
142
+ ```typescript
143
+ @final
144
+ export class MyToken extends OP20 {
145
+ ```
146
+
147
+ | Component | Meaning |
148
+ |-----------|---------|
149
+ | `@final` | AssemblyScript decorator - prevents inheritance |
150
+ | `export` | Makes the class accessible outside the file |
151
+ | `extends OP20` | Inherits from the fungible token standard |
152
+
153
+ **Solidity equivalent:**
154
+ ```solidity
155
+ contract MyToken is ERC20 {
156
+ ```
157
+
158
+ ### The Constructor
159
+
160
+ ```typescript
161
+ public constructor() {
162
+ super();
163
+ }
164
+ ```
165
+
166
+ **IMPORTANT:** In OPNet, the constructor runs on **every** contract interaction, not just deployment. This is different from Solidity!
167
+
168
+ ```typescript
169
+ // OPNet // Solidity
170
+ public constructor() { // constructor() {
171
+ super(); // // Runs ONCE at deployment
172
+ // Runs EVERY time! // }
173
+ }
174
+ ```
175
+
176
+ Never put initialization logic in the constructor. Use `onDeployment` instead.
177
+
178
+ ### The Deployment Hook
179
+
180
+ ```typescript
181
+ public override onDeployment(_calldata: Calldata): void {
182
+ const maxSupply: u256 = u256.fromString('1000000000000000000000000');
183
+ const decimals: u8 = 18;
184
+ const name: string = 'MyToken';
185
+ const symbol: string = 'MTK';
186
+
187
+ this.instantiate(new OP20InitParameters(maxSupply, decimals, name, symbol));
188
+ this._mint(Blockchain.tx.origin, maxSupply);
189
+ }
190
+ ```
191
+
192
+ This method runs **once** when the contract is first deployed. It's the equivalent of Solidity's `constructor()`.
193
+
194
+ | Parameter | Value | Meaning |
195
+ |-----------|-------|---------|
196
+ | `maxSupply` | 1,000,000 (with 18 decimals) | Maximum tokens that can ever exist |
197
+ | `decimals` | 18 | Decimal places (like ETH/wei) |
198
+ | `name` | "MyToken" | Human-readable name |
199
+ | `symbol` | "MTK" | Ticker symbol |
200
+
201
+ **Solidity equivalent:**
202
+ ```solidity
203
+ constructor() ERC20("MyToken", "MTK") {
204
+ _mint(msg.sender, 1000000 * 10**18);
205
+ }
206
+ ```
207
+
208
+ ### The Mint Function
209
+
210
+ ```typescript
211
+ @method(
212
+ { name: 'address', type: ABIDataTypes.ADDRESS },
213
+ { name: 'amount', type: ABIDataTypes.UINT256 }
214
+ )
215
+ @emit('Minted')
216
+ public mint(calldata: Calldata): BytesWriter {
217
+ this.onlyDeployer(Blockchain.tx.sender);
218
+
219
+ this._mint(calldata.readAddress(), calldata.readU256());
220
+
221
+ return new BytesWriter(0);
222
+ }
223
+ ```
224
+
225
+ ### Mint Operation Data Flow
226
+
227
+ This sequence diagram shows what happens when the mint function is called:
228
+
229
+ ```mermaid
230
+ ---
231
+ config:
232
+ theme: dark
233
+ ---
234
+ sequenceDiagram
235
+ participant User as 👤 User
236
+ participant Blockchain as Blockchain
237
+ participant MyToken as MyToken
238
+ participant OP20 as OP20
239
+ participant Storage as Storage
240
+
241
+ User->>Blockchain: Call mint(to, amount)
242
+ Blockchain->>MyToken: constructor()
243
+ Note over MyToken: Runs on EVERY call
244
+ Blockchain->>MyToken: execute(selector, calldata)
245
+
246
+ MyToken->>MyToken: mint(calldata)
247
+ MyToken->>Blockchain: Check tx.sender
248
+ Blockchain-->>MyToken: sender address
249
+
250
+ MyToken->>MyToken: onlyDeployer(sender)
251
+ alt sender != deployer
252
+ MyToken-->>User: Revert("Only deployer")
253
+ end
254
+
255
+ MyToken->>MyToken: readAddress() from calldata
256
+ MyToken->>MyToken: readU256() from calldata
257
+
258
+ MyToken->>OP20: _mint(to, amount)
259
+
260
+ OP20->>Storage: Read current balance[to]
261
+ Storage-->>OP20: currentBalance
262
+
263
+ OP20->>OP20: newBalance = currentBalance + amount
264
+
265
+ OP20->>Storage: Write balance[to] = newBalance
266
+ OP20->>Storage: Read totalSupply
267
+ Storage-->>OP20: currentSupply
268
+
269
+ OP20->>OP20: newSupply = currentSupply + amount
270
+ OP20->>Storage: Write totalSupply = newSupply
271
+
272
+ OP20->>Blockchain: emit(MintEvent)
273
+
274
+ OP20-->>MyToken: Success
275
+ MyToken->>MyToken: new BytesWriter(0)
276
+ MyToken-->>Blockchain: Empty response
277
+ Blockchain-->>User: Transaction success
278
+
279
+ Note over Storage: All changes persisted<br/>to blockchain state
280
+ ```
281
+
282
+ Breaking this down:
283
+
284
+ | Line | Purpose |
285
+ |------|---------|
286
+ | `@method(...)` | Declares method parameters for ABI generation |
287
+ | `@emit('Minted')` | Declares event emission for ABI documentation |
288
+ | `onlyDeployer(...)` | Access control - only the deployer can call |
289
+ | `calldata.readAddress()` | Parse the recipient address from input |
290
+ | `calldata.readU256()` | Parse the amount from input |
291
+ | `_mint(to, amount)` | Internal mint function from OP20 |
292
+ | `return new BytesWriter(0)` | Return empty response |
293
+
294
+ **Solidity equivalent:**
295
+ ```solidity
296
+ function mint(address to, uint256 amount) external onlyOwner {
297
+ _mint(to, amount);
298
+ }
299
+ ```
300
+
301
+ ## Step 3: Understanding Types
302
+
303
+ ### u256 - Big Numbers
304
+
305
+ OPNet uses `u256` for large numbers (like balances):
306
+
307
+ ```typescript
308
+ import { u256 } from '@btc-vision/as-bignum/assembly';
309
+
310
+ // Creating u256 values
311
+ const a = u256.fromU64(100); // From small number
312
+ const b = u256.fromU64(1_000_000); // From u64
313
+ const c = u256.fromString('99999999999999'); // From string (large numbers)
314
+
315
+ // NEVER use floating point!
316
+ // const bad = u256.fromU64(1.5); // WRONG! - No floating point!
317
+ ```
318
+
319
+ **Why not native numbers?**
320
+
321
+ | JavaScript/TypeScript | AssemblyScript/OPNet |
322
+ |----------------------|----------------------|
323
+ | `number` (64-bit float) | Non-deterministic! |
324
+ | `BigInt` | Not supported in WASM |
325
+ | N/A | `u256` (deterministic) |
326
+
327
+ ### Address
328
+
329
+ Addresses are 32 bytes in OPNet:
330
+
331
+ ```typescript
332
+ import { Address, Blockchain } from '@btc-vision/btc-runtime/runtime';
333
+
334
+ // Get the current sender
335
+ const sender: Address = Blockchain.tx.sender;
336
+
337
+ // Zero address (like address(0) in Solidity)
338
+ const zero = Address.zero();
339
+
340
+ // Compare addresses
341
+ if (sender.equals(zero)) {
342
+ throw new Revert('Cannot be zero address');
343
+ }
344
+ ```
345
+
346
+ ### Calldata
347
+
348
+ Input parsing uses `Calldata`:
349
+
350
+ ```typescript
351
+ public myMethod(calldata: Calldata): BytesWriter {
352
+ // Read parameters in order
353
+ const address = calldata.readAddress(); // 32 bytes
354
+ const amount = calldata.readU256(); // 32 bytes
355
+ const flag = calldata.readBoolean(); // 1 byte
356
+ const data = calldata.readBytes(); // Variable length
357
+
358
+ // ...
359
+ }
360
+ ```
361
+
362
+ ## Step 4: Inherited OP20 Methods
363
+
364
+ By extending `OP20`, your token automatically gets these methods:
365
+
366
+ | Method | Description | Selector |
367
+ |--------|-------------|----------|
368
+ | `transfer(to, amount)` | Transfer tokens | Built-in |
369
+ | `transferFrom(from, to, amount)` | Transfer with approval | Built-in |
370
+ | `approve(spender, amount)` | Approve spender | Built-in |
371
+ | `balanceOf(address)` | Get balance | Built-in |
372
+ | `allowance(owner, spender)` | Get allowance | Built-in |
373
+ | `totalSupply()` | Total supply | Built-in |
374
+ | `name()` | Token name | Built-in |
375
+ | `symbol()` | Token symbol | Built-in |
376
+ | `decimals()` | Decimal places | Built-in |
377
+
378
+ ## Step 5: Building the Contract
379
+
380
+ Add to your `package.json`:
381
+
382
+ ```json
383
+ {
384
+ "scripts": {
385
+ "build:token": "asc src/token/index.ts --target token --measure --uncheckedBehavior never"
386
+ }
387
+ }
388
+ ```
389
+
390
+ Create `src/token/index.ts`:
391
+
392
+ ```typescript
393
+ import { Blockchain } from '@btc-vision/btc-runtime/runtime';
394
+ import { revertOnError } from '@btc-vision/btc-runtime/runtime/abort/abort';
395
+ import { MyToken } from './MyToken';
396
+
397
+ // DO NOT TOUCH TO THIS.
398
+ Blockchain.contract = () => {
399
+ // ONLY CHANGE THE CONTRACT CLASS NAME.
400
+ // DO NOT ADD CUSTOM LOGIC HERE.
401
+
402
+ return new MyToken();
403
+ };
404
+
405
+ // VERY IMPORTANT
406
+ export * from '@btc-vision/btc-runtime/runtime/exports';
407
+
408
+ // VERY IMPORTANT
409
+ export function abort(message: string, fileName: string, line: u32, column: u32): void {
410
+ revertOnError(message, fileName, line, column);
411
+ }
412
+ ```
413
+
414
+ Build:
415
+
416
+ ```bash
417
+ npm run build:token
418
+ ```
419
+
420
+ ## Solidity Comparison
421
+
422
+ Here's a side-by-side comparison of the complete contract:
423
+
424
+ <table>
425
+ <tr>
426
+ <th>OPNet (AssemblyScript)</th>
427
+ <th>Solidity</th>
428
+ </tr>
429
+ <tr>
430
+ <td>
431
+
432
+ ```typescript
433
+ import { u256 } from '@btc-vision/as-bignum/assembly';
434
+ import {
435
+ Blockchain,
436
+ BytesWriter,
437
+ Calldata,
438
+ OP20,
439
+ OP20InitParameters,
440
+ } from '@btc-vision/btc-runtime/runtime';
441
+
442
+ @final
443
+ export class MyToken extends OP20 {
444
+ public constructor() {
445
+ super();
446
+ }
447
+
448
+ public override onDeployment(_: Calldata): void {
449
+ const maxSupply = u256.fromString('1000000000000000000000000');
450
+ this.instantiate(new OP20InitParameters(
451
+ maxSupply,
452
+ 18,
453
+ 'MyToken',
454
+ 'MTK'
455
+ ));
456
+ this._mint(Blockchain.tx.origin, maxSupply);
457
+ }
458
+
459
+ @method(
460
+ { name: 'address', type: ABIDataTypes.ADDRESS },
461
+ { name: 'amount', type: ABIDataTypes.UINT256 }
462
+ )
463
+ @emit('Minted')
464
+ public mint(calldata: Calldata): BytesWriter {
465
+ this.onlyDeployer(Blockchain.tx.sender);
466
+ this._mint(
467
+ calldata.readAddress(),
468
+ calldata.readU256()
469
+ );
470
+ return new BytesWriter(0);
471
+ }
472
+ }
473
+ ```
474
+
475
+ </td>
476
+ <td>
477
+
478
+ ```solidity
479
+ // SPDX-License-Identifier: MIT
480
+ pragma solidity ^0.8.0;
481
+
482
+ import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
483
+ import "@openzeppelin/contracts/access/Ownable.sol";
484
+
485
+ contract MyToken is ERC20, Ownable {
486
+ uint256 public constant MAX_SUPPLY =
487
+ 1000000 * 10**18;
488
+
489
+ constructor()
490
+ ERC20("MyToken", "MTK")
491
+ Ownable(msg.sender)
492
+ {
493
+ _mint(msg.sender, MAX_SUPPLY);
494
+ }
495
+
496
+ function mint(
497
+ address to,
498
+ uint256 amount
499
+ ) external onlyOwner {
500
+ _mint(to, amount);
501
+ }
502
+ }
503
+ ```
504
+
505
+ </td>
506
+ </tr>
507
+ </table>
508
+
509
+ ## Common Patterns
510
+
511
+ ### Access Control
512
+
513
+ ```typescript
514
+ // Only deployer can call
515
+ @method({ name: 'param', type: ABIDataTypes.UINT256 })
516
+ @returns({ name: 'result', type: ABIDataTypes.UINT256 })
517
+ public adminFunction(calldata: Calldata): BytesWriter {
518
+ this.onlyDeployer(Blockchain.tx.sender);
519
+ const param = calldata.readU256();
520
+ // ... perform admin logic
521
+ const result = new BytesWriter(32);
522
+ result.writeU256(param);
523
+ return result;
524
+ }
525
+ ```
526
+
527
+ ### Error Handling
528
+
529
+ ```typescript
530
+ import { Revert, Address, BytesWriter, Calldata } from '@btc-vision/btc-runtime/runtime';
531
+
532
+ @method(
533
+ { name: 'to', type: ABIDataTypes.ADDRESS },
534
+ { name: 'amount', type: ABIDataTypes.UINT256 }
535
+ )
536
+ @emit('Transferred')
537
+ public transfer(calldata: Calldata): BytesWriter {
538
+ const to = calldata.readAddress();
539
+ const amount = calldata.readU256();
540
+
541
+ if (to.equals(Address.zero())) {
542
+ throw new Revert('Cannot transfer to zero address');
543
+ }
544
+ // ...
545
+ return new BytesWriter(0);
546
+ }
547
+ ```
548
+
549
+ ### Reading Storage
550
+
551
+ ```typescript
552
+ import { SafeMath } from '@btc-vision/btc-runtime/runtime';
553
+
554
+ // In OP20, balances are managed automatically
555
+ const balance: u256 = this.balanceOf(address);
556
+
557
+ // When performing u256 operations, always use SafeMath
558
+ const newBalance = SafeMath.add(balance, amount);
559
+ const result = SafeMath.sub(balance, amount);
560
+ ```
561
+
562
+ ## Next Steps
563
+
564
+ Now that you've created your first contract:
565
+
566
+ 1. [Understand the project structure](./project-structure.md)
567
+ 2. [Learn about the blockchain environment](../core-concepts/blockchain-environment.md)
568
+ 3. [Explore storage in depth](../core-concepts/storage-system.md)
569
+ 4. [See more examples](../examples/basic-token.md)
570
+
571
+ ---
572
+
573
+ **Navigation:**
574
+ - Previous: [Installation](./installation.md)
575
+ - Next: [Project Structure](./project-structure.md)