# BytesWriter and BytesReader

`BytesWriter` and `BytesReader` handle binary serialization and deserialization. They're used for encoding return values, event data, and cross-contract communication.

## BytesWriter

### Overview

```typescript
import { BytesWriter, Address } from '@btc-vision/btc-runtime/runtime';
import { u256 } from '@btc-vision/as-bignum/assembly';

// Create writer with initial capacity
const writer = new BytesWriter(64);

// Write data
writer.writeAddress(recipient);
writer.writeU256(amount);
writer.writeBoolean(true);

// Get encoded bytes
const data: Uint8Array = writer.getBuffer();
```

### BytesWriter/Reader Architecture

```mermaid
classDiagram
    class BytesWriter {
        -DataView buffer
        -Uint8Array typedArray
        -u32 currentOffset
        +BytesWriter(length)
        +write~T~(value) void
        +writeU8(value)
        +writeU16(value, be)
        +writeU32(value, be)
        +writeU64(value, be)
        +writeU128(value, be)
        +writeU256(value, be)
        +writeAddress(addr)
        +writeExtendedAddress(extAddr)
        +writeSchnorrSignature(addr, sig)
        +writeString(str)
        +writeBytes(data)
        +writeBoolean(bool)
        +writeExtendedAddressArray(arr)
        +writeExtendedAddressMapU256(map)
        +getBuffer() Uint8Array
    }

    class BytesReader {
        -DataView buffer
        -i32 currentOffset
        +BytesReader(bytes)
        +read~T~() T
        +readU8() u8
        +readU16(be) u16
        +readU32(be) u32
        +readU64(be) u64
        +readU128(be) u128
        +readU256(be) u256
        +readAddress() Address
        +readExtendedAddress() ExtendedAddress
        +readSchnorrSignature() SchnorrSignature
        +readString() string
        +readBytes(length) Uint8Array
        +readBoolean() bool
        +readExtendedAddressArray() ExtendedAddress[]
        +readExtendedAddressMapU256() ExtendedAddressMap
        +hasMoreData() bool
    }

    class DataView {
        <<built-in>>
        +getUint8()
        +setUint8()
        +getUint32()
        +setUint32()
    }

    BytesWriter --> DataView : uses
    BytesReader --> DataView : uses

    note for BytesWriter "Sequential write\nfixed-size buffer"
    note for BytesReader "Sequential read\nwith position tracking"
```

### Creating a BytesWriter

```typescript
// With initial capacity (recommended - buffer does NOT auto-grow)
const writer = new BytesWriter(128);

// Note: Buffer does NOT grow dynamically - will throw Revert if exceeded
// Always pre-calculate required size or use generous initial capacity
```

### Writing Primitives

```typescript
// Boolean
writer.writeBoolean(true);   // 1 byte

// Unsigned integers
writer.writeU8(255);         // 1 byte
writer.writeU16(65535);      // 2 bytes
writer.writeU32(4294967295); // 4 bytes
writer.writeU64(value);      // 8 bytes
writer.writeU128(value);     // 16 bytes
writer.writeU256(value);     // 32 bytes

// Signed integers
writer.writeI8(-128);
writer.writeI16(-32768);
writer.writeI32(-2147483648);
writer.writeI64(value);
```

### Writing Complex Types

```typescript
// Address (32 bytes)
writer.writeAddress(address);

// ExtendedAddress (64 bytes: 32 tweaked key + 32 ML-DSA key hash)
writer.writeExtendedAddress(extendedAddress);

// Schnorr signature with signer address (128 bytes total)
writer.writeSchnorrSignature(signerAddress, signature);

// String without length prefix
writer.writeString('Hello, World!');

// String with u32 length prefix
writer.writeStringWithLength('Hello, World!');

// Bytes without length prefix
writer.writeBytes(data);

// Bytes with u32 length prefix
writer.writeBytesWithLength(data);

// Selector (4 bytes, big-endian)
writer.writeSelector(selector);
```

### Generic Write Method

The `write<T>()` method automatically selects the correct write method based on the type:

