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 Serialization
Convert RLP data structures to and from JSON for persistence, debugging, and transmission.
Overview
RLP serialization provides JSON conversion for:
- Persistence - Save RLP structures to databases or files
- Debugging - Human-readable representation
- Transmission - Send over JSON APIs
- Testing - Compare structures in test assertions
toJSON
Convert RLP data to JSON-serializable format.
toJSON converts Uint8Array to number arrays while preserving structure:
// Bytes data: Uint8Array → number[]
{
type: 'bytes',
value: [1, 2, 3] // Array of numbers
}
// List data: recursive conversion
{
type: 'list',
value: [/* JSON representations of items */]
}
fromJSON
Convert JSON representation back to RLP data.
Validation
fromJSON validates structure:
import { Rlp } from 'tevm'
// Missing fields
try {
Rlp.fromJSON({ type: 'bytes' })
} catch (error) {
// Error: Invalid JSON format
}
// Invalid type
try {
Rlp.fromJSON({ type: 'invalid', value: [] })
} catch (error) {
// Error: Invalid type: invalid
}
// Bytes value not array
try {
Rlp.fromJSON({ type: 'bytes', value: 'not-array' })
} catch (error) {
// Error: Bytes value must be array
}
// List value not array
try {
Rlp.fromJSON({ type: 'list', value: 'not-array' })
} catch (error) {
// Error: List value must be array
}
Round-trip Serialization
Convert to JSON and back produces equal structures:
import { Rlp } from 'tevm'
const original = {
type: 'list',
value: [
{ type: 'bytes', value: new Uint8Array([1, 2, 3]) },
{
type: 'list',
value: [{ type: 'bytes', value: new Uint8Array([4, 5]) }]
}
]
}
// Convert to JSON
const json = Rlp.toJSON(original)
// Convert back
const restored = Rlp.fromJSON(json)
// Should be equal
console.log(Rlp.equals(original, restored)) // true
Use Cases
Database Storage
Store RLP structures in JSON databases:
import { Rlp } from 'tevm'
// Save transaction
async function saveTransaction(tx: BrandedRlp) {
const json = Rlp.toJSON(tx)
await db.transactions.insert({
id: generateId(),
data: json,
createdAt: new Date()
})
}
// Load transaction
async function loadTransaction(id: string): Promise<BrandedRlp> {
const record = await db.transactions.findById(id)
return Rlp.fromJSON(record.data)
}
API Transmission
Send RLP data over HTTP APIs:
import { Rlp } from 'tevm'
// Client: send RLP data
async function submitData(data: BrandedRlp) {
const json = Rlp.toJSON(data)
const response = await fetch('/api/rlp', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(json)
})
return response.json()
}
// Server: receive RLP data
app.post('/api/rlp', (req, res) => {
try {
const data = Rlp.fromJSON(req.body)
// Process RLP data
const result = processRlp(data)
res.json({ success: true, result })
} catch (error) {
res.status(400).json({ error: error.message })
}
})
Debugging
Pretty-print RLP structures:
import { Rlp } from 'tevm'
function debugRlp(data: BrandedRlp) {
const json = Rlp.toJSON(data)
console.log('RLP Structure:')
console.log(JSON.stringify(json, null, 2))
}
// Example output:
// {
// "type": "list",
// "value": [
// {
// "type": "bytes",
// "value": [1, 2, 3]
// },
// {
// "type": "bytes",
// "value": [4, 5, 6]
// }
// ]
// }
Testing
Compare RLP structures in tests:
import { Rlp } from 'tevm'
test('encodes transaction correctly', () => {
const tx = buildTransaction(/* ... */)
const encoded = Rlp.encode(tx)
const decoded = Rlp.decode(encoded)
// Compare using JSON (easier to debug failures)
expect(Rlp.toJSON(decoded.data)).toEqual(Rlp.toJSON(tx))
// Or use equals
expect(Rlp.equals(decoded.data, tx)).toBe(true)
})
Caching
Cache RLP structures as JSON:
import { Rlp } from 'tevm'
class RlpCache {
private cache = new Map<string, string>()
set(key: string, data: BrandedRlp) {
const json = JSON.stringify(Rlp.toJSON(data))
this.cache.set(key, json)
}
get(key: string): BrandedRlp | undefined {
const json = this.cache.get(key)
if (!json) return undefined
return Rlp.fromJSON(JSON.parse(json))
}
has(key: string): boolean {
return this.cache.has(key)
}
}
Migration
Convert between RLP versions:
import { Rlp } from 'tevm'
// Old format migration
function migrateOldFormat(oldData: any): BrandedRlp {
// Convert old format to JSON
const json = convertOldToJson(oldData)
// Parse as RLP
return Rlp.fromJSON(json)
}
// Version upgrade
function upgradeV1ToV2(v1Data: BrandedRlp): BrandedRlp {
const json = Rlp.toJSON(v1Data)
// Modify JSON structure
const v2Json = transformV1ToV2(json)
// Parse as new RLP
return Rlp.fromJSON(v2Json)
}
JSON vs Binary
JSON serialization is less efficient than binary RLP:
import { Rlp } from 'tevm'
const data = {
type: 'bytes',
value: new Uint8Array([1, 2, 3])
}
// RLP encoding (efficient)
const rlpBytes = Rlp.encode(data)
console.log('RLP size:', rlpBytes.length) // 4 bytes
// JSON encoding (verbose)
const json = JSON.stringify(Rlp.toJSON(data))
console.log('JSON size:', json.length) // ~40+ chars
Use RLP for:
- Network transmission
- On-chain storage
- Binary protocols
Use JSON for:
- Debugging
- APIs where JSON expected
- Database storage
- Human interaction
Tree-shaking
Import only needed functions:
// Import both
import { toJSON, fromJSON } from 'tevm/BrandedRlp'
const json = toJSON(data)
const restored = fromJSON(json)
// Or use namespace
import { Rlp } from 'tevm'
const json = Rlp.toJSON(data)