@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,938 @@
1
+ # BytesWriter and BytesReader
2
+
3
+ `BytesWriter` and `BytesReader` handle binary serialization and deserialization. They're used for encoding return values, event data, and cross-contract communication.
4
+
5
+ ## BytesWriter
6
+
7
+ ### Overview
8
+
9
+ ```typescript
10
+ import { BytesWriter, Address } from '@btc-vision/btc-runtime/runtime';
11
+ import { u256 } from '@btc-vision/as-bignum/assembly';
12
+
13
+ // Create writer with initial capacity
14
+ const writer = new BytesWriter(64);
15
+
16
+ // Write data
17
+ writer.writeAddress(recipient);
18
+ writer.writeU256(amount);
19
+ writer.writeBoolean(true);
20
+
21
+ // Get encoded bytes
22
+ const data: Uint8Array = writer.getBuffer();
23
+ ```
24
+
25
+ ### BytesWriter/Reader Architecture
26
+
27
+ ```mermaid
28
+ classDiagram
29
+ class BytesWriter {
30
+ -DataView buffer
31
+ -Uint8Array typedArray
32
+ -u32 currentOffset
33
+ +BytesWriter(length)
34
+ +writeU8(value)
35
+ +writeU16(value, be)
36
+ +writeU32(value, be)
37
+ +writeU64(value, be)
38
+ +writeU128(value, be)
39
+ +writeU256(value, be)
40
+ +writeAddress(addr)
41
+ +writeString(str)
42
+ +writeBytes(data)
43
+ +writeBoolean(bool)
44
+ +getBuffer() Uint8Array
45
+ }
46
+
47
+ class BytesReader {
48
+ -DataView buffer
49
+ -i32 currentOffset
50
+ +BytesReader(bytes)
51
+ +readU8() u8
52
+ +readU16(be) u16
53
+ +readU32(be) u32
54
+ +readU64(be) u64
55
+ +readU128(be) u128
56
+ +readU256(be) u256
57
+ +readAddress() Address
58
+ +readString() string
59
+ +readBytes(length) Uint8Array
60
+ +readBoolean() bool
61
+ +hasMoreData() bool
62
+ }
63
+
64
+ class DataView {
65
+ <<built-in>>
66
+ +getUint8()
67
+ +setUint8()
68
+ +getUint32()
69
+ +setUint32()
70
+ }
71
+
72
+ BytesWriter --> DataView : uses
73
+ BytesReader --> DataView : uses
74
+
75
+ note for BytesWriter "Sequential write\nfixed-size buffer"
76
+ note for BytesReader "Sequential read\nwith position tracking"
77
+ ```
78
+
79
+ ### Creating a BytesWriter
80
+
81
+ ```typescript
82
+ // With initial capacity (recommended - buffer does NOT auto-grow)
83
+ const writer = new BytesWriter(128);
84
+
85
+ // Note: Buffer does NOT grow dynamically - will throw Revert if exceeded
86
+ // Always pre-calculate required size or use generous initial capacity
87
+ ```
88
+
89
+ ### Writing Primitives
90
+
91
+ ```typescript
92
+ // Boolean
93
+ writer.writeBoolean(true); // 1 byte
94
+
95
+ // Unsigned integers
96
+ writer.writeU8(255); // 1 byte
97
+ writer.writeU16(65535); // 2 bytes
98
+ writer.writeU32(4294967295); // 4 bytes
99
+ writer.writeU64(value); // 8 bytes
100
+ writer.writeU128(value); // 16 bytes
101
+ writer.writeU256(value); // 32 bytes
102
+
103
+ // Signed integers
104
+ writer.writeI8(-128);
105
+ writer.writeI16(-32768);
106
+ writer.writeI32(-2147483648);
107
+ writer.writeI64(value);
108
+ ```
109
+
110
+ ### Writing Complex Types
111
+
112
+ ```typescript
113
+ // Address (32 bytes)
114
+ writer.writeAddress(address);
115
+
116
+ // String without length prefix
117
+ writer.writeString('Hello, World!');
118
+
119
+ // String with u32 length prefix
120
+ writer.writeStringWithLength('Hello, World!');
121
+
122
+ // Bytes without length prefix
123
+ writer.writeBytes(data);
124
+
125
+ // Bytes with u32 length prefix
126
+ writer.writeBytesWithLength(data);
127
+
128
+ // Selector (4 bytes, big-endian)
129
+ writer.writeSelector(selector);
130
+ ```
131
+
132
+ ### Writing Arrays
133
+
134
+ All array methods use a u16 length prefix (max 65535 elements):
135
+
136
+ ```typescript
137
+ // Address array (u16 length prefix + addresses)
138
+ writer.writeAddressArray(addresses);
139
+
140
+ // Numeric arrays (u16 length prefix + values)
141
+ writer.writeU8Array(u8Values);
142
+ writer.writeU16Array(u16Values);
143
+ writer.writeU32Array(u32Values);
144
+ writer.writeU64Array(u64Values);
145
+ writer.writeU128Array(u128Values);
146
+ writer.writeU256Array(u256Values);
147
+
148
+ // Array of variable-length buffers (u16 count, then u32 length + data for each)
149
+ writer.writeArrayOfBuffer(buffers);
150
+
151
+ // AddressMap<u256> (u16 count, then address + u256 pairs)
152
+ writer.writeAddressMapU256(addressMap);
153
+ ```
154
+
155
+ ### Getting Results
156
+
157
+ ```typescript
158
+ // Get the full buffer
159
+ const buffer: Uint8Array = writer.getBuffer();
160
+
161
+ // Get current write offset (bytes written so far)
162
+ const offset: u32 = writer.getOffset();
163
+
164
+ // Get total buffer capacity
165
+ const capacity: u32 = writer.bufferLength();
166
+
167
+ // Convert to BytesReader for reading
168
+ const reader: BytesReader = writer.toBytesReader();
169
+ ```
170
+
171
+ ## BytesReader
172
+
173
+ ### Overview
174
+
175
+ ```typescript
176
+ import { BytesReader, Address } from '@btc-vision/btc-runtime/runtime';
177
+ import { u256 } from '@btc-vision/as-bignum/assembly';
178
+
179
+ // Create reader from buffer
180
+ const reader = new BytesReader(data);
181
+
182
+ // Read data in same order it was written
183
+ const recipient: Address = reader.readAddress();
184
+ const amount: u256 = reader.readU256();
185
+ const flag: bool = reader.readBoolean();
186
+ ```
187
+
188
+ ### Creating a BytesReader
189
+
190
+ ```typescript
191
+ // From Uint8Array
192
+ const reader = new BytesReader(buffer);
193
+
194
+ // From BytesWriter
195
+ const reader = writer.toBytesReader();
196
+ ```
197
+
198
+ ### Reading Primitives
199
+
200
+ ```typescript
201
+ // Boolean
202
+ const flag: bool = reader.readBoolean();
203
+
204
+ // Unsigned integers
205
+ const u8val: u8 = reader.readU8();
206
+ const u16val: u16 = reader.readU16();
207
+ const u32val: u32 = reader.readU32();
208
+ const u64val: u64 = reader.readU64();
209
+ const u128val: u128 = reader.readU128();
210
+ const u256val: u256 = reader.readU256();
211
+
212
+ // Signed integers
213
+ const i8val: i8 = reader.readI8();
214
+ const i16val: i16 = reader.readI16();
215
+ const i32val: i32 = reader.readI32();
216
+ const i64val: i64 = reader.readI64();
217
+ ```
218
+
219
+ ### Reading Complex Types
220
+
221
+ ```typescript
222
+ // Address (32 bytes)
223
+ const addr: Address = reader.readAddress();
224
+
225
+ // String with known length (bytes read, zeroStop = true)
226
+ const name: string = reader.readString(32); // reads up to 32 bytes, stops at null
227
+
228
+ // String with u32 length prefix
229
+ const name2: string = reader.readStringWithLength();
230
+
231
+ // Bytes with known length
232
+ const data: Uint8Array = reader.readBytes(64);
233
+
234
+ // Bytes with u32 length prefix
235
+ const data2: Uint8Array = reader.readBytesWithLength();
236
+
237
+ // Selector (4 bytes, big-endian)
238
+ const selector: Selector = reader.readSelector();
239
+ ```
240
+
241
+ ### Reading Arrays
242
+
243
+ All array methods expect a u16 length prefix:
244
+
245
+ ```typescript
246
+ // Address array
247
+ const addresses: Address[] = reader.readAddressArray();
248
+
249
+ // Numeric arrays
250
+ const u8Values: u8[] = reader.readU8Array();
251
+ const u16Values: u16[] = reader.readU16Array();
252
+ const u32Values: u32[] = reader.readU32Array();
253
+ const u64Values: u64[] = reader.readU64Array();
254
+ const u128Values: u128[] = reader.readU128Array();
255
+ const u256Values: u256[] = reader.readU256Array();
256
+
257
+ // Array of variable-length buffers
258
+ const buffers: Uint8Array[] = reader.readArrayOfBuffer();
259
+
260
+ // AddressMap<u256>
261
+ const addressMap: AddressMap<u256> = reader.readAddressMapU256();
262
+ ```
263
+
264
+ ### Position Management
265
+
266
+ ```typescript
267
+ // Get current read offset
268
+ const offset: i32 = reader.getOffset();
269
+
270
+ // Set read position (must be within buffer bounds)
271
+ reader.setOffset(32);
272
+
273
+ // Get total buffer length
274
+ const total: i32 = reader.byteLength;
275
+
276
+ // Verify enough bytes remain for next read
277
+ reader.verifyEnd(offset + 32); // Throws Revert if not enough bytes
278
+ ```
279
+
280
+ ## Data Format
281
+
282
+ ### Encoding Summary
283
+
284
+ | Type | Size | Format |
285
+ |------|------|--------|
286
+ | `bool` | 1 | 0 or 1 |
287
+ | `u8`/`i8` | 1 | Raw byte |
288
+ | `u16`/`i16` | 2 | Big-endian (default) |
289
+ | `u32`/`i32` | 4 | Big-endian (default) |
290
+ | `u64`/`i64` | 8 | Big-endian (default) |
291
+ | `u128`/`i128` | 16 | Big-endian (default) |
292
+ | `u256` | 32 | Big-endian (default) |
293
+ | `Address` | 32 | Raw bytes |
294
+ | `Selector` | 4 | Big-endian (u32) |
295
+ | `string` | 4 + n | Length prefix (u32 BE) + UTF-8 |
296
+ | `bytes` | 4 + n | Length prefix (u32 BE) + raw |
297
+ | `arrays` | 2 + n | Length prefix (u16 BE) + elements |
298
+
299
+ **Note:** All multi-byte integers default to big-endian. Pass `be = false` for little-endian:
300
+ ```typescript
301
+ writer.writeU32(value, false); // Little-endian
302
+ reader.readU32(false); // Little-endian
303
+ ```
304
+
305
+ ### Data Encoding Flow
306
+
307
+ ```mermaid
308
+ ---
309
+ config:
310
+ theme: dark
311
+ ---
312
+ flowchart LR
313
+ A["Data"] --> B{"Type?"}
314
+ B -->|"Primitive"| C["Fixed Size"]
315
+ B -->|"String/Array"| D["Length Prefix + Data"]
316
+ C --> E["Write to Buffer"]
317
+ D --> E
318
+ E --> F["Increment Offset"]
319
+ F --> G["Final Buffer"]
320
+ ```
321
+
322
+ ### String Encoding Example
323
+
324
+ ```
325
+ "Hello" encoded:
326
+ | 05 00 00 00 | 48 65 6c 6c 6f |
327
+ | length=5 | "Hello" UTF-8 |
328
+ ```
329
+
330
+ ### Array Encoding Example
331
+
332
+ ```
333
+ [1, 2, 3] as u256 encoded:
334
+ | 03 00 00 00 | 01 00...00 | 02 00...00 | 03 00...00 |
335
+ | length=3 | 1 (32 bytes)| 2 (32 bytes)| 3 (32 bytes)|
336
+ ```
337
+
338
+ ## Common Patterns
339
+
340
+ ### Return Values
341
+
342
+ ```typescript
343
+ // Simple return
344
+ public getValue(_calldata: Calldata): BytesWriter {
345
+ const writer = new BytesWriter(32);
346
+ writer.writeU256(this._value.value);
347
+ return writer;
348
+ }
349
+
350
+ // Multiple return values
351
+ public getState(_calldata: Calldata): BytesWriter {
352
+ const writer = new BytesWriter(96);
353
+ writer.writeU256(this._value1.value);
354
+ writer.writeU256(this._value2.value);
355
+ writer.writeAddress(this._owner.value);
356
+ return writer;
357
+ }
358
+
359
+ // No return
360
+ public setState(calldata: Calldata): BytesWriter {
361
+ // ... do work ...
362
+ return new BytesWriter(0); // Empty response
363
+ }
364
+ ```
365
+
366
+ ### Event Encoding
367
+
368
+ ```typescript
369
+ export class SwapEvent extends NetEvent {
370
+ constructor(
371
+ public readonly user: Address,
372
+ public readonly tokenIn: Address,
373
+ public readonly tokenOut: Address,
374
+ public readonly amountIn: u256,
375
+ public readonly amountOut: u256
376
+ ) {
377
+ super('Swap');
378
+ }
379
+
380
+ protected override encodeData(writer: BytesWriter): void {
381
+ writer.writeAddress(this.user);
382
+ writer.writeAddress(this.tokenIn);
383
+ writer.writeAddress(this.tokenOut);
384
+ writer.writeU256(this.amountIn);
385
+ writer.writeU256(this.amountOut);
386
+ }
387
+ }
388
+ ```
389
+
390
+ ### Cross-Contract Call Data
391
+
392
+ ```typescript
393
+ // Define method selectors (sha256 first 4 bytes of method signature)
394
+ const TRANSFER_SELECTOR: u32 = 0xa9059cbb; // transfer(address,uint256)
395
+
396
+ // Encode call to another contract
397
+ private encodeTransfer(to: Address, amount: u256): Uint8Array {
398
+ const writer = new BytesWriter(68);
399
+ writer.writeSelector(TRANSFER_SELECTOR);
400
+ writer.writeAddress(to);
401
+ writer.writeU256(amount);
402
+ return writer.getBuffer();
403
+ }
404
+
405
+ // Make the call
406
+ const calldata = this.encodeTransfer(recipient, tokenAmount);
407
+ const result = Blockchain.call(tokenContract, calldata, true);
408
+
409
+ // Decode result
410
+ if (result.success) {
411
+ const reader = new BytesReader(result.data);
412
+ const success: bool = reader.readBoolean();
413
+ }
414
+ ```
415
+
416
+ ### Cross-Contract Call Sequence
417
+
418
+ ```mermaid
419
+ sequenceDiagram
420
+ participant C1 as Calling Contract
421
+ participant W as BytesWriter
422
+ participant BC as Blockchain
423
+ participant C2 as Target Contract
424
+ participant R as BytesReader
425
+
426
+ C1->>W: new BytesWriter(68)
427
+ C1->>W: writeSelector(0xa9059cbb)
428
+ Note over W: [4 bytes] Selector
429
+ C1->>W: writeAddress(recipient)
430
+ Note over W: [4 + 32 bytes] Selector + Address
431
+ C1->>W: writeU256(amount)
432
+ Note over W: [4 + 32 + 32 bytes] Complete calldata
433
+ W-->>C1: getBuffer()
434
+
435
+ C1->>BC: call(tokenContract, calldata, true)
436
+ BC->>C2: Execute with calldata
437
+ C2->>R: new BytesReader(calldata)
438
+ R->>C2: readSelector() → 0xa9059cbb
439
+ R->>C2: readAddress() → recipient
440
+ R->>C2: readU256() → amount
441
+ C2->>C2: Execute transfer logic
442
+ C2->>W: new BytesWriter(1)
443
+ C2->>W: writeBoolean(true)
444
+ W-->>C2: getBuffer()
445
+ C2-->>BC: Return encoded result
446
+ BC-->>C1: result.data
447
+
448
+ C1->>R: new BytesReader(result.data)
449
+ R-->>C1: readBoolean() → true
450
+
451
+ Note over C1,C2: Total calldata: 68 bytes<br/>Selector(4) + Address(32) + u256(32)
452
+ ```
453
+
454
+ ### Struct-like Encoding
455
+
456
+ ```typescript
457
+ // Define a struct-like type
458
+ class Order {
459
+ maker: Address;
460
+ taker: Address;
461
+ makerAmount: u256;
462
+ takerAmount: u256;
463
+ expiry: u64;
464
+ }
465
+
466
+ // Encode a "struct"
467
+ private encodeOrder(
468
+ maker: Address,
469
+ taker: Address,
470
+ makerAmount: u256,
471
+ takerAmount: u256,
472
+ expiry: u64
473
+ ): Uint8Array {
474
+ const writer = new BytesWriter(104);
475
+ writer.writeAddress(maker);
476
+ writer.writeAddress(taker);
477
+ writer.writeU256(makerAmount);
478
+ writer.writeU256(takerAmount);
479
+ writer.writeU64(expiry);
480
+ return writer.getBuffer();
481
+ }
482
+
483
+ // Decode a "struct"
484
+ private decodeOrder(data: Uint8Array): Order {
485
+ const reader = new BytesReader(data);
486
+ const order = new Order();
487
+ order.maker = reader.readAddress();
488
+ order.taker = reader.readAddress();
489
+ order.makerAmount = reader.readU256();
490
+ order.takerAmount = reader.readU256();
491
+ order.expiry = reader.readU64();
492
+ return order;
493
+ }
494
+ ```
495
+
496
+ ### Struct Encoding Memory Layout
497
+
498
+ ```mermaid
499
+ graph LR
500
+ subgraph Order["Order Struct Encoding (104 bytes)"]
501
+ direction LR
502
+ A["Offset 0-31<br/>maker Address<br/>32 bytes"]
503
+ B["Offset 32-63<br/>taker Address<br/>32 bytes"]
504
+ C["Offset 64-95<br/>makerAmount u256<br/>32 bytes"]
505
+ D["Offset 96-127<br/>takerAmount u256<br/>32 bytes"]
506
+ E["Offset 128-135<br/>expiry u64<br/>8 bytes"]
507
+ end
508
+
509
+ A --> B --> C --> D --> E
510
+ ```
511
+
512
+ ## Size Limits
513
+
514
+ ### Array Limits
515
+
516
+ Maximum array length: **65,535 elements**
517
+
518
+ ```typescript
519
+ // Writing large arrays
520
+ if (items.length > 65535) {
521
+ throw new Revert('Array too large');
522
+ }
523
+ writer.writeU256Array(items);
524
+ ```
525
+
526
+ ### Event Size Limit
527
+
528
+ Maximum event data: **352 bytes**
529
+
530
+ ```typescript
531
+ protected override encodeData(writer: BytesWriter): void {
532
+ // Calculate total size
533
+ // 32 + 32 + 32 + 32 + 8 = 136 bytes - OK
534
+ writer.writeAddress(this.addr1); // 32
535
+ writer.writeAddress(this.addr2); // 32
536
+ writer.writeU256(this.amount1); // 32
537
+ writer.writeU256(this.amount2); // 32
538
+ writer.writeU64(this.timestamp); // 8
539
+ }
540
+ ```
541
+
542
+ ## Solidity vs OPNet Comparison
543
+
544
+ ### Encoding/Decoding Comparison Table
545
+
546
+ | Feature | Solidity | OPNet |
547
+ |---------|----------|-------|
548
+ | **Encode function** | `abi.encode(...)` | `BytesWriter` methods |
549
+ | **Decode function** | `abi.decode(data, (types))` | `BytesReader` methods |
550
+ | **Packed encoding** | `abi.encodePacked(...)` | Default behavior (no padding) |
551
+ | **Encode with selector** | `abi.encodeWithSelector(sel, ...)` | `writer.writeSelector(sel); writer.write...()` |
552
+ | **Encode with signature** | `abi.encodeWithSignature(sig, ...)` | Manual selector + params |
553
+ | **Byte order** | Big-endian | Big-endian (default) |
554
+ | **Padding** | 32-byte aligned | No padding (native sizes) |
555
+ | **Return encoding** | Automatic | Manual via BytesWriter |
556
+
557
+ ### Type Encoding Comparison
558
+
559
+ | Solidity Encoding | OPNet Encoding |
560
+ |-------------------|----------------|
561
+ | `abi.encode(uint256)` | `writer.writeU256(value)` |
562
+ | `abi.encode(uint128)` | `writer.writeU128(value)` |
563
+ | `abi.encode(uint64)` | `writer.writeU64(value)` |
564
+ | `abi.encode(uint32)` | `writer.writeU32(value)` |
565
+ | `abi.encode(uint16)` | `writer.writeU16(value)` |
566
+ | `abi.encode(uint8)` | `writer.writeU8(value)` |
567
+ | `abi.encode(bool)` | `writer.writeBoolean(value)` |
568
+ | `abi.encode(address)` | `writer.writeAddress(addr)` |
569
+ | `abi.encode(bytes32)` | `writer.writeBytes(data)` |
570
+ | `abi.encode(string)` | `writer.writeString(str)` |
571
+ | `abi.encode(bytes)` | `writer.writeBytes(data)` |
572
+ | `abi.encode(address[])` | `writer.writeAddressArray(addrs)` |
573
+ | `abi.encode(uint256[])` | `writer.writeU256Array(values)` |
574
+
575
+ ### Side-by-Side Code Examples
576
+
577
+ #### Basic Encoding
578
+
579
+ ```solidity
580
+ // Solidity
581
+ bytes memory data = abi.encode(recipient, amount);
582
+ bytes memory packed = abi.encodePacked(recipient, amount);
583
+ ```
584
+
585
+ ```typescript
586
+ // OPNet
587
+ const writer = new BytesWriter(64); // 32 + 32 bytes
588
+ writer.writeAddress(recipient);
589
+ writer.writeU256(amount);
590
+ const data: Uint8Array = writer.getBuffer();
591
+ // OPNet encoding is similar to encodePacked (no padding)
592
+ ```
593
+
594
+ #### Basic Decoding
595
+
596
+ ```solidity
597
+ // Solidity
598
+ (address to, uint256 amount) = abi.decode(data, (address, uint256));
599
+ ```
600
+
601
+ ```typescript
602
+ // OPNet
603
+ const reader = new BytesReader(data);
604
+ const to: Address = reader.readAddress();
605
+ const amount: u256 = reader.readU256();
606
+ ```
607
+
608
+ #### Encoding Multiple Values
609
+
610
+ ```solidity
611
+ // Solidity
612
+ function encodeTransferData(
613
+ address from,
614
+ address to,
615
+ uint256 amount,
616
+ string memory memo
617
+ ) public pure returns (bytes memory) {
618
+ return abi.encode(from, to, amount, memo);
619
+ }
620
+ ```
621
+
622
+ ```typescript
623
+ // OPNet
624
+ function encodeTransferData(
625
+ from: Address,
626
+ to: Address,
627
+ amount: u256,
628
+ memo: string
629
+ ): Uint8Array {
630
+ // Calculate size: 32 + 32 + 32 + (4 + memo.length)
631
+ const writer = new BytesWriter(100 + memo.length);
632
+ writer.writeAddress(from);
633
+ writer.writeAddress(to);
634
+ writer.writeU256(amount);
635
+ writer.writeString(memo);
636
+ return writer.getBuffer();
637
+ }
638
+ ```
639
+
640
+ #### Encoding with Function Selector
641
+
642
+ ```solidity
643
+ // Solidity
644
+ bytes4 selector = bytes4(keccak256("transfer(address,uint256)"));
645
+ bytes memory callData = abi.encodeWithSelector(selector, to, amount);
646
+ // Or
647
+ bytes memory callData = abi.encodeWithSignature("transfer(address,uint256)", to, amount);
648
+ ```
649
+
650
+ ```typescript
651
+ // OPNet
652
+ const TRANSFER_SELECTOR: Selector = Selector.from("transfer(address,uint256)");
653
+ const writer = new BytesWriter(68); // 4 + 32 + 32
654
+ writer.writeSelector(TRANSFER_SELECTOR);
655
+ writer.writeAddress(to);
656
+ writer.writeU256(amount);
657
+ const callData: Uint8Array = writer.getBuffer();
658
+ ```
659
+
660
+ #### Return Value Encoding
661
+
662
+ ```solidity
663
+ // Solidity - Automatic return encoding
664
+ function getInfo() public view returns (address, uint256, bool) {
665
+ return (owner, balance, isActive);
666
+ }
667
+
668
+ // ABI automatically encodes the return tuple
669
+ ```
670
+
671
+ ```typescript
672
+ // OPNet - Manual return encoding
673
+ public getInfo(_calldata: Calldata): BytesWriter {
674
+ const writer = new BytesWriter(65); // 32 + 32 + 1
675
+ writer.writeAddress(this._owner.value);
676
+ writer.writeU256(this._balance.value);
677
+ writer.writeBoolean(this._isActive.value);
678
+ return writer;
679
+ }
680
+ ```
681
+
682
+ #### Decoding Return Values
683
+
684
+ ```solidity
685
+ // Solidity
686
+ (bool success, bytes memory returnData) = target.call(callData);
687
+ if (success) {
688
+ (address addr, uint256 amount) = abi.decode(returnData, (address, uint256));
689
+ }
690
+ ```
691
+
692
+ ```typescript
693
+ // OPNet
694
+ const result = Blockchain.call(target, callData, true);
695
+ if (result.success) {
696
+ const reader = new BytesReader(result.data);
697
+ const addr: Address = reader.readAddress();
698
+ const amount: u256 = reader.readU256();
699
+ }
700
+ ```
701
+
702
+ #### Encoding Structs
703
+
704
+ ```solidity
705
+ // Solidity
706
+ struct Order {
707
+ address maker;
708
+ address taker;
709
+ uint256 makerAmount;
710
+ uint256 takerAmount;
711
+ uint64 expiry;
712
+ }
713
+
714
+ function encodeOrder(Order memory order) public pure returns (bytes memory) {
715
+ return abi.encode(order.maker, order.taker, order.makerAmount, order.takerAmount, order.expiry);
716
+ }
717
+ ```
718
+
719
+ ```typescript
720
+ // OPNet
721
+ class Order {
722
+ maker: Address;
723
+ taker: Address;
724
+ makerAmount: u256;
725
+ takerAmount: u256;
726
+ expiry: u64;
727
+ }
728
+
729
+ function encodeOrder(order: Order): Uint8Array {
730
+ const writer = new BytesWriter(136); // 32+32+32+32+8
731
+ writer.writeAddress(order.maker);
732
+ writer.writeAddress(order.taker);
733
+ writer.writeU256(order.makerAmount);
734
+ writer.writeU256(order.takerAmount);
735
+ writer.writeU64(order.expiry);
736
+ return writer.getBuffer();
737
+ }
738
+ ```
739
+
740
+ #### Event Data Encoding
741
+
742
+ ```solidity
743
+ // Solidity - Events are indexed/non-indexed
744
+ event Transfer(address indexed from, address indexed to, uint256 amount);
745
+
746
+ function _transfer(address from, address to, uint256 amount) internal {
747
+ // ... transfer logic
748
+ emit Transfer(from, to, amount); // Automatic encoding
749
+ }
750
+ ```
751
+
752
+ ```typescript
753
+ // OPNet - Custom event class with manual encoding
754
+ export class TransferEvent extends NetEvent {
755
+ constructor(
756
+ public readonly from: Address,
757
+ public readonly to: Address,
758
+ public readonly amount: u256
759
+ ) {
760
+ super('Transfer');
761
+ }
762
+
763
+ protected override encodeData(writer: BytesWriter): void {
764
+ writer.writeAddress(this.from);
765
+ writer.writeAddress(this.to);
766
+ writer.writeU256(this.amount);
767
+ }
768
+ }
769
+
770
+ // Usage
771
+ this.emitEvent(new TransferEvent(from, to, amount));
772
+ ```
773
+
774
+ #### Low-Level Bytes Manipulation
775
+
776
+ ```solidity
777
+ // Solidity
778
+ function extractSelector(bytes calldata data) public pure returns (bytes4) {
779
+ return bytes4(data[:4]);
780
+ }
781
+
782
+ function extractAddress(bytes calldata data, uint offset) public pure returns (address) {
783
+ return abi.decode(data[offset:offset+32], (address));
784
+ }
785
+ ```
786
+
787
+ ```typescript
788
+ // OPNet
789
+ function extractSelector(data: Uint8Array): Selector {
790
+ const reader = new BytesReader(data);
791
+ return reader.readSelector();
792
+ }
793
+
794
+ function extractAddress(data: Uint8Array, offset: u32): Address {
795
+ const reader = new BytesReader(data);
796
+ // Skip to offset by reading and discarding bytes
797
+ for (let i: u32 = 0; i < offset; i++) {
798
+ reader.readU8();
799
+ }
800
+ return reader.readAddress();
801
+ }
802
+ ```
803
+
804
+ ### Size Comparison (Encoding Overhead)
805
+
806
+ | Data | Solidity abi.encode | Solidity encodePacked | OPNet |
807
+ |------|---------------------|----------------------|-------|
808
+ | `bool` | 32 bytes | 1 byte | 1 byte |
809
+ | `uint8` | 32 bytes | 1 byte | 1 byte |
810
+ | `uint16` | 32 bytes | 2 bytes | 2 bytes |
811
+ | `uint32` | 32 bytes | 4 bytes | 4 bytes |
812
+ | `uint64` | 32 bytes | 8 bytes | 8 bytes |
813
+ | `uint128` | 32 bytes | 16 bytes | 16 bytes |
814
+ | `uint256` | 32 bytes | 32 bytes | 32 bytes |
815
+ | `address` | 32 bytes | 20 bytes | 32 bytes |
816
+ | `(addr, u256, bool)` | 96 bytes | 53 bytes | 65 bytes |
817
+
818
+ ### Key Differences Summary
819
+
820
+ | Aspect | Solidity | OPNet |
821
+ |--------|----------|-------|
822
+ | **Encoding approach** | `abi.encode()` function | BytesWriter object methods |
823
+ | **Decoding approach** | `abi.decode()` with type tuple | BytesReader sequential reads |
824
+ | **Packed encoding** | `abi.encodePacked()` (separate) | Default (always packed) |
825
+ | **Selector encoding** | `abi.encodeWithSelector()` | `writeSelector()` + params |
826
+ | **Dynamic sizing** | Automatic | Pre-calculate or auto-grow |
827
+ | **Return values** | Automatic encoding | Manual BytesWriter construction |
828
+ | **Error on overflow** | Reverts | Reverts |
829
+ | **Endianness** | Big-endian | Big-endian (default) |
830
+
831
+ ### Migration Patterns
832
+
833
+ #### From Solidity `abi.encode`
834
+
835
+ ```solidity
836
+ // Solidity
837
+ bytes memory encoded = abi.encode(addr, amount, flag);
838
+ ```
839
+
840
+ ```typescript
841
+ // OPNet equivalent
842
+ const writer = new BytesWriter(65); // 32 + 32 + 1
843
+ writer.writeAddress(addr);
844
+ writer.writeU256(amount);
845
+ writer.writeBoolean(flag);
846
+ const encoded = writer.getBuffer();
847
+ ```
848
+
849
+ #### From Solidity `abi.decode`
850
+
851
+ ```solidity
852
+ // Solidity
853
+ (address addr, uint256 amount, bool flag) = abi.decode(data, (address, uint256, bool));
854
+ ```
855
+
856
+ ```typescript
857
+ // OPNet equivalent
858
+ const reader = new BytesReader(data);
859
+ const addr = reader.readAddress();
860
+ const amount = reader.readU256();
861
+ const flag = reader.readBoolean();
862
+ ```
863
+
864
+ #### From Solidity `abi.encodeWithSelector`
865
+
866
+ ```solidity
867
+ // Solidity
868
+ bytes memory data = abi.encodeWithSelector(
869
+ IERC20.transfer.selector,
870
+ recipient,
871
+ amount
872
+ );
873
+ ```
874
+
875
+ ```typescript
876
+ // OPNet equivalent
877
+ const TRANSFER_SELECTOR: Selector = Selector.from("transfer(address,uint256)");
878
+ const writer = new BytesWriter(68);
879
+ writer.writeSelector(TRANSFER_SELECTOR);
880
+ writer.writeAddress(recipient);
881
+ writer.writeU256(amount);
882
+ const data = writer.getBuffer();
883
+ ```
884
+
885
+ ## Best Practices
886
+
887
+ ### 1. Pre-calculate Capacity
888
+
889
+ ```typescript
890
+ // Calculate required size
891
+ const size = 32 + 32 + 4; // address + u256 + u32
892
+ const writer = new BytesWriter(size);
893
+
894
+ // Better than growing the buffer
895
+ ```
896
+
897
+ ### 2. Consistent Order
898
+
899
+ ```typescript
900
+ // Always read in the same order as written
901
+ // Document the order clearly
902
+
903
+ /**
904
+ * Encoded format:
905
+ * - to: Address (32 bytes)
906
+ * - amount: u256 (32 bytes)
907
+ * - data: bytes (4 + n bytes)
908
+ */
909
+ ```
910
+
911
+ ### 3. Validate Before Reading
912
+
913
+ ```typescript
914
+ // Check if enough data remains
915
+ if (reader.remaining < 32) {
916
+ throw new Revert('Insufficient data');
917
+ }
918
+ const value = reader.readU256();
919
+ ```
920
+
921
+ ### 4. Handle Variable-Length Data Last
922
+
923
+ ```typescript
924
+ // Fixed-size fields first
925
+ writer.writeAddress(addr);
926
+ writer.writeU256(amount);
927
+ writer.writeU64(timestamp);
928
+
929
+ // Variable-size fields last
930
+ writer.writeString(message);
931
+ writer.writeBytes(data);
932
+ ```
933
+
934
+ ---
935
+
936
+ **Navigation:**
937
+ - Previous: [Calldata](./calldata.md)
938
+ - Next: [Stored Primitives](../storage/stored-primitives.md)