```typescript
// Automatically dispatches to the correct write method
writer.write<u64>(12345);                    // writeU64
writer.write<u256>(amount);                  // writeU256
writer.write<bool>(true);                    // writeBoolean
writer.write<Address>(addr);                 // writeAddress
writer.write<ExtendedAddress>(extAddr);      // writeExtendedAddress
writer.write<string>('hello');               // writeStringWithLength

// Supported types:
// - Primitives: bool, u8, u16, u32, u64, i8, i16, i32, i64
// - Big numbers: u128, u256, i128
// - Complex: Address, ExtendedAddress, Selector, string, Uint8Array
```

### Writing Arrays

All array methods use a u16 length prefix (max 65535 elements):

```typescript
// Address array (u16 length prefix + addresses)
writer.writeAddressArray(addresses);

// ExtendedAddress array (u16 length prefix + 64-byte addresses)
writer.writeExtendedAddressArray(extendedAddresses);

// Numeric arrays (u16 length prefix + values)
writer.writeU8Array(u8Values);
writer.writeU16Array(u16Values);
writer.writeU32Array(u32Values);
writer.writeU64Array(u64Values);
writer.writeU128Array(u128Values);
writer.writeU256Array(u256Values);

// Array of variable-length buffers (u16 count, then u32 length + data for each)
writer.writeArrayOfBuffer(buffers);

// AddressMap<u256> (u16 count, then address + u256 pairs)
writer.writeAddressMapU256(addressMap);

// ExtendedAddressMap<u256> (u16 count, then 64-byte address + u256 pairs)
writer.writeExtendedAddressMapU256(extendedAddressMap);
```

### Getting Results

```typescript
// Get the full buffer
const buffer: Uint8Array = writer.getBuffer();

// Get current write offset (bytes written so far)
const offset: u32 = writer.getOffset();

// Get total buffer capacity
const capacity: u32 = writer.bufferLength();

// Convert to BytesReader for reading
const reader: BytesReader = writer.toBytesReader();
```

## BytesReader

### Overview

```typescript
import { BytesReader, Address } from '@btc-vision/btc-runtime/runtime';
import { u256 } from '@btc-vision/as-bignum/assembly';

// Create reader from buffer
const reader = new BytesReader(data);

// Read data in same order it was written
const recipient: Address = reader.readAddress();
const amount: u256 = reader.readU256();
const flag: bool = reader.readBoolean();
```

### Creating a BytesReader

```typescript
// From Uint8Array
const reader = new BytesReader(buffer);

// From BytesWriter
const reader = writer.toBytesReader();
```

### Reading Primitives

```typescript
// Boolean
const flag: bool = reader.readBoolean();

// Unsigned integers
const u8val: u8 = reader.readU8();
const u16val: u16 = reader.readU16();
const u32val: u32 = reader.readU32();
const u64val: u64 = reader.readU64();
const u128val: u128 = reader.readU128();
const u256val: u256 = reader.readU256();

// Signed integers
const i8val: i8 = reader.readI8();
const i16val: i16 = reader.readI16();
const i32val: i32 = reader.readI32();
const i64val: i64 = reader.readI64();
```

### Reading Complex Types

```typescript
// Address (32 bytes)
const addr: Address = reader.readAddress();

// ExtendedAddress (64 bytes: 32 tweaked key + 32 ML-DSA key hash)
const extAddr: ExtendedAddress = reader.readExtendedAddress();

// Schnorr signature with signer address (128 bytes)
const schnorrSig: SchnorrSignature = reader.readSchnorrSignature();
const signer: ExtendedAddress = schnorrSig.address;
const signature: Uint8Array = schnorrSig.signature;

// String with known length (bytes read, zeroStop = true)
const name: string = reader.readString(32);  // reads up to 32 bytes, stops at null

// String with u32 length prefix
const name2: string = reader.readStringWithLength();

// Bytes with known length
const data: Uint8Array = reader.readBytes(64);

// Bytes with u32 length prefix
const data2: Uint8Array = reader.readBytesWithLength();

// Selector (4 bytes, big-endian)
const selector: Selector = reader.readSelector();
```

### Generic Read Method

The `read<T>()` method automatically selects the correct read method based on the type:

