UNPKG

ox

Version:

Ethereum Standard Library

325 lines (311 loc) 11.1 kB
import type * as Address from './Address.js' import type * as Errors from './Errors.js' import * as Hex from './Hex.js' import type { Compute, OneOf } from './internal/types.js' import * as Transaction from './Transaction.js' import * as Withdrawal from './Withdrawal.js' /** A Block as defined in the [Execution API specification](https://github.com/ethereum/execution-apis/blob/main/src/schemas/block.yaml). */ export type Block< includeTransactions extends boolean = false, blockTag extends Tag = 'latest', bigintType = bigint, numberType = number, transaction = Transaction.Transaction< blockTag extends 'pending' ? true : false, bigintType, numberType >, > = Compute<{ /** Base fee per gas */ baseFeePerGas?: bigintType | undefined /** Total used blob gas by all transactions in this block */ blobGasUsed?: bigintType | undefined /** Difficulty for this block */ difficulty?: bigintType | undefined /** Excess blob gas */ excessBlobGas?: bigintType | undefined /** "Extra data" field of this block */ extraData?: Hex.Hex | undefined /** Maximum gas allowed in this block */ gasLimit: bigintType /** Total used gas by all transactions in this block */ gasUsed: bigintType /** Block hash or `null` if pending */ hash: blockTag extends 'pending' ? null : Hex.Hex /** Logs bloom filter or `null` if pending */ logsBloom: blockTag extends 'pending' ? null : Hex.Hex /** Address that received this block’s mining rewards */ miner: Address.Address /** Unique identifier for the block. */ mixHash: Hex.Hex /** Proof-of-work hash or `null` if pending */ nonce: blockTag extends 'pending' ? null : Hex.Hex /** Block number or `null` if pending */ number: blockTag extends 'pending' ? null : bigintType parentBeaconBlockRoot?: Hex.Hex | undefined /** Parent block hash */ parentHash: Hex.Hex /** Root of the this block’s receipts trie */ receiptsRoot: Hex.Hex sealFields?: readonly Hex.Hex[] | undefined /** SHA3 of the uncles data in this block */ sha3Uncles: Hex.Hex /** Size of this block in bytes */ size: bigintType /** Root of this block’s final state trie */ stateRoot: Hex.Hex /** Unix timestamp of when this block was collated */ timestamp: bigintType /** Total difficulty of the chain until this block */ totalDifficulty?: bigintType | undefined /** List of transaction objects or hashes */ transactions: includeTransactions extends true ? readonly transaction[] : readonly Hex.Hex[] /** Root of this block’s transaction trie */ transactionsRoot: Hex.Hex /** List of uncle hashes */ uncles: readonly Hex.Hex[] /** List of withdrawal objects */ withdrawals?: | readonly Withdrawal.Withdrawal<bigintType, numberType>[] | undefined /** Root of the this block’s withdrawals trie */ withdrawalsRoot?: Hex.Hex | undefined }> /** A Block hash. */ export type Hash = Hex.Hex /** A Block identifier. */ export type Identifier<bigintType = bigint> = { /** Whether or not to throw an error if the block is not in the canonical chain as described below. Only allowed in conjunction with the blockHash tag. Defaults to false. */ requireCanonical?: boolean | undefined } & OneOf< | { /** The block in the canonical chain with this number */ blockNumber: Number<bigintType> } | { /** The block uniquely identified by this hash. The `blockNumber` and `blockHash` properties are mutually exclusive; exactly one of them must be set. */ blockHash: Hash } > /** A Block number. */ export type Number<bigintType = bigint> = bigintType /** An RPC Block as defined in the [Execution API specification](https://github.com/ethereum/execution-apis/blob/main/src/schemas/block.yaml). */ export type Rpc< includeTransactions extends boolean = boolean, blockTag extends Tag = 'latest', transaction = Transaction.Rpc<blockTag extends 'pending' ? true : false>, > = Block<includeTransactions, blockTag, Hex.Hex, Hex.Hex, transaction> /** * A Block Tag as defined in the [Execution API specification](https://github.com/ethereum/execution-apis/blob/main/src/schemas/block.yaml). * * - `earliest`: The lowest numbered block the client has available; * - `finalized`: The most recent crypto-economically secure block, cannot be re-orged outside of manual intervention driven by community coordination; * - `safe`: The most recent block that is safe from re-orgs under honest majority and certain synchronicity assumptions; * - `latest`: The most recent block in the canonical chain observed by the client, this block may be re-orged out of the canonical chain even under healthy/normal conditions; * - `pending`: A sample next block built by the client on top of `latest` and containing the set of transactions usually taken from local mempool. */ export type Tag = 'latest' | 'earliest' | 'pending' | 'safe' | 'finalized' /** * Converts a {@link ox#Block.Block} to an {@link ox#Block.Rpc}. * * @example * ```ts twoslash * // @noErrors * import { Block } from 'ox' * * const block = Block.toRpc({ * // ... * hash: '0xebc3644804e4040c0a74c5a5bbbc6b46a71a5d4010fe0c92ebb2fdf4a43ea5dd', * number: 19868020n, * size: 520n * timestamp: 1662222222n, * // ... * }) * // @log: { * // @log: // ... * // @log: hash: '0xebc3644804e4040c0a74c5a5bbbc6b46a71a5d4010fe0c92ebb2fdf4a43ea5dd', * // @log: number: '0xec6fc6', * // @log: size: '0x208', * // @log: timestamp: '0x63198f6f', * // @log: // ... * // @log: } * ``` * * @param block - The Block to convert. * @returns An RPC Block. */ export function toRpc< includeTransactions extends boolean = false, blockTag extends Tag = 'latest', >( block: Block<includeTransactions, blockTag>, _options: toRpc.Options<includeTransactions, blockTag> = {}, ): Rpc<boolean, blockTag> { const transactions = block.transactions.map((transaction) => { if (typeof transaction === 'string') return transaction return Transaction.toRpc(transaction as any) as any }) return { baseFeePerGas: typeof block.baseFeePerGas === 'bigint' ? Hex.fromNumber(block.baseFeePerGas) : undefined, blobGasUsed: typeof block.blobGasUsed === 'bigint' ? Hex.fromNumber(block.blobGasUsed) : undefined, excessBlobGas: typeof block.excessBlobGas === 'bigint' ? Hex.fromNumber(block.excessBlobGas) : undefined, extraData: block.extraData, difficulty: typeof block.difficulty === 'bigint' ? Hex.fromNumber(block.difficulty) : undefined, gasLimit: Hex.fromNumber(block.gasLimit), gasUsed: Hex.fromNumber(block.gasUsed), hash: block.hash, logsBloom: block.logsBloom, miner: block.miner, mixHash: block.mixHash, nonce: block.nonce, number: (typeof block.number === 'bigint' ? Hex.fromNumber(block.number) : null) as never, parentBeaconBlockRoot: block.parentBeaconBlockRoot, parentHash: block.parentHash, receiptsRoot: block.receiptsRoot, sealFields: block.sealFields, sha3Uncles: block.sha3Uncles, size: Hex.fromNumber(block.size), stateRoot: block.stateRoot, timestamp: Hex.fromNumber(block.timestamp), totalDifficulty: typeof block.totalDifficulty === 'bigint' ? Hex.fromNumber(block.totalDifficulty) : undefined, transactions, transactionsRoot: block.transactionsRoot, uncles: block.uncles, withdrawals: block.withdrawals?.map(Withdrawal.toRpc), withdrawalsRoot: block.withdrawalsRoot, } } export declare namespace toRpc { type Options< includeTransactions extends boolean = false, blockTag extends Tag = 'latest', > = { blockTag?: blockTag | Tag | undefined includeTransactions?: includeTransactions | boolean | undefined } type ErrorType = Errors.GlobalErrorType } /** * Converts a {@link ox#Block.Rpc} to an {@link ox#Block.Block}. * * @example * ```ts twoslash * // @noErrors * import { Block } from 'ox' * * const block = Block.fromRpc({ * // ... * hash: '0xebc3644804e4040c0a74c5a5bbbc6b46a71a5d4010fe0c92ebb2fdf4a43ea5dd', * number: '0xec6fc6', * size: '0x208', * timestamp: '0x63198f6f', * // ... * }) * // @log: { * // @log: // ... * // @log: hash: '0xebc3644804e4040c0a74c5a5bbbc6b46a71a5d4010fe0c92ebb2fdf4a43ea5dd', * // @log: number: 19868020n, * // @log: size: 520n, * // @log: timestamp: 1662222222n, * // @log: // ... * // @log: } * ``` * * @example * ### End-to-end * * Below is an end-to-end example of using `Block.fromRpc` to fetch a block from the network and convert it to an {@link ox#Block.Block}. * * ```ts twoslash * import 'ox/window' * import { Block } from 'ox' * * const block = await window.ethereum! * .request({ * method: 'eth_getBlockByNumber', * params: ['latest', false], * }) * .then(Block.fromRpc) // [!code hl] * // @log: { * // @log: // ... * // @log: hash: '0xebc3644804e4040c0a74c5a5bbbc6b46a71a5d4010fe0c92ebb2fdf4a43ea5dd', * // @log: number: 19868020n, * // @log: size: 520n, * // @log: timestamp: 1662222222n, * // @log: // ... * // @log: } * ``` * * :::note * * For simplicity, the above example uses `window.ethereum.request`, but you can use any * type of JSON-RPC interface. * * ::: * * @param block - The RPC block to convert. * @returns An instantiated {@link ox#Block.Block}. */ export function fromRpc< const block extends Rpc | null, includeTransactions extends boolean = false, blockTag extends Tag = 'latest', >( block: block | Rpc | null, _options: fromRpc.Options<includeTransactions, blockTag> = {}, ): block extends Rpc ? Block<includeTransactions, blockTag> : null { if (!block) return null as never const transactions = block.transactions.map((transaction) => { if (typeof transaction === 'string') return transaction return Transaction.fromRpc(transaction) as any }) return { ...block, baseFeePerGas: block.baseFeePerGas ? BigInt(block.baseFeePerGas) : undefined, blobGasUsed: block.blobGasUsed ? BigInt(block.blobGasUsed) : undefined, difficulty: block.difficulty ? BigInt(block.difficulty) : undefined, excessBlobGas: block.excessBlobGas ? BigInt(block.excessBlobGas) : undefined, gasLimit: BigInt(block.gasLimit ?? 0n), gasUsed: BigInt(block.gasUsed ?? 0n), number: block.number ? BigInt(block.number) : null, size: BigInt(block.size ?? 0n), stateRoot: block.stateRoot, timestamp: BigInt(block.timestamp ?? 0n), totalDifficulty: BigInt(block.totalDifficulty ?? 0n), transactions, withdrawals: block.withdrawals?.map(Withdrawal.fromRpc), } as Block as never } export declare namespace fromRpc { type Options< includeTransactions extends boolean = false, blockTag extends Tag = 'latest', > = { blockTag?: blockTag | Tag | undefined includeTransactions?: includeTransactions | boolean | undefined } type ErrorType = Errors.GlobalErrorType }