> ## Documentation Index
> Fetch the complete documentation index at: https://voltaire.tevm.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Rlp.encodeBytes

> Encode byte array according to RLP string rules

<Card title="Try it Live" icon="play" href="https://playground.tevm.sh?example=primitives/rlp.ts">
  Run RLP examples in the interactive playground
</Card>

## String Encoding Rules

RLP string encoding has three cases based on byte length:

### 1. Single Byte \< 0x80

For a single byte with value less than 0x80 (128), the byte encodes as itself with no prefix:

```typescript theme={null}
import { Rlp } from 'tevm'

// Byte value 0x7f (127)
const input = new Uint8Array([0x7f])
const encoded = Rlp.encodeBytes(input)
// => Uint8Array([0x7f])

// Byte value 0x00
const zero = new Uint8Array([0x00])
const encoded = Rlp.encodeBytes(zero)
// => Uint8Array([0x00])

// Note: Single byte >= 0x80 still needs prefix
const high = new Uint8Array([0x80])
const encoded = Rlp.encodeBytes(high)
// => Uint8Array([0x81, 0x80])
```

### 2. Short String (0-55 bytes)

For strings of 0-55 bytes, prefix with `0x80 + length`:

```typescript theme={null}
import { Rlp } from 'tevm'

// Empty string
const empty = Bytes()
const encoded = Rlp.encodeBytes(empty)
// => Uint8Array([0x80])
// 0x80 = 0x80 + 0

// 3 bytes
const bytes = new Uint8Array([1, 2, 3])
const encoded = Rlp.encodeBytes(bytes)
// => Uint8Array([0x83, 1, 2, 3])
// 0x83 = 0x80 + 3

// 55 bytes (maximum for short form)
const max = new Uint8Array(55).fill(0x42)
const encoded = Rlp.encodeBytes(max)
// => Uint8Array([0xb7, ...max])
// 0xb7 = 0x80 + 55
```

### 3. Long String (56+ bytes)

For strings of 56+ bytes, use long form: `[0xb7 + length_of_length, ...length_bytes, ...bytes]`

```typescript theme={null}
import { Rlp } from 'tevm'

// 56 bytes (minimum for long form)
const min = new Uint8Array(56).fill(0x42)
const encoded = Rlp.encodeBytes(min)
// => Uint8Array([0xb8, 56, ...min])
// 0xb8 = 0xb7 + 1 (length needs 1 byte)
// 56 = length value

// 256 bytes (length needs 2 bytes)
const large = new Uint8Array(256).fill(0x42)
const encoded = Rlp.encodeBytes(large)
// => Uint8Array([0xb9, 0x01, 0x00, ...large])
// 0xb9 = 0xb7 + 2 (length needs 2 bytes)
// [0x01, 0x00] = 256 in big-endian

// 65536 bytes (length needs 3 bytes)
const huge = new Uint8Array(65536).fill(0x42)
const encoded = Rlp.encodeBytes(huge)
// => Uint8Array([0xba, 0x01, 0x00, 0x00, ...huge])
// 0xba = 0xb7 + 3
```

<Note>
  The threshold of 56 bytes is chosen because lengths 0-55 fit in a single prefix byte (0x80-0xb7). Starting at 56, we need an additional byte to encode the length.
</Note>

## Usage Patterns

### Transaction Field Encoding

Encode individual transaction fields:

```typescript theme={null}
import { Rlp } from 'tevm'

// Encode nonce (typically small number)
const nonce = new Uint8Array([0x00])
const encodedNonce = Rlp.encodeBytes(nonce)
// => Uint8Array([0x00])  (single byte < 0x80)

// Encode address (20 bytes)
const address = new Uint8Array(20).fill(0x01)
const encodedAddress = Rlp.encodeBytes(address)
// => Uint8Array([0x94, ...address])
// 0x94 = 0x80 + 20

// Encode signature r (32 bytes)
const r = Bytes32().fill(0x02)
const encodedR = Rlp.encodeBytes(r)
// => Uint8Array([0xa0, ...r])
// 0xa0 = 0x80 + 32
```

### Contract Bytecode

Encode contract bytecode (often > 56 bytes):

```typescript theme={null}
import { Rlp } from 'tevm'

// Contract bytecode (example: 500 bytes)
const bytecode = new Uint8Array(500).fill(0x60)
const encoded = Rlp.encodeBytes(bytecode)
// => Uint8Array([0xb9, 0x01, 0xf4, ...bytecode])
// 0xb9 = 0xb7 + 2 (length needs 2 bytes)
// [0x01, 0xf4] = 500 in big-endian
```

### Keccak256 Hash

Encode 32-byte hash values:

```typescript theme={null}
import { Rlp } from 'tevm'
import { keccak256 } from 'tevm/crypto'

const data = new Uint8Array([1, 2, 3, 4])
const hash = keccak256(data)  // 32 bytes

const encoded = Rlp.encodeBytes(hash)
// => Uint8Array([0xa0, ...hash])
// 0xa0 = 0x80 + 32
```

### Empty Data

Encode empty byte arrays:

```typescript theme={null}
import { Rlp } from 'tevm'

// Empty transaction data field
const empty = Bytes()
const encoded = Rlp.encodeBytes(empty)
// => Uint8Array([0x80])

// Use in transaction encoding
const tx = [
  nonce,
  gasPrice,
  gasLimit,
  to,
  value,
  Rlp.encodeBytes(Bytes()),  // Empty data
  v,
  r,
  s
]
```

## Performance

### When to Use encodeBytes

Use `encodeBytes` instead of generic `encode` when you know the input is bytes (not a list), to skip type dispatch overhead.

```typescript theme={null}
import { Rlp } from 'tevm'

// Direct encoding
const encoded = Rlp.encodeBytes(bytes)

// vs generic with dispatch overhead
const encoded = Rlp.encode(bytes)
```

### Pre-calculate Sizes

For repeated encoding, pre-calculate buffer sizes:

```typescript theme={null}
import { Rlp } from 'tevm'

// Calculate size first
function calculateEncodedSize(bytes: Uint8Array): number {
  if (bytes.length === 1 && bytes[0]! < 0x80) {
    return 1
  }
  if (bytes.length <= 55) {
    return 1 + bytes.length
  }
  const lengthBytes = Math.ceil(Math.log2(bytes.length + 1) / 8)
  return 1 + lengthBytes + bytes.length
}

const size = calculateEncodedSize(data)
// Allocate buffer of exact size
const buffer = new Uint8Array(size)
```

### Batch Encoding

Encode multiple byte arrays efficiently:

```typescript theme={null}
import { Rlp } from 'tevm'

const items = [
  new Uint8Array([1, 2, 3]),
  new Uint8Array([4, 5, 6]),
  new Uint8Array([7, 8, 9])
]

// Encode all at once as list
const encoded = Rlp.encode(items)

// vs encoding individually (less efficient)
const encoded = items.map(item => Rlp.encodeBytes(item))
```

## See Also

* [encode](/primitives/rlp/encode) - Universal encoder
* [encodeList](/primitives/rlp/encode-list) - Encode list
* [encodeArray](/primitives/rlp/encode-array) - Encode with schema
* [decode](/primitives/rlp/decode) - Decode RLP bytes
* [Algorithm](/primitives/rlp/algorithm) - RLP specification