```typescript
// Automatically dispatches to the correct read method
const num: u64 = reader.read<u64>();              // readU64
const amount: u256 = reader.read<u256>();         // readU256
const flag: bool = reader.read<bool>();           // readBoolean
const addr: Address = reader.read<Address>();     // readAddress
const extAddr: ExtendedAddress = reader.read<ExtendedAddress>();  // readExtendedAddress
const text: string = reader.read<string>();       // readStringWithLength

// Supported types:
// - Primitives: bool, u8, u16, u32, u64, i8, i16, i32, i64
// - Big numbers: u128, u256, i128
// - Complex: Address, ExtendedAddress, Selector, string
```

### Reading Arrays

All array methods expect a u16 length prefix:

```typescript
// Address array
const addresses: Address[] = reader.readAddressArray();

// ExtendedAddress array (64 bytes each)
const extAddresses: ExtendedAddress[] = reader.readExtendedAddressArray();

// Numeric arrays
const u8Values: u8[] = reader.readU8Array();
const u16Values: u16[] = reader.readU16Array();
const u32Values: u32[] = reader.readU32Array();
const u64Values: u64[] = reader.readU64Array();
const u128Values: u128[] = reader.readU128Array();
const u256Values: u256[] = reader.readU256Array();

// Array of variable-length buffers
const buffers: Uint8Array[] = reader.readArrayOfBuffer();

// AddressMap<u256>
const addressMap: AddressMap<u256> = reader.readAddressMapU256();

// ExtendedAddressMap<u256> (64-byte addresses as keys)
const extAddressMap: ExtendedAddressMap<u256> = reader.readExtendedAddressMapU256();
```

### Position Management

```typescript
// Get current read offset
const offset: i32 = reader.getOffset();

// Set read position (must be within buffer bounds)
reader.setOffset(32);

// Get total buffer length
const total: i32 = reader.byteLength;

// Verify enough bytes remain for next read
reader.verifyEnd(offset + 32);  // Throws Revert if not enough bytes
```

## Data Format

### Encoding Summary

| Type | Size | Format |
|------|------|--------|
| `bool` | 1 | 0 or 1 |
| `u8`/`i8` | 1 | Raw byte |
| `u16`/`i16` | 2 | Big-endian (default) |
| `u32`/`i32` | 4 | Big-endian (default) |
| `u64`/`i64` | 8 | Big-endian (default) |
| `u128`/`i128` | 16 | Big-endian (default) |
| `u256` | 32 | Big-endian (default) |
| `Address` | 32 | Raw bytes |
| `ExtendedAddress` | 64 | 32 bytes tweaked key + 32 bytes ML-DSA key hash |
| `SchnorrSignature` | 128 | 64 bytes ExtendedAddress + 64 bytes signature |
| `Selector` | 4 | Big-endian (u32) |
| `string` | 4 + n | Length prefix (u32 BE) + UTF-8 |
| `bytes` | 4 + n | Length prefix (u32 BE) + raw |
| `arrays` | 2 + n | Length prefix (u16 BE) + elements |

**Note:** All multi-byte integers default to big-endian. Pass `be = false` for little-endian:
```typescript
writer.writeU32(value, false);  // Little-endian
reader.readU32(false);          // Little-endian
```

### Data Encoding Flow

```mermaid
---
config:
  theme: dark
---
flowchart LR
    A["Data"] --> B{"Type?"}
    B -->|"Primitive"| C["Fixed Size"]
    B -->|"String/Array"| D["Length Prefix + Data"]
    C --> E["Write to Buffer"]
    D --> E
    E --> F["Increment Offset"]
    F --> G["Final Buffer"]
```

### String Encoding Example

```
"Hello" encoded:
| 05 00 00 00 | 48 65 6c 6c 6f |
| length=5    | "Hello" UTF-8  |
```

### Array Encoding Example

```
[1, 2, 3] as u256 encoded:
| 03 00 00 00 | 01 00...00 | 02 00...00 | 03 00...00 |
| length=3    | 1 (32 bytes)| 2 (32 bytes)| 3 (32 bytes)|
```

## Common Patterns

### Return Values

