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 Types
RLP data type system defining bytes data, list data, and type guards for working with RLP structures.
Overview
RLP uses a discriminated union type to represent two kinds of data:
BytesData - Leaf nodes containing raw bytes
ListData - Nested arrays of RLP data
Type guards provide runtime type checking and TypeScript type narrowing for safe data access.
BrandedRlp
Core type representing RLP data structures.
export type BrandedRlp =
| { type : "bytes" ; value : Uint8Array }
| { type : "list" ; value : BrandedRlp [] }
Branded type using discriminated union pattern. The type field distinguishes between bytes and list variants.
Properties:
type: "bytes" | "list" - Discriminant field
value: Uint8Array | BrandedRlp[] - Data payload
Source: BrandedRlp.ts:4-6
BytesData Variant
Represents a leaf node with raw bytes:
type BytesData = {
type : "bytes"
value : Uint8Array
}
Examples:
import { Rlp } from 'tevm'
// Single byte
const single : BrandedRlp = {
type: 'bytes' ,
value: new Uint8Array ([ 0x7f ])
}
// Empty bytes
const empty : BrandedRlp = {
type: 'bytes' ,
value: Bytes ()
}
// Multiple bytes
const bytes : BrandedRlp = {
type: 'bytes' ,
value: new Uint8Array ([ 1 , 2 , 3 , 4 , 5 ])
}
// Created via constructor
const data = Rlp ( new Uint8Array ([ 1 , 2 , 3 ]))
// => { type: 'bytes', value: Uint8Array([1, 2, 3]) }
ListData Variant
Represents a nested array of RLP data:
type ListData = {
type : "list"
value : BrandedRlp []
}
Examples:
import { Rlp } from 'tevm'
// Empty list
const empty : BrandedRlp = {
type: 'list' ,
value: []
}
// List of bytes
const list : BrandedRlp = {
type: 'list' ,
value: [
{ type: 'bytes' , value: new Uint8Array ([ 1 ]) },
{ type: 'bytes' , value: new Uint8Array ([ 2 ]) }
]
}
// Nested lists
const nested : BrandedRlp = {
type: 'list' ,
value: [
{ type: 'bytes' , value: new Uint8Array ([ 1 ]) },
{
type: 'list' ,
value: [
{ type: 'bytes' , value: new Uint8Array ([ 2 ]) },
{ type: 'bytes' , value: new Uint8Array ([ 3 ]) }
]
}
]
}
// Created via constructor
const listData = Rlp ([
new Uint8Array ([ 1 ]),
new Uint8Array ([ 2 ])
])
// => {
// type: 'list',
// value: [
// { type: 'bytes', value: Uint8Array([1]) },
// { type: 'bytes', value: Uint8Array([2]) }
// ]
// }
Type Guards
Runtime type checking functions that narrow TypeScript types.
isData
Checks if value is any RLP data structure.
Implementation
Checks for required structure:
Value is object (not null)
Has type field
Has value field
Type is either “bytes” or “list”
function isData ( value ) {
return (
typeof value === "object" &&
value !== null &&
"type" in value &&
"value" in value &&
( value . type === "bytes" || value . type === "list" )
)
}
isBytesData
Checks if value is bytes data specifically.
Implementation
Uses isData then checks type field:
function isBytesData ( value ) {
return isData ( value ) && value . type === "bytes"
}
isListData
Checks if value is list data specifically.
Implementation
Uses isData then checks type field:
function isListData ( value ) {
return isData ( value ) && value . type === "list"
}
Type Patterns
Discriminated Union Pattern
RLP uses TypeScript discriminated unions for type safety:
import { Rlp } from 'tevm'
function processRlpData ( data : BrandedRlp ) {
// TypeScript uses 'type' field to narrow types
if ( data . type === 'bytes' ) {
// Here, TypeScript knows data.value is Uint8Array
console . log ( 'Bytes length:' , data . value . length )
const byte = data . value [ 0 ] // OK
} else {
// Here, TypeScript knows data.value is BrandedRlp[]
console . log ( 'List items:' , data . value . length )
const item = data . value [ 0 ] // OK, item is BrandedRlp
}
}
Type Narrowing with Guards
Combine type guards with TypeScript narrowing:
import { Rlp } from 'tevm'
function extractBytes ( data : unknown ) : Uint8Array [] {
// First check if it's RLP data at all
if ( ! Rlp . isData ( data )) {
return []
}
// Now TypeScript knows data is BrandedRlp
if ( Rlp . isBytesData ( data )) {
// Return single item
return [ data . value ]
}
// Must be list data
const bytes : Uint8Array [] = []
for ( const item of data . value ) {
// Recursively extract bytes
bytes . push ( ... extractBytes ( item ))
}
return bytes
}
Exhaustive Type Checking
Ensure all type variants are handled:
import { Rlp } from 'tevm'
function serializeRlp ( data : BrandedRlp ) : string {
switch ( data . type ) {
case 'bytes' :
return `bytes( ${ data . value . length } )`
case 'list' :
return `list( ${ data . value . length } )`
default :
// TypeScript error if new type added
const _exhaustive : never = data
return _exhaustive
}
}
Working with Types
Creating RLP Data
Use Rlp() constructor for automatic type detection:
import { Rlp } from 'tevm'
// From bytes
const bytesData = Rlp ( new Uint8Array ([ 1 , 2 , 3 ]))
// => { type: 'bytes', value: Uint8Array([1, 2, 3]) }
// From array (becomes list)
const listData = Rlp ([
new Uint8Array ([ 1 ]),
new Uint8Array ([ 2 ])
])
// => { type: 'list', value: [...] }
// Already RLP data (pass through)
const existing = { type: 'bytes' , value: new Uint8Array ([ 1 ]) }
const same = Rlp ( existing )
// => Returns existing unchanged
Manual Construction
Create structures directly:
import { Rlp } from 'tevm'
// Manual bytes data
const bytes : BrandedRlp = {
type: 'bytes' ,
value: new Uint8Array ([ 1 , 2 , 3 ])
}
// Manual list data
const list : BrandedRlp = {
type: 'list' ,
value: [ bytes ]
}
// Nested structure
const nested : BrandedRlp = {
type: 'list' ,
value: [
{ type: 'bytes' , value: new Uint8Array ([ 1 ]) },
{
type: 'list' ,
value: [
{ type: 'bytes' , value: new Uint8Array ([ 2 ]) }
]
}
]
}
Type-safe Access
Use type guards for safe access:
import { Rlp } from 'tevm'
function getFirstByte ( data : BrandedRlp ) : number | undefined {
if ( Rlp . isBytesData ( data )) {
return data . value [ 0 ]
}
if ( Rlp . isListData ( data ) && data . value . length > 0 ) {
return getFirstByte ( data . value [ 0 ])
}
return undefined
}
function countItems ( data : BrandedRlp ) : number {
if ( Rlp . isBytesData ( data )) {
return 1
}
if ( Rlp . isListData ( data )) {
return data . value . reduce (( sum , item ) => sum + countItems ( item ), 0 )
}
return 0
}
Type Validation
Runtime Validation
Type guards perform runtime validation:
import { Rlp } from 'tevm'
function validateRlpData ( value : unknown ) : asserts value is BrandedRlp {
if ( ! Rlp . isData ( value )) {
throw new Error ( 'Invalid RLP data structure' )
}
if ( value . type === 'bytes' ) {
if ( ! ( value . value instanceof Uint8Array )) {
throw new Error ( 'Bytes data must have Uint8Array value' )
}
} else if ( value . type === 'list' ) {
if ( ! Array . isArray ( value . value )) {
throw new Error ( 'List data must have array value' )
}
// Recursively validate items
for ( const item of value . value ) {
validateRlpData ( item )
}
}
}
// Usage
const data : unknown = JSON . parse ( jsonString )
validateRlpData ( data )
// Now TypeScript knows data is BrandedRlp
Type Assertions
Assert types when you know the structure:
import { Rlp } from 'tevm'
function processTransaction ( data : BrandedRlp ) {
// We know transactions are lists
if ( data . type !== 'list' ) {
throw new Error ( 'Transaction must be list' )
}
// We know list has 9 items
if ( data . value . length !== 9 ) {
throw new Error ( 'Invalid transaction' )
}
// Access fields safely
const [ nonce , gasPrice , gas , to , value , calldata , v , r , s ] = data . value
// Each field should be bytes
if ( ! Rlp . isBytesData ( nonce )) {
throw new Error ( 'Nonce must be bytes' )
}
}
Encodable
Input type for encoding operations:
type Encodable =
| Uint8Array
| BrandedRlp
| Array < Uint8Array | BrandedRlp | any >
Accepts raw bytes, RLP data, or nested arrays. See Encoding .
Decoded
Output type from decoding operations:
type Decoded = {
data : BrandedRlp
remainder : Uint8Array
}
Contains decoded RLP data and remaining bytes. See Decoding .
Type Examples
Transaction Type
import { Rlp } from 'tevm'
type Transaction = {
nonce : Uint8Array
gasPrice : Uint8Array
gas : Uint8Array
to : Uint8Array
value : Uint8Array
data : Uint8Array
v : Uint8Array
r : Uint8Array
s : Uint8Array
}
function toRlp ( tx : Transaction ) : BrandedRlp {
return {
type: 'list' ,
value: [
{ type: 'bytes' , value: tx . nonce },
{ type: 'bytes' , value: tx . gasPrice },
{ type: 'bytes' , value: tx . gas },
{ type: 'bytes' , value: tx . to },
{ type: 'bytes' , value: tx . value },
{ type: 'bytes' , value: tx . data },
{ type: 'bytes' , value: tx . v },
{ type: 'bytes' , value: tx . r },
{ type: 'bytes' , value: tx . s }
]
}
}
function fromRlp ( data : BrandedRlp ) : Transaction {
if ( data . type !== 'list' || data . value . length !== 9 ) {
throw new Error ( 'Invalid transaction' )
}
const [ nonce , gasPrice , gas , to , value , calldata , v , r , s ] = data . value
if ( ! data . value . every ( Rlp . isBytesData )) {
throw new Error ( 'All fields must be bytes' )
}
return {
nonce: nonce . value ,
gasPrice: gasPrice . value ,
gas: gas . value ,
to: to . value ,
value: value . value ,
data: calldata . value ,
v: v . value ,
r: r . value ,
s: s . value
}
}
Merkle Proof Type
import { Rlp } from 'tevm'
type MerkleProof = Uint8Array []
function proofToRlp ( proof : MerkleProof ) : BrandedRlp {
return {
type: 'list' ,
value: proof . map ( node => ({
type: 'bytes' ,
value: node
}))
}
}
function proofFromRlp ( data : BrandedRlp ) : MerkleProof {
if ( data . type !== 'list' ) {
throw new Error ( 'Proof must be list' )
}
if ( ! data . value . every ( Rlp . isBytesData )) {
throw new Error ( 'All proof nodes must be bytes' )
}
return data . value . map ( node => node . value )
}