UNPKG

ox

Version:

Ethereum Standard Library

944 lines (860 loc) 27.4 kB
import * as Bytes from './Bytes.js' import * as Errors from './Errors.js' import * as Hash from './Hash.js' import * as Hex from './Hex.js' import * as Kzg from './Kzg.js' import * as Cursor from './internal/cursor.js' import type { Compute, Mutable, OneOf, UnionCompute } from './internal/types.js' /** Blob limit per transaction. */ const blobsPerTransaction = 6 /** The number of bytes in a BLS scalar field element. */ export const bytesPerFieldElement = 32 /** The number of field elements in a blob. */ export const fieldElementsPerBlob = 4096 /** The number of bytes in a blob. */ export const bytesPerBlob = bytesPerFieldElement * fieldElementsPerBlob /** Blob bytes limit per transaction. */ export const maxBytesPerTransaction = bytesPerBlob * blobsPerTransaction - // terminator byte (0x80). 1 - // zero byte (0x00) appended to each field element. 1 * fieldElementsPerBlob * blobsPerTransaction /** Root type for a Blob. */ export type Blob<type extends Hex.Hex | Bytes.Bytes = Hex.Hex | Bytes.Bytes> = type /** A list of {@link ox#Blobs.Blob}. */ export type Blobs<type extends Hex.Hex | Bytes.Bytes = Hex.Hex | Bytes.Bytes> = readonly Blob<type>[] /** Type for a Blob Sidecar that contains a blob, as well as its KZG commitment and proof. */ export type BlobSidecar< type extends Hex.Hex | Bytes.Bytes = Hex.Hex | Bytes.Bytes, > = Compute<{ /** The blob associated with the transaction. */ blob: type /** The KZG commitment corresponding to this blob. */ commitment: type /** The KZG proof corresponding to this blob and commitment. */ proof: type }> /** * Transform a list of Commitments to Blob Versioned Hashes. * * @example * ```ts twoslash * // @noErrors * import { Blobs } from 'viem' * import { kzg } from './kzg' * * const blobs = Blobs.from('0xdeadbeef') * const commitments = Blobs.toCommitments(blobs, { kzg }) * const versionedHashes = Blobs.commitmentsToVersionedHashes(commitments) // [!code focus] * // @log: ['0x...', '0x...'] * ``` * * @example * ### Configuring Return Type * * It is possible to configure the return type for the Versioned Hashes with the `as` option. * * ```ts twoslash * // @noErrors * import { Blobs } from 'viem' * import { kzg } from './kzg' * * const blobs = Blobs.from('0xdeadbeef') * const commitments = Blobs.toCommitments(blobs, { kzg }) * const versionedHashes = Blobs.commitmentsToVersionedHashes(commitments, { * as: 'Bytes', // [!code focus] * }) * // @log: [Uint8Array [ ... ], Uint8Array [ ... ]] * ``` * * @example * ### Versioning Hashes * * It is possible to configure the version for the Versioned Hashes with the `version` option. * * ```ts twoslash * // @noErrors * import { Blobs } from 'viem' * import { kzg } from './kzg' * * const blobs = Blobs.from('0xdeadbeef') * const commitments = Blobs.toCommitments(blobs, { kzg }) * const versionedHashes = Blobs.commitmentsToVersionedHashes(commitments, { * version: 2, // [!code focus] * }) * ``` * * @param commitments - A list of commitments. * @param options - Options. * @returns A list of Blob Versioned Hashes. */ export function commitmentsToVersionedHashes< const commitments extends readonly Bytes.Bytes[] | readonly Hex.Hex[], as extends 'Hex' | 'Bytes' = | (commitments extends readonly Hex.Hex[] ? 'Hex' : never) | (commitments extends readonly Bytes.Bytes[] ? 'Bytes' : never), >( commitments: commitments | readonly Bytes.Bytes[] | readonly Hex.Hex[], options: commitmentsToVersionedHashes.Options<as> = {}, ): commitmentsToVersionedHashes.ReturnType<as> { const { version } = options const as = options.as ?? (typeof commitments[0] === 'string' ? 'Hex' : 'Bytes') const hashes: Uint8Array[] | Hex.Hex[] = [] for (const commitment of commitments) { hashes.push( commitmentToVersionedHash(commitment, { as, version, }) as never, ) } return hashes as never } export declare namespace commitmentsToVersionedHashes { type Options<as extends 'Hex' | 'Bytes' | undefined = undefined> = { /** Return type. */ as?: as | 'Hex' | 'Bytes' | undefined /** Version to tag onto the hashes. */ version?: number | undefined } type ReturnType<as extends 'Hex' | 'Bytes' = 'Hex' | 'Bytes'> = | (as extends 'Bytes' ? readonly Bytes.Bytes[] : never) | (as extends 'Hex' ? readonly Hex.Hex[] : never) type ErrorType = Errors.GlobalErrorType } /** * Transform a Commitment to its Blob Versioned Hash. * * @example * ```ts twoslash * // @noErrors * import { Blobs } from 'ox' * import { kzg } from './kzg' * * const blobs = Blobs.from('0xdeadbeef') * const [commitment] = Blobs.toCommitments(blobs, { kzg }) * const versionedHash = Blobs.commitmentToVersionedHash(commitment) // [!code focus] * ``` * * @example * ### Configuring Return Type * * It is possible to configure the return type for the Versioned Hash with the `as` option. * * ```ts twoslash * // @noErrors * import { Blobs } from 'viem' * import { kzg } from './kzg' * * const blobs = Blobs.from('0xdeadbeef') * const [commitment] = Blobs.toCommitments(blobs, { kzg }) * const versionedHashes = Blobs.commitmentToVersionedHash(commitment, { * as: 'Bytes', // [!code focus] * }) * // @log: [Uint8Array [ ... ], Uint8Array [ ... ]] * ``` * * @example * ### Versioning Hashes * * It is possible to configure the version for the Versioned Hash with the `version` option. * * ```ts twoslash * // @noErrors * import { Blobs } from 'viem' * import { kzg } from './kzg' * * const blobs = Blobs.from('0xdeadbeef') * const [commitment] = Blobs.toCommitments(blobs, { kzg }) * const versionedHashes = Blobs.commitmentToVersionedHash(commitment, { * version: 2, // [!code focus] * }) * ``` * * @param commitment - The commitment. * @param options - Options. * @returns The Blob Versioned Hash. */ export function commitmentToVersionedHash< const commitment extends Hex.Hex | Bytes.Bytes, as extends 'Hex' | 'Bytes' = | (commitment extends Hex.Hex ? 'Hex' : never) | (commitment extends Bytes.Bytes ? 'Bytes' : never), >( commitment: commitment | Hex.Hex | Bytes.Bytes, options: commitmentToVersionedHash.Options<as> = {}, ): commitmentToVersionedHash.ReturnType<as> { const { version = 1 } = options const as = options.as ?? (typeof commitment === 'string' ? 'Hex' : 'Bytes') const versionedHash = Hash.sha256(commitment, { as: 'Bytes' }) versionedHash.set([version], 0) return ( as === 'Bytes' ? versionedHash : Hex.fromBytes(versionedHash) ) as commitmentToVersionedHash.ReturnType<as> } export declare namespace commitmentToVersionedHash { type Options<as extends 'Hex' | 'Bytes' | undefined = undefined> = { /** Return type. */ as?: as | 'Hex' | 'Bytes' | undefined /** Version to tag onto the hash. */ version?: number | undefined } type ReturnType<as extends 'Hex' | 'Bytes' = 'Hex' | 'Bytes'> = | (as extends 'Bytes' ? Bytes.Bytes : never) | (as extends 'Hex' ? Hex.Hex : never) type ErrorType = Errors.GlobalErrorType } /** * Transforms arbitrary data to {@link ox#Blobs.Blobs}. * * @example * ```ts twoslash * import { Blobs } from 'ox' * * const blobs = Blobs.from('0xdeadbeef') * ``` * * @example * ### Creating Blobs from a String * * An example of creating Blobs from a string using {@link ox#Hex.(from:function)}: * * ```ts twoslash * import { Blobs, Hex } from 'ox' * * const blobs = Blobs.from(Hex.fromString('Hello world!')) * ``` * * @example * ### Configuring Return Type * * It is possible to configure the return type for the Blobs with the `as` option. * * ```ts twoslash * import { Blobs } from 'ox' * * const blobs = Blobs.from('0xdeadbeef', { as: 'Bytes' }) * // ^? * * * ``` * * @param data - The data to convert to {@link ox#Blobs.Blobs}. * @param options - Options. * @returns The {@link ox#Blobs.Blobs}. */ export function from< const data extends Hex.Hex | Bytes.Bytes, as extends 'Hex' | 'Bytes' = | (data extends Hex.Hex ? 'Hex' : never) | (data extends Bytes.Bytes ? 'Bytes' : never), >( data: data | Hex.Hex | Bytes.Bytes, options: from.Options<as> = {}, ): from.ReturnType<as> { const as = options.as ?? (typeof data === 'string' ? 'Hex' : 'Bytes') const data_ = ( typeof data === 'string' ? Bytes.fromHex(data) : data ) as Bytes.Bytes const size_ = Bytes.size(data_) if (!size_) throw new EmptyBlobError() if (size_ > maxBytesPerTransaction) throw new BlobSizeTooLargeError({ maxSize: maxBytesPerTransaction, size: size_, }) const blobs = [] let active = true let position = 0 while (active) { const blob = Cursor.create(new Uint8Array(bytesPerBlob)) let size = 0 while (size < fieldElementsPerBlob) { const bytes = data_.slice(position, position + (bytesPerFieldElement - 1)) // Push a zero byte so the field element doesn't overflow the BLS modulus. blob.pushByte(0x00) // Push the current segment of data bytes. blob.pushBytes(bytes) // If we detect that the current segment of data bytes is less than 31 bytes, // we can stop processing and push a terminator byte to indicate the end of the blob. if (bytes.length < 31) { blob.pushByte(0x80) active = false break } size++ position += 31 } blobs.push(blob) } return ( as === 'Bytes' ? blobs.map((x) => x.bytes) : blobs.map((x) => Hex.fromBytes(x.bytes)) ) as never } export declare namespace from { type Options<as extends 'Hex' | 'Bytes' | undefined = undefined> = { /** Return type. */ as?: as | 'Hex' | 'Bytes' | undefined } type ReturnType<as extends 'Hex' | 'Bytes' = 'Hex' | 'Bytes'> = | (as extends 'Bytes' ? readonly Bytes.Bytes[] : never) | (as extends 'Hex' ? readonly Hex.Hex[] : never) type ErrorType = | BlobSizeTooLargeError | EmptyBlobError | Bytes.fromHex.ErrorType | Hex.fromBytes.ErrorType | Cursor.create.ErrorType | Bytes.size.ErrorType | Errors.GlobalErrorType } /** * Transforms a list of {@link ox#Blobs.BlobSidecars} to their Blob Versioned Hashes. * * @example * ```ts twoslash * // @noErrors * import { Blobs } from 'ox' * * const blobs = Blobs.from('0xdeadbeef') * const sidecars = Blobs.toSidecars(blobs, { kzg }) * const versionedHashes = Blobs.sidecarsToVersionedHashes(sidecars) // [!code focus] * ``` * * @example * ### Configuring Return Type * * It is possible to configure the return type for the Versioned Hashes with the `as` option. * * ```ts twoslash * // @noErrors * import { Blobs } from 'viem' * import { kzg } from './kzg' * * const blobs = Blobs.from('0xdeadbeef') * const sidecars = Blobs.toSidecars(blobs, { kzg }) * const versionedHashes = Blobs.sidecarsToVersionedHashes(sidecars, { * as: 'Bytes', // [!code focus] * }) * // @log: [Uint8Array [ ... ], Uint8Array [ ... ]] * ``` * * @example * ### Versioning Hashes * * It is possible to configure the version for the Versioned Hashes with the `version` option. * * ```ts twoslash * // @noErrors * import { Blobs } from 'viem' * import { kzg } from './kzg' * * const blobs = Blobs.from('0xdeadbeef') * const sidecars = Blobs.toSidecars(blobs, { kzg }) * const versionedHashes = Blobs.sidecarsToVersionedHashes(sidecars, { * version: 2, // [!code focus] * }) * ``` * * @param sidecars - The {@link ox#Blobs.BlobSidecars} to transform to Blob Versioned Hashes. * @param options - Options. * @returns The versioned hashes. */ export function sidecarsToVersionedHashes< const sidecars extends BlobSidecars, as extends 'Hex' | 'Bytes' = | (sidecars extends BlobSidecars<Hex.Hex> ? 'Hex' : never) | (sidecars extends BlobSidecars<Bytes.Bytes> ? 'Bytes' : never), >( sidecars: sidecars | BlobSidecars, options: sidecarsToVersionedHashes.Options<as> = {}, ): sidecarsToVersionedHashes.ReturnType<as> { const { version } = options const as = options.as ?? (typeof sidecars[0]!.blob === 'string' ? 'Hex' : 'Bytes') const hashes: Uint8Array[] | Hex.Hex[] = [] for (const { commitment } of sidecars) { hashes.push( commitmentToVersionedHash(commitment, { as, version, }) as any, ) } return hashes as any } export declare namespace sidecarsToVersionedHashes { type Options<as extends 'Hex' | 'Bytes' | undefined = undefined> = { /** Return type. */ as?: as | 'Hex' | 'Bytes' | undefined /** Version to tag onto the hashes. */ version?: number | undefined } type ReturnType<as extends 'Hex' | 'Bytes' = 'Hex' | 'Bytes'> = | (as extends 'Bytes' ? readonly Bytes.Bytes[] : never) | (as extends 'Hex' ? readonly Hex.Hex[] : never) type ErrorType = commitmentToVersionedHash.ErrorType | Errors.GlobalErrorType } /** * Transforms Ox-shaped {@link ox#Blobs.Blobs} into the originating data. * * @example * ```ts twoslash * import { Blobs, Hex } from 'ox' * * const blobs = Blobs.from('0xdeadbeef') * const data = Blobs.to(blobs) // [!code focus] * // @log: '0xdeadbeef' * ``` * * @example * ### Configuring Return Type * * It is possible to configure the return type with second argument. * * ```ts twoslash * import { Blobs } from 'ox' * * const blobs = Blobs.from('0xdeadbeef') * const data = Blobs.to(blobs, 'Bytes') * // @log: Uint8Array [ 13, 174, 190, 239 ] * ``` * * @param blobs - The {@link ox#Blobs.Blobs} to transform. * @param to - The type to transform to. * @returns The originating data. */ export function to< const blobs extends Blobs<Hex.Hex> | Blobs<Bytes.Bytes>, to extends 'Hex' | 'Bytes' = | (blobs extends Blobs<Hex.Hex> ? 'Hex' : never) | (blobs extends Blobs<Bytes.Bytes> ? 'Bytes' : never), >( blobs: blobs | Blobs<Hex.Hex> | Blobs<Bytes.Bytes>, to?: to | 'Hex' | 'Bytes' | undefined, ): to.ReturnType<to> { const to_ = to ?? (typeof blobs[0] === 'string' ? 'Hex' : 'Bytes') const blobs_ = ( typeof blobs[0] === 'string' ? blobs.map((x) => Bytes.fromHex(x as Hex.Hex)) : blobs ) as Bytes.Bytes[] const length = blobs_.reduce((length, blob) => length + blob.length, 0) const data = Cursor.create(new Uint8Array(length)) let active = true for (const blob of blobs_) { const cursor = Cursor.create(blob) while (active && cursor.position < blob.length) { // First byte will be a zero 0x00 byte – we can skip. cursor.incrementPosition(1) let consume = 31 if (blob.length - cursor.position < 31) consume = blob.length - cursor.position for (const _ in Array.from({ length: consume })) { const byte = cursor.readByte() const isTerminator = byte === 0x80 && !cursor.inspectBytes(cursor.remaining).includes(0x80) if (isTerminator) { active = false break } data.pushByte(byte) } } } const trimmedData = data.bytes.slice(0, data.position) return (to_ === 'Hex' ? Hex.fromBytes(trimmedData) : trimmedData) as never } export declare namespace to { type ReturnType<to extends 'Hex' | 'Bytes' = 'Hex'> = | (to extends 'Bytes' ? Bytes.Bytes : never) | (to extends 'Hex' ? Hex.Hex : never) type ErrorType = | Hex.fromBytes.ErrorType | Bytes.fromHex.ErrorType | Cursor.create.ErrorType | Errors.GlobalErrorType } /** * Transforms Ox-shaped {@link ox#Blobs.Blobs} into the originating data. * * @example * ```ts twoslash * import { Blobs, Hex } from 'ox' * * const blobs = Blobs.from('0xdeadbeef') * const data = Blobs.toHex(blobs) // [!code focus] * // @log: '0xdeadbeef' * ``` */ export function toHex( blobs: Blobs<Hex.Hex> | Blobs<Bytes.Bytes>, ): toHex.ReturnType { return to(blobs, 'Hex') } export declare namespace toHex { type ReturnType = to.ReturnType<'Hex'> type ErrorType = to.ErrorType | Errors.GlobalErrorType } /** * Transforms Ox-shaped {@link ox#Blobs.Blobs} into the originating data. * * @example * ```ts * import { Blobs, Hex } from 'ox' * * const blobs = Blobs.from('0xdeadbeef') * const data = Blobs.toBytes(blobs) // [!code focus] * // @log: Uint8Array [ 13, 174, 190, 239 ] * ``` */ export function toBytes( blobs: Blobs<Hex.Hex> | Blobs<Bytes.Bytes>, ): toBytes.ReturnType { return to(blobs, 'Bytes') } /** * Compute commitments from a list of {@link ox#Blobs.Blobs}. * * @example * ```ts twoslash * // @noErrors * import { Blobs } from 'ox' * import { kzg } from './kzg' * * const blobs = Blobs.from('0xdeadbeef') * const commitments = Blobs.toCommitments(blobs, { kzg }) // [!code focus] * ``` * * @example * ### Configuring Return Type * * It is possible to configure the return type with the `as` option. * * ```ts twoslash * // @noErrors * import { Blobs } from 'ox' * import { kzg } from './kzg' * * const blobs = Blobs.from('0xdeadbeef') * const commitments = Blobs.toCommitments(blobs, { * as: 'Bytes', // [!code focus] * kzg, * }) * // @log: [Uint8Array [ ... ], Uint8Array [ ... ]] * ``` * * @param blobs - The {@link ox#Blobs.Blobs} to transform to commitments. * @param options - Options. * @returns The commitments. */ export function toCommitments< const blobs extends Blobs<Bytes.Bytes> | Blobs<Hex.Hex>, as extends 'Hex' | 'Bytes' = | (blobs extends Blobs<Hex.Hex> ? 'Hex' : never) | (blobs extends Blobs<Bytes.Bytes> ? 'Bytes' : never), >( blobs: blobs | Blobs<Bytes.Bytes> | Blobs<Hex.Hex>, options: toCommitments.Options<as>, ): toCommitments.ReturnType<as> { const { kzg } = options const as = options.as ?? (typeof blobs[0] === 'string' ? 'Hex' : 'Bytes') const blobs_ = ( typeof blobs[0] === 'string' ? blobs.map((x) => Bytes.fromHex(x as any)) : blobs ) as Bytes.Bytes[] const commitments: Bytes.Bytes[] = [] for (const blob of blobs_) commitments.push(Uint8Array.from(kzg.blobToKzgCommitment(blob))) return ( as === 'Bytes' ? commitments : commitments.map((x) => Hex.fromBytes(x)) ) as never } export declare namespace toCommitments { type Options<as extends 'Hex' | 'Bytes' = 'Hex'> = { /** KZG implementation. */ kzg: Pick<Kzg.Kzg, 'blobToKzgCommitment'> /** Return type. */ as?: as | 'Hex' | 'Bytes' | undefined } type ReturnType<as extends 'Hex' | 'Bytes' = 'Hex'> = Compute< | (as extends 'Bytes' ? readonly Bytes.Bytes[] : never) | (as extends 'Hex' ? readonly Hex.Hex[] : never) > type ErrorType = | Bytes.fromHex.ErrorType | Hex.fromBytes.ErrorType | Errors.GlobalErrorType } export declare namespace toBytes { type ReturnType = to.ReturnType<'Bytes'> type ErrorType = to.ErrorType | Errors.GlobalErrorType } /** * Compute the proofs for a list of {@link ox#Blobs.Blobs} and their commitments. * * @example * ```ts twoslash * // @noErrors * import { Blobs } from 'viem' * import { kzg } from './kzg' * * const blobs = Blobs.from('0xdeadbeef') * const commitments = Blobs.toCommitments(blobs, { kzg }) * const proofs = Blobs.toProofs(blobs, { commitments, kzg }) // [!code focus] * ``` * * @param blobs - The {@link ox#Blobs.Blobs} to compute proofs for. * @param options - Options. * @returns The Blob proofs. */ export function toProofs< const blobs extends readonly Bytes.Bytes[] | readonly Hex.Hex[], const commitments extends readonly Bytes.Bytes[] | readonly Hex.Hex[], as extends 'Hex' | 'Bytes' = | (blobs extends readonly Hex.Hex[] ? 'Hex' : never) | (blobs extends readonly Bytes.Bytes[] ? 'Bytes' : never), >( blobs: blobs | Blobs<Bytes.Bytes> | Blobs<Hex.Hex>, options: toProofs.Options<blobs, commitments, as>, ): toProofs.ReturnType<as> { const { kzg } = options const as = options.as ?? (typeof blobs[0] === 'string' ? 'Hex' : 'Bytes') const blobs_ = ( typeof blobs[0] === 'string' ? blobs.map((x) => Bytes.fromHex(x as any)) : blobs ) as Bytes.Bytes[] const commitments = ( typeof options.commitments[0] === 'string' ? options.commitments.map((x) => Bytes.fromHex(x as any)) : options.commitments ) as Bytes.Bytes[] const proofs: Bytes.Bytes[] = [] for (let i = 0; i < blobs_.length; i++) { const blob = blobs_[i]! const commitment = commitments[i]! proofs.push(Uint8Array.from(kzg.computeBlobKzgProof(blob, commitment))) } return ( as === 'Bytes' ? proofs : proofs.map((x) => Hex.fromBytes(x)) ) as never } export declare namespace toProofs { type Options< blobs extends Blobs<Bytes.Bytes> | Blobs<Hex.Hex> = | Blobs<Bytes.Bytes> | Blobs<Hex.Hex>, commitments extends readonly Bytes.Bytes[] | readonly Hex.Hex[] = | readonly Bytes.Bytes[] | readonly Hex.Hex[], as extends 'Hex' | 'Bytes' = | (blobs extends Blobs<Hex.Hex> ? 'Hex' : never) | (blobs extends Blobs<Bytes.Bytes> ? 'Bytes' : never), > = { /** Commitments for the blobs. */ commitments: (commitments | readonly Bytes.Bytes[] | readonly Hex.Hex[]) & (commitments extends blobs ? {} : `commitments must be the same type as blobs`) /** KZG implementation. */ kzg: Pick<Kzg.Kzg, 'computeBlobKzgProof'> /** Return type. */ as?: as | 'Hex' | 'Bytes' | undefined } type ReturnType<as extends 'Hex' | 'Bytes' = 'Hex' | 'Bytes'> = | (as extends 'Bytes' ? readonly Bytes.Bytes[] : never) | (as extends 'Hex' ? readonly Hex.Hex[] : never) type ErrorType = | Hex.fromBytes.ErrorType | Bytes.fromHex.ErrorType | Errors.GlobalErrorType } /** * Transforms {@link ox#Blobs.Blobs} into a {@link ox#Blobs.BlobSidecars} array. * * @example * ```ts twoslash * // @noErrors * import { Blobs } from 'ox' * import { kzg } from './kzg' * * const blobs = Blobs.from('0xdeadbeef') * const sidecars = Blobs.toSidecars(blobs, { kzg }) // [!code focus] * ``` * * @example * You can also provide your own commitments and proofs if you do not want `toSidecars` * to compute them. * * ```ts twoslash * // @noErrors * import { Blobs } from 'ox' * import { kzg } from './kzg' * * const blobs = Blobs.from('0xdeadbeef') * const commitments = Blobs.toCommitments(blobs, { kzg }) * const proofs = Blobs.toProofs(blobs, { commitments, kzg }) * * const sidecars = Blobs.toSidecars(blobs, { commitments, kzg, proofs }) // [!code focus] * ``` * * @param blobs - The {@link ox#Blobs.Blobs} to transform into {@link ox#Blobs.BlobSidecars}. * @param options - Options. * @returns The {@link ox#Blobs.BlobSidecars}. */ export function toSidecars< const blobs extends Blobs<Hex.Hex> | Blobs<Bytes.Bytes>, >( blobs: blobs, options: toSidecars.Options<blobs>, ): toSidecars.ReturnType<blobs> { const { kzg } = options const commitments = options.commitments ?? toCommitments(blobs, { kzg: kzg! }) const proofs = options.proofs ?? toProofs(blobs, { commitments: commitments as any, kzg: kzg! }) const sidecars: Mutable<BlobSidecars> = [] for (let i = 0; i < blobs.length; i++) sidecars.push({ blob: blobs[i]!, commitment: commitments[i]!, proof: proofs[i]!, }) return sidecars as never } export declare namespace toSidecars { type Options< blobs extends Blobs<Hex.Hex> | Blobs<Bytes.Bytes> = | Blobs<Hex.Hex> | Blobs<Bytes.Bytes>, > = { kzg?: Kzg.Kzg | undefined } & OneOf< | {} | { /** Commitment for each blob. */ commitments: blobs | readonly Hex.Hex[] | readonly Bytes.Bytes[] /** Proof for each blob. */ proofs: blobs | readonly Hex.Hex[] | readonly Bytes.Bytes[] } > type ReturnType<blobs extends Blobs<Hex.Hex> | Blobs<Bytes.Bytes>> = UnionCompute< | (blobs extends Blobs<Hex.Hex> ? BlobSidecars<Hex.Hex> : never) | (blobs extends Blobs<Bytes.Bytes> ? BlobSidecars<Bytes.Bytes> : never) > type ErrorType = Errors.GlobalErrorType } /** * Compute Blob Versioned Hashes from a list of {@link ox#Blobs.Blobs}. * * @example * ```ts twoslash * // @noErrors * import { Blobs } from 'ox' * import { kzg } from './kzg' * * const blobs = Blobs.from('0xdeadbeef') * const versionedHashes = Blobs.toVersionedHashes(blobs, { kzg }) // [!code focus] * ``` * * @param blobs - The {@link ox#Blobs.Blobs} to transform into Blob Versioned Hashes. * @param options - Options. * @returns The Blob Versioned Hashes. */ export function toVersionedHashes< const blobs extends Blobs<Bytes.Bytes> | Blobs<Hex.Hex>, as extends 'Hex' | 'Bytes' = | (blobs extends Blobs<Hex.Hex> ? 'Hex' : never) | (blobs extends Blobs<Bytes.Bytes> ? 'Bytes' : never), >( blobs: blobs | Blobs<Bytes.Bytes> | Blobs<Hex.Hex>, options: toVersionedHashes.Options<as>, ): toVersionedHashes.ReturnType<as> { const commitments = toCommitments(blobs, options) return commitmentsToVersionedHashes(commitments, options) } export declare namespace toVersionedHashes { type Options<as extends 'Hex' | 'Bytes' = 'Hex'> = { /** KZG implementation. */ kzg: Pick<Kzg.Kzg, 'blobToKzgCommitment'> /** Return type. */ as?: as | 'Hex' | 'Bytes' | undefined } type ReturnType<as extends 'Hex' | 'Bytes' = 'Hex'> = Compute< | (as extends 'Bytes' ? readonly Bytes.Bytes[] : never) | (as extends 'Hex' ? readonly Hex.Hex[] : never) > type ErrorType = | toCommitments.ErrorType | commitmentsToVersionedHashes.ErrorType | Errors.GlobalErrorType } /** A list of {@link ox#Blobs.BlobSidecar}. */ export type BlobSidecars< type extends Hex.Hex | Bytes.Bytes = Hex.Hex | Bytes.Bytes, > = readonly Compute<BlobSidecar<type>>[] /** Thrown when the blob size is too large. */ export class BlobSizeTooLargeError extends Errors.BaseError { override readonly name = 'Blobs.BlobSizeTooLargeError' constructor({ maxSize, size }: { maxSize: number; size: number }) { super('Blob size is too large.', { metaMessages: [`Max: ${maxSize} bytes`, `Given: ${size} bytes`], }) } } /** Thrown when the blob is empty. */ export class EmptyBlobError extends Errors.BaseError { override readonly name = 'Blobs.EmptyBlobError' constructor() { super('Blob data must not be empty.') } } /** Thrown when the blob versioned hashes are empty. */ export class EmptyBlobVersionedHashesError extends Errors.BaseError { override readonly name = 'Blobs.EmptyBlobVersionedHashesError' constructor() { super('Blob versioned hashes must not be empty.') } } /** Thrown when the blob versioned hash size is invalid. */ export class InvalidVersionedHashSizeError extends Errors.BaseError { override readonly name = 'Blobs.InvalidVersionedHashSizeError' constructor({ hash, size, }: { hash: Hex.Hex size: number }) { super(`Versioned hash "${hash}" size is invalid.`, { metaMessages: ['Expected: 32', `Received: ${size}`], }) } } /** Thrown when the blob versioned hash version is invalid. */ export class InvalidVersionedHashVersionError extends Errors.BaseError { override readonly name = 'Blobs.InvalidVersionedHashVersionError' constructor({ hash, version, }: { hash: Hex.Hex version: number }) { super(`Versioned hash "${hash}" version is invalid.`, { metaMessages: [ `Expected: ${Kzg.versionedHashVersion}`, `Received: ${version}`, ], }) } }