```typescript
// Simple return
public getValue(_calldata: Calldata): BytesWriter {
    const writer = new BytesWriter(32);
    writer.writeU256(this._value.value);
    return writer;
}

// Multiple return values
public getState(_calldata: Calldata): BytesWriter {
    const writer = new BytesWriter(96);
    writer.writeU256(this._value1.value);
    writer.writeU256(this._value2.value);
    writer.writeAddress(this._owner.value);
    return writer;
}

// No return
public setState(calldata: Calldata): BytesWriter {
    // ... do work ...
    return new BytesWriter(0);  // Empty response
}
```

### Event Encoding

```typescript
export class SwapEvent extends NetEvent {
    constructor(
        public readonly user: Address,
        public readonly tokenIn: Address,
        public readonly tokenOut: Address,
        public readonly amountIn: u256,
        public readonly amountOut: u256
    ) {
        super('Swap');
    }

    protected override encodeData(writer: BytesWriter): void {
        writer.writeAddress(this.user);
        writer.writeAddress(this.tokenIn);
        writer.writeAddress(this.tokenOut);
        writer.writeU256(this.amountIn);
        writer.writeU256(this.amountOut);
    }
}
```

### Cross-Contract Call Data

```typescript
// Define method selectors (sha256 first 4 bytes of method signature)
const TRANSFER_SELECTOR: u32 = 0xa9059cbb;  // transfer(address,uint256)

// Encode call to another contract
private encodeTransfer(to: Address, amount: u256): Uint8Array {
    const writer = new BytesWriter(68);
    writer.writeSelector(TRANSFER_SELECTOR);
    writer.writeAddress(to);
    writer.writeU256(amount);
    return writer.getBuffer();
}

// Make the call
const calldata = this.encodeTransfer(recipient, tokenAmount);
const result = Blockchain.call(tokenContract, calldata, true);

// Decode result
if (result.success) {
    const reader = new BytesReader(result.data);
    const success: bool = reader.readBoolean();
}
```

### Cross-Contract Call Sequence

```mermaid
sequenceDiagram
    participant C1 as Calling Contract
    participant W as BytesWriter
    participant BC as Blockchain
    participant C2 as Target Contract
    participant R as BytesReader

    C1->>W: new BytesWriter(68)
    C1->>W: writeSelector(0xa9059cbb)
    Note over W: [4 bytes] Selector
    C1->>W: writeAddress(recipient)
    Note over W: [4 + 32 bytes] Selector + Address
    C1->>W: writeU256(amount)
    Note over W: [4 + 32 + 32 bytes] Complete calldata
    W-->>C1: getBuffer()

    C1->>BC: call(tokenContract, calldata, true)
    BC->>C2: Execute with calldata
    C2->>R: new BytesReader(calldata)
    R->>C2: readSelector() → 0xa9059cbb
    R->>C2: readAddress() → recipient
    R->>C2: readU256() → amount
    C2->>C2: Execute transfer logic
    C2->>W: new BytesWriter(1)
    C2->>W: writeBoolean(true)
    W-->>C2: getBuffer()
    C2-->>BC: Return encoded result
    BC-->>C1: result.data

    C1->>R: new BytesReader(result.data)
    R-->>C1: readBoolean() → true

    Note over C1,C2: Total calldata: 68 bytes<br/>Selector(4) + Address(32) + u256(32)
```

### Struct-like Encoding

```typescript
// Define a struct-like type
class Order {
    maker: Address;
    taker: Address;
    makerAmount: u256;
    takerAmount: u256;
    expiry: u64;
}

// Encode a "struct"
private encodeOrder(
    maker: Address,
    taker: Address,
    makerAmount: u256,
    takerAmount: u256,
    expiry: u64
): Uint8Array {
    const writer = new BytesWriter(104);
    writer.writeAddress(maker);
    writer.writeAddress(taker);
    writer.writeU256(makerAmount);
    writer.writeU256(takerAmount);
    writer.writeU64(expiry);
    return writer.getBuffer();
}

// Decode a "struct"
private decodeOrder(data: Uint8Array): Order {
    const reader = new BytesReader(data);
    const order = new Order();
    order.maker = reader.readAddress();
    order.taker = reader.readAddress();
    order.makerAmount = reader.readU256();
    order.takerAmount = reader.readU256();
    order.expiry = reader.readU64();
    return order;
}
```

### Struct Encoding Memory Layout

