import * as Bytes from './Bytes.js' import * as Errors from './Errors.js' import * as Hex from './Hex.js' import * as internal_bytes from './internal/bytes.js' import * as Cursor from './internal/cursor.js' import type { ExactPartial, RecursiveArray } from './internal/types.js' /** Maximum nesting depth permitted when decoding an RLP value. */ const depthLimit = 1_024 /** * Decodes a Recursive-Length Prefix (RLP) value into a {@link ox#Bytes.Bytes} value. * * @example * ```ts twoslash * import { Rlp } from 'ox' * Rlp.toBytes('0x8b68656c6c6f20776f726c64') * // Uint8Array([139, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]) * ``` * * @param value - The value to decode. * @returns The decoded {@link ox#Bytes.Bytes} value. */ export function toBytes( value: Bytes.Bytes | Hex.Hex, ): RecursiveArray { return to(value, 'Bytes') } export declare namespace toBytes { type ErrorType = to.ErrorType } /** * Decodes a Recursive-Length Prefix (RLP) value into a {@link ox#Hex.Hex} value. * * @example * ```ts twoslash * import { Rlp } from 'ox' * Rlp.toHex('0x8b68656c6c6f20776f726c64') * // 0x68656c6c6f20776f726c64 * ``` * * @param value - The value to decode. * @returns The decoded {@link ox#Hex.Hex} value. */ export function toHex(value: Bytes.Bytes | Hex.Hex): RecursiveArray { return to(value, 'Hex') } export declare namespace toHex { type ErrorType = to.ErrorType } ///////////////////////////////////////////////////////////////////////////////// // Internal ///////////////////////////////////////////////////////////////////////////////// /** @internal */ export function to< value extends Bytes.Bytes | Hex.Hex, to extends 'Hex' | 'Bytes', >(value: value, to: to | 'Hex' | 'Bytes'): to.ReturnType { const to_ = to ?? (typeof value === 'string' ? 'Hex' : 'Bytes') const bytes = typeof value === 'string' ? Bytes.fromHex(value) : (value as Bytes.Bytes) const cursor = Cursor.create(bytes, { recursiveReadLimit: Number.POSITIVE_INFINITY, }) const result = decodeRlpCursor(cursor, to_) // RLP payloads encode exactly one item (Yellow Paper, Appendix B). if (cursor.position < cursor.bytes.length) throw new TrailingBytesError({ count: cursor.bytes.length - cursor.position, }) return result as to.ReturnType } /** @internal */ export declare namespace to { type ReturnType = | (to extends 'Bytes' ? RecursiveArray : never) | (to extends 'Hex' ? RecursiveArray : never) type ErrorType = | Bytes.fromHex.ErrorType | decodeRlpCursor.ErrorType | Cursor.create.ErrorType | Hex.InvalidLengthError | TrailingBytesError | Errors.GlobalErrorType } /** @internal */ /** @internal */ export function decodeRlpCursor( cursor: Cursor.Cursor, to: to | 'Hex' | 'Bytes' | undefined = 'Hex', depth = 0, ): decodeRlpCursor.ReturnType { if (depth >= depthLimit) throw new DepthLimitExceededError({ limit: depthLimit }) if (cursor.bytes.length === 0) return ( to === 'Hex' ? Hex.fromBytes(cursor.bytes) : cursor.bytes ) as decodeRlpCursor.ReturnType const prefix = cursor.readByte() if (prefix < 0x80) cursor.decrementPosition(1) // bytes if (prefix < 0xc0) { const length = readLength(cursor, prefix, 0x80) const bytes = cursor.readBytes(length) return ( to === 'Hex' ? Hex.fromBytes(bytes) : bytes ) as decodeRlpCursor.ReturnType } // list const length = readLength(cursor, prefix, 0xc0) return readList( cursor, length, to, depth + 1, ) as {} as decodeRlpCursor.ReturnType } /** @internal */ export declare namespace decodeRlpCursor { type ReturnType = to.ReturnType type ErrorType = | Hex.fromBytes.ErrorType | readLength.ErrorType | readList.ErrorType | DepthLimitExceededError | Errors.GlobalErrorType } /** @internal */ export function readLength( cursor: Cursor.Cursor, prefix: number, offset: number, ) { if (offset === 0x80 && prefix < 0x80) return 1 if (prefix <= offset + 55) return prefix - offset if (prefix === offset + 55 + 1) return cursor.readUint8() if (prefix === offset + 55 + 2) return cursor.readUint16() if (prefix === offset + 55 + 3) return cursor.readUint24() if (prefix === offset + 55 + 4) return cursor.readUint32() throw new Errors.BaseError('Invalid RLP prefix') } /** @internal */ export declare namespace readLength { type ErrorType = Errors.BaseError | Errors.GlobalErrorType } /** @internal */ export function readList( cursor: Cursor.Cursor, length: number, to: to | 'Hex' | 'Bytes', depth = 0, ) { const position = cursor.position const value: decodeRlpCursor.ReturnType[] = [] while (cursor.position - position < length) value.push(decodeRlpCursor(cursor, to, depth)) // Items must consume exactly the declared list length. if (cursor.position - position !== length) throw new ListBoundaryExceededError({ consumed: cursor.position - position, declared: length, }) return value } /** @internal */ export declare namespace readList { type ErrorType = | DepthLimitExceededError | ListBoundaryExceededError | Errors.GlobalErrorType } /** * Encodes a value as Recursive-Length Prefix (RLP) and writes the encoded bytes * through a synchronous callback without allocating the complete encoding. * * Ox validates the complete input before the first write. If the callback * throws, the error propagates and earlier callback side effects remain. * * Ox does not mutate a chunk after the callback returns. A chunk may alias a * byte-array leaf from the encoded value. The callback must not mutate the * chunk before `encodeTo` returns. * * @example * ```ts twoslash * import { Bytes, Rlp } from 'ox' * * const chunks: Uint8Array[] = [] * Rlp.encodeTo(['0x01', '0x0203'], (chunk) => { * chunks.push(chunk) * }) * * const encoded = Bytes.concat(...chunks) * // @log: Uint8Array([196, 1, 130, 2, 3]) * ``` * * @param value - The bytes or Hex value, or nested list of values, to encode. * @param write - The synchronous callback for encoded chunks. * @returns Nothing. */ export function encodeTo( value: RecursiveArray | RecursiveArray, write: (chunk: Bytes.Bytes) => undefined, ): void { const snapshot = snapshotValue(value) const ctx: EncodeCtx = { lengths: [], cursor: 0 } measure(snapshot, ctx) writeEncodedTo(write, snapshot, { lengths: ctx.lengths, cursor: 0 }) } export declare namespace encodeTo { type ErrorType = Errors.BaseError | Errors.GlobalErrorType } /** * Encodes a {@link ox#Bytes.Bytes} or {@link ox#Hex.Hex} value into a Recursive-Length Prefix (RLP) value. * * @example * ```ts twoslash * import { Bytes, Rlp } from 'ox' * * Rlp.from('0x68656c6c6f20776f726c64', { as: 'Hex' }) * // @log: 0x8b68656c6c6f20776f726c64 * * Rlp.from( * Bytes.from([ * 139, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, * 100 * ]), * { as: 'Bytes' } * ) * // @log: Uint8Array([104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]) * ``` * * @param value - The {@link ox#Bytes.Bytes} or {@link ox#Hex.Hex} value to encode. * @param options - Options. * @returns The RLP value. */ export function from( value: RecursiveArray | RecursiveArray, options: from.Options, ): from.ReturnType { const { as } = options // Two-walk encode without the per-node `Encodable` closure tree: // 1. `measure` walks the input once and caches each list's `bodyLength` // in a side array indexed by visit order. This makes the second walk // O(N) instead of O(N²) for nested inputs. // 2. `writeEncoded` walks again, reads cached body lengths, and writes // bytes straight into the pre-sized buffer. Hex leaves are // nibble-decoded directly into the destination, skipping the per-leaf // `Bytes.fromHex` allocation. const ctx: EncodeCtx = { lengths: [], cursor: 0 } const totalLength = measure(value, ctx) // Hex-output fast path: when the caller asked for hex AND every leaf is // already hex, emit a hex string directly instead of allocating an // intermediate `Uint8Array` and round-tripping through `Hex.fromBytes`. // This is the dominant shape for transaction envelope serialize. if (as === 'Hex' && isAllHex(value)) { const parts: string[] = [] writeEncodedHex(parts, value as RecursiveArray, { lengths: ctx.lengths, cursor: 0, }) return `0x${parts.join('')}` as from.ReturnType } const bytes = new Uint8Array(totalLength) writeEncoded(bytes, 0, value, { lengths: ctx.lengths, cursor: 0 }) if (as === 'Hex') return Hex.fromBytes(bytes) as from.ReturnType return bytes as from.ReturnType } export declare namespace from { type Options = { /** The type to convert the RLP value to. */ as: as | 'Hex' | 'Bytes' } type ReturnType = | (as extends 'Bytes' ? Bytes.Bytes : never) | (as extends 'Hex' ? Hex.Hex : never) type ErrorType = | Cursor.create.ErrorType | Hex.fromBytes.ErrorType | Bytes.fromHex.ErrorType | Errors.GlobalErrorType } /** * Encodes a {@link ox#Bytes.Bytes} value into a Recursive-Length Prefix (RLP) value. * * @example * ```ts twoslash * import { Bytes, Rlp } from 'ox' * * Rlp.fromBytes( * Bytes.from([ * 139, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, * 100 * ]) * ) * // @log: Uint8Array([104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]) * ``` * * @param bytes - The {@link ox#Bytes.Bytes} value to encode. * @param options - Options. * @returns The RLP value. */ export function fromBytes( bytes: RecursiveArray, options: fromBytes.Options = {}, ): fromBytes.ReturnType { const { as = 'Bytes' } = options return from(bytes, { as }) as never } export declare namespace fromBytes { type Options = ExactPartial< from.Options > type ReturnType = from.ReturnType type ErrorType = from.ErrorType | Errors.GlobalErrorType } /** * Encodes a {@link ox#Hex.Hex} value into a Recursive-Length Prefix (RLP) value. * * @example * ```ts twoslash * import { Rlp } from 'ox' * * Rlp.fromHex('0x68656c6c6f20776f726c64') * // @log: 0x8b68656c6c6f20776f726c64 * ``` * * @param hex - The {@link ox#Hex.Hex} value to encode. * @param options - Options. * @returns The RLP value. */ export function fromHex( hex: RecursiveArray, options: fromHex.Options = {}, ): fromHex.ReturnType { const { as = 'Hex' } = options return from(hex, { as }) as never } export declare namespace fromHex { type Options = ExactPartial< from.Options > type ReturnType = from.ReturnType type ErrorType = from.ErrorType | Errors.GlobalErrorType } ///////////////////////////////////////////////////////////////////////////////// // Internal ///////////////////////////////////////////////////////////////////////////////// /** * Returns the byte length needed to encode `length` itself (1-4 bytes per * RLP), or throws when `length` exceeds the protocol cap. * * @internal */ function getSizeOfLength(length: number) { if (length <= 0xff) return 1 if (length <= 0xff_ff) return 2 if (length <= 0xff_ff_ff) return 3 if (length <= 0xff_ff_ff_ff) return 4 throw new Errors.BaseError('Length is too large.') } /** * Side-channel used by `measure` and `writeEncoded` to share precomputed * list body lengths. `lengths` is filled in pre-order (DFS) by `measure` and * read back in the same order by `writeEncoded` via `cursor`. This avoids * re-walking subtrees from `writeEncoded` (which would be O(N²) on nested * inputs) without allocating a per-node `Encodable` closure tree. * * @internal */ type EncodeCtx = { cursor: number lengths: number[] } type Encodable = RecursiveArray /** * Validates Hex leaves and copies list structure before the callback can mutate * the input during encoding. Byte leaves remain zero-copy. * * @internal */ function snapshotValue(value: Encodable): Encodable { if (Array.isArray(value)) { const snapshot: Encodable[] = [] for (let i = 0; i < value.length; i++) snapshot.push(snapshotValue(value[i]!)) return snapshot } if (typeof value === 'string') validateHexLeaf(value) return value } /** * Walks `value` once, caches each list's `bodyLength` into `ctx.lengths`, * and returns the total encoded byte length. Allocates nothing per node * beyond the shared `lengths` array entries. * * @internal */ function measure(value: Encodable, ctx: EncodeCtx): number { if (Array.isArray(value)) { // Reserve this list's slot before descending so children's slots come // after ours; `writeEncoded` walks in the same order and reads slot N // when it visits the Nth list. const slot = ctx.lengths.length ctx.lengths.push(0) let bodyLength = 0 for (let i = 0; i < value.length; i++) bodyLength += measure(value[i]!, ctx) ctx.lengths[slot] = bodyLength if (bodyLength <= 55) return 1 + bodyLength return 1 + getSizeOfLength(bodyLength) + bodyLength } // Hex leaf: byte length = ceil((hex.length - 2) / 2). The `>> 1` of // `length - 1` yields ceil for both odd- and even-nibble inputs. if (typeof value === 'string') { const byteLen = (value.length - 1) >> 1 if (byteLen === 0) return 1 if (byteLen === 1) { // Single-byte values < 0x80 encode as themselves (no prefix). const odd = (value.length & 1) === 1 const firstChar = value.charCodeAt(odd ? 2 : 3) const high = odd ? 0 : (internal_bytes.charCodeToBase16(value.charCodeAt(2)) ?? 0) << 4 const low = internal_bytes.charCodeToBase16(firstChar) ?? 0 const byte = high | low if (byte < 0x80) return 1 return 2 } if (byteLen <= 55) return 1 + byteLen return 1 + getSizeOfLength(byteLen) + byteLen } // Bytes leaf const len = (value as Bytes.Bytes).length if (len === 1 && (value as Bytes.Bytes)[0]! < 0x80) return 1 if (len <= 55) return 1 + len return 1 + getSizeOfLength(len) + len } /** * Writes `value`'s RLP encoding into `bytes` starting at `offset` and returns * the next free offset. Reads list body lengths from `ctx.lengths` in the * same DFS order that `measure` filled them. Hex leaves are nibble-decoded * directly into the destination, skipping the per-leaf `Bytes.fromHex` * allocation. * * @internal */ function writeEncoded( bytes: Uint8Array, offset: number, value: RecursiveArray | RecursiveArray, ctx: EncodeCtx, ): number { if (Array.isArray(value)) { const bodyLength = ctx.lengths[ctx.cursor++]! let cursor = offset if (bodyLength <= 55) { bytes[cursor++] = 0xc0 + bodyLength } else { const sizeOfBodyLength = getSizeOfLength(bodyLength) bytes[cursor++] = 0xc0 + 55 + sizeOfBodyLength cursor = writeBigEndian(bytes, cursor, bodyLength, sizeOfBodyLength) } for (let i = 0; i < value.length; i++) cursor = writeEncoded(bytes, cursor, value[i]!, ctx) return cursor } if (typeof value === 'string') return writeHexLeaf(bytes, offset, value) return writeBytesLeaf(bytes, offset, value as Bytes.Bytes) } const writeChunkSize = 16_384 type Write = (chunk: Bytes.Bytes) => undefined /** * Writes an RLP value using list lengths cached by `measure`. * * @internal */ function writeEncodedTo(write: Write, value: Encodable, ctx: EncodeCtx): void { if (Array.isArray(value)) { write(encodePrefix(ctx.lengths[ctx.cursor++]!, 0xc0)) for (let i = 0; i < value.length; i++) writeEncodedTo(write, value[i]!, ctx) return } if (typeof value === 'string') { writeHexLeafTo(write, value) return } writeBytesLeafTo(write, value as Bytes.Bytes) } /** * Writes a Hex leaf in bounded chunks. `snapshotValue` validated every nibble * before this function can run. * * @internal */ function writeHexLeafTo(write: Write, hex: Hex.Hex): void { const byteLength = (hex.length - 1) >> 1 if (byteLength === 1 && readHexByte(hex, 0) < 0x80) { write(Uint8Array.of(readHexByte(hex, 0))) return } write(encodePrefix(byteLength, 0x80)) for (let offset = 0; offset < byteLength; offset += writeChunkSize) { const length = Math.min(writeChunkSize, byteLength - offset) const chunk = new Uint8Array(length) for (let i = 0; i < length; i++) chunk[i] = readHexByte(hex, offset + i) write(chunk) } } /** * Writes a byte leaf without copying its body. * * @internal */ function writeBytesLeafTo(write: Write, leaf: Bytes.Bytes): void { const length = leaf.length if (length === 1 && leaf[0]! < 0x80) { write(leaf) return } write(encodePrefix(length, 0x80)) if (length > 0) write(leaf) } /** * Encodes a string or list length prefix. * * @internal */ function encodePrefix(length: number, offset: 0x80 | 0xc0): Uint8Array { if (length <= 55) return Uint8Array.of(offset + length) const size = getSizeOfLength(length) const prefix = new Uint8Array(size + 1) prefix[0] = offset + 55 + size writeBigEndian(prefix, 1, length, size) return prefix } /** * Reads one byte from an odd- or even-nibble Hex value. * * @internal */ function readHexByte(hex: Hex.Hex, index: number): number { const odd = (hex.length & 1) === 1 if (odd && index === 0) return internal_bytes.charCodeToBase16(hex.charCodeAt(2))! const nibble = odd ? index * 2 - 1 : index * 2 const high = internal_bytes.charCodeToBase16(hex.charCodeAt(2 + nibble))! const low = internal_bytes.charCodeToBase16(hex.charCodeAt(3 + nibble))! return (high << 4) | low } /** * Validates a Hex leaf while preserving RLP's odd-nibble left-padding. * * @internal */ function validateHexLeaf(hex: string): asserts hex is Hex.Hex { if (hex.length < 2 || hex.charCodeAt(0) !== 48 || hex.charCodeAt(1) !== 120) throw invalidNibble(hex as Hex.Hex) for (let i = 2; i < hex.length; i++) if (internal_bytes.charCodeToBase16(hex.charCodeAt(i)) === undefined) throw invalidNibble(hex as Hex.Hex) } /** * Hex-leaf fast path: writes the RLP encoding of a hex string directly into * `bytes` by nibble-decoding the source hex chars into the destination * buffer. Even-pads odd-nibble hex (e.g. `'0x1'`) on the fly. * * @internal */ function writeHexLeaf(bytes: Uint8Array, offset: number, hex: Hex.Hex): number { const dataStart = 2 let byteLen = (hex.length - 2) >> 1 let highNibbleFromOddPad = false if ((hex.length & 1) === 1) { // Odd-nibble: first emitted byte's high nibble is `0` (left-pad). byteLen += 1 highNibbleFromOddPad = true } // Empty leaf -> single 0x80 prefix byte (zero-length string in RLP). if (byteLen === 0) { bytes[offset] = 0x80 return offset + 1 } // Single-byte fast path: if the byte < 0x80, write it as-is (no prefix). if (byteLen === 1) { let byte: number if (highNibbleFromOddPad) { byte = internal_bytes.charCodeToBase16(hex.charCodeAt(dataStart)) ?? 0 } else { const high = internal_bytes.charCodeToBase16(hex.charCodeAt(dataStart)) ?? 0 const low = internal_bytes.charCodeToBase16(hex.charCodeAt(dataStart + 1)) ?? 0 byte = (high << 4) | low } if (byte < 0x80) { bytes[offset] = byte return offset + 1 } bytes[offset] = 0x80 + 1 bytes[offset + 1] = byte return offset + 2 } let dest = offset if (byteLen <= 55) { bytes[dest++] = 0x80 + byteLen } else { const sizeOfBytesLength = getSizeOfLength(byteLen) bytes[dest++] = 0x80 + 55 + sizeOfBytesLength dest = writeBigEndian(bytes, dest, byteLen, sizeOfBytesLength) } // Decode hex nibbles directly into the destination buffer. let src = dataStart if (highNibbleFromOddPad) { const low = internal_bytes.charCodeToBase16(hex.charCodeAt(src++)) if (low === undefined) throw invalidNibble(hex) bytes[dest++] = low } while (src < hex.length) { const high = internal_bytes.charCodeToBase16(hex.charCodeAt(src++)) const low = internal_bytes.charCodeToBase16(hex.charCodeAt(src++)) if (high === undefined || low === undefined) throw invalidNibble(hex) bytes[dest++] = (high << 4) | low } return dest } function writeBytesLeaf( bytes: Uint8Array, offset: number, leaf: Bytes.Bytes, ): number { const len = leaf.length if (len === 1 && leaf[0]! < 0x80) { bytes[offset] = leaf[0]! return offset + 1 } let dest = offset if (len <= 55) { bytes[dest++] = 0x80 + len } else { const sizeOfBytesLength = getSizeOfLength(len) bytes[dest++] = 0x80 + 55 + sizeOfBytesLength dest = writeBigEndian(bytes, dest, len, sizeOfBytesLength) } bytes.set(leaf, dest) return dest + len } /** * Returns true if every leaf in the (possibly nested) input is a hex string. * Used to gate the hex-output fast path in `from`. * * @internal */ function isAllHex( value: RecursiveArray | RecursiveArray, ): boolean { if (Array.isArray(value)) { for (let i = 0; i < value.length; i++) if (!isAllHex(value[i]!)) return false return true } return typeof value === 'string' } /** * Hex-output fast path: writes the RLP encoding of `value` directly as hex * substrings into `parts`. Avoids the intermediate `Uint8Array` allocation * (and the trailing `Hex.fromBytes` round-trip) used by the bytes path. * * Length-prefix bytes are formatted via the cached `hexes[]` table so we * never call `toString(16)` per node. * * @internal */ function writeEncodedHex( parts: string[], value: RecursiveArray, ctx: EncodeCtx, ): void { if (Array.isArray(value)) { const bodyLength = ctx.lengths[ctx.cursor++]! if (bodyLength <= 55) { parts.push(hexes[0xc0 + bodyLength]!) } else { const sizeOfBodyLength = getSizeOfLength(bodyLength) parts.push(hexes[0xc0 + 55 + sizeOfBodyLength]!) parts.push(bigEndianHex(bodyLength, sizeOfBodyLength)) } for (let i = 0; i < value.length; i++) writeEncodedHex(parts, value[i]!, ctx) return } // Hex leaf: even-pad odd-nibble inputs and skip the `0x` prefix. const hex = value as string const odd = (hex.length & 1) === 1 const body = odd ? `0${hex.slice(2)}` : hex.slice(2) const byteLen = body.length >> 1 if (byteLen === 0) { parts.push(hexes[0x80]!) return } if (byteLen === 1) { const byte = parseInt(body, 16) if (byte < 0x80) { parts.push(body) } else { parts.push(hexes[0x81]!) parts.push(body) } return } if (byteLen <= 55) { parts.push(hexes[0x80 + byteLen]!) parts.push(body) return } const sizeOfBytesLength = getSizeOfLength(byteLen) parts.push(hexes[0x80 + 55 + sizeOfBytesLength]!) parts.push(bigEndianHex(byteLen, sizeOfBytesLength)) parts.push(body) } const hexes = /*#__PURE__*/ Array.from({ length: 256 }, (_v, i) => i.toString(16).padStart(2, '0'), ) /** * Returns the big-endian hex encoding of `value` in `size` bytes. * * @internal */ function bigEndianHex(value: number, size: number): string { if (size === 1) return hexes[value & 0xff]! if (size === 2) return `${hexes[(value >>> 8) & 0xff]!}${hexes[value & 0xff]!}` if (size === 3) return `${hexes[(value >>> 16) & 0xff]!}${hexes[(value >>> 8) & 0xff]!}${hexes[value & 0xff]!}` return `${hexes[(value >>> 24) & 0xff]!}${hexes[(value >>> 16) & 0xff]!}${hexes[(value >>> 8) & 0xff]!}${hexes[value & 0xff]!}` } function writeBigEndian( bytes: Uint8Array, offset: number, value: number, size: number, ): number { if (size === 1) { bytes[offset] = value & 0xff } else if (size === 2) { bytes[offset] = (value >>> 8) & 0xff bytes[offset + 1] = value & 0xff } else if (size === 3) { bytes[offset] = (value >>> 16) & 0xff bytes[offset + 1] = (value >>> 8) & 0xff bytes[offset + 2] = value & 0xff } else { bytes[offset] = (value >>> 24) & 0xff bytes[offset + 1] = (value >>> 16) & 0xff bytes[offset + 2] = (value >>> 8) & 0xff bytes[offset + 3] = value & 0xff } return offset + size } function invalidNibble(hex: Hex.Hex): Errors.BaseError { return new Errors.BaseError(`Invalid hex string \`${hex}\`.`) } /** Thrown when an RLP value nests deeper than the decode depth limit. */ export class DepthLimitExceededError extends Errors.BaseError { override readonly name = 'Rlp.DepthLimitExceededError' constructor({ limit }: { limit: number }) { super(`RLP depth limit of \`${limit}\` exceeded.`) } } /** Thrown when RLP list items overrun the list's declared length. */ export class ListBoundaryExceededError extends Errors.BaseError { override readonly name = 'Rlp.ListBoundaryExceededError' constructor({ consumed, declared }: { consumed: number; declared: number }) { super( `RLP list items consumed \`${consumed}\` bytes but the list declared a length of \`${declared}\`.`, ) } } /** Thrown when an RLP payload contains bytes after the decoded item. */ export class TrailingBytesError extends Errors.BaseError { override readonly name = 'Rlp.TrailingBytesError' constructor({ count }: { count: number }) { super( `RLP payload encodes a single item, but \`${count}\` trailing ${ count === 1 ? 'byte remains' : 'bytes remain' }.`, ) } }