Refactor Block object and provider getBlock operations.
This commit is contained in:
parent
e1c8b99307
commit
35cd8135d4
@ -72,7 +72,7 @@ export class ContractUnknownEventPayload extends EventPayload<ContractEventName
|
||||
defineProperties<ContractUnknownEventPayload>(this, { log });
|
||||
}
|
||||
|
||||
async getBlock(): Promise<Block<string>> {
|
||||
async getBlock(): Promise<Block> {
|
||||
return await this.log.getBlock();
|
||||
}
|
||||
|
||||
|
@ -26,8 +26,7 @@ import {
|
||||
|
||||
import { EnsResolver } from "./ens-resolver.js";
|
||||
import {
|
||||
formatBlock, formatBlockWithTransactions, formatLog, formatTransactionReceipt,
|
||||
formatTransactionResponse
|
||||
formatBlock, formatLog, formatTransactionReceipt, formatTransactionResponse
|
||||
} from "./format.js";
|
||||
import { Network } from "./network.js";
|
||||
import { copyRequest, Block, FeeData, Log, TransactionReceipt, TransactionResponse } from "./provider.js";
|
||||
@ -473,14 +472,10 @@ export class AbstractProvider implements Provider {
|
||||
});
|
||||
}
|
||||
|
||||
_wrapBlock(value: BlockParams<string>, network: Network): Block<string> {
|
||||
_wrapBlock(value: BlockParams, network: Network): Block {
|
||||
return new Block(formatBlock(value), this);
|
||||
}
|
||||
|
||||
_wrapBlockWithTransactions(value: BlockParams<TransactionResponseParams>, network: Network): Block<TransactionResponse> {
|
||||
return new Block(formatBlockWithTransactions(value), this);
|
||||
}
|
||||
|
||||
_wrapLog(value: LogParams, network: Network): Log {
|
||||
return new Log(formatLog(value), this);
|
||||
}
|
||||
@ -884,26 +879,16 @@ export class AbstractProvider implements Provider {
|
||||
}
|
||||
|
||||
// Queries
|
||||
async getBlock(block: BlockTag | string): Promise<null | Block<string>> {
|
||||
async getBlock(block: BlockTag | string, prefetchTxs?: boolean): Promise<null | Block> {
|
||||
const { network, params } = await resolveProperties({
|
||||
network: this.getNetwork(),
|
||||
params: this.#getBlock(block, false)
|
||||
params: this.#getBlock(block, !!prefetchTxs)
|
||||
});
|
||||
if (params == null) { return null; }
|
||||
|
||||
return this._wrapBlock(formatBlock(params), network);
|
||||
}
|
||||
|
||||
async getBlockWithTransactions(block: BlockTag | string): Promise<null | Block<TransactionResponse>> {
|
||||
const { network, params } = await resolveProperties({
|
||||
network: this.getNetwork(),
|
||||
params: this.#getBlock(block, true)
|
||||
});
|
||||
if (params == null) { return null; }
|
||||
|
||||
return this._wrapBlockWithTransactions(formatBlockWithTransactions(params), network);
|
||||
}
|
||||
|
||||
async getTransaction(hash: string): Promise<null | TransactionResponse> {
|
||||
const { network, params } = await resolveProperties({
|
||||
network: this.getNetwork(),
|
||||
@ -1033,7 +1018,7 @@ export class AbstractProvider implements Provider {
|
||||
});
|
||||
}
|
||||
|
||||
async waitForBlock(blockTag?: BlockTag): Promise<Block<string>> {
|
||||
async waitForBlock(blockTag?: BlockTag): Promise<Block> {
|
||||
throw new Error();
|
||||
//return new Block(<any><unknown>{ }, this);
|
||||
}
|
||||
|
@ -14,10 +14,6 @@ import type {
|
||||
TransactionReceiptParams, TransactionResponseParams,
|
||||
} from "./formatting.js";
|
||||
|
||||
import type {
|
||||
TransactionResponse
|
||||
} from "./provider.js";
|
||||
|
||||
|
||||
const BN_0 = BigInt(0);
|
||||
|
||||
@ -111,38 +107,31 @@ export function formatLog(value: any): LogParams {
|
||||
return _formatLog(value);
|
||||
}
|
||||
|
||||
function _formatBlockWith(txFunc: FormatFunc): FormatFunc {
|
||||
return object({
|
||||
hash: allowNull(formatHash),
|
||||
parentHash: formatHash,
|
||||
number: getNumber,
|
||||
const _formatBlock = object({
|
||||
hash: allowNull(formatHash),
|
||||
parentHash: formatHash,
|
||||
number: getNumber,
|
||||
|
||||
timestamp: getNumber,
|
||||
nonce: allowNull(formatData),
|
||||
difficulty: getBigInt,
|
||||
timestamp: getNumber,
|
||||
nonce: allowNull(formatData),
|
||||
difficulty: getBigInt,
|
||||
|
||||
gasLimit: getBigInt,
|
||||
gasUsed: getBigInt,
|
||||
gasLimit: getBigInt,
|
||||
gasUsed: getBigInt,
|
||||
|
||||
miner: allowNull(getAddress),
|
||||
extraData: formatData,
|
||||
miner: allowNull(getAddress),
|
||||
extraData: formatData,
|
||||
|
||||
transactions: arrayOf(txFunc),
|
||||
baseFeePerGas: allowNull(getBigInt)
|
||||
});
|
||||
|
||||
baseFeePerGas: allowNull(getBigInt)
|
||||
export function formatBlock(value: any): BlockParams {
|
||||
const result = _formatBlock(value);
|
||||
result.transactions = value.transactions.map((tx: string | TransactionResponseParams) => {
|
||||
if (typeof(tx) === "string") { return tx; }
|
||||
return formatTransactionResponse(tx);
|
||||
});
|
||||
}
|
||||
|
||||
const _formatBlock = _formatBlockWith(formatHash);
|
||||
|
||||
export function formatBlock(value: any): BlockParams<string> {
|
||||
return _formatBlock(value);
|
||||
}
|
||||
|
||||
const _formatBlockWithTransactions = _formatBlockWith(formatTransactionResponse);
|
||||
|
||||
export function formatBlockWithTransactions(value: any): BlockParams<TransactionResponse> {
|
||||
return _formatBlockWithTransactions(value);
|
||||
return result;
|
||||
}
|
||||
|
||||
const _formatReceiptLog = object({
|
||||
|
@ -72,7 +72,7 @@ export interface PreparedTransactionRequest {
|
||||
//////////////////////
|
||||
// Block
|
||||
|
||||
export interface BlockParams<T extends string | TransactionResponseParams> {
|
||||
export interface BlockParams {
|
||||
hash?: null | string;
|
||||
|
||||
number: number;
|
||||
@ -91,7 +91,7 @@ export interface BlockParams<T extends string | TransactionResponseParams> {
|
||||
|
||||
baseFeePerGas: null | bigint;
|
||||
|
||||
transactions: ReadonlyArray<T>;
|
||||
transactions: ReadonlyArray<string | TransactionResponseParams>;
|
||||
};
|
||||
|
||||
|
||||
|
@ -2,10 +2,8 @@ import { defineProperties } from "../utils/properties.js";
|
||||
|
||||
import { assertArgument } from "../utils/index.js";
|
||||
|
||||
import type { BlockParams, TransactionResponseParams } from "./formatting.js";
|
||||
|
||||
import type {
|
||||
Block, FeeData, Provider, TransactionResponse
|
||||
FeeData, Provider
|
||||
} from "./provider.js";
|
||||
|
||||
|
||||
@ -142,7 +140,7 @@ export class FeeDataNetworkPlugin extends NetworkPlugin {
|
||||
return new FeeDataNetworkPlugin(this.#feeDataFunc);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
export class CustomBlockNetworkPlugin extends NetworkPlugin {
|
||||
readonly #blockFunc: (provider: Provider, block: BlockParams<string>) => Block<string>;
|
||||
readonly #blockWithTxsFunc: (provider: Provider, block: BlockParams<TransactionResponseParams>) => Block<TransactionResponse>;
|
||||
@ -157,7 +155,7 @@ export class CustomBlockNetworkPlugin extends NetworkPlugin {
|
||||
return await this.#blockFunc(provider, block);
|
||||
}
|
||||
|
||||
async getBlockWithTransactions(provider: Provider, block: BlockParams<TransactionResponseParams>): Promise<Block<TransactionResponse>> {
|
||||
async getBlockions(provider: Provider, block: BlockParams<TransactionResponseParams>): Promise<Block<TransactionResponse>> {
|
||||
return await this.#blockWithTxsFunc(provider, block);
|
||||
}
|
||||
|
||||
@ -165,3 +163,4 @@ export class CustomBlockNetworkPlugin extends NetworkPlugin {
|
||||
return new CustomBlockNetworkPlugin(this.#blockFunc, this.#blockWithTxsFunc);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
@ -358,10 +358,7 @@ export class FallbackProvider extends AbstractProvider {
|
||||
return await provider.getBalance(req.address, req.blockTag);
|
||||
case "getBlock": {
|
||||
const block = ("blockHash" in req) ? req.blockHash: req.blockTag;
|
||||
if (req.includeTransactions) {
|
||||
return await provider.getBlockWithTransactions(block);
|
||||
}
|
||||
return await provider.getBlock(block);
|
||||
return await provider.getBlock(block, req.includeTransactions);
|
||||
}
|
||||
case "getBlockNumber":
|
||||
return await provider.getBlockNumber();
|
||||
|
@ -242,7 +242,7 @@ export interface BlockParams<T extends string | TransactionResponseParams> {
|
||||
};
|
||||
*/
|
||||
|
||||
export interface MinedBlock<T extends string | TransactionResponse = string> extends Block<T> {
|
||||
export interface MinedBlock extends Block {
|
||||
readonly number: number;
|
||||
readonly hash: string;
|
||||
readonly timestamp: number;
|
||||
@ -254,7 +254,7 @@ export interface MinedBlock<T extends string | TransactionResponse = string> ext
|
||||
* A **Block** represents the data associated with a full block on
|
||||
* Ethereum.
|
||||
*/
|
||||
export class Block<T extends string | TransactionResponse> implements BlockParams<T>, Iterable<T> {
|
||||
export class Block implements BlockParams, Iterable<string> {
|
||||
/**
|
||||
* The provider connected to the block used to fetch additional details
|
||||
* if necessary.
|
||||
@ -333,7 +333,7 @@ export class Block<T extends string | TransactionResponse> implements BlockParam
|
||||
*/
|
||||
readonly baseFeePerGas!: null | bigint;
|
||||
|
||||
readonly #transactions: ReadonlyArray<T>;
|
||||
readonly #transactions: Array<string | TransactionResponse>;
|
||||
|
||||
/**
|
||||
* Create a new **Block** object.
|
||||
@ -341,16 +341,16 @@ export class Block<T extends string | TransactionResponse> implements BlockParam
|
||||
* This should generally not be necessary as the unless implementing a
|
||||
* low-level library.
|
||||
*/
|
||||
constructor(block: BlockParams<T>, provider: Provider) {
|
||||
constructor(block: BlockParams, provider: Provider) {
|
||||
|
||||
this.#transactions = Object.freeze(block.transactions.map((tx) => {
|
||||
if (typeof(tx) !== "string" && tx.provider !== provider) {
|
||||
return <T>(new TransactionResponse(tx, provider));
|
||||
this.#transactions = block.transactions.map((tx) => {
|
||||
if (typeof(tx) !== "string") {
|
||||
return new TransactionResponse(tx, provider);
|
||||
}
|
||||
return <T>tx;
|
||||
}));;
|
||||
return tx;
|
||||
});
|
||||
|
||||
defineProperties<Block<T>>(this, {
|
||||
defineProperties<Block>(this, {
|
||||
provider,
|
||||
|
||||
hash: getValue(block.hash),
|
||||
@ -373,9 +373,14 @@ export class Block<T extends string | TransactionResponse> implements BlockParam
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of transactions.
|
||||
* Returns the list of transaction hashes.
|
||||
*/
|
||||
get transactions(): ReadonlyArray<T> { return this.#transactions; }
|
||||
get transactions(): ReadonlyArray<string> {
|
||||
return this.#transactions.map((tx) => {
|
||||
if (typeof(tx) === "string") { return tx; }
|
||||
return tx.hash;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a JSON-friendly value.
|
||||
@ -398,13 +403,14 @@ export class Block<T extends string | TransactionResponse> implements BlockParam
|
||||
};
|
||||
}
|
||||
|
||||
[Symbol.iterator](): Iterator<T> {
|
||||
[Symbol.iterator](): Iterator<string> {
|
||||
let index = 0;
|
||||
const txs = this.transactions;
|
||||
return {
|
||||
next: () => {
|
||||
if (index < this.length) {
|
||||
return {
|
||||
value: this.transactions[index++], done: false
|
||||
value: txs[index++], done: false
|
||||
}
|
||||
}
|
||||
return { value: undefined, done: true };
|
||||
@ -415,7 +421,7 @@ export class Block<T extends string | TransactionResponse> implements BlockParam
|
||||
/**
|
||||
* The number of transactions in this block.
|
||||
*/
|
||||
get length(): number { return this.transactions.length; }
|
||||
get length(): number { return this.#transactions.length; }
|
||||
|
||||
/**
|
||||
* The [date](link-js-data) this block was included at.
|
||||
@ -424,14 +430,31 @@ export class Block<T extends string | TransactionResponse> implements BlockParam
|
||||
if (this.timestamp == null) { return null; }
|
||||
return new Date(this.timestamp * 1000);
|
||||
}
|
||||
// @TODO: Don't have 2 block types?
|
||||
// just populate this with all the values? simplifies provider
|
||||
|
||||
/**
|
||||
* Get the transaction at %%indexe%% within this block.
|
||||
*/
|
||||
async getTransaction(index: number): Promise<TransactionResponse> {
|
||||
const tx = this.transactions[index];
|
||||
async getTransaction(indexOrHash: number | string): Promise<TransactionResponse> {
|
||||
// Find the internal value by its index or hash
|
||||
let tx: string | TransactionResponse | undefined = undefined;
|
||||
if (typeof(indexOrHash) === "number") {
|
||||
tx = this.#transactions[indexOrHash];
|
||||
} else {
|
||||
const hash = indexOrHash.toLowerCase();
|
||||
for (const v of this.#transactions) {
|
||||
if (typeof(v) === "string") {
|
||||
if (v !== hash) { continue; }
|
||||
tx = v;
|
||||
break;
|
||||
} else {
|
||||
if (v.hash === hash) { continue; }
|
||||
tx = v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tx == null) { throw new Error("no such tx"); }
|
||||
|
||||
if (typeof(tx) === "string") {
|
||||
return <TransactionResponse>(await this.provider.getTransaction(tx));
|
||||
} else {
|
||||
@ -445,12 +468,12 @@ export class Block<T extends string | TransactionResponse> implements BlockParam
|
||||
* If true, the block has been typed-gaurded that all mined
|
||||
* properties are non-null.
|
||||
*/
|
||||
isMined(): this is MinedBlock<T> { return !!this.hash; }
|
||||
isMined(): this is MinedBlock { return !!this.hash; }
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
isLondon(): this is (Block<T> & { baseFeePerGas: bigint }) {
|
||||
isLondon(): this is (Block & { baseFeePerGas: bigint }) {
|
||||
return !!this.baseFeePerGas;
|
||||
}
|
||||
|
||||
@ -536,16 +559,22 @@ export class Log implements LogParams {
|
||||
};
|
||||
}
|
||||
|
||||
async getBlock(): Promise<Block<string>> {
|
||||
return <Block<string>>(await this.provider.getBlock(this.blockHash));
|
||||
async getBlock(): Promise<Block> {
|
||||
const block = await this.provider.getBlock(this.blockHash);
|
||||
assert(!!block, "failed to find transaction", "UNKNOWN_ERROR", { });
|
||||
return block;
|
||||
}
|
||||
|
||||
async getTransaction(): Promise<TransactionResponse> {
|
||||
return <TransactionResponse>(await this.provider.getTransaction(this.transactionHash));
|
||||
const tx = await this.provider.getTransaction(this.transactionHash);
|
||||
assert(!!tx, "failed to find transaction", "UNKNOWN_ERROR", { });
|
||||
return tx;
|
||||
}
|
||||
|
||||
async getTransactionReceipt(): Promise<TransactionReceipt> {
|
||||
return <TransactionReceipt>(await this.provider.getTransactionReceipt(this.transactionHash));
|
||||
const receipt = await this.provider.getTransactionReceipt(this.transactionHash);
|
||||
assert(!!receipt, "failed to find transaction receipt", "UNKNOWN_ERROR", { });
|
||||
return receipt;
|
||||
}
|
||||
|
||||
removedEvent(): OrphanFilter {
|
||||
@ -697,7 +726,7 @@ export class TransactionReceipt implements TransactionReceiptParams, Iterable<Lo
|
||||
return this.gasUsed * this.gasPrice;
|
||||
}
|
||||
|
||||
async getBlock(): Promise<Block<string>> {
|
||||
async getBlock(): Promise<Block> {
|
||||
const block = await this.provider.getBlock(this.blockHash);
|
||||
if (block == null) { throw new Error("TODO"); }
|
||||
return block;
|
||||
@ -863,7 +892,7 @@ export class TransactionResponse implements TransactionLike<string>, Transaction
|
||||
};
|
||||
}
|
||||
|
||||
async getBlock(): Promise<null | Block<string>> {
|
||||
async getBlock(): Promise<null | Block> {
|
||||
let blockNumber = this.blockNumber;
|
||||
if (blockNumber == null) {
|
||||
const tx = await this.getTransaction();
|
||||
@ -919,15 +948,19 @@ export class TransactionResponse implements TransactionLike<string>, Transaction
|
||||
|
||||
// Get the next block to scan
|
||||
if (stopScanning) { return null; }
|
||||
const block = await this.provider.getBlockWithTransactions(nextScan);
|
||||
const block = await this.provider.getBlock(nextScan, true);
|
||||
|
||||
// This should not happen; but we'll try again shortly
|
||||
if (block == null) { return; }
|
||||
|
||||
for (const tx of block.transactions) {
|
||||
// We were mined; no replacement
|
||||
for (const hash of block) {
|
||||
if (hash === this.hash) { return; }
|
||||
}
|
||||
|
||||
// We were mined; no replacement
|
||||
if (tx.hash === this.hash) { return; }
|
||||
// Search for the transaction that replaced us
|
||||
for (let i = 0; i < block.length; i++) {
|
||||
const tx: TransactionResponse = await block.getTransaction(i);
|
||||
|
||||
if (tx.from === this.from && tx.nonce === this.nonce) {
|
||||
// Get the receipt
|
||||
@ -1284,16 +1317,13 @@ export interface Provider extends ContractRunner, EventEmitterable<ProviderEvent
|
||||
|
||||
/**
|
||||
* Resolves to the block for %%blockHashOrBlockTag%%.
|
||||
*/
|
||||
getBlock(blockHashOrBlockTag: BlockTag | string): Promise<null | Block<string>>;
|
||||
|
||||
/**
|
||||
* Resolves to the block for %%blockHashOrBlockTag%%, including each
|
||||
* full transaction.
|
||||
*
|
||||
* If a block is unknonw, this resolved to ``null``.
|
||||
* If %%prefetchTxs%%, and the backend supports including transactions
|
||||
* with block requests, all transactions will be included and the
|
||||
* [[Block]] object will not need to make remote calls for getting
|
||||
* transactions.
|
||||
*/
|
||||
getBlockWithTransactions(blockHashOrBlockTag: BlockTag | string): Promise<null | Block<TransactionResponse>>
|
||||
getBlock(blockHashOrBlockTag: BlockTag | string, prefetchTxs?: boolean): Promise<null | Block>;
|
||||
|
||||
/**
|
||||
* Resolves to the transaction for %%hash%%.
|
||||
@ -1360,5 +1390,5 @@ export interface Provider extends ContractRunner, EventEmitterable<ProviderEvent
|
||||
* This can be useful for waiting some number of blocks by using
|
||||
* the ``currentBlockNumber + N``.
|
||||
*/
|
||||
waitForBlock(blockTag?: BlockTag): Promise<Block<string>>;
|
||||
waitForBlock(blockTag?: BlockTag): Promise<Block>;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user