```mermaid
graph LR
    subgraph Order["Order Struct Encoding (104 bytes)"]
        direction LR
        A["Offset 0-31<br/>maker Address<br/>32 bytes"]
        B["Offset 32-63<br/>taker Address<br/>32 bytes"]
        C["Offset 64-95<br/>makerAmount u256<br/>32 bytes"]
        D["Offset 96-127<br/>takerAmount u256<br/>32 bytes"]
        E["Offset 128-135<br/>expiry u64<br/>8 bytes"]
    end

    A --> B --> C --> D --> E
```

## Size Limits

### Array Limits

Maximum array length: **65,535 elements**

```typescript
// Writing large arrays
if (items.length > 65535) {
    throw new Revert('Array too large');
}
writer.writeU256Array(items);
```

### Event Size Limit

Maximum event data: **352 bytes**

```typescript
protected override encodeData(writer: BytesWriter): void {
    // Calculate total size
    // 32 + 32 + 32 + 32 + 8 = 136 bytes - OK
    writer.writeAddress(this.addr1);     // 32
    writer.writeAddress(this.addr2);     // 32
    writer.writeU256(this.amount1);      // 32
    writer.writeU256(this.amount2);      // 32
    writer.writeU64(this.timestamp);     // 8
}
```

## Solidity vs OP_NET Comparison

### Encoding/Decoding Comparison Table

| Feature | Solidity | OP_NET |
|---------|----------|-------|
| **Encode function** | `abi.encode(...)` | `BytesWriter` methods |
| **Decode function** | `abi.decode(data, (types))` | `BytesReader` methods |
| **Packed encoding** | `abi.encodePacked(...)` | Default behavior (no padding) |
| **Encode with selector** | `abi.encodeWithSelector(sel, ...)` | `writer.writeSelector(sel); writer.write...()` |
| **Encode with signature** | `abi.encodeWithSignature(sig, ...)` | Manual selector + params |
| **Byte order** | Big-endian | Big-endian (default) |
| **Padding** | 32-byte aligned | No padding (native sizes) |
| **Return encoding** | Automatic | Manual via BytesWriter |

### Type Encoding Comparison

| Solidity Encoding | OP_NET Encoding |
|-------------------|----------------|
| `abi.encode(uint256)` | `writer.writeU256(value)` |
| `abi.encode(uint128)` | `writer.writeU128(value)` |
| `abi.encode(uint64)` | `writer.writeU64(value)` |
| `abi.encode(uint32)` | `writer.writeU32(value)` |
| `abi.encode(uint16)` | `writer.writeU16(value)` |
| `abi.encode(uint8)` | `writer.writeU8(value)` |
| `abi.encode(bool)` | `writer.writeBoolean(value)` |
| `abi.encode(address)` | `writer.writeAddress(addr)` |
| `abi.encode(bytes32)` | `writer.writeBytes(data)` |
| `abi.encode(string)` | `writer.writeString(str)` |
| `abi.encode(bytes)` | `writer.writeBytes(data)` |
| `abi.encode(address[])` | `writer.writeAddressArray(addrs)` |
| `abi.encode(uint256[])` | `writer.writeU256Array(values)` |

### Side-by-Side Code Examples

#### Basic Encoding

```solidity
// Solidity
bytes memory data = abi.encode(recipient, amount);
bytes memory packed = abi.encodePacked(recipient, amount);
```

```typescript
// OP_NET
const writer = new BytesWriter(64);  // 32 + 32 bytes
writer.writeAddress(recipient);
writer.writeU256(amount);
const data: Uint8Array = writer.getBuffer();
// OP_NET encoding is similar to encodePacked (no padding)
```

#### Basic Decoding

```solidity
// Solidity
(address to, uint256 amount) = abi.decode(data, (address, uint256));
```

```typescript
// OP_NET
const reader = new BytesReader(data);
const to: Address = reader.readAddress();
const amount: u256 = reader.readU256();
```

#### Encoding Multiple Values

```solidity
// Solidity
function encodeTransferData(
    address from,
    address to,
    uint256 amount,
    string memory memo
) public pure returns (bytes memory) {
    return abi.encode(from, to, amount, memo);
}
```

