Refactor Block object and provider getBlock operations.

This commit is contained in:
Richard Moore 2022-11-30 15:41:17 -05:00
parent e1c8b99307
commit 35cd8135d4
7 changed files with 102 additions and 102 deletions

@ -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>;
}