Small refactoring of providers.

This commit is contained in:
Richard Moore 2022-11-27 21:58:07 -05:00
parent d280070a2b
commit 1f4499c174
17 changed files with 124 additions and 113 deletions

@ -384,8 +384,8 @@ export class AbstractProvider implements Provider {
return <T>(this.#plugins.get(name)) || null;
}
set disableCcipRead(value: boolean) { this.#disableCcipRead = !!value; }
get disableCcipRead(): boolean { return this.#disableCcipRead; }
set disableCcipRead(value: boolean) { this.#disableCcipRead = !!value; }
// Shares multiple identical requests made during the same 250ms
async #perform<T = any>(req: PerformActionRequest): Promise<T> {
@ -467,7 +467,7 @@ export class AbstractProvider implements Provider {
}
_wrapBlockWithTransactions(value: BlockParams<TransactionResponseParams>, network: Network): Block<TransactionResponse> {
return new Block(formatBlock(value), this);
return new Block(formatBlockWithTransactions(value), this);
}
_wrapLog(value: LogParams, network: Network): Log {
@ -1034,7 +1034,8 @@ export class AbstractProvider implements Provider {
this.#timers.delete(timerId);
}
_setTimeout(_func: () => void, timeout: number = 0): number {
_setTimeout(_func: () => void, timeout?: number): number {
if (timeout == null) { timeout = 0; }
const timerId = this.#nextTimer++;
const func = () => {
this.#timers.delete(timerId);
@ -1330,7 +1331,7 @@ function bytesPad(value: Uint8Array): Uint8Array {
return result;
}
const empty = new Uint8Array([ ]);
const empty: Uint8Array = new Uint8Array([ ]);
// ABI Encodes a series of (bytes, bytes, ...)
function encodeBytes(datas: Array<BytesLike>): string {

@ -5,7 +5,7 @@ export interface CommunityResourcable {
// Show the throttle message only once
const shown: Set<string> = new Set();
export function showThrottleMessage(service: string) {
export function showThrottleMessage(service: string): void {
if (shown.has(service)) { return; }
shown.add(service);

@ -190,7 +190,8 @@ export class EnsResolver {
return await this.#supports2544;
}
async _fetch(selector: string, parameters: BytesLike = "0x"): Promise<null | string> {
async _fetch(selector: string, parameters?: BytesLike): Promise<null | string> {
if (parameters == null) { parameters = "0x"; }
// e.g. keccak256("addr(bytes32,uint256)")
const addrData = concat([ selector, namehash(this.name), parameters ]);
@ -226,7 +227,8 @@ export class EnsResolver {
return null;
}
async getAddress(coinType: number = 60): Promise<null | string> {
async getAddress(coinType?: number): Promise<null | string> {
if (coinType == null) { coinType = 60; }
if (coinType === 60) {
try {
// keccak256("addr(bytes32)")

@ -1,4 +1,6 @@
/**
* @_ignore
*/
import { getAddress, getCreateAddress } from "../address/index.js";
import { Signature } from "../crypto/index.js"
import { accessListify } from "../transaction/index.js";
@ -7,6 +9,11 @@ import {
assert, assertArgument
} from "../utils/index.js";
import type {
BlockParams, LogParams,
TransactionReceiptParams, TransactionResponseParams,
TransactionResponse
} from "./provider.js";
const BN_0 = BigInt(0);
@ -83,7 +90,7 @@ export function formatUint256(value: any): string {
return zeroPadValue(value, 32);
}
export const formatLog = object({
const _formatLog = object({
address: getAddress,
blockHash: formatHash,
blockNumber: getNumber,
@ -97,7 +104,11 @@ export const formatLog = object({
index: [ "logIndex" ]
});
function _formatBlock(txFunc: FormatFunc): FormatFunc {
export function formatLog(value: any): LogParams {
return _formatLog(value);
}
function _formatBlockWith(txFunc: FormatFunc): FormatFunc {
return object({
hash: allowNull(formatHash),
parentHash: formatHash,
@ -119,11 +130,19 @@ function _formatBlock(txFunc: FormatFunc): FormatFunc {
});
}
export const formatBlock = _formatBlock(formatHash);
const _formatBlock = _formatBlockWith(formatHash);
export const formatBlockWithTransactions = _formatBlock(formatTransactionResponse);
export function formatBlock(value: any): BlockParams<string> {
return _formatBlock(value);
}
export const formatReceiptLog = object({
const _formatBlockWithTransactions = _formatBlockWith(formatTransactionResponse);
export function formatBlockWithTransactions(value: any): BlockParams<TransactionResponse> {
return _formatBlockWithTransactions(value);
}
const _formatReceiptLog = object({
transactionIndex: getNumber,
blockNumber: getNumber,
transactionHash: formatHash,
@ -136,7 +155,11 @@ export const formatReceiptLog = object({
index: [ "logIndex" ]
});
export const formatTransactionReceipt = object({
export function formatReceiptLog(value: any): LogParams {
return _formatReceiptLog(value);
}
const _formatTransactionReceipt = object({
to: allowNull(getAddress, null),
from: allowNull(getAddress, null),
contractAddress: allowNull(getAddress, null),
@ -160,7 +183,11 @@ export const formatTransactionReceipt = object({
index: [ "transactionIndex" ],
});
export function formatTransactionResponse(value: any) {
export function formatTransactionReceipt(value: any): TransactionReceiptParams {
return _formatTransactionReceipt(value);
}
export function formatTransactionResponse(value: any): TransactionResponseParams {
// Some clients (TestRPC) do strange things like return 0x0 for the
// 0 address; correct this to be a real address

@ -1,8 +1,11 @@
/**
* About providers.
*
* @_section: api/providers:Providers [providers]
*/
/////
export {
AbstractProvider, UnmanagedSubscriber
} from "./abstract-provider.js";
@ -49,13 +52,14 @@ export { JsonRpcApiProvider, JsonRpcProvider, JsonRpcSigner } from "./provider-j
export { BrowserProvider } from "./provider-browser.js";
export { AlchemyProvider } from "./provider-alchemy.js";
export { AnkrProvider } from "./provider-ankr.js";
export { CloudflareProvider } from "./provider-cloudflare.js";
export { BaseEtherscanProvider, EtherscanPlugin } from "./provider-etherscan-base.js";
export { EtherscanProvider } from "./provider-etherscan.js";
export { InfuraProvider } from "./provider-infura.js";
//export { PocketProvider } from "./provider-pocket.js";
export {
AlchemyProvider,
AnkrProvider,
CloudflareProvider,
BaseEtherscanProvider, EtherscanPlugin, EtherscanProvider,
InfuraProvider
// PocketProvider
} from "./thirdparty.js";
import { IpcSocketProvider } from "./provider-ipcsocket.js"; /*-browser*/
export { IpcSocketProvider };
@ -88,8 +92,10 @@ export type { GasCostParameters } from "./plugins-network.js";
export type {
BlockTag,
BlockParams, LogParams, TransactionReceiptParams, TransactionResponseParams,
TransactionRequest, PreparedTransactionRequest,
EventFilter, Filter, FilterByBlockHash, OrphanFilter, ProviderEvent, TopicFilter,
EventFilter, Filter, FilterByBlockHash, OrphanFilter, ProviderEvent,
TopicFilter,
Provider,
} from "./provider.js";

@ -1,7 +1,11 @@
/**
* About networks
*
* @_subsection: api/providers:Networks
*/
import { accessListify } from "../transaction/index.js";
import {
getStore, getBigInt, setStore, assertArgument
} from "../utils/index.js";
import { getBigInt, assertArgument } from "../utils/index.js";
import { EnsPlugin, GasCostPlugin } from "./plugins-network.js";
//import { EtherscanPlugin } from "./provider-etherscan-base.js";
@ -88,43 +92,41 @@ const Networks: Map<string | bigint, () => Network> = new Map();
// @TODO: Add a _ethersNetworkObj variable to better detect network ovjects
export class Network {
#props: {
name: string,
chainId: bigint,
#name: string;
#chainId: bigint;
plugins: Map<string, NetworkPlugin>
};
#plugins: Map<string, NetworkPlugin>;
constructor(name: string, _chainId: BigNumberish) {
const chainId = getBigInt(_chainId);
const plugins = new Map();
this.#props = { name, chainId, plugins };
constructor(name: string, chainId: BigNumberish) {
this.#name = name;
this.#chainId = getBigInt(chainId);
this.#plugins = new Map();
}
toJSON(): any {
return { name: this.name, chainId: this.chainId };
}
get name(): string { return getStore(this.#props, "name"); }
set name(value: string) { setStore(this.#props, "name", value); }
get name(): string { return this.#name; }
set name(value: string) { this.#name = value; }
get chainId(): bigint { return getStore(this.#props, "chainId"); }
set chainId(value: BigNumberish) { setStore(this.#props, "chainId", getBigInt(value, "chainId")); }
get chainId(): bigint { return this.#chainId; }
set chainId(value: BigNumberish) { this.#chainId = getBigInt(value, "chainId"); }
get plugins(): Array<NetworkPlugin> {
return Array.from(this.#props.plugins.values());
return Array.from(this.#plugins.values());
}
attachPlugin(plugin: NetworkPlugin): this {
if (this.#props.plugins.get(plugin.name)) {
if (this.#plugins.get(plugin.name)) {
throw new Error(`cannot replace existing plugin: ${ plugin.name } `);
}
this.#props.plugins.set(plugin.name, plugin.clone());
this.#plugins.set(plugin.name, plugin.clone());
return this;
}
getPlugin<T extends NetworkPlugin = NetworkPlugin>(name: string): null | T {
return <T>(this.#props.plugins.get(name)) || null;
return <T>(this.#plugins.get(name)) || null;
}
// Gets a list of Plugins which match basename, ignoring any fragment
@ -139,16 +141,7 @@ export class Network {
});
return clone;
}
/*
freeze(): Frozen<Network> {
Object.freeze(this.#props);
return this;
}
isFrozen(): boolean {
return Object.isFrozen(this.#props);
}
*/
computeIntrinsicGas(tx: TransactionLike): number {
const costs = this.getPlugin<GasCostPlugin>("org.ethers.gas-cost") || (new GasCostPlugin());

@ -45,7 +45,8 @@ export class GasCostPlugin extends NetworkPlugin implements GasCostParameters {
readonly txAccessListStorageKey!: number;
readonly txAccessListAddress!: number;
constructor(effectiveBlock: number = 0, costs?: GasCostParameters) {
constructor(effectiveBlock?: number, costs?: GasCostParameters) {
if (effectiveBlock == null) { effectiveBlock = 0; }
super(`org.ethers.network-plugins.gas-cost#${ (effectiveBlock || 0) }`);
const props: Record<string, number> = { effectiveBlock };

@ -39,10 +39,14 @@ function getHost(name: string): string {
assertArgument(false, "unsupported network", "network", name);
}
/**
* The AlchemyProvider is backed by the [[alchemyapu]] API.
*/
export class AlchemyProvider extends JsonRpcProvider implements CommunityResourcable {
readonly apiKey!: string;
constructor(_network: Networkish = "mainnet", apiKey?: null | string) {
constructor(_network?: Networkish, apiKey?: null | string) {
if (_network == null) { _network = "mainnet"; }
const network = Network.from(_network);
if (apiKey == null) { apiKey = defaultApiKey; }

@ -17,10 +17,6 @@ function getHost(name: string): string {
switch (name) {
case "mainnet":
return "rpc.ankr.com/eth";
case "ropsten":
return "rpc.ankr.com/eth_ropsten";
case "rinkeby":
return "rpc.ankr.com/eth_rinkeby";
case "goerli":
return "rpc.ankr.com/eth_goerli";
case "matic":
@ -36,7 +32,8 @@ function getHost(name: string): string {
export class AnkrProvider extends JsonRpcProvider implements CommunityResourcable {
readonly apiKey!: string;
constructor(_network: Networkish = "mainnet", apiKey?: null | string) {
constructor(_network?: Networkish, apiKey?: null | string) {
if (_network == null) { _network = "mainnet"; }
const network = Network.from(_network);
if (apiKey == null) { apiKey = defaultApiKey; }

@ -13,7 +13,7 @@ export interface Eip1193Provider {
request(request: { method: string, params?: Array<any> | Record<string, any> }): Promise<any>;
};
export type DebugEventJsonRpcApiProvider = {
export type DebugEventBrowserProvider = {
action: "sendEip1193Payload",
payload: { method: string, params: Array<any> }
} | {

@ -7,7 +7,8 @@ import type { Networkish } from "./network.js";
export class CloudflareProvider extends JsonRpcProvider {
constructor(_network: Networkish = "mainnet") {
constructor(_network?: Networkish) {
if (_network == null) { _network = "mainnet"; }
const network = Network.from(_network);
assertArgument(network.name === "mainnet", "unsupported network", "network", _network);
super("https:/\/cloudflare-eth.com/", network, { staticNetwork: network });

@ -1,4 +1,4 @@
import { getBuiltinCallException } from "../abi/index.js";
import { AbiCoder } from "../abi/index.js";
import { accessListify } from "../transaction/index.js";
import {
defineProperties,
@ -257,7 +257,7 @@ export class BaseEtherscanProvider extends AbstractProvider {
_checkError(req: PerformActionRequest, error: Error, transaction: any): never {
if (req.method === "call" || req.method === "estimateGas") {
if (error.message.match(/execution reverted/i)) {
const e = getBuiltinCallException(req.method, <any>req.transaction, (<any>error).data);
const e = AbiCoder.getBuiltinCallException(req.method, <any>req.transaction, (<any>error).data);
e.info = { request: req, error }
throw e;
}

@ -44,8 +44,8 @@ export class InfuraWebSocketProvider extends WebSocketProvider implements Commun
readonly projectId!: string;
readonly projectSecret!: null | string;
constructor(network?: Networkish, apiKey?: any) {
const provider = new InfuraProvider(network, apiKey);
constructor(network?: Networkish, projectId?: string) {
const provider = new InfuraProvider(network, projectId);
const req = provider._getConnection();
assert(!req.credentials, "INFURA WebSocket project secrets unsupported",
@ -69,7 +69,8 @@ export class InfuraProvider extends JsonRpcProvider implements CommunityResourca
readonly projectId!: string;
readonly projectSecret!: null | string;
constructor(_network: Networkish = "mainnet", projectId?: null | string, projectSecret?: null | string) {
constructor(_network?: Networkish, projectId?: null | string, projectSecret?: null | string) {
if (_network == null) { _network = "mainnet"; }
const network = Network.from(_network);
if (projectId == null) { projectId = defaultProjectId; }
if (projectSecret == null) { projectSecret = null; }
@ -91,8 +92,8 @@ export class InfuraProvider extends JsonRpcProvider implements CommunityResourca
return (this.projectId === defaultProjectId);
}
static getWebSocketProvider(network?: Networkish, apiKey?: any): InfuraWebSocketProvider {
return new InfuraWebSocketProvider(network, apiKey);
static getWebSocketProvider(network?: Networkish, projectId?: string): InfuraWebSocketProvider {
return new InfuraWebSocketProvider(network, projectId);
}
static getRequest(network: Network, projectId?: null | string, projectSecret?: null | string): FetchRequest {
@ -112,5 +113,4 @@ export class InfuraProvider extends JsonRpcProvider implements CommunityResourca
return request;
}
}

@ -66,40 +66,3 @@ export class IpcSocketProvider extends SocketProvider {
this.socket.write(message);
}
}
/*
import { defineProperties } from "@ethersproject/properties";
import { SocketLike, SocketProvider } from "./provider-socket.js";
import type { Socket } from "net";
export class SocketWrapper implements SocketLike {
#socket: Socket;
constructor(path: string) {
this.#socket = connect(path);
}
send(data: string): void {
this.#socket.write(data, () => { });
}
addEventListener(event: string, listener: (data: string) => void): void {
//this.#socket.on(event, (value: ) => {
//});
}
close(): void {
}
}
export class IpcProvider extends SocketProvider {
readonly path!: string;
constructor(path: string) {
super(new SocketWrapper(path));
defineProperties<IpcProvider>(this, { path });
}
}
*/

@ -3,7 +3,7 @@
// https://playground.open-rpc.org/?schemaUrl=https://raw.githubusercontent.com/ethereum/eth1.0-apis/assembled-spec/openrpc.json&uiSchema%5BappBar%5D%5Bui:splitView%5D=true&uiSchema%5BappBar%5D%5Bui:input%5D=false&uiSchema%5BappBar%5D%5Bui:examplesDropdown%5D=false
import { getBuiltinCallException } from "../abi/index.js";
import { AbiCoder } from "../abi/index.js";
import { getAddress, resolveAddress } from "../address/index.js";
import { TypedDataEncoder } from "../hash/index.js";
import { accessListify } from "../transaction/index.js";
@ -528,7 +528,8 @@ export class JsonRpcApiProvider extends AbstractProvider {
return super._perform(req);
}
/** Sub-classes may override this; it detects the *actual* network that
/**
* Sub-classes may override this; it detects the *actual* network that
* we are **currently** connected to.
*
* Keep in mind that [[send]] may only be used once [[ready]], otherwise the
@ -783,7 +784,7 @@ export class JsonRpcApiProvider extends AbstractProvider {
if (method === "eth_call" || method === "eth_estimateGas") {
const result = spelunkData(error);
const e = getBuiltinCallException(
const e = AbiCoder.getBuiltinCallException(
(method === "eth_call") ? "call": "estimateGas",
((<any>payload).params[0]),
(result ? result.data: null)
@ -961,7 +962,8 @@ export class JsonRpcApiProvider extends AbstractProvider {
*
* Throws if the account doesn't exist.
*/
async getSigner(address: number | string = 0): Promise<JsonRpcSigner> {
async getSigner(address?: number | string): Promise<JsonRpcSigner> {
if (address == null) { address = 0; }
const accountsPromise = this.send("eth_accounts", [ ]);

@ -73,7 +73,6 @@ export interface Signer extends Addressable, ContractRunner, NameResolver {
* node to populate the nonce and fee data.
*
* @param tx - The call to prepare
* @returns The fully prepared {@link TransactionLike<string>}
*/
populateTransaction(tx: TransactionRequest): Promise<TransactionLike<string>>;

@ -0,0 +1,15 @@
/**
* About thirdparty...
*
* @_section: api/providers/third-party:Third Party Providers [third-party]
*/
/**
*
*/
export { AlchemyProvider } from "./provider-alchemy.js";
export { AnkrProvider } from "./provider-ankr.js";
export { CloudflareProvider } from "./provider-cloudflare.js";
export { BaseEtherscanProvider, EtherscanPlugin } from "./provider-etherscan-base.js";
export { EtherscanProvider } from "./provider-etherscan.js";
export { InfuraProvider } from "./provider-infura.js";