```typescript
// OP_NET
function encodeTransferData(
    from: Address,
    to: Address,
    amount: u256,
    memo: string
): Uint8Array {
    // Calculate size: 32 + 32 + 32 + (4 + memo.length)
    const writer = new BytesWriter(100 + memo.length);
    writer.writeAddress(from);
    writer.writeAddress(to);
    writer.writeU256(amount);
    writer.writeString(memo);
    return writer.getBuffer();
}
```

#### Encoding with Function Selector

```solidity
// Solidity
bytes4 selector = bytes4(keccak256("transfer(address,uint256)"));
bytes memory callData = abi.encodeWithSelector(selector, to, amount);
// Or
bytes memory callData = abi.encodeWithSignature("transfer(address,uint256)", to, amount);
```

```typescript
// OP_NET
const TRANSFER_SELECTOR: Selector = Selector.from("transfer(address,uint256)");
const writer = new BytesWriter(68);  // 4 + 32 + 32
writer.writeSelector(TRANSFER_SELECTOR);
writer.writeAddress(to);
writer.writeU256(amount);
const callData: Uint8Array = writer.getBuffer();
```

#### Return Value Encoding

```solidity
// Solidity - Automatic return encoding
function getInfo() public view returns (address, uint256, bool) {
    return (owner, balance, isActive);
}

// ABI automatically encodes the return tuple
```

```typescript
// OP_NET - Manual return encoding
public getInfo(_calldata: Calldata): BytesWriter {
    const writer = new BytesWriter(65);  // 32 + 32 + 1
    writer.writeAddress(this._owner.value);
    writer.writeU256(this._balance.value);
    writer.writeBoolean(this._isActive.value);
    return writer;
}
```

#### Decoding Return Values

```solidity
// Solidity
(bool success, bytes memory returnData) = target.call(callData);
if (success) {
    (address addr, uint256 amount) = abi.decode(returnData, (address, uint256));
}
```

```typescript
// OP_NET
const result = Blockchain.call(target, callData, true);
if (result.success) {
    const reader = new BytesReader(result.data);
    const addr: Address = reader.readAddress();
    const amount: u256 = reader.readU256();
}
```

#### Encoding Structs

```solidity
// Solidity
struct Order {
    address maker;
    address taker;
    uint256 makerAmount;
    uint256 takerAmount;
    uint64 expiry;
}

function encodeOrder(Order memory order) public pure returns (bytes memory) {
    return abi.encode(order.maker, order.taker, order.makerAmount, order.takerAmount, order.expiry);
}
```

```typescript
// OP_NET
class Order {
    maker: Address;
    taker: Address;
    makerAmount: u256;
    takerAmount: u256;
    expiry: u64;
}

function encodeOrder(order: Order): Uint8Array {
    const writer = new BytesWriter(136);  // 32+32+32+32+8
    writer.writeAddress(order.maker);
    writer.writeAddress(order.taker);
    writer.writeU256(order.makerAmount);
    writer.writeU256(order.takerAmount);
    writer.writeU64(order.expiry);
    return writer.getBuffer();
}
```

#### Event Data Encoding

```solidity
// Solidity - Events are indexed/non-indexed
event Transfer(address indexed from, address indexed to, uint256 amount);

function _transfer(address from, address to, uint256 amount) internal {
    // ... transfer logic
    emit Transfer(from, to, amount);  // Automatic encoding
}
```

```typescript
// OP_NET - Custom event class with manual encoding
export class TransferEvent extends NetEvent {
    constructor(
        public readonly from: Address,
        public readonly to: Address,
        public readonly amount: u256
    ) {
        super('Transfer');
    }

    protected override encodeData(writer: BytesWriter): void {
        writer.writeAddress(this.from);
        writer.writeAddress(this.to);
        writer.writeU256(this.amount);
    }
}

// Usage
this.emitEvent(new TransferEvent(from, to, amount));
```

#### Low-Level Bytes Manipulation

```solidity
// Solidity
function extractSelector(bytes calldata data) public pure returns (bytes4) {
    return bytes4(data[:4]);
}

function extractAddress(bytes calldata data, uint offset) public pure returns (address) {
    return abi.decode(data[offset:offset+32], (address));
}
```

