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.
Try it Live Run RLP examples in the interactive playground
RLP Encoding
Methods for encoding bytes, lists, and nested structures into RLP format.
Overview
RLP encoding converts arbitrary nested data structures into a compact byte representation. The encoder handles three main cases:
Bytes - Raw byte arrays (strings in RLP terminology)
Lists - Arrays of encodable items
Nested - Lists containing other lists
All encoding operations are deterministic and canonical, ensuring the same input always produces identical output.
RLP Encoding Tree Diagram
RLP encodes nested structures hierarchically:
Input Data Structure:
┌─────────────────────────────┐
│ List │
│ ├─ Bytes: [0x7f] │
│ ├─ Bytes: [0x01, 0x02] │
│ └─ List │
│ ├─ Bytes: [0x03] │
│ └─ Bytes: [0x04, 0x05] │
└─────────────────────────────┘
↓
RLP Encoding
↓
Output RLP Bytes:
┌──────────────────────────────────────────┐
│ 0xc6 (list prefix + length) │
│ 0x7f (single byte [0x7f]) │
│ 0x82 0x01 0x02 (bytes: length + data)│
│ 0xc4 (nested list prefix) │
│ 0x03 (single byte [0x03]) │
│ 0x82 0x04 0x05 (bytes: length + data)│
└──────────────────────────────────────────┘
RLP Encoding Algorithm
Determine Data Type
Is input a single byte < 0x80? Encode as-is
Is input bytes? Encode with length prefix
Is input array? Encode each element recursively
Encode Bytes
If length = 1 and byte < 0x80: no prefix
If length ≤ 55: prefix = 0x80 + length
If length > 55: prefix = 0xb7 + (length of length) + length
Encode Lists
Recursively encode each element
Concatenate all encoded elements
Calculate total payload length
If length ≤ 55: prefix = 0xc0 + length
If length > 55: prefix = 0xf7 + (length of length) + length
Return RLP Bytes
Prefix + (length if needed) + data
encode
General-purpose encoding method that accepts bytes, lists, or RLP data structures.
Signature
function encode ( data : Encodable ) : Uint8Array
type Encodable =
| Uint8Array
| BrandedRlp
| Array < Uint8Array | BrandedRlp | any >
Parameters:
data: Encodable - Data to encode (Uint8Array, RlpData, or array)
Returns:
Uint8Array - RLP-encoded bytes
Throws:
Error('UnexpectedInput') - Invalid encodable data type
Source: encode.js:38-59
Usage
import { Rlp } from 'tevm'
// Encode bytes
const bytes = new Uint8Array ([ 1 , 2 , 3 ])
const encoded = Rlp . encode ( bytes )
// => Uint8Array([0x83, 1, 2, 3])
// Encode list
const list = [ new Uint8Array ([ 1 ]), new Uint8Array ([ 2 ])]
const encoded = Rlp . encode ( list )
// => Uint8Array([0xc4, 0x01, 0x02])
// Encode nested structures
const nested = [
new Uint8Array ([ 1 ]),
[ new Uint8Array ([ 2 ]), new Uint8Array ([ 3 ])]
]
const encoded = Rlp . encode ( nested )
// Encode RLP data structure
const data = Rlp ( new Uint8Array ([ 1 , 2 , 3 ]))
const encoded = Rlp . encode ( data )
// Instance method
const rlpData = Rlp ( new Uint8Array ([ 1 , 2 , 3 ]))
const encoded = rlpData . encode ()
Algorithm
The encode method dispatches to specialized encoders based on input type:
Uint8Array → Uses encodeBytes for string encoding
BrandedRlp (bytes) → Uses encodeBytes on value
BrandedRlp (list) → Uses encodeList on value
Array → Uses encodeList for list encoding
This automatic dispatch simplifies encoding of complex structures:
// These are equivalent
const manual = Rlp . encodeList ([
Rlp . encodeBytes ( new Uint8Array ([ 1 ])),
Rlp . encodeBytes ( new Uint8Array ([ 2 ]))
])
const automatic = Rlp . encode ([
new Uint8Array ([ 1 ]),
new Uint8Array ([ 2 ])
])
encodeBytes
Encodes a byte array according to RLP string rules.
Signature
function encodeBytes ( bytes : Uint8Array ) : Uint8Array
Parameters:
bytes: Uint8Array - Byte array to encode
Returns:
Uint8Array - RLP-encoded bytes
Source: encodeBytes.js:32-53
Usage
import { Rlp } from 'tevm'
// Single byte < 0x80 (no prefix)
const single = new Uint8Array ([ 0x7f ])
const encoded = Rlp . encodeBytes ( single )
// => Uint8Array([0x7f])
// Short string (0-55 bytes)
const short = new Uint8Array ([ 1 , 2 , 3 ])
const encoded = Rlp . encodeBytes ( short )
// => Uint8Array([0x83, 1, 2, 3])
// 0x83 = 0x80 + 3
// Empty string
const empty = Bytes ()
const encoded = Rlp . encodeBytes ( empty )
// => Uint8Array([0x80])
// Long string (56+ bytes)
const long = new Uint8Array ( 60 ). fill ( 0x42 )
const encoded = Rlp . encodeBytes ( long )
// => Uint8Array([0xb8, 60, ...long])
// 0xb8 = 0xb7 + 1 (length needs 1 byte)
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:
// 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])
2. Short String (0-55 bytes)
For strings of 0-55 bytes, prefix with 0x80 + length:
// 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]
// 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
encodeList
Encodes a list of RLP-encodable items.
Signature
function encodeList ( items : Encodable []) : Uint8Array
type Encodable =
| Uint8Array
| BrandedRlp
| Array < any >
Parameters:
items: Encodable[] - Array of items to encode
Returns:
Uint8Array - RLP-encoded list
Source: encodeList.js:33-63
Usage
import { Rlp } from 'tevm'
// Empty list
const empty = []
const encoded = Rlp . encodeList ( empty )
// => Uint8Array([0xc0])
// Simple list
const list = [ new Uint8Array ([ 1 ]), new Uint8Array ([ 2 ])]
const encoded = Rlp . encodeList ( list )
// => Uint8Array([0xc4, 0x01, 0x02])
// 0xc4 = 0xc0 + 4 (2 items, each 1 byte encoded)
// Nested list
const nested = [
new Uint8Array ([ 1 ]),
[ new Uint8Array ([ 2 ]), new Uint8Array ([ 3 ])]
]
const encoded = Rlp . encodeList ( nested )
// List with various types
const mixed = [
new Uint8Array ([ 0x7f ]), // Single byte
new Uint8Array ([ 1 , 2 , 3 ]), // Short string
[], // Empty list
[ new Uint8Array ([ 4 ])] // Nested list
]
const encoded = Rlp . encodeList ( mixed )
List Encoding Rules
RLP list encoding has two cases based on total payload length:
1. Short List (< 56 bytes total)
For lists with total payload < 56 bytes, prefix with 0xc0 + total_length:
// Empty list
const empty = []
const encoded = Rlp . encodeList ( empty )
// => Uint8Array([0xc0])
// 0xc0 = 0xc0 + 0
// List with 2 single bytes
const list = [ new Uint8Array ([ 0x01 ]), new Uint8Array ([ 0x02 ])]
const encoded = Rlp . encodeList ( list )
// => Uint8Array([0xc2, 0x01, 0x02])
// 0xc2 = 0xc0 + 2 (each item is 1 byte)
// List with short strings
const strings = [
new Uint8Array ([ 1 , 2 ]), // Encodes as [0x82, 1, 2] (3 bytes)
new Uint8Array ([ 3 , 4 , 5 ]) // Encodes as [0x83, 3, 4, 5] (4 bytes)
]
const encoded = Rlp . encodeList ( strings )
// => Uint8Array([0xc7, 0x82, 1, 2, 0x83, 3, 4, 5])
// 0xc7 = 0xc0 + 7 (3 + 4 bytes)
2. Long List (56+ bytes total)
For lists with total payload >= 56 bytes, use long form: [0xf7 + length_of_length, ...length_bytes, ...encoded_items]
// Create list with 60 bytes total payload
const items = Array ({ length: 30 }, () => new Uint8Array ([ 0x01 , 0x02 ]))
const encoded = Rlp . encodeList ( items )
// First bytes: [0xf8, 60, ...]
// 0xf8 = 0xf7 + 1 (length needs 1 byte)
// 60 = total payload length
// Large list with 256 bytes total
const large = Array ({ length: 128 }, () => new Uint8Array ([ 0x01 , 0x02 ]))
const encoded = Rlp . encodeList ( large )
// First bytes: [0xf9, 0x01, 0x00, ...]
// 0xf9 = 0xf7 + 2 (length needs 2 bytes)
// [0x01, 0x00] = 256 in big-endian
Algorithm Details
The encodeList implementation:
Encode each item using encode() (dispatches to appropriate encoder)
Calculate total length by summing encoded item lengths
Choose encoding based on total length:
< 56 bytes : Short form with single prefix byte
>= 56 bytes : Long form with length-of-length encoding
Concatenate prefix + encoded items into result buffer
// Conceptual implementation
function encodeList ( items ) {
// Step 1: Encode each item
const encodedItems = items . map ( item => encode ( item ))
// Step 2: Calculate total length
const totalLength = encodedItems . reduce (( sum , item ) => sum + item . length , 0 )
// Step 3: Choose encoding
if ( totalLength < 56 ) {
// Short list: [0xc0 + length, ...items]
return concat ([ 0xc0 + totalLength ], ... encodedItems )
} else {
// Long list: [0xf7 + len_of_len, ...len_bytes, ...items]
const lengthBytes = encodeLength ( totalLength )
return concat ([ 0xf7 + lengthBytes . length ], lengthBytes , ... encodedItems )
}
}
Encoding Examples
Transaction Encoding
Ethereum transactions use RLP encoding for signing and broadcasting:
import { Rlp } from 'tevm'
// Legacy transaction: [nonce, gasPrice, gas, to, value, data, v, r, s]
const txData = [
new Uint8Array ([ 0x00 ]), // nonce
new Uint8Array ([ 0x04 , 0xa8 , 0x17 , 0xc8 ]), // gasPrice
new Uint8Array ([ 0x52 , 0x08 ]), // gas
new Uint8Array ( 20 ). fill ( 0x01 ), // to (address)
new Uint8Array ([ 0x00 ]), // value
Bytes (), // data
new Uint8Array ([ 0x1b ]), // v
Bytes32 (). fill ( 0x02 ), // r
Bytes32 (). fill ( 0x03 ) // s
]
const encoded = Rlp . encodeList ( txData )
// Ready for broadcast or signature verification
Block headers are RLP-encoded lists:
import { Rlp } from 'tevm'
// Simplified block header
const header = [
Bytes32 (). fill ( 0x01 ), // parentHash
Bytes32 (). fill ( 0x02 ), // uncleHash
new Uint8Array ( 20 ). fill ( 0x03 ), // coinbase
Bytes32 (). fill ( 0x04 ), // stateRoot
Bytes32 (). fill ( 0x05 ), // transactionsRoot
Bytes32 (). fill ( 0x06 ), // receiptsRoot
new Uint8Array ( 256 ). fill ( 0x00 ), // logsBloom
new Uint8Array ([ 0x01 ]), // difficulty
new Uint8Array ([ 0x01 ]), // number
new Uint8Array ([ 0x5f , 0x5e , 0x100 ]), // gasLimit
new Uint8Array ([ 0x00 ]), // gasUsed
Bytes4 (), // timestamp
Bytes () // extraData
]
const encoded = Rlp . encodeList ( header )
Nested Data Structures
RLP handles arbitrary nesting:
import { Rlp } from 'tevm'
// Deeply nested structure
const nested = [
new Uint8Array ([ 1 ]),
[
new Uint8Array ([ 2 ]),
[
new Uint8Array ([ 3 ]),
[
new Uint8Array ([ 4 ])
]
]
]
]
const encoded = Rlp . encode ( nested )
// List of lists
const matrix = [
[ new Uint8Array ([ 1 , 2 , 3 ])],
[ new Uint8Array ([ 4 , 5 , 6 ])],
[ new Uint8Array ([ 7 , 8 , 9 ])]
]
const encoded = Rlp . encode ( matrix )
Pre-sizing Buffers
For better performance when encoding many items, pre-calculate total size:
import { Rlp } from 'tevm'
// Calculate size without encoding
const items = [
new Uint8Array ([ 1 , 2 , 3 ]),
new Uint8Array ([ 4 , 5 , 6 ])
]
const size = Rlp . getEncodedLength ( items )
console . log ( `Will need ${ size } bytes` )
// Then encode
const encoded = Rlp . encode ( items )
Use specific encoders when type is known for better performance:
import { Rlp } from 'tevm'
// Generic encoder with type dispatch
const bytesEncoded = Rlp . encodeBytes ( new Uint8Array ([ 1 , 2 , 3 ]))
const listEncoded = Rlp . encodeList ([ bytes1 , bytes2 ])
Avoiding Re-encoding
Cache encoded results when encoding the same data multiple times:
import { Rlp } from 'tevm'
const data = new Uint8Array ([ 1 , 2 , 3 ])
const encoded = Rlp . encode ( data )
// Reuse encoded result
for ( let i = 0 ; i < 1000 ; i ++ ) {
// Use cached 'encoded' instead of re-encoding
processEncoded ( encoded )
}