You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

325 lines
11 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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 blocks 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 blocks 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 blocks 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 blocks 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 blocks 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
}