```typescript
// OP_NET
function extractSelector(data: Uint8Array): Selector {
    const reader = new BytesReader(data);
    return reader.readSelector();
}

function extractAddress(data: Uint8Array, offset: u32): Address {
    const reader = new BytesReader(data);
    // Skip to offset by reading and discarding bytes
    for (let i: u32 = 0; i < offset; i++) {
        reader.readU8();
    }
    return reader.readAddress();
}
```

### Size Comparison (Encoding Overhead)

| Data | Solidity abi.encode | Solidity encodePacked | OP_NET |
|------|---------------------|----------------------|-------|
| `bool` | 32 bytes | 1 byte | 1 byte |
| `uint8` | 32 bytes | 1 byte | 1 byte |
| `uint16` | 32 bytes | 2 bytes | 2 bytes |
| `uint32` | 32 bytes | 4 bytes | 4 bytes |
| `uint64` | 32 bytes | 8 bytes | 8 bytes |
| `uint128` | 32 bytes | 16 bytes | 16 bytes |
| `uint256` | 32 bytes | 32 bytes | 32 bytes |
| `address` | 32 bytes | 20 bytes | 32 bytes |
| `(addr, u256, bool)` | 96 bytes | 53 bytes | 65 bytes |

### Key Differences Summary

| Aspect | Solidity | OP_NET |
|--------|----------|-------|
| **Encoding approach** | `abi.encode()` function | BytesWriter object methods |
| **Decoding approach** | `abi.decode()` with type tuple | BytesReader sequential reads |
| **Packed encoding** | `abi.encodePacked()` (separate) | Default (always packed) |
| **Selector encoding** | `abi.encodeWithSelector()` | `writeSelector()` + params |
| **Dynamic sizing** | Automatic | Pre-calculate or auto-grow |
| **Return values** | Automatic encoding | Manual BytesWriter construction |
| **Error on overflow** | Reverts | Reverts |
| **Endianness** | Big-endian | Big-endian (default) |

### Migration Patterns

#### From Solidity `abi.encode`

```solidity
// Solidity
bytes memory encoded = abi.encode(addr, amount, flag);
```

```typescript
// OP_NET equivalent
const writer = new BytesWriter(65);  // 32 + 32 + 1
writer.writeAddress(addr);
writer.writeU256(amount);
writer.writeBoolean(flag);
const encoded = writer.getBuffer();
```

#### From Solidity `abi.decode`

```solidity
// Solidity
(address addr, uint256 amount, bool flag) = abi.decode(data, (address, uint256, bool));
```

```typescript
// OP_NET equivalent
const reader = new BytesReader(data);
const addr = reader.readAddress();
const amount = reader.readU256();
const flag = reader.readBoolean();
```

#### From Solidity `abi.encodeWithSelector`

```solidity
// Solidity
bytes memory data = abi.encodeWithSelector(
    IERC20.transfer.selector,
    recipient,
    amount
);
```

```typescript
// OP_NET equivalent
const TRANSFER_SELECTOR: Selector = Selector.from("transfer(address,uint256)");
const writer = new BytesWriter(68);
writer.writeSelector(TRANSFER_SELECTOR);
writer.writeAddress(recipient);
writer.writeU256(amount);
const data = writer.getBuffer();
```

## Best Practices

### 1. Pre-calculate Capacity

```typescript
// Calculate required size
const size = 32 + 32 + 4;  // address + u256 + u32
const writer = new BytesWriter(size);

// Better than growing the buffer
```

### 2. Consistent Order

```typescript
// Always read in the same order as written
// Document the order clearly

/**
 * Encoded format:
 * - to: Address (32 bytes)
 * - amount: u256 (32 bytes)
 * - data: bytes (4 + n bytes)
 */
```

### 3. Validate Before Reading

```typescript
// Check if enough data remains
if (reader.remaining < 32) {
    throw new Revert('Insufficient data');
}
const value = reader.readU256();
```

### 4. Handle Variable-Length Data Last

```typescript
// Fixed-size fields first
writer.writeAddress(addr);
writer.writeU256(amount);
writer.writeU64(timestamp);

// Variable-size fields last
writer.writeString(message);
writer.writeBytes(data);
```

---

**Navigation:**
- Previous: [Calldata](./calldata.md)
- Next: [Stored Primitives](../storage/stored-primitives.md)
