Skip to main content

Try it Live

Run RLP examples in the interactive playground

    Return Type

    decodeArray returns a JavaScript array with decoded elements:
    import { Rlp } from 'tevm'
    
    const encoded = Rlp.encodeArray([
      new Uint8Array([1, 2, 3]),
      new Uint8Array([4, 5, 6])
    ])
    
    const arr = Rlp.decodeArray(encoded)
    // arr is an array: [Uint8Array([1,2,3]), Uint8Array([4,5,6])]
    
    // Can destructure
    const [first, second] = arr
    console.log(first)  // Uint8Array([1, 2, 3])
    console.log(second)  // Uint8Array([4, 5, 6])
    
    // Can iterate
    for (const item of arr) {
      console.log('Item:', item)
    }
    

    Usage Patterns

    Transaction Decoding

    Decode transaction fields:
    import { Rlp } from 'tevm'
    
    interface LegacyTx {
      nonce: bigint
      gasPrice: bigint
      gasLimit: bigint
      to: Uint8Array
      value: bigint
      data: Uint8Array
      v: bigint
      r: Uint8Array
      s: Uint8Array
    }
    
    function decodeLegacyTx(bytes: Uint8Array): LegacyTx {
      const arr = Rlp.decodeArray(bytes)
    
      return {
        nonce: bytesToBigint(arr[0]),
        gasPrice: bytesToBigint(arr[1]),
        gasLimit: bytesToBigint(arr[2]),
        to: arr[3],
        value: bytesToBigint(arr[4]),
        data: arr[5],
        v: bytesToBigint(arr[6]),
        r: arr[7],
        s: arr[8]
      }
    }
    
    const txBytes = new Uint8Array([...])
    const tx = decodeLegacyTx(txBytes)
    console.log('Nonce:', tx.nonce)
    console.log('To:', tx.to)
    

    Block Header Decoding

    Decode block header:
    import { Rlp } from 'tevm'
    
    interface BlockHeader {
      parentHash: Uint8Array
      uncleHash: Uint8Array
      coinbase: Uint8Array
      stateRoot: Uint8Array
      transactionsRoot: Uint8Array
      receiptsRoot: Uint8Array
      logsBloom: Uint8Array
      difficulty: bigint
      number: bigint
      gasLimit: bigint
      gasUsed: bigint
      timestamp: bigint
      extraData: Uint8Array
    }
    
    function decodeBlockHeader(bytes: Uint8Array): BlockHeader {
      const [
        parentHash,
        uncleHash,
        coinbase,
        stateRoot,
        transactionsRoot,
        receiptsRoot,
        logsBloom,
        difficulty,
        number,
        gasLimit,
        gasUsed,
        timestamp,
        extraData
      ] = Rlp.decodeArray(bytes)
    
      return {
        parentHash,
        uncleHash,
        coinbase,
        stateRoot,
        transactionsRoot,
        receiptsRoot,
        logsBloom,
        difficulty: bytesToBigint(difficulty),
        number: bytesToBigint(number),
        gasLimit: bytesToBigint(gasLimit),
        gasUsed: bytesToBigint(gasUsed),
        timestamp: bytesToBigint(timestamp),
        extraData
      }
    }
    

    Log Entry Decoding

    Decode event logs:
    import { Rlp } from 'tevm'
    
    interface Log {
      address: Uint8Array
      topics: Uint8Array[]
      data: Uint8Array
    }
    
    function decodeLog(bytes: Uint8Array): Log {
      const [address, topics, data] = Rlp.decodeArray(bytes)
    
      return {
        address,
        topics,  // Already an array
        data
      }
    }
    
    const logBytes = new Uint8Array([...])
    const log = decodeLog(logBytes)
    console.log('Address:', log.address)
    console.log('Topics:', log.topics)
    

    Account State Decoding

    Decode account state:
    import { Rlp } from 'tevm'
    
    interface AccountState {
      nonce: bigint
      balance: bigint
      storageRoot: Uint8Array
      codeHash: Uint8Array
    }
    
    function decodeAccountState(bytes: Uint8Array): AccountState {
      const [nonce, balance, storageRoot, codeHash] = Rlp.decodeArray(bytes)
    
      return {
        nonce: bytesToBigint(nonce),
        balance: bytesToBigint(balance),
        storageRoot,
        codeHash
      }
    }
    
    const accountBytes = new Uint8Array([...])
    const account = decodeAccountState(accountBytes)
    console.log('Balance:', account.balance)
    

    Merkle Patricia Trie Node

    Decode trie nodes:
    import { Rlp } from 'tevm'
    
    type TrieNode =
      | { type: 'branch'; children: Uint8Array[]; value: Uint8Array }
      | { type: 'extension'; path: Uint8Array; child: Uint8Array }
      | { type: 'leaf'; path: Uint8Array; value: Uint8Array }
    
    function decodeTrieNode(bytes: Uint8Array): TrieNode {
      const arr = Rlp.decodeArray(bytes)
    
      if (arr.length === 17) {
        // Branch node: 16 children + value
        return {
          type: 'branch',
          children: arr.slice(0, 16),
          value: arr[16]
        }
      }
    
      if (arr.length === 2) {
        // Extension or leaf
        const path = arr[0]
        const hasTerminator = (path[0] & 0x20) !== 0
    
        if (hasTerminator) {
          return {
            type: 'leaf',
            path,
            value: arr[1]
          }
        } else {
          return {
            type: 'extension',
            path,
            child: arr[1]
          }
        }
      }
    
      throw new Error('Invalid trie node')
    }
    

    Validation

    decodeArray validates RLP encoding:
    import { Rlp } from 'tevm'
    
    // Valid encoding
    const valid = new Uint8Array([0xc4, 0x01, 0x02, 0x03])
    const arr = Rlp.decodeArray(valid)
    // => [Uint8Array([1]), Uint8Array([2]), Uint8Array([3])]
    
    // Invalid encoding throws
    const invalid = new Uint8Array([0xc4, 0x01])  // Incomplete
    try {
      Rlp.decodeArray(invalid)
    } catch (error) {
      console.error('Invalid RLP:', error)
    }
    
    // Not a list throws
    const notList = new Uint8Array([0x83, 1, 2, 3])  // Bytes, not list
    try {
      Rlp.decodeArray(notList)
    } catch (error) {
      console.error('Expected list:', error)
    }
    

    Schema-based Decoding

    Define typed decoders:
    import { Rlp } from 'tevm'
    
    // Define schema
    type Schema<T> = {
      encode: (value: T) => Uint8Array
      decode: (bytes: Uint8Array) => T
    }
    
    // Create schema
    function createArraySchema<T>(
      itemSchema: Schema<T>
    ): Schema<T[]> {
      return {
        encode: (items) => {
          const encoded = items.map(item => itemSchema.encode(item))
          return Rlp.encodeArray(encoded)
        },
        decode: (bytes) => {
          const arr = Rlp.decodeArray(bytes)
          return arr.map(item => itemSchema.decode(item))
        }
      }
    }
    
    // Use schema
    const uint256Schema: Schema<bigint> = {
      encode: (n) => bigintToBytes(n),
      decode: (b) => bytesToBigint(b)
    }
    
    const uint256ArraySchema = createArraySchema(uint256Schema)
    
    // Encode
    const numbers = [1n, 2n, 3n]
    const encoded = uint256ArraySchema.encode(numbers)
    
    // Decode
    const decoded = uint256ArraySchema.decode(encoded)
    // => [1n, 2n, 3n]
    

    Performance

    Direct Array Access

    decodeArray returns a plain JavaScript array for efficient access:
    import { Rlp } from 'tevm'
    
    const encoded = Rlp.encodeArray([
      new Uint8Array([1]),
      new Uint8Array([2]),
      new Uint8Array([3])
    ])
    
    const arr = Rlp.decodeArray(encoded)
    
    // Fast array access
    console.log(arr[0])  // O(1)
    console.log(arr.length)  // O(1)
    
    // Fast iteration
    for (const item of arr) {
      process(item)
    }
    

    vs decode

    decodeArray is more convenient than decode for array access:
    import { Rlp } from 'tevm'
    
    const encoded = new Uint8Array([...])
    
    // Using decodeArray (convenient)
    const arr = Rlp.decodeArray(encoded)
    const first = arr[0]
    
    // Using decode (more verbose)
    const decoded = Rlp.decode(encoded)
    if (decoded.data.type === 'list') {
      const first = decoded.data.value[0]
      if (first.type === 'bytes') {
        const value = first.value
      }
    }
    

    Caching

    Cache decoded results for repeated access:
    import { Rlp } from 'tevm'
    
    class TxCache {
      private cache = new Map<string, any[]>()
    
      decode(bytes: Uint8Array): any[] {
        const key = Hex.fromBytes(bytes).slice(2)
    
        let cached = this.cache.get(key)
        if (!cached) {
          cached = Rlp.decodeArray(bytes)
          this.cache.set(key, cached)
        }
    
        return cached
      }
    }
    

    See Also