refactor: move brother_node development artifact to dev/test-nodes subdirectory
Development Artifact Cleanup: ✅ BROTHER_NODE REORGANIZATION: Moved development test node to appropriate location - dev/test-nodes/brother_node/: Moved from root directory for better organization - Contains development configuration, test logs, and test chain data - No impact on production systems - purely development/testing artifact ✅ DEVELOPMENT ARTIFACTS IDENTIFIED: - Chain ID: aitbc-brother-chain (test/development chain) - Ports: 8010 (P2P) and 8011 (RPC) - different from production - Environment: .env file with test configuration - Logs: rpc.log and node.log from development testing session (March 15, 2026) ✅ ROOT DIRECTORY CLEANUP: Removed development clutter from production directory - brother_node/ moved to dev/test-nodes/brother_node/ - Root directory now contains only production-ready components - Development artifacts properly organized in dev/ subdirectory DIRECTORY STRUCTURE IMPROVEMENT: 📁 dev/test-nodes/: Development and testing node configurations 🏗️ Root Directory: Clean production structure with only essential components 🧪 Development Isolation: Test environments separated from production BENEFITS: ✅ Clean Production Directory: No development artifacts in root ✅ Better Organization: Development nodes grouped in dev/ subdirectory ✅ Clear Separation: Production vs development environments clearly distinguished ✅ Maintainability: Easier to identify and manage development components RESULT: Successfully moved brother_node development artifact to dev/test-nodes/ subdirectory, cleaning up the root directory while preserving development testing environment for future use.
This commit is contained in:
1761
dev/env/node_modules/ethers/src.ts/providers/abstract-provider.ts
generated
vendored
Executable file
1761
dev/env/node_modules/ethers/src.ts/providers/abstract-provider.ts
generated
vendored
Executable file
File diff suppressed because it is too large
Load Diff
314
dev/env/node_modules/ethers/src.ts/providers/abstract-signer.ts
generated
vendored
Executable file
314
dev/env/node_modules/ethers/src.ts/providers/abstract-signer.ts
generated
vendored
Executable file
@@ -0,0 +1,314 @@
|
||||
/**
|
||||
* Generally the [[Wallet]] and [[JsonRpcSigner]] and their sub-classes
|
||||
* are sufficient for most developers, but this is provided to
|
||||
* fascilitate more complex Signers.
|
||||
*
|
||||
* @_section: api/providers/abstract-signer: Subclassing Signer [abstract-signer]
|
||||
*/
|
||||
import { resolveAddress } from "../address/index.js";
|
||||
import { Transaction } from "../transaction/index.js";
|
||||
import {
|
||||
defineProperties, getBigInt, resolveProperties,
|
||||
assert, assertArgument
|
||||
} from "../utils/index.js";
|
||||
|
||||
import { copyRequest } from "./provider.js";
|
||||
|
||||
import type {
|
||||
AuthorizationRequest, TypedDataDomain, TypedDataField
|
||||
} from "../hash/index.js";
|
||||
import type { Authorization, TransactionLike } from "../transaction/index.js";
|
||||
|
||||
import type {
|
||||
BlockTag, Provider, TransactionRequest, TransactionResponse
|
||||
} from "./provider.js";
|
||||
import type { Signer } from "./signer.js";
|
||||
|
||||
function checkProvider(signer: AbstractSigner, operation: string): Provider {
|
||||
if (signer.provider) { return signer.provider; }
|
||||
assert(false, "missing provider", "UNSUPPORTED_OPERATION", { operation });
|
||||
}
|
||||
|
||||
async function populate(signer: AbstractSigner, tx: TransactionRequest): Promise<TransactionLike<string>> {
|
||||
let pop: any = copyRequest(tx);
|
||||
|
||||
if (pop.to != null) { pop.to = resolveAddress(pop.to, signer); }
|
||||
|
||||
if (pop.from != null) {
|
||||
const from = pop.from;
|
||||
pop.from = Promise.all([
|
||||
signer.getAddress(),
|
||||
resolveAddress(from, signer)
|
||||
]).then(([ address, from ]) => {
|
||||
assertArgument(address.toLowerCase() === from.toLowerCase(),
|
||||
"transaction from mismatch", "tx.from", from);
|
||||
return address;
|
||||
});
|
||||
} else {
|
||||
pop.from = signer.getAddress();
|
||||
}
|
||||
|
||||
return await resolveProperties(pop);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* An **AbstractSigner** includes most of teh functionality required
|
||||
* to get a [[Signer]] working as expected, but requires a few
|
||||
* Signer-specific methods be overridden.
|
||||
*
|
||||
*/
|
||||
export abstract class AbstractSigner<P extends null | Provider = null | Provider> implements Signer {
|
||||
/**
|
||||
* The provider this signer is connected to.
|
||||
*/
|
||||
readonly provider!: P;
|
||||
|
||||
/**
|
||||
* Creates a new Signer connected to %%provider%%.
|
||||
*/
|
||||
constructor(provider?: P) {
|
||||
defineProperties<AbstractSigner>(this, { provider: (provider || null) });
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves to the Signer address.
|
||||
*/
|
||||
abstract getAddress(): Promise<string>;
|
||||
|
||||
/**
|
||||
* Returns the signer connected to %%provider%%.
|
||||
*
|
||||
* This may throw, for example, a Signer connected over a Socket or
|
||||
* to a specific instance of a node may not be transferrable.
|
||||
*/
|
||||
abstract connect(provider: null | Provider): Signer;
|
||||
|
||||
async getNonce(blockTag?: BlockTag): Promise<number> {
|
||||
return checkProvider(this, "getTransactionCount").getTransactionCount(await this.getAddress(), blockTag);
|
||||
}
|
||||
|
||||
async populateCall(tx: TransactionRequest): Promise<TransactionLike<string>> {
|
||||
const pop = await populate(this, tx);
|
||||
return pop;
|
||||
}
|
||||
|
||||
async populateTransaction(tx: TransactionRequest): Promise<TransactionLike<string>> {
|
||||
const provider = checkProvider(this, "populateTransaction");
|
||||
|
||||
const pop = await populate(this, tx);
|
||||
|
||||
if (pop.nonce == null) {
|
||||
pop.nonce = await this.getNonce("pending");
|
||||
}
|
||||
|
||||
if (pop.gasLimit == null) {
|
||||
pop.gasLimit = await this.estimateGas(pop);
|
||||
}
|
||||
|
||||
// Populate the chain ID
|
||||
const network = await (<Provider>(this.provider)).getNetwork();
|
||||
if (pop.chainId != null) {
|
||||
const chainId = getBigInt(pop.chainId);
|
||||
assertArgument(chainId === network.chainId, "transaction chainId mismatch", "tx.chainId", tx.chainId);
|
||||
} else {
|
||||
pop.chainId = network.chainId;
|
||||
}
|
||||
|
||||
// Do not allow mixing pre-eip-1559 and eip-1559 properties
|
||||
const hasEip1559 = (pop.maxFeePerGas != null || pop.maxPriorityFeePerGas != null);
|
||||
if (pop.gasPrice != null && (pop.type === 2 || hasEip1559)) {
|
||||
assertArgument(false, "eip-1559 transaction do not support gasPrice", "tx", tx);
|
||||
} else if ((pop.type === 0 || pop.type === 1) && hasEip1559) {
|
||||
assertArgument(false, "pre-eip-1559 transaction do not support maxFeePerGas/maxPriorityFeePerGas", "tx", tx);
|
||||
}
|
||||
|
||||
if ((pop.type === 2 || pop.type == null) && (pop.maxFeePerGas != null && pop.maxPriorityFeePerGas != null)) {
|
||||
// Fully-formed EIP-1559 transaction (skip getFeeData)
|
||||
pop.type = 2;
|
||||
|
||||
} else if (pop.type === 0 || pop.type === 1) {
|
||||
// Explicit Legacy or EIP-2930 transaction
|
||||
|
||||
// We need to get fee data to determine things
|
||||
const feeData = await provider.getFeeData();
|
||||
|
||||
assert(feeData.gasPrice != null, "network does not support gasPrice", "UNSUPPORTED_OPERATION", {
|
||||
operation: "getGasPrice" });
|
||||
|
||||
// Populate missing gasPrice
|
||||
if (pop.gasPrice == null) { pop.gasPrice = feeData.gasPrice; }
|
||||
|
||||
} else {
|
||||
|
||||
// We need to get fee data to determine things
|
||||
const feeData = await provider.getFeeData();
|
||||
|
||||
if (pop.type == null) {
|
||||
// We need to auto-detect the intended type of this transaction...
|
||||
|
||||
if (feeData.maxFeePerGas != null && feeData.maxPriorityFeePerGas != null) {
|
||||
// The network supports EIP-1559!
|
||||
|
||||
// Upgrade transaction from null to eip-1559
|
||||
if (pop.authorizationList && pop.authorizationList.length) {
|
||||
pop.type = 4;
|
||||
} else {
|
||||
pop.type = 2;
|
||||
}
|
||||
|
||||
if (pop.gasPrice != null) {
|
||||
// Using legacy gasPrice property on an eip-1559 network,
|
||||
// so use gasPrice as both fee properties
|
||||
const gasPrice = pop.gasPrice;
|
||||
delete pop.gasPrice;
|
||||
pop.maxFeePerGas = gasPrice;
|
||||
pop.maxPriorityFeePerGas = gasPrice;
|
||||
|
||||
} else {
|
||||
// Populate missing fee data
|
||||
|
||||
if (pop.maxFeePerGas == null) {
|
||||
pop.maxFeePerGas = feeData.maxFeePerGas;
|
||||
}
|
||||
|
||||
if (pop.maxPriorityFeePerGas == null) {
|
||||
pop.maxPriorityFeePerGas = feeData.maxPriorityFeePerGas;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (feeData.gasPrice != null) {
|
||||
// Network doesn't support EIP-1559...
|
||||
|
||||
// ...but they are trying to use EIP-1559 properties
|
||||
assert(!hasEip1559, "network does not support EIP-1559", "UNSUPPORTED_OPERATION", {
|
||||
operation: "populateTransaction" });
|
||||
|
||||
// Populate missing fee data
|
||||
if (pop.gasPrice == null) {
|
||||
pop.gasPrice = feeData.gasPrice;
|
||||
}
|
||||
|
||||
// Explicitly set untyped transaction to legacy
|
||||
// @TODO: Maybe this shold allow type 1?
|
||||
pop.type = 0;
|
||||
|
||||
} else {
|
||||
// getFeeData has failed us.
|
||||
assert(false, "failed to get consistent fee data", "UNSUPPORTED_OPERATION", {
|
||||
operation: "signer.getFeeData" });
|
||||
}
|
||||
|
||||
} else if (pop.type === 2 || pop.type === 3 || pop.type === 4) {
|
||||
// Explicitly using EIP-1559 or EIP-4844
|
||||
|
||||
// Populate missing fee data
|
||||
if (pop.maxFeePerGas == null) {
|
||||
pop.maxFeePerGas = feeData.maxFeePerGas;
|
||||
}
|
||||
|
||||
if (pop.maxPriorityFeePerGas == null) {
|
||||
pop.maxPriorityFeePerGas = feeData.maxPriorityFeePerGas;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//@TOOD: Don't await all over the place; save them up for
|
||||
// the end for better batching
|
||||
return await resolveProperties(pop);
|
||||
}
|
||||
|
||||
async populateAuthorization(_auth: AuthorizationRequest): Promise<AuthorizationRequest> {
|
||||
const auth = Object.assign({ }, _auth);
|
||||
|
||||
// Add a chain ID if not explicitly set to 0
|
||||
if (auth.chainId == null) {
|
||||
auth.chainId = (await checkProvider(this, "getNetwork").getNetwork()).chainId;
|
||||
}
|
||||
|
||||
// @TODO: Take chain ID into account when populating noce?
|
||||
|
||||
if (auth.nonce == null) { auth.nonce = await this.getNonce(); }
|
||||
|
||||
return auth;
|
||||
}
|
||||
|
||||
async estimateGas(tx: TransactionRequest): Promise<bigint> {
|
||||
return checkProvider(this, "estimateGas").estimateGas(await this.populateCall(tx));
|
||||
}
|
||||
|
||||
async call(tx: TransactionRequest): Promise<string> {
|
||||
return checkProvider(this, "call").call(await this.populateCall(tx));
|
||||
}
|
||||
|
||||
async resolveName(name: string): Promise<null | string> {
|
||||
const provider = checkProvider(this, "resolveName");
|
||||
return await provider.resolveName(name);
|
||||
}
|
||||
|
||||
async sendTransaction(tx: TransactionRequest): Promise<TransactionResponse> {
|
||||
const provider = checkProvider(this, "sendTransaction");
|
||||
|
||||
const pop = await this.populateTransaction(tx);
|
||||
delete pop.from;
|
||||
const txObj = Transaction.from(pop);
|
||||
return await provider.broadcastTransaction(await this.signTransaction(txObj));
|
||||
}
|
||||
|
||||
// @TODO: in v7 move this to be abstract
|
||||
authorize(authorization: AuthorizationRequest): Promise<Authorization> {
|
||||
assert(false, "authorization not implemented for this signer",
|
||||
"UNSUPPORTED_OPERATION", { operation: "authorize" });
|
||||
}
|
||||
|
||||
abstract signTransaction(tx: TransactionRequest): Promise<string>;
|
||||
abstract signMessage(message: string | Uint8Array): Promise<string>;
|
||||
abstract signTypedData(domain: TypedDataDomain, types: Record<string, Array<TypedDataField>>, value: Record<string, any>): Promise<string>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A **VoidSigner** is a class designed to allow an address to be used
|
||||
* in any API which accepts a Signer, but for which there are no
|
||||
* credentials available to perform any actual signing.
|
||||
*
|
||||
* This for example allow impersonating an account for the purpose of
|
||||
* static calls or estimating gas, but does not allow sending transactions.
|
||||
*/
|
||||
export class VoidSigner extends AbstractSigner {
|
||||
/**
|
||||
* The signer address.
|
||||
*/
|
||||
readonly address!: string;
|
||||
|
||||
/**
|
||||
* Creates a new **VoidSigner** with %%address%% attached to
|
||||
* %%provider%%.
|
||||
*/
|
||||
constructor(address: string, provider?: null | Provider) {
|
||||
super(provider);
|
||||
defineProperties<VoidSigner>(this, { address });
|
||||
}
|
||||
|
||||
async getAddress(): Promise<string> { return this.address; }
|
||||
|
||||
connect(provider: null | Provider): VoidSigner {
|
||||
return new VoidSigner(this.address, provider);
|
||||
}
|
||||
|
||||
#throwUnsupported(suffix: string, operation: string): never {
|
||||
assert(false, `VoidSigner cannot sign ${ suffix }`, "UNSUPPORTED_OPERATION", { operation });
|
||||
}
|
||||
|
||||
async signTransaction(tx: TransactionRequest): Promise<string> {
|
||||
this.#throwUnsupported("transactions", "signTransaction");
|
||||
}
|
||||
|
||||
async signMessage(message: string | Uint8Array): Promise<string> {
|
||||
this.#throwUnsupported("messages", "signMessage");
|
||||
}
|
||||
|
||||
async signTypedData(domain: TypedDataDomain, types: Record<string, Array<TypedDataField>>, value: Record<string, any>): Promise<string> {
|
||||
this.#throwUnsupported("typed-data", "signTypedData");
|
||||
}
|
||||
}
|
||||
|
||||
49
dev/env/node_modules/ethers/src.ts/providers/community.ts
generated
vendored
Executable file
49
dev/env/node_modules/ethers/src.ts/providers/community.ts
generated
vendored
Executable file
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* There are many awesome community services that provide Ethereum
|
||||
* nodes both for developers just starting out and for large-scale
|
||||
* communities.
|
||||
*
|
||||
* @_section: api/providers/thirdparty: Community Providers [thirdparty]
|
||||
*/
|
||||
|
||||
/**
|
||||
* Providers which offer community credentials should extend this
|
||||
* to notify any interested consumers whether community credentials
|
||||
* are in-use.
|
||||
*/
|
||||
export interface CommunityResourcable {
|
||||
/**
|
||||
* Returns true if the instance is connected using the community
|
||||
* credentials.
|
||||
*/
|
||||
isCommunityResource(): boolean;
|
||||
}
|
||||
|
||||
// Show the throttle message only once per service
|
||||
const shown: Set<string> = new Set();
|
||||
|
||||
/**
|
||||
* Displays a warning in the console when the community resource is
|
||||
* being used too heavily by the app, recommending the developer
|
||||
* acquire their own credentials instead of using the community
|
||||
* credentials.
|
||||
*
|
||||
* The notification will only occur once per service.
|
||||
*/
|
||||
export function showThrottleMessage(service: string): void {
|
||||
if (shown.has(service)) { return; }
|
||||
shown.add(service);
|
||||
|
||||
console.log("========= NOTICE =========")
|
||||
console.log(`Request-Rate Exceeded for ${ service } (this message will not be repeated)`);
|
||||
console.log("");
|
||||
console.log("The default API keys for each service are provided as a highly-throttled,");
|
||||
console.log("community resource for low-traffic projects and early prototyping.");
|
||||
console.log("");
|
||||
console.log("While your application will continue to function, we highly recommended");
|
||||
console.log("signing up for your own API keys to improve performance, increase your");
|
||||
console.log("request rate/limit and enable other perks, such as metrics and advanced APIs.");
|
||||
console.log("");
|
||||
console.log("For more details: https:/\/docs.ethers.org/api-keys/");
|
||||
console.log("==========================");
|
||||
}
|
||||
42
dev/env/node_modules/ethers/src.ts/providers/contracts.ts
generated
vendored
Executable file
42
dev/env/node_modules/ethers/src.ts/providers/contracts.ts
generated
vendored
Executable file
@@ -0,0 +1,42 @@
|
||||
import type {
|
||||
Provider, TransactionRequest, TransactionResponse
|
||||
} from "./provider.js";
|
||||
|
||||
/**
|
||||
* A **ContractRunner** is a generic interface which defines an object
|
||||
* capable of interacting with a Contract on the network.
|
||||
*
|
||||
* The more operations supported, the more utility it is capable of.
|
||||
*
|
||||
* The most common ContractRunners are [Providers](Provider) which enable
|
||||
* read-only access and [Signers](Signer) which enable write-access.
|
||||
*/
|
||||
export interface ContractRunner {
|
||||
/**
|
||||
* The provider used for necessary state querying operations.
|
||||
*
|
||||
* This can also point to the **ContractRunner** itself, in the
|
||||
* case of an [[AbstractProvider]].
|
||||
*/
|
||||
provider: null | Provider;
|
||||
|
||||
/**
|
||||
* Required to estimate gas.
|
||||
*/
|
||||
estimateGas?: (tx: TransactionRequest) => Promise<bigint>;
|
||||
|
||||
/**
|
||||
* Required for pure, view or static calls to contracts.
|
||||
*/
|
||||
call?: (tx: TransactionRequest) => Promise<string>;
|
||||
|
||||
/**
|
||||
* Required to support ENS names
|
||||
*/
|
||||
resolveName?: (name: string) => Promise<null | string>;
|
||||
|
||||
/**
|
||||
* Required for state mutating calls
|
||||
*/
|
||||
sendTransaction?: (tx: TransactionRequest) => Promise<TransactionResponse>;
|
||||
}
|
||||
202
dev/env/node_modules/ethers/src.ts/providers/default-provider.ts
generated
vendored
Executable file
202
dev/env/node_modules/ethers/src.ts/providers/default-provider.ts
generated
vendored
Executable file
@@ -0,0 +1,202 @@
|
||||
|
||||
import { assert } from "../utils/index.js";
|
||||
|
||||
import { AnkrProvider } from "./provider-ankr.js";
|
||||
import { AlchemyProvider } from "./provider-alchemy.js";
|
||||
//import { BlockscoutProvider } from "./provider-blockscout.js";
|
||||
import { ChainstackProvider } from "./provider-chainstack.js";
|
||||
import { CloudflareProvider } from "./provider-cloudflare.js";
|
||||
import { EtherscanProvider } from "./provider-etherscan.js";
|
||||
import { InfuraProvider } from "./provider-infura.js";
|
||||
//import { PocketProvider } from "./provider-pocket.js";
|
||||
import { QuickNodeProvider } from "./provider-quicknode.js";
|
||||
|
||||
import { FallbackProvider } from "./provider-fallback.js";
|
||||
import { JsonRpcProvider } from "./provider-jsonrpc.js";
|
||||
import { Network } from "./network.js";
|
||||
import { WebSocketProvider } from "./provider-websocket.js";
|
||||
|
||||
import type { AbstractProvider } from "./abstract-provider.js";
|
||||
import type { Networkish } from "./network.js";
|
||||
import { WebSocketLike } from "./provider-websocket.js";
|
||||
|
||||
function isWebSocketLike(value: any): value is WebSocketLike {
|
||||
return (value && typeof(value.send) === "function" &&
|
||||
typeof(value.close) === "function");
|
||||
}
|
||||
|
||||
const Testnets = "goerli kovan sepolia classicKotti optimism-goerli arbitrum-goerli matic-mumbai bnbt".split(" ");
|
||||
|
||||
/**
|
||||
* Returns a default provider for %%network%%.
|
||||
*
|
||||
* If %%network%% is a [[WebSocketLike]] or string that begins with
|
||||
* ``"ws:"`` or ``"wss:"``, a [[WebSocketProvider]] is returned backed
|
||||
* by that WebSocket or URL.
|
||||
*
|
||||
* If %%network%% is a string that begins with ``"HTTP:"`` or ``"HTTPS:"``,
|
||||
* a [[JsonRpcProvider]] is returned connected to that URL.
|
||||
*
|
||||
* Otherwise, a default provider is created backed by well-known public
|
||||
* Web3 backends (such as [[link-infura]]) using community-provided API
|
||||
* keys.
|
||||
*
|
||||
* The %%options%% allows specifying custom API keys per backend (setting
|
||||
* an API key to ``"-"`` will omit that provider) and ``options.exclusive``
|
||||
* can be set to either a backend name or and array of backend names, which
|
||||
* will whitelist **only** those backends.
|
||||
*
|
||||
* Current backend strings supported are:
|
||||
* - ``"alchemy"``
|
||||
* - ``"ankr"``
|
||||
* - ``"cloudflare"``
|
||||
* - ``"chainstack"``
|
||||
* - ``"etherscan"``
|
||||
* - ``"infura"``
|
||||
* - ``"publicPolygon"``
|
||||
* - ``"quicknode"``
|
||||
*
|
||||
* @example:
|
||||
* // Connect to a local Geth node
|
||||
* provider = getDefaultProvider("http://localhost:8545/");
|
||||
*
|
||||
* // Connect to Ethereum mainnet with any current and future
|
||||
* // third-party services available
|
||||
* provider = getDefaultProvider("mainnet");
|
||||
*
|
||||
* // Connect to Polygon, but only allow Etherscan and
|
||||
* // INFURA and use "MY_API_KEY" in calls to Etherscan.
|
||||
* provider = getDefaultProvider("matic", {
|
||||
* etherscan: "MY_API_KEY",
|
||||
* exclusive: [ "etherscan", "infura" ]
|
||||
* });
|
||||
*/
|
||||
export function getDefaultProvider(network?: string | Networkish | WebSocketLike, options?: any): AbstractProvider {
|
||||
if (options == null) { options = { }; }
|
||||
|
||||
const allowService = (name: string) => {
|
||||
if (options[name] === "-") { return false; }
|
||||
if (typeof(options.exclusive) === "string") {
|
||||
return (name === options.exclusive);
|
||||
}
|
||||
if (Array.isArray(options.exclusive)) {
|
||||
return (options.exclusive.indexOf(name) !== -1);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
if (typeof(network) === "string" && network.match(/^https?:/)) {
|
||||
return new JsonRpcProvider(network);
|
||||
}
|
||||
|
||||
if (typeof(network) === "string" && network.match(/^wss?:/) || isWebSocketLike(network)) {
|
||||
return new WebSocketProvider(network);
|
||||
}
|
||||
|
||||
// Get the network and name, if possible
|
||||
let staticNetwork: null | Network = null;
|
||||
try {
|
||||
staticNetwork = Network.from(network);
|
||||
} catch (error) { }
|
||||
|
||||
|
||||
const providers: Array<AbstractProvider> = [ ];
|
||||
|
||||
if (allowService("publicPolygon") && staticNetwork) {
|
||||
if (staticNetwork.name === "matic") {
|
||||
providers.push(new JsonRpcProvider("https:/\/polygon-rpc.com/", staticNetwork, { staticNetwork }));
|
||||
} else if (staticNetwork.name === "matic-amoy") {
|
||||
providers.push(new JsonRpcProvider("https:/\/rpc-amoy.polygon.technology/", staticNetwork, { staticNetwork }));
|
||||
}
|
||||
}
|
||||
|
||||
if (allowService("alchemy")) {
|
||||
try {
|
||||
providers.push(new AlchemyProvider(network, options.alchemy));
|
||||
} catch (error) { }
|
||||
}
|
||||
|
||||
if (allowService("ankr") && options.ankr != null) {
|
||||
try {
|
||||
providers.push(new AnkrProvider(network, options.ankr));
|
||||
} catch (error) { }
|
||||
}
|
||||
/* Temporarily remove until custom error issue is fixed
|
||||
if (allowService("blockscout")) {
|
||||
try {
|
||||
providers.push(new BlockscoutProvider(network, options.blockscout));
|
||||
} catch (error) { }
|
||||
}
|
||||
*/
|
||||
if (allowService("chainstack")) {
|
||||
try {
|
||||
providers.push(new ChainstackProvider(network, options.chainstack));
|
||||
} catch (error) { }
|
||||
}
|
||||
|
||||
if (allowService("cloudflare")) {
|
||||
try {
|
||||
providers.push(new CloudflareProvider(network));
|
||||
} catch (error) { }
|
||||
}
|
||||
|
||||
if (allowService("etherscan")) {
|
||||
try {
|
||||
providers.push(new EtherscanProvider(network, options.etherscan));
|
||||
} catch (error) { }
|
||||
}
|
||||
|
||||
if (allowService("infura")) {
|
||||
try {
|
||||
let projectId = options.infura;
|
||||
let projectSecret: undefined | string = undefined;
|
||||
if (typeof(projectId) === "object") {
|
||||
projectSecret = projectId.projectSecret;
|
||||
projectId = projectId.projectId;
|
||||
}
|
||||
providers.push(new InfuraProvider(network, projectId, projectSecret));
|
||||
} catch (error) { }
|
||||
}
|
||||
/*
|
||||
if (options.pocket !== "-") {
|
||||
try {
|
||||
let appId = options.pocket;
|
||||
let secretKey: undefined | string = undefined;
|
||||
let loadBalancer: undefined | boolean = undefined;
|
||||
if (typeof(appId) === "object") {
|
||||
loadBalancer = !!appId.loadBalancer;
|
||||
secretKey = appId.secretKey;
|
||||
appId = appId.appId;
|
||||
}
|
||||
providers.push(new PocketProvider(network, appId, secretKey, loadBalancer));
|
||||
} catch (error) { console.log(error); }
|
||||
}
|
||||
*/
|
||||
if (allowService("quicknode")) {
|
||||
try {
|
||||
let token = options.quicknode;
|
||||
providers.push(new QuickNodeProvider(network, token));
|
||||
} catch (error) { }
|
||||
}
|
||||
|
||||
assert(providers.length, "unsupported default network", "UNSUPPORTED_OPERATION", {
|
||||
operation: "getDefaultProvider"
|
||||
});
|
||||
|
||||
// No need for a FallbackProvider
|
||||
if (providers.length === 1) { return providers[0]; }
|
||||
|
||||
// We use the floor because public third-party providers can be unreliable,
|
||||
// so a low number of providers with a large quorum will fail too often
|
||||
let quorum = Math.floor(providers.length / 2);
|
||||
if (quorum > 2) { quorum = 2; }
|
||||
|
||||
// Testnets don't need as strong a security gaurantee and speed is
|
||||
// more useful during testing
|
||||
if (staticNetwork && Testnets.indexOf(staticNetwork.name) !== -1) { quorum = 1; }
|
||||
|
||||
// Provided override qorum takes priority
|
||||
if (options && options.quorum) { quorum = options.quorum; }
|
||||
|
||||
return new FallbackProvider(providers, undefined, { quorum });
|
||||
}
|
||||
606
dev/env/node_modules/ethers/src.ts/providers/ens-resolver.ts
generated
vendored
Executable file
606
dev/env/node_modules/ethers/src.ts/providers/ens-resolver.ts
generated
vendored
Executable file
@@ -0,0 +1,606 @@
|
||||
/**
|
||||
* ENS is a service which allows easy-to-remember names to map to
|
||||
* network addresses.
|
||||
*
|
||||
* @_section: api/providers/ens-resolver:ENS Resolver [about-ens-rsolver]
|
||||
*/
|
||||
|
||||
import { getAddress } from "../address/index.js";
|
||||
import { ZeroAddress } from "../constants/index.js";
|
||||
import { Contract } from "../contract/index.js";
|
||||
import { dnsEncode, namehash } from "../hash/index.js";
|
||||
import {
|
||||
hexlify, isHexString, toBeHex,
|
||||
defineProperties, encodeBase58,
|
||||
assert, assertArgument, isError,
|
||||
FetchRequest
|
||||
} from "../utils/index.js";
|
||||
|
||||
import type { FunctionFragment } from "../abi/index.js";
|
||||
|
||||
import type { BytesLike } from "../utils/index.js";
|
||||
|
||||
import type { AbstractProvider, AbstractProviderPlugin } from "./abstract-provider.js";
|
||||
import type { EnsPlugin } from "./plugins-network.js";
|
||||
import type { Provider } from "./provider.js";
|
||||
|
||||
// @TODO: This should use the fetch-data:ipfs gateway
|
||||
// Trim off the ipfs:// prefix and return the default gateway URL
|
||||
function getIpfsLink(link: string): string {
|
||||
if (link.match(/^ipfs:\/\/ipfs\//i)) {
|
||||
link = link.substring(12);
|
||||
} else if (link.match(/^ipfs:\/\//i)) {
|
||||
link = link.substring(7);
|
||||
} else {
|
||||
assertArgument(false, "unsupported IPFS format", "link", link);
|
||||
}
|
||||
|
||||
return `https:/\/gateway.ipfs.io/ipfs/${ link }`;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of data found during a steip during avatar resolution.
|
||||
*/
|
||||
export type AvatarLinkageType = "name" | "avatar" | "!avatar" | "url" | "data" | "ipfs" |
|
||||
"erc721" | "erc1155" | "!erc721-caip" | "!erc1155-caip" |
|
||||
"!owner" | "owner" | "!balance" | "balance" |
|
||||
"metadata-url-base" | "metadata-url-expanded" | "metadata-url" | "!metadata-url" |
|
||||
"!metadata" | "metadata" |
|
||||
"!imageUrl" | "imageUrl-ipfs" | "imageUrl" | "!imageUrl-ipfs";
|
||||
|
||||
/**
|
||||
* An individual record for each step during avatar resolution.
|
||||
*/
|
||||
export interface AvatarLinkage {
|
||||
/**
|
||||
* The type of linkage.
|
||||
*/
|
||||
type: AvatarLinkageType;
|
||||
|
||||
/**
|
||||
* The linkage value.
|
||||
*/
|
||||
value: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* When resolving an avatar for an ENS name, there are many
|
||||
* steps involved, fetching metadata, validating results, et cetera.
|
||||
*
|
||||
* Some applications may wish to analyse this data, or use this data
|
||||
* to diagnose promblems, so an **AvatarResult** provides details of
|
||||
* each completed step during avatar resolution.
|
||||
*/
|
||||
export interface AvatarResult {
|
||||
/**
|
||||
* How the [[url]] was arrived at, resolving the many steps required
|
||||
* for an avatar URL.
|
||||
*/
|
||||
linkage: Array<AvatarLinkage>;
|
||||
|
||||
/**
|
||||
* The avatar URL or null if the avatar was not set, or there was
|
||||
* an issue during validation (such as the address not owning the
|
||||
* avatar or a metadata error).
|
||||
*/
|
||||
url: null | string;
|
||||
};
|
||||
|
||||
/**
|
||||
* A provider plugin super-class for processing multicoin address types.
|
||||
*/
|
||||
export abstract class MulticoinProviderPlugin implements AbstractProviderPlugin {
|
||||
/**
|
||||
* The name.
|
||||
*/
|
||||
readonly name!: string;
|
||||
|
||||
/**
|
||||
* Creates a new **MulticoinProviderPluing** for %%name%%.
|
||||
*/
|
||||
constructor(name: string) {
|
||||
defineProperties<MulticoinProviderPlugin>(this, { name });
|
||||
}
|
||||
|
||||
connect(proivder: Provider): MulticoinProviderPlugin {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns ``true`` if %%coinType%% is supported by this plugin.
|
||||
*/
|
||||
supportsCoinType(coinType: number): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves to the encoded %%address%% for %%coinType%%.
|
||||
*/
|
||||
async encodeAddress(coinType: number, address: string): Promise<string> {
|
||||
throw new Error("unsupported coin");
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves to the decoded %%data%% for %%coinType%%.
|
||||
*/
|
||||
async decodeAddress(coinType: number, data: BytesLike): Promise<string> {
|
||||
throw new Error("unsupported coin");
|
||||
}
|
||||
}
|
||||
|
||||
const BasicMulticoinPluginId = "org.ethers.plugins.provider.BasicMulticoin";
|
||||
|
||||
/**
|
||||
* A **BasicMulticoinProviderPlugin** provides service for common
|
||||
* coin types, which do not require additional libraries to encode or
|
||||
* decode.
|
||||
*/
|
||||
export class BasicMulticoinProviderPlugin extends MulticoinProviderPlugin {
|
||||
/**
|
||||
* Creates a new **BasicMulticoinProviderPlugin**.
|
||||
*/
|
||||
constructor() {
|
||||
super(BasicMulticoinPluginId);
|
||||
}
|
||||
}
|
||||
|
||||
const matcherIpfs = new RegExp("^(ipfs):/\/(.*)$", "i");
|
||||
const matchers = [
|
||||
new RegExp("^(https):/\/(.*)$", "i"),
|
||||
new RegExp("^(data):(.*)$", "i"),
|
||||
matcherIpfs,
|
||||
new RegExp("^eip155:[0-9]+/(erc[0-9]+):(.*)$", "i"),
|
||||
];
|
||||
|
||||
/**
|
||||
* A connected object to a resolved ENS name resolver, which can be
|
||||
* used to query additional details.
|
||||
*/
|
||||
export class EnsResolver {
|
||||
/**
|
||||
* The connected provider.
|
||||
*/
|
||||
provider!: AbstractProvider;
|
||||
|
||||
/**
|
||||
* The address of the resolver.
|
||||
*/
|
||||
address!: string;
|
||||
|
||||
/**
|
||||
* The name this resolver was resolved against.
|
||||
*/
|
||||
name!: string;
|
||||
|
||||
// For EIP-2544 names, the ancestor that provided the resolver
|
||||
#supports2544: null | Promise<boolean>;
|
||||
|
||||
#resolver: Contract;
|
||||
|
||||
constructor(provider: AbstractProvider, address: string, name: string) {
|
||||
defineProperties<EnsResolver>(this, { provider, address, name });
|
||||
this.#supports2544 = null;
|
||||
|
||||
this.#resolver = new Contract(address, [
|
||||
"function supportsInterface(bytes4) view returns (bool)",
|
||||
"function resolve(bytes, bytes) view returns (bytes)",
|
||||
"function addr(bytes32) view returns (address)",
|
||||
"function addr(bytes32, uint) view returns (bytes)",
|
||||
"function text(bytes32, string) view returns (string)",
|
||||
"function contenthash(bytes32) view returns (bytes)",
|
||||
], provider);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves to true if the resolver supports wildcard resolution.
|
||||
*/
|
||||
async supportsWildcard(): Promise<boolean> {
|
||||
if (this.#supports2544 == null) {
|
||||
this.#supports2544 = (async () => {
|
||||
try {
|
||||
return await this.#resolver.supportsInterface("0x9061b923");
|
||||
} catch (error) {
|
||||
// Wildcard resolvers must understand supportsInterface
|
||||
// and return true.
|
||||
if (isError(error, "CALL_EXCEPTION")) { return false; }
|
||||
|
||||
// Let future attempts try again...
|
||||
this.#supports2544 = null;
|
||||
|
||||
throw error;
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
return await this.#supports2544;
|
||||
}
|
||||
|
||||
async #fetch(funcName: string, params?: Array<any>): Promise<null | any> {
|
||||
params = (params || []).slice();
|
||||
const iface = this.#resolver.interface;
|
||||
|
||||
// The first parameters is always the nodehash
|
||||
params.unshift(namehash(this.name))
|
||||
|
||||
let fragment: null | FunctionFragment = null;
|
||||
if (await this.supportsWildcard()) {
|
||||
fragment = iface.getFunction(funcName);
|
||||
assert(fragment, "missing fragment", "UNKNOWN_ERROR", {
|
||||
info: { funcName }
|
||||
});
|
||||
|
||||
params = [
|
||||
dnsEncode(this.name, 255),
|
||||
iface.encodeFunctionData(fragment, params)
|
||||
];
|
||||
|
||||
funcName = "resolve(bytes,bytes)";
|
||||
}
|
||||
|
||||
params.push({
|
||||
enableCcipRead: true
|
||||
});
|
||||
|
||||
try {
|
||||
const result = await this.#resolver[funcName](...params);
|
||||
|
||||
if (fragment) {
|
||||
return iface.decodeFunctionResult(fragment, result)[0];
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error: any) {
|
||||
if (!isError(error, "CALL_EXCEPTION")) { throw error; }
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves to the address for %%coinType%% or null if the
|
||||
* provided %%coinType%% has not been configured.
|
||||
*/
|
||||
async getAddress(coinType?: number): Promise<null | string> {
|
||||
if (coinType == null) { coinType = 60; }
|
||||
if (coinType === 60) {
|
||||
try {
|
||||
const result = await this.#fetch("addr(bytes32)");
|
||||
|
||||
// No address
|
||||
if (result == null || result === ZeroAddress) { return null; }
|
||||
|
||||
return result;
|
||||
} catch (error: any) {
|
||||
if (isError(error, "CALL_EXCEPTION")) { return null; }
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Try decoding its EVM canonical chain as an EVM chain address first
|
||||
if (coinType >= 0 && coinType < 0x80000000) {
|
||||
let ethCoinType = coinType + 0x80000000;
|
||||
|
||||
const data = await this.#fetch("addr(bytes32,uint)", [ ethCoinType ]);
|
||||
if (isHexString(data, 20)) { return getAddress(data); }
|
||||
}
|
||||
|
||||
let coinPlugin: null | MulticoinProviderPlugin = null;
|
||||
for (const plugin of this.provider.plugins) {
|
||||
if (!(plugin instanceof MulticoinProviderPlugin)) { continue; }
|
||||
if (plugin.supportsCoinType(coinType)) {
|
||||
coinPlugin = plugin;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (coinPlugin == null) { return null; }
|
||||
|
||||
// keccak256("addr(bytes32,uint256")
|
||||
const data = await this.#fetch("addr(bytes32,uint)", [ coinType ]);
|
||||
|
||||
// No address
|
||||
if (data == null || data === "0x") { return null; }
|
||||
|
||||
// Compute the address
|
||||
const address = await coinPlugin.decodeAddress(coinType, data);
|
||||
|
||||
if (address != null) { return address; }
|
||||
|
||||
assert(false, `invalid coin data`, "UNSUPPORTED_OPERATION", {
|
||||
operation: `getAddress(${ coinType })`,
|
||||
info: { coinType, data }
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves to the EIP-634 text record for %%key%%, or ``null``
|
||||
* if unconfigured.
|
||||
*/
|
||||
async getText(key: string): Promise<null | string> {
|
||||
const data = await this.#fetch("text(bytes32,string)", [ key ]);
|
||||
if (data == null || data === "0x") { return null; }
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rsolves to the content-hash or ``null`` if unconfigured.
|
||||
*/
|
||||
async getContentHash(): Promise<null | string> {
|
||||
// keccak256("contenthash()")
|
||||
const data = await this.#fetch("contenthash(bytes32)");
|
||||
|
||||
// No contenthash
|
||||
if (data == null || data === "0x") { return null; }
|
||||
|
||||
// IPFS (CID: 1, Type: 70=DAG-PB, 72=libp2p-key)
|
||||
const ipfs = data.match(/^0x(e3010170|e5010172)(([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f]*))$/);
|
||||
if (ipfs) {
|
||||
const scheme = (ipfs[1] === "e3010170") ? "ipfs": "ipns";
|
||||
const length = parseInt(ipfs[4], 16);
|
||||
if (ipfs[5].length === length * 2) {
|
||||
return `${ scheme }:/\/${ encodeBase58("0x" + ipfs[2])}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Swarm (CID: 1, Type: swarm-manifest; hash/length hard-coded to keccak256/32)
|
||||
const swarm = data.match(/^0xe40101fa011b20([0-9a-f]*)$/)
|
||||
if (swarm && swarm[1].length === 64) {
|
||||
return `bzz:/\/${ swarm[1] }`;
|
||||
}
|
||||
|
||||
assert(false, `invalid or unsupported content hash data`, "UNSUPPORTED_OPERATION", {
|
||||
operation: "getContentHash()",
|
||||
info: { data }
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves to the avatar url or ``null`` if the avatar is either
|
||||
* unconfigured or incorrectly configured (e.g. references an NFT
|
||||
* not owned by the address).
|
||||
*
|
||||
* If diagnosing issues with configurations, the [[_getAvatar]]
|
||||
* method may be useful.
|
||||
*/
|
||||
async getAvatar(): Promise<null | string> {
|
||||
const avatar = await this._getAvatar();
|
||||
return avatar.url;
|
||||
}
|
||||
|
||||
/**
|
||||
* When resolving an avatar, there are many steps involved, such
|
||||
* fetching metadata and possibly validating ownership of an
|
||||
* NFT.
|
||||
*
|
||||
* This method can be used to examine each step and the value it
|
||||
* was working from.
|
||||
*/
|
||||
async _getAvatar(): Promise<AvatarResult> {
|
||||
const linkage: Array<AvatarLinkage> = [ { type: "name", value: this.name } ];
|
||||
try {
|
||||
// test data for ricmoo.eth
|
||||
//const avatar = "eip155:1/erc721:0x265385c7f4132228A0d54EB1A9e7460b91c0cC68/29233";
|
||||
const avatar = await this.getText("avatar");
|
||||
if (avatar == null) {
|
||||
linkage.push({ type: "!avatar", value: "" });
|
||||
return { url: null, linkage };
|
||||
}
|
||||
linkage.push({ type: "avatar", value: avatar });
|
||||
|
||||
for (let i = 0; i < matchers.length; i++) {
|
||||
const match = avatar.match(matchers[i]);
|
||||
if (match == null) { continue; }
|
||||
|
||||
const scheme = match[1].toLowerCase();
|
||||
|
||||
switch (scheme) {
|
||||
case "https":
|
||||
case "data":
|
||||
linkage.push({ type: "url", value: avatar });
|
||||
return { linkage, url: avatar };
|
||||
case "ipfs": {
|
||||
const url = getIpfsLink(avatar);
|
||||
linkage.push({ type: "ipfs", value: avatar });
|
||||
linkage.push({ type: "url", value: url });
|
||||
return { linkage, url };
|
||||
}
|
||||
|
||||
case "erc721":
|
||||
case "erc1155": {
|
||||
// Depending on the ERC type, use tokenURI(uint256) or url(uint256)
|
||||
const selector = (scheme === "erc721") ? "tokenURI(uint256)": "uri(uint256)";
|
||||
linkage.push({ type: scheme, value: avatar });
|
||||
|
||||
// The owner of this name
|
||||
const owner = await this.getAddress();
|
||||
if (owner == null) {
|
||||
linkage.push({ type: "!owner", value: "" });
|
||||
return { url: null, linkage };
|
||||
}
|
||||
|
||||
const comps = (match[2] || "").split("/");
|
||||
if (comps.length !== 2) {
|
||||
linkage.push({ type: <any>`!${ scheme }caip`, value: (match[2] || "") });
|
||||
return { url: null, linkage };
|
||||
}
|
||||
|
||||
const tokenId = comps[1];
|
||||
|
||||
const contract = new Contract(comps[0], [
|
||||
// ERC-721
|
||||
"function tokenURI(uint) view returns (string)",
|
||||
"function ownerOf(uint) view returns (address)",
|
||||
|
||||
// ERC-1155
|
||||
"function uri(uint) view returns (string)",
|
||||
"function balanceOf(address, uint256) view returns (uint)"
|
||||
], this.provider);
|
||||
|
||||
// Check that this account owns the token
|
||||
if (scheme === "erc721") {
|
||||
const tokenOwner = await contract.ownerOf(tokenId);
|
||||
|
||||
if (owner !== tokenOwner) {
|
||||
linkage.push({ type: "!owner", value: tokenOwner });
|
||||
return { url: null, linkage };
|
||||
}
|
||||
linkage.push({ type: "owner", value: tokenOwner });
|
||||
|
||||
} else if (scheme === "erc1155") {
|
||||
const balance = await contract.balanceOf(owner, tokenId);
|
||||
if (!balance) {
|
||||
linkage.push({ type: "!balance", value: "0" });
|
||||
return { url: null, linkage };
|
||||
}
|
||||
linkage.push({ type: "balance", value: balance.toString() });
|
||||
}
|
||||
|
||||
// Call the token contract for the metadata URL
|
||||
let metadataUrl = await contract[selector](tokenId);
|
||||
if (metadataUrl == null || metadataUrl === "0x") {
|
||||
linkage.push({ type: "!metadata-url", value: "" });
|
||||
return { url: null, linkage };
|
||||
}
|
||||
|
||||
linkage.push({ type: "metadata-url-base", value: metadataUrl });
|
||||
|
||||
// ERC-1155 allows a generic {id} in the URL
|
||||
if (scheme === "erc1155") {
|
||||
metadataUrl = metadataUrl.replace("{id}", toBeHex(tokenId, 32).substring(2));
|
||||
linkage.push({ type: "metadata-url-expanded", value: metadataUrl });
|
||||
}
|
||||
|
||||
// Transform IPFS metadata links
|
||||
if (metadataUrl.match(/^ipfs:/i)) {
|
||||
metadataUrl = getIpfsLink(metadataUrl);
|
||||
}
|
||||
linkage.push({ type: "metadata-url", value: metadataUrl });
|
||||
|
||||
// Get the token metadata
|
||||
let metadata: any = { };
|
||||
const response = await (new FetchRequest(metadataUrl)).send();
|
||||
response.assertOk();
|
||||
|
||||
try {
|
||||
metadata = response.bodyJson;
|
||||
} catch (error) {
|
||||
try {
|
||||
linkage.push({ type: "!metadata", value: response.bodyText });
|
||||
} catch (error) {
|
||||
const bytes = response.body;
|
||||
if (bytes) {
|
||||
linkage.push({ type: "!metadata", value: hexlify(bytes) });
|
||||
}
|
||||
return { url: null, linkage };
|
||||
}
|
||||
return { url: null, linkage };
|
||||
}
|
||||
|
||||
if (!metadata) {
|
||||
linkage.push({ type: "!metadata", value: "" });
|
||||
return { url: null, linkage };
|
||||
}
|
||||
|
||||
linkage.push({ type: "metadata", value: JSON.stringify(metadata) });
|
||||
|
||||
// Pull the image URL out
|
||||
let imageUrl = metadata.image;
|
||||
if (typeof(imageUrl) !== "string") {
|
||||
linkage.push({ type: "!imageUrl", value: "" });
|
||||
return { url: null, linkage };
|
||||
}
|
||||
|
||||
if (imageUrl.match(/^(https:\/\/|data:)/i)) {
|
||||
// Allow
|
||||
} else {
|
||||
// Transform IPFS link to gateway
|
||||
const ipfs = imageUrl.match(matcherIpfs);
|
||||
if (ipfs == null) {
|
||||
linkage.push({ type: "!imageUrl-ipfs", value: imageUrl });
|
||||
return { url: null, linkage };
|
||||
}
|
||||
|
||||
linkage.push({ type: "imageUrl-ipfs", value: imageUrl });
|
||||
imageUrl = getIpfsLink(imageUrl);
|
||||
}
|
||||
|
||||
linkage.push({ type: "url", value: imageUrl });
|
||||
|
||||
return { linkage, url: imageUrl };
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) { }
|
||||
|
||||
return { linkage, url: null };
|
||||
}
|
||||
|
||||
static async getEnsAddress(provider: Provider): Promise<string> {
|
||||
const network = await provider.getNetwork();
|
||||
|
||||
const ensPlugin = network.getPlugin<EnsPlugin>("org.ethers.plugins.network.Ens");
|
||||
|
||||
// No ENS...
|
||||
assert(ensPlugin, "network does not support ENS", "UNSUPPORTED_OPERATION", {
|
||||
operation: "getEnsAddress", info: { network } });
|
||||
|
||||
return ensPlugin.address;
|
||||
}
|
||||
|
||||
static async #getResolver(provider: Provider, name: string): Promise<null | string> {
|
||||
const ensAddr = await EnsResolver.getEnsAddress(provider);
|
||||
|
||||
try {
|
||||
const contract = new Contract(ensAddr, [
|
||||
"function resolver(bytes32) view returns (address)"
|
||||
], provider);
|
||||
|
||||
const addr = await contract.resolver(namehash(name), {
|
||||
enableCcipRead: true
|
||||
});
|
||||
|
||||
if (addr === ZeroAddress) { return null; }
|
||||
return addr;
|
||||
|
||||
} catch (error) {
|
||||
// ENS registry cannot throw errors on resolver(bytes32),
|
||||
// so probably a link error
|
||||
throw error;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve to the ENS resolver for %%name%% using %%provider%% or
|
||||
* ``null`` if unconfigured.
|
||||
*/
|
||||
static async fromName(provider: AbstractProvider, name: string): Promise<null | EnsResolver> {
|
||||
|
||||
let currentName = name;
|
||||
while (true) {
|
||||
if (currentName === "" || currentName === ".") { return null; }
|
||||
|
||||
// Optimization since the eth node cannot change and does
|
||||
// not have a wildcard resolver
|
||||
if (name !== "eth" && currentName === "eth") { return null; }
|
||||
|
||||
// Check the current node for a resolver
|
||||
const addr = await EnsResolver.#getResolver(provider, currentName);
|
||||
|
||||
// Found a resolver!
|
||||
if (addr != null) {
|
||||
const resolver = new EnsResolver(provider, addr, name);
|
||||
|
||||
// Legacy resolver found, using EIP-2544 so it isn't safe to use
|
||||
if (currentName !== name && !(await resolver.supportsWildcard())) { return null; }
|
||||
|
||||
return resolver;
|
||||
}
|
||||
|
||||
// Get the parent node
|
||||
currentName = currentName.split(".").slice(1).join(".");
|
||||
}
|
||||
}
|
||||
}
|
||||
335
dev/env/node_modules/ethers/src.ts/providers/format.ts
generated
vendored
Executable file
335
dev/env/node_modules/ethers/src.ts/providers/format.ts
generated
vendored
Executable file
@@ -0,0 +1,335 @@
|
||||
/**
|
||||
* @_ignore
|
||||
*/
|
||||
import { getAddress, getCreateAddress } from "../address/index.js";
|
||||
import { Signature } from "../crypto/index.js"
|
||||
import { accessListify } from "../transaction/index.js";
|
||||
import {
|
||||
getBigInt, getNumber, hexlify, isHexString, zeroPadValue,
|
||||
assert, assertArgument
|
||||
} from "../utils/index.js";
|
||||
|
||||
import type { SignatureLike } from "../crypto/index.js"
|
||||
import type {
|
||||
BlockParams, LogParams,
|
||||
TransactionReceiptParams, TransactionResponseParams,
|
||||
} from "./formatting.js";
|
||||
|
||||
|
||||
const BN_0 = BigInt(0);
|
||||
|
||||
export type FormatFunc = (value: any) => any;
|
||||
|
||||
export function allowNull(format: FormatFunc, nullValue?: any): FormatFunc {
|
||||
return (function(value: any) {
|
||||
if (value == null) { return nullValue; }
|
||||
return format(value);
|
||||
});
|
||||
}
|
||||
|
||||
export function arrayOf(format: FormatFunc, allowNull?: boolean): FormatFunc {
|
||||
return ((array: any) => {
|
||||
if (allowNull && array == null) { return null; }
|
||||
if (!Array.isArray(array)) { throw new Error("not an array"); }
|
||||
return array.map((i) => format(i));
|
||||
});
|
||||
}
|
||||
|
||||
// Requires an object which matches a fleet of other formatters
|
||||
// Any FormatFunc may return `undefined` to have the value omitted
|
||||
// from the result object. Calls preserve `this`.
|
||||
export function object(format: Record<string, FormatFunc>, altNames?: Record<string, Array<string>>): FormatFunc {
|
||||
return ((value: any) => {
|
||||
const result: any = { };
|
||||
for (const key in format) {
|
||||
let srcKey = key;
|
||||
if (altNames && key in altNames && !(srcKey in value)) {
|
||||
for (const altKey of altNames[key]) {
|
||||
if (altKey in value) {
|
||||
srcKey = altKey;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const nv = format[key](value[srcKey]);
|
||||
if (nv !== undefined) { result[key] = nv; }
|
||||
} catch (error) {
|
||||
const message = (error instanceof Error) ? error.message: "not-an-error";
|
||||
assert(false, `invalid value for value.${ key } (${ message })`, "BAD_DATA", { value })
|
||||
}
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
export function formatBoolean(value: any): boolean {
|
||||
switch (value) {
|
||||
case true: case "true":
|
||||
return true;
|
||||
case false: case "false":
|
||||
return false;
|
||||
}
|
||||
assertArgument(false, `invalid boolean; ${ JSON.stringify(value) }`, "value", value);
|
||||
}
|
||||
|
||||
export function formatData(value: string): string {
|
||||
assertArgument(isHexString(value, true), "invalid data", "value", value);
|
||||
return value;
|
||||
}
|
||||
|
||||
export function formatHash(value: any): string {
|
||||
assertArgument(isHexString(value, 32), "invalid hash", "value", value);
|
||||
return value;
|
||||
}
|
||||
|
||||
export function formatUint256(value: any): string {
|
||||
if (!isHexString(value)) {
|
||||
throw new Error("invalid uint256");
|
||||
}
|
||||
return zeroPadValue(value, 32);
|
||||
}
|
||||
|
||||
const _formatLog = object({
|
||||
address: getAddress,
|
||||
blockHash: formatHash,
|
||||
blockNumber: getNumber,
|
||||
data: formatData,
|
||||
index: getNumber,
|
||||
removed: allowNull(formatBoolean, false),
|
||||
topics: arrayOf(formatHash),
|
||||
transactionHash: formatHash,
|
||||
transactionIndex: getNumber,
|
||||
}, {
|
||||
index: [ "logIndex" ]
|
||||
});
|
||||
|
||||
export function formatLog(value: any): LogParams {
|
||||
return _formatLog(value);
|
||||
}
|
||||
|
||||
const _formatBlock = object({
|
||||
hash: allowNull(formatHash),
|
||||
parentHash: formatHash,
|
||||
parentBeaconBlockRoot: allowNull(formatHash, null),
|
||||
|
||||
number: getNumber,
|
||||
|
||||
timestamp: getNumber,
|
||||
nonce: allowNull(formatData),
|
||||
difficulty: getBigInt,
|
||||
|
||||
gasLimit: getBigInt,
|
||||
gasUsed: getBigInt,
|
||||
|
||||
stateRoot: allowNull(formatHash, null),
|
||||
receiptsRoot: allowNull(formatHash, null),
|
||||
|
||||
blobGasUsed: allowNull(getBigInt, null),
|
||||
excessBlobGas: allowNull(getBigInt, null),
|
||||
|
||||
miner: allowNull(getAddress),
|
||||
prevRandao: allowNull(formatHash, null),
|
||||
extraData: formatData,
|
||||
|
||||
baseFeePerGas: allowNull(getBigInt)
|
||||
}, {
|
||||
prevRandao: [ "mixHash" ]
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
const _formatReceiptLog = object({
|
||||
transactionIndex: getNumber,
|
||||
blockNumber: getNumber,
|
||||
transactionHash: formatHash,
|
||||
address: getAddress,
|
||||
topics: arrayOf(formatHash),
|
||||
data: formatData,
|
||||
index: getNumber,
|
||||
blockHash: formatHash,
|
||||
}, {
|
||||
index: [ "logIndex" ]
|
||||
});
|
||||
|
||||
export function formatReceiptLog(value: any): LogParams {
|
||||
return _formatReceiptLog(value);
|
||||
}
|
||||
|
||||
const _formatTransactionReceipt = object({
|
||||
to: allowNull(getAddress, null),
|
||||
from: allowNull(getAddress, null),
|
||||
contractAddress: allowNull(getAddress, null),
|
||||
// should be allowNull(hash), but broken-EIP-658 support is handled in receipt
|
||||
index: getNumber,
|
||||
root: allowNull(hexlify),
|
||||
gasUsed: getBigInt,
|
||||
blobGasUsed: allowNull(getBigInt, null),
|
||||
logsBloom: allowNull(formatData),
|
||||
blockHash: formatHash,
|
||||
hash: formatHash,
|
||||
logs: arrayOf(formatReceiptLog),
|
||||
blockNumber: getNumber,
|
||||
//confirmations: allowNull(getNumber, null),
|
||||
cumulativeGasUsed: getBigInt,
|
||||
effectiveGasPrice: allowNull(getBigInt),
|
||||
blobGasPrice: allowNull(getBigInt, null),
|
||||
status: allowNull(getNumber),
|
||||
type: allowNull(getNumber, 0)
|
||||
}, {
|
||||
effectiveGasPrice: [ "gasPrice" ],
|
||||
hash: [ "transactionHash" ],
|
||||
index: [ "transactionIndex" ],
|
||||
});
|
||||
|
||||
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
|
||||
if (value.to && getBigInt(value.to) === BN_0) {
|
||||
value.to = "0x0000000000000000000000000000000000000000";
|
||||
}
|
||||
|
||||
const result = object({
|
||||
hash: formatHash,
|
||||
|
||||
// Some nodes do not return this, usually test nodes (like Ganache)
|
||||
index: allowNull(getNumber, undefined),
|
||||
|
||||
type: (value: any) => {
|
||||
if (value === "0x" || value == null) { return 0; }
|
||||
return getNumber(value);
|
||||
},
|
||||
accessList: allowNull(accessListify, null),
|
||||
blobVersionedHashes: allowNull(arrayOf(formatHash, true), null),
|
||||
|
||||
authorizationList: allowNull(arrayOf((v: any) => {
|
||||
let sig: SignatureLike;
|
||||
if (v.signature) {
|
||||
sig = v.signature;
|
||||
|
||||
} else {
|
||||
let yParity = v.yParity;
|
||||
if (yParity === "0x1b") {
|
||||
yParity = 0;
|
||||
} else if (yParity === "0x1c") {
|
||||
yParity = 1;
|
||||
}
|
||||
sig = Object.assign({ }, v, { yParity });
|
||||
}
|
||||
|
||||
return {
|
||||
address: getAddress(v.address),
|
||||
chainId: getBigInt(v.chainId),
|
||||
nonce: getBigInt(v.nonce),
|
||||
signature: Signature.from(sig)
|
||||
};
|
||||
}, false), null),
|
||||
|
||||
blockHash: allowNull(formatHash, null),
|
||||
blockNumber: allowNull(getNumber, null),
|
||||
transactionIndex: allowNull(getNumber, null),
|
||||
|
||||
from: getAddress,
|
||||
|
||||
// either (gasPrice) or (maxPriorityFeePerGas + maxFeePerGas) must be set
|
||||
gasPrice: allowNull(getBigInt),
|
||||
maxPriorityFeePerGas: allowNull(getBigInt),
|
||||
maxFeePerGas: allowNull(getBigInt),
|
||||
maxFeePerBlobGas: allowNull(getBigInt, null),
|
||||
|
||||
gasLimit: getBigInt,
|
||||
to: allowNull(getAddress, null),
|
||||
value: getBigInt,
|
||||
nonce: getNumber,
|
||||
data: formatData,
|
||||
|
||||
creates: allowNull(getAddress, null),
|
||||
|
||||
chainId: allowNull(getBigInt, null)
|
||||
}, {
|
||||
data: [ "input" ],
|
||||
gasLimit: [ "gas" ],
|
||||
index: [ "transactionIndex" ]
|
||||
})(value);
|
||||
|
||||
// If to and creates are empty, populate the creates from the value
|
||||
if (result.to == null && result.creates == null) {
|
||||
result.creates = getCreateAddress(result);
|
||||
}
|
||||
|
||||
// @TODO: Check fee data
|
||||
|
||||
// Add an access list to supported transaction types
|
||||
if ((value.type === 1 || value.type === 2) && value.accessList == null) {
|
||||
result.accessList = [ ];
|
||||
}
|
||||
|
||||
// Compute the signature
|
||||
if (value.signature) {
|
||||
result.signature = Signature.from(value.signature);
|
||||
} else {
|
||||
result.signature = Signature.from(value);
|
||||
}
|
||||
|
||||
// Some backends omit ChainId on legacy transactions, but we can compute it
|
||||
if (result.chainId == null) {
|
||||
const chainId = result.signature.legacyChainId;
|
||||
if (chainId != null) { result.chainId = chainId; }
|
||||
}
|
||||
|
||||
|
||||
// @TODO: check chainID
|
||||
/*
|
||||
if (value.chainId != null) {
|
||||
let chainId = value.chainId;
|
||||
|
||||
if (isHexString(chainId)) {
|
||||
chainId = BigNumber.from(chainId).toNumber();
|
||||
}
|
||||
|
||||
result.chainId = chainId;
|
||||
|
||||
} else {
|
||||
let chainId = value.networkId;
|
||||
|
||||
// geth-etc returns chainId
|
||||
if (chainId == null && result.v == null) {
|
||||
chainId = value.chainId;
|
||||
}
|
||||
|
||||
if (isHexString(chainId)) {
|
||||
chainId = BigNumber.from(chainId).toNumber();
|
||||
}
|
||||
|
||||
if (typeof(chainId) !== "number" && result.v != null) {
|
||||
chainId = (result.v - 35) / 2;
|
||||
if (chainId < 0) { chainId = 0; }
|
||||
chainId = parseInt(chainId);
|
||||
}
|
||||
|
||||
if (typeof(chainId) !== "number") { chainId = 0; }
|
||||
|
||||
result.chainId = chainId;
|
||||
}
|
||||
*/
|
||||
|
||||
// 0x0000... should actually be null
|
||||
if (result.blockHash && getBigInt(result.blockHash) === BN_0) {
|
||||
result.blockHash = null;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
418
dev/env/node_modules/ethers/src.ts/providers/formatting.ts
generated
vendored
Executable file
418
dev/env/node_modules/ethers/src.ts/providers/formatting.ts
generated
vendored
Executable file
@@ -0,0 +1,418 @@
|
||||
/**
|
||||
* About provider formatting?
|
||||
*
|
||||
* @_section: api/providers/formatting:Formatting [provider-formatting]
|
||||
*/
|
||||
|
||||
import type { Signature } from "../crypto/index.js";
|
||||
import type { Authorization, AccessList } from "../transaction/index.js";
|
||||
|
||||
|
||||
//////////////////////
|
||||
// Block
|
||||
|
||||
/**
|
||||
* a **BlockParams** encodes the minimal required properties for a
|
||||
* formatted block.
|
||||
*/
|
||||
export interface BlockParams {
|
||||
/**
|
||||
* The block hash.
|
||||
*/
|
||||
hash?: null | string;
|
||||
|
||||
/**
|
||||
* The block number.
|
||||
*/
|
||||
number: number;
|
||||
|
||||
/**
|
||||
* The timestamp for this block, which is the number of seconds
|
||||
* since epoch that this block was included.
|
||||
*/
|
||||
timestamp: number;
|
||||
|
||||
/**
|
||||
* The hash of the previous block in the blockchain. The genesis block
|
||||
* has the parentHash of the [[ZeroHash]].
|
||||
*/
|
||||
parentHash: string;
|
||||
|
||||
/**
|
||||
* The hash tree root of the parent beacon block for the given
|
||||
* execution block. See [[link-eip-4788]].
|
||||
*/
|
||||
parentBeaconBlockRoot?: null | string;
|
||||
|
||||
/**
|
||||
* A random sequence provided during the mining process for
|
||||
* proof-of-work networks.
|
||||
*/
|
||||
nonce: string;
|
||||
|
||||
/**
|
||||
* For proof-of-work networks, the difficulty target is used to
|
||||
* adjust the difficulty in mining to ensure an expected block rate.
|
||||
*/
|
||||
difficulty: bigint;
|
||||
|
||||
/**
|
||||
* The maximum amount of gas a block can consume.
|
||||
*/
|
||||
gasLimit: bigint;
|
||||
|
||||
/**
|
||||
* The amount of gas a block consumed.
|
||||
*/
|
||||
gasUsed: bigint;
|
||||
|
||||
/**
|
||||
* The total amount of BLOb gas consumed by transactions within
|
||||
* the block. See [[link-eip4844].
|
||||
*/
|
||||
blobGasUsed?: null | bigint;
|
||||
|
||||
/**
|
||||
* The running total of BLOb gas consumed in excess of the target
|
||||
* prior to the block. See [[link-eip-4844]].
|
||||
*/
|
||||
excessBlobGas?: null | bigint;
|
||||
|
||||
/**
|
||||
* The miner (or author) of a block.
|
||||
*/
|
||||
miner: string;
|
||||
|
||||
/**
|
||||
* The latest RANDAO mix of the post beacon state of
|
||||
* the previous block.
|
||||
*/
|
||||
prevRandao?: null | string;
|
||||
|
||||
/**
|
||||
* Additional data the miner choose to include.
|
||||
*/
|
||||
extraData: string;
|
||||
|
||||
/**
|
||||
* The protocol-defined base fee per gas in an [[link-eip-1559]]
|
||||
* block.
|
||||
*/
|
||||
baseFeePerGas: null | bigint;
|
||||
|
||||
/**
|
||||
* The root hash for the global state after applying changes
|
||||
* in this block.
|
||||
*/
|
||||
stateRoot?: null | string;
|
||||
|
||||
/**
|
||||
* The hash of the transaction receipts trie.
|
||||
*/
|
||||
receiptsRoot?: null | string;
|
||||
|
||||
/**
|
||||
* The list of transactions in the block.
|
||||
*/
|
||||
transactions: ReadonlyArray<string | TransactionResponseParams>;
|
||||
};
|
||||
|
||||
|
||||
//////////////////////
|
||||
// Log
|
||||
|
||||
/**
|
||||
* a **LogParams** encodes the minimal required properties for a
|
||||
* formatted log.
|
||||
*/
|
||||
export interface LogParams {
|
||||
/**
|
||||
* The transaction hash for the transaxction the log occurred in.
|
||||
*/
|
||||
transactionHash: string;
|
||||
|
||||
/**
|
||||
* The block hash of the block that included the transaction for this
|
||||
* log.
|
||||
*/
|
||||
blockHash: string;
|
||||
|
||||
/**
|
||||
* The block number of the block that included the transaction for this
|
||||
* log.
|
||||
*/
|
||||
blockNumber: number;
|
||||
|
||||
/**
|
||||
* Whether this log was removed due to the transaction it was included
|
||||
* in being removed dur to an orphaned block.
|
||||
*/
|
||||
removed: boolean;
|
||||
|
||||
/**
|
||||
* The address of the contract that emitted this log.
|
||||
*/
|
||||
address: string;
|
||||
|
||||
/**
|
||||
* The data emitted with this log.
|
||||
*/
|
||||
data: string;
|
||||
|
||||
/**
|
||||
* The topics emitted with this log.
|
||||
*/
|
||||
topics: ReadonlyArray<string>;
|
||||
|
||||
/**
|
||||
* The index of this log.
|
||||
*/
|
||||
index: number;
|
||||
|
||||
/**
|
||||
* The transaction index of this log.
|
||||
*/
|
||||
transactionIndex: number;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////
|
||||
// Transaction Receipt
|
||||
|
||||
/**
|
||||
* a **TransactionReceiptParams** encodes the minimal required properties
|
||||
* for a formatted transaction receipt.
|
||||
*/
|
||||
export interface TransactionReceiptParams {
|
||||
/**
|
||||
* The target of the transaction. If null, the transaction was trying
|
||||
* to deploy a transaction with the ``data`` as the initi=code.
|
||||
*/
|
||||
to: null | string;
|
||||
|
||||
/**
|
||||
* The sender of the transaction.
|
||||
*/
|
||||
from: string;
|
||||
|
||||
/**
|
||||
* If the transaction was directly deploying a contract, the [[to]]
|
||||
* will be null, the ``data`` will be initcode and if successful, this
|
||||
* will be the address of the contract deployed.
|
||||
*/
|
||||
contractAddress: null | string;
|
||||
|
||||
/**
|
||||
* The transaction hash.
|
||||
*/
|
||||
hash: string;
|
||||
|
||||
/**
|
||||
* The transaction index.
|
||||
*/
|
||||
index: number;
|
||||
|
||||
/**
|
||||
* The block hash of the block that included this transaction.
|
||||
*/
|
||||
blockHash: string;
|
||||
|
||||
/**
|
||||
* The block number of the block that included this transaction.
|
||||
*/
|
||||
blockNumber: number;
|
||||
|
||||
/**
|
||||
* The bloom filter for the logs emitted during execution of this
|
||||
* transaction.
|
||||
*/
|
||||
logsBloom: string;
|
||||
|
||||
/**
|
||||
* The logs emitted during the execution of this transaction.
|
||||
*/
|
||||
logs: ReadonlyArray<LogParams>;
|
||||
|
||||
/**
|
||||
* The amount of gas consumed executing this transaction.
|
||||
*/
|
||||
gasUsed: bigint;
|
||||
|
||||
/**
|
||||
* The amount of BLOb gas used. See [[link-eip-4844]].
|
||||
*/
|
||||
blobGasUsed?: null | bigint;
|
||||
|
||||
/**
|
||||
* The total amount of gas consumed during the entire block up to
|
||||
* and including this transaction.
|
||||
*/
|
||||
cumulativeGasUsed: bigint;
|
||||
|
||||
/**
|
||||
* The actual gas price per gas charged for this transaction.
|
||||
*/
|
||||
gasPrice?: null | bigint;
|
||||
|
||||
/**
|
||||
* The actual BLOb gas price that was charged. See [[link-eip-4844]].
|
||||
*/
|
||||
blobGasPrice?: null | bigint;
|
||||
|
||||
/**
|
||||
* The actual gas price per gas charged for this transaction.
|
||||
*/
|
||||
effectiveGasPrice?: null | bigint;
|
||||
|
||||
/**
|
||||
* The [[link-eip-2718]] envelope type.
|
||||
*/
|
||||
type: number;
|
||||
//byzantium: boolean;
|
||||
|
||||
/**
|
||||
* The status of the transaction execution. If ``1`` then the
|
||||
* the transaction returned success, if ``0`` then the transaction
|
||||
* was reverted. For pre-byzantium blocks, this is usually null, but
|
||||
* some nodes may have backfilled this data.
|
||||
*/
|
||||
status: null | number;
|
||||
|
||||
/**
|
||||
* The root of this transaction in a pre-bazatium block. In
|
||||
* post-byzantium blocks this is null.
|
||||
*/
|
||||
root: null | string;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
export interface LegacyTransactionReceipt {
|
||||
byzantium: false;
|
||||
status: null;
|
||||
root: string;
|
||||
}
|
||||
|
||||
export interface ByzantiumTransactionReceipt {
|
||||
byzantium: true;
|
||||
status: number;
|
||||
root: null;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
//////////////////////
|
||||
// Transaction Response
|
||||
|
||||
/**
|
||||
* a **TransactionResponseParams** encodes the minimal required properties
|
||||
* for a formatted transaction response.
|
||||
*/
|
||||
export interface TransactionResponseParams {
|
||||
/**
|
||||
* The block number of the block that included this transaction.
|
||||
*/
|
||||
blockNumber: null | number;
|
||||
|
||||
/**
|
||||
* The block hash of the block that included this transaction.
|
||||
*/
|
||||
blockHash: null | string;
|
||||
|
||||
/**
|
||||
* The transaction hash.
|
||||
*/
|
||||
hash: string;
|
||||
|
||||
/**
|
||||
* The transaction index.
|
||||
*/
|
||||
index: number;
|
||||
|
||||
/**
|
||||
* The [[link-eip-2718]] transaction type.
|
||||
*/
|
||||
type: number;
|
||||
|
||||
/**
|
||||
* The target of the transaction. If ``null``, the ``data`` is initcode
|
||||
* and this transaction is a deployment transaction.
|
||||
*/
|
||||
to: null | string;
|
||||
|
||||
/**
|
||||
* The sender of the transaction.
|
||||
*/
|
||||
from: string;
|
||||
|
||||
/**
|
||||
* The nonce of the transaction, used for replay protection.
|
||||
*/
|
||||
nonce: number;
|
||||
|
||||
/**
|
||||
* The maximum amount of gas this transaction is authorized to consume.
|
||||
*/
|
||||
gasLimit: bigint;
|
||||
|
||||
/**
|
||||
* For legacy transactions, this is the gas price per gas to pay.
|
||||
*/
|
||||
gasPrice: bigint;
|
||||
|
||||
/**
|
||||
* For [[link-eip-1559]] transactions, this is the maximum priority
|
||||
* fee to allow a producer to claim.
|
||||
*/
|
||||
maxPriorityFeePerGas: null | bigint;
|
||||
|
||||
/**
|
||||
* For [[link-eip-1559]] transactions, this is the maximum fee that
|
||||
* will be paid.
|
||||
*/
|
||||
maxFeePerGas: null | bigint;
|
||||
|
||||
/**
|
||||
* For [[link-eip-4844]] transactions, this is the maximum fee that
|
||||
* will be paid per BLOb.
|
||||
*/
|
||||
maxFeePerBlobGas?: null | bigint;
|
||||
|
||||
/**
|
||||
* The transaction data.
|
||||
*/
|
||||
data: string;
|
||||
|
||||
/**
|
||||
* The transaction value (in wei).
|
||||
*/
|
||||
value: bigint;
|
||||
|
||||
/**
|
||||
* The chain ID this transaction is valid on.
|
||||
*/
|
||||
chainId: bigint;
|
||||
|
||||
/**
|
||||
* The signature of the transaction.
|
||||
*/
|
||||
signature: Signature;
|
||||
|
||||
/**
|
||||
* The transaction access list.
|
||||
*/
|
||||
accessList: null | AccessList;
|
||||
|
||||
/**
|
||||
* The [[link-eip-4844]] BLOb versioned hashes.
|
||||
*/
|
||||
blobVersionedHashes?: null | Array<string>; // @TODO: drop the "?"? (v7)
|
||||
|
||||
/**
|
||||
* The [[link-eip-7702]] authorizations (if any).
|
||||
*/
|
||||
authorizationList: null | Array<Authorization>;
|
||||
};
|
||||
|
||||
|
||||
135
dev/env/node_modules/ethers/src.ts/providers/index.ts
generated
vendored
Executable file
135
dev/env/node_modules/ethers/src.ts/providers/index.ts
generated
vendored
Executable file
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* A **Provider** provides a connection to the blockchain, whch can be
|
||||
* used to query its current state, simulate execution and send transactions
|
||||
* to update the state.
|
||||
*
|
||||
* It is one of the most fundamental components of interacting with a
|
||||
* blockchain application, and there are many ways to connect, such as over
|
||||
* HTTP, WebSockets or injected providers such as [MetaMask](link-metamask).
|
||||
*
|
||||
* @_section: api/providers:Providers [about-providers]
|
||||
*/
|
||||
|
||||
|
||||
export {
|
||||
AbstractProvider, UnmanagedSubscriber
|
||||
} from "./abstract-provider.js";
|
||||
|
||||
export {
|
||||
AbstractSigner,
|
||||
VoidSigner,
|
||||
} from "./abstract-signer.js";
|
||||
|
||||
export {
|
||||
showThrottleMessage
|
||||
} from "./community.js";
|
||||
|
||||
export { getDefaultProvider } from "./default-provider.js";
|
||||
|
||||
export {
|
||||
EnsResolver,
|
||||
MulticoinProviderPlugin
|
||||
} from "./ens-resolver.js";
|
||||
|
||||
export { Network } from "./network.js";
|
||||
|
||||
export { NonceManager } from "./signer-noncemanager.js";
|
||||
|
||||
export {
|
||||
NetworkPlugin,
|
||||
GasCostPlugin,
|
||||
EnsPlugin,
|
||||
FeeDataNetworkPlugin,
|
||||
FetchUrlFeeDataNetworkPlugin,
|
||||
} from "./plugins-network.js";
|
||||
|
||||
export {
|
||||
Block,
|
||||
FeeData,
|
||||
Log,
|
||||
TransactionReceipt,
|
||||
TransactionResponse,
|
||||
|
||||
copyRequest,
|
||||
//resolveTransactionRequest,
|
||||
} from "./provider.js";
|
||||
|
||||
export { FallbackProvider } from "./provider-fallback.js";
|
||||
export { JsonRpcApiProvider, JsonRpcProvider, JsonRpcSigner } from "./provider-jsonrpc.js"
|
||||
|
||||
export { BrowserProvider } from "./provider-browser.js";
|
||||
|
||||
export { AlchemyProvider } from "./provider-alchemy.js";
|
||||
export { BlockscoutProvider } from "./provider-blockscout.js";
|
||||
export { AnkrProvider } from "./provider-ankr.js";
|
||||
export { CloudflareProvider } from "./provider-cloudflare.js";
|
||||
export { ChainstackProvider } from "./provider-chainstack.js";
|
||||
export { EtherscanProvider, EtherscanPlugin } from "./provider-etherscan.js";
|
||||
export { InfuraProvider, InfuraWebSocketProvider } from "./provider-infura.js";
|
||||
export { PocketProvider } from "./provider-pocket.js";
|
||||
export { QuickNodeProvider } from "./provider-quicknode.js";
|
||||
|
||||
import { IpcSocketProvider } from "./provider-ipcsocket.js"; /*-browser*/
|
||||
export { IpcSocketProvider };
|
||||
export { SocketProvider } from "./provider-socket.js";
|
||||
export { WebSocketProvider } from "./provider-websocket.js";
|
||||
|
||||
export {
|
||||
SocketSubscriber, SocketBlockSubscriber, SocketPendingSubscriber,
|
||||
SocketEventSubscriber
|
||||
} from "./provider-socket.js";
|
||||
|
||||
export type {
|
||||
AbstractProviderOptions, Subscription, Subscriber,
|
||||
AbstractProviderPlugin,
|
||||
PerformActionFilter, PerformActionTransaction, PerformActionRequest,
|
||||
} from "./abstract-provider.js"
|
||||
|
||||
export type { ContractRunner } from "./contracts.js";
|
||||
|
||||
export type {
|
||||
BlockParams, LogParams, TransactionReceiptParams,
|
||||
TransactionResponseParams,
|
||||
} from "./formatting.js";
|
||||
|
||||
export type {
|
||||
CommunityResourcable
|
||||
} from "./community.js";
|
||||
|
||||
/*
|
||||
export type {
|
||||
AvatarLinkageType, AvatarLinkage, AvatarResult
|
||||
} from "./ens-resolver.js";
|
||||
*/
|
||||
export type { Networkish } from "./network.js";
|
||||
|
||||
export type { GasCostParameters } from "./plugins-network.js";
|
||||
|
||||
export type {
|
||||
BlockTag,
|
||||
TransactionRequest, PreparedTransactionRequest,
|
||||
EventFilter, Filter, FilterByBlockHash, OrphanFilter, ProviderEvent,
|
||||
TopicFilter,
|
||||
Provider,
|
||||
MinedBlock, MinedTransactionResponse
|
||||
} from "./provider.js";
|
||||
|
||||
export type {
|
||||
BrowserDiscoverOptions, BrowserProviderOptions, DebugEventBrowserProvider,
|
||||
Eip1193Provider, Eip6963ProviderInfo
|
||||
} from "./provider-browser.js";
|
||||
|
||||
export type { FallbackProviderOptions } from "./provider-fallback.js";
|
||||
|
||||
export type {
|
||||
JsonRpcPayload, JsonRpcResult, JsonRpcError,
|
||||
JsonRpcApiProviderOptions,
|
||||
JsonRpcTransactionRequest,
|
||||
} from "./provider-jsonrpc.js";
|
||||
|
||||
export type {
|
||||
WebSocketCreator, WebSocketLike
|
||||
} from "./provider-websocket.js";
|
||||
|
||||
export type { Signer } from "./signer.js";
|
||||
|
||||
438
dev/env/node_modules/ethers/src.ts/providers/network.ts
generated
vendored
Executable file
438
dev/env/node_modules/ethers/src.ts/providers/network.ts
generated
vendored
Executable file
@@ -0,0 +1,438 @@
|
||||
/**
|
||||
* A **Network** encapsulates the various properties required to
|
||||
* interact with a specific chain.
|
||||
*
|
||||
* @_subsection: api/providers:Networks [networks]
|
||||
*/
|
||||
|
||||
import { accessListify } from "../transaction/index.js";
|
||||
import { getBigInt, assert, assertArgument } from "../utils/index.js";
|
||||
|
||||
import {
|
||||
EnsPlugin, FetchUrlFeeDataNetworkPlugin, GasCostPlugin
|
||||
} from "./plugins-network.js";
|
||||
|
||||
import type { BigNumberish } from "../utils/index.js";
|
||||
import type { TransactionLike } from "../transaction/index.js";
|
||||
|
||||
import type { NetworkPlugin } from "./plugins-network.js";
|
||||
|
||||
|
||||
/**
|
||||
* A Networkish can be used to allude to a Network, by specifing:
|
||||
* - a [[Network]] object
|
||||
* - a well-known (or registered) network name
|
||||
* - a well-known (or registered) chain ID
|
||||
* - an object with sufficient details to describe a network
|
||||
*/
|
||||
export type Networkish = Network | number | bigint | string | {
|
||||
name?: string,
|
||||
chainId?: number,
|
||||
//layerOneConnection?: Provider,
|
||||
ensAddress?: string,
|
||||
ensNetwork?: number
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/* * * *
|
||||
// Networks which operation against an L2 can use this plugin to
|
||||
// specify how to access L1, for the purpose of resolving ENS,
|
||||
// for example.
|
||||
export class LayerOneConnectionPlugin extends NetworkPlugin {
|
||||
readonly provider!: Provider;
|
||||
// @TODO: Rename to ChainAccess and allow for connecting to any chain
|
||||
constructor(provider: Provider) {
|
||||
super("org.ethers.plugins.layer-one-connection");
|
||||
defineProperties<LayerOneConnectionPlugin>(this, { provider });
|
||||
}
|
||||
|
||||
clone(): LayerOneConnectionPlugin {
|
||||
return new LayerOneConnectionPlugin(this.provider);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
const Networks: Map<string | bigint, () => Network> = new Map();
|
||||
|
||||
|
||||
/**
|
||||
* A **Network** provides access to a chain's properties and allows
|
||||
* for plug-ins to extend functionality.
|
||||
*/
|
||||
export class Network {
|
||||
#name: string;
|
||||
#chainId: bigint;
|
||||
|
||||
#plugins: Map<string, NetworkPlugin>;
|
||||
|
||||
/**
|
||||
* Creates a new **Network** for %%name%% and %%chainId%%.
|
||||
*/
|
||||
constructor(name: string, chainId: BigNumberish) {
|
||||
this.#name = name;
|
||||
this.#chainId = getBigInt(chainId);
|
||||
this.#plugins = new Map();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a JSON-compatible representation of a Network.
|
||||
*/
|
||||
toJSON(): any {
|
||||
return { name: this.name, chainId: String(this.chainId) };
|
||||
}
|
||||
|
||||
/**
|
||||
* The network common name.
|
||||
*
|
||||
* This is the canonical name, as networks migh have multiple
|
||||
* names.
|
||||
*/
|
||||
get name(): string { return this.#name; }
|
||||
set name(value: string) { this.#name = value; }
|
||||
|
||||
/**
|
||||
* The network chain ID.
|
||||
*/
|
||||
get chainId(): bigint { return this.#chainId; }
|
||||
set chainId(value: BigNumberish) { this.#chainId = getBigInt(value, "chainId"); }
|
||||
|
||||
/**
|
||||
* Returns true if %%other%% matches this network. Any chain ID
|
||||
* must match, and if no chain ID is present, the name must match.
|
||||
*
|
||||
* This method does not currently check for additional properties,
|
||||
* such as ENS address or plug-in compatibility.
|
||||
*/
|
||||
matches(other: Networkish): boolean {
|
||||
if (other == null) { return false; }
|
||||
|
||||
if (typeof(other) === "string") {
|
||||
try {
|
||||
return (this.chainId === getBigInt(other));
|
||||
} catch (error) { }
|
||||
return (this.name === other);
|
||||
}
|
||||
|
||||
if (typeof(other) === "number" || typeof(other) === "bigint") {
|
||||
try {
|
||||
return (this.chainId === getBigInt(other));
|
||||
} catch (error) { }
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof(other) === "object") {
|
||||
if (other.chainId != null) {
|
||||
try {
|
||||
return (this.chainId === getBigInt(other.chainId));
|
||||
} catch (error) { }
|
||||
return false;
|
||||
}
|
||||
if (other.name != null) {
|
||||
return (this.name === other.name);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of plugins currently attached to this Network.
|
||||
*/
|
||||
get plugins(): Array<NetworkPlugin> {
|
||||
return Array.from(this.#plugins.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach a new %%plugin%% to this Network. The network name
|
||||
* must be unique, excluding any fragment.
|
||||
*/
|
||||
attachPlugin(plugin: NetworkPlugin): this {
|
||||
if (this.#plugins.get(plugin.name)) {
|
||||
throw new Error(`cannot replace existing plugin: ${ plugin.name } `);
|
||||
}
|
||||
this.#plugins.set(plugin.name, plugin.clone());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the plugin, if any, matching %%name%% exactly. Plugins
|
||||
* with fragments will not be returned unless %%name%% includes
|
||||
* a fragment.
|
||||
*/
|
||||
getPlugin<T extends NetworkPlugin = NetworkPlugin>(name: string): null | T {
|
||||
return <T>(this.#plugins.get(name)) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of all plugins that match %%name%%, with otr without
|
||||
* a fragment.
|
||||
*/
|
||||
getPlugins<T extends NetworkPlugin = NetworkPlugin>(basename: string): Array<T> {
|
||||
return <Array<T>>(this.plugins.filter((p) => (p.name.split("#")[0] === basename)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a copy of this Network.
|
||||
*/
|
||||
clone(): Network {
|
||||
const clone = new Network(this.name, this.chainId);
|
||||
this.plugins.forEach((plugin) => {
|
||||
clone.attachPlugin(plugin.clone());
|
||||
});
|
||||
return clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the intrinsic gas required for a transaction.
|
||||
*
|
||||
* A GasCostPlugin can be attached to override the default
|
||||
* values.
|
||||
*/
|
||||
computeIntrinsicGas(tx: TransactionLike): number {
|
||||
const costs = this.getPlugin<GasCostPlugin>("org.ethers.plugins.network.GasCost") || (new GasCostPlugin());
|
||||
|
||||
let gas = costs.txBase;
|
||||
if (tx.to == null) { gas += costs.txCreate; }
|
||||
if (tx.data) {
|
||||
for (let i = 2; i < tx.data.length; i += 2) {
|
||||
if (tx.data.substring(i, i + 2) === "00") {
|
||||
gas += costs.txDataZero;
|
||||
} else {
|
||||
gas += costs.txDataNonzero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tx.accessList) {
|
||||
const accessList = accessListify(tx.accessList);
|
||||
for (const addr in accessList) {
|
||||
gas += costs.txAccessListAddress + costs.txAccessListStorageKey * accessList[addr].storageKeys.length;
|
||||
}
|
||||
}
|
||||
|
||||
return gas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new Network for the %%network%% name or chainId.
|
||||
*/
|
||||
static from(network?: Networkish): Network {
|
||||
injectCommonNetworks();
|
||||
|
||||
// Default network
|
||||
if (network == null) { return Network.from("mainnet"); }
|
||||
|
||||
// Canonical name or chain ID
|
||||
if (typeof(network) === "number") { network = BigInt(network); }
|
||||
if (typeof(network) === "string" || typeof(network) === "bigint") {
|
||||
const networkFunc = Networks.get(network);
|
||||
if (networkFunc) { return networkFunc(); }
|
||||
if (typeof(network) === "bigint") {
|
||||
return new Network("unknown", network);
|
||||
}
|
||||
|
||||
assertArgument(false, "unknown network", "network", network);
|
||||
}
|
||||
|
||||
// Clonable with network-like abilities
|
||||
if (typeof((<Network>network).clone) === "function") {
|
||||
const clone = (<Network>network).clone();
|
||||
//if (typeof(network.name) !== "string" || typeof(network.chainId) !== "number") {
|
||||
//}
|
||||
return clone;
|
||||
}
|
||||
|
||||
// Networkish
|
||||
if (typeof(network) === "object") {
|
||||
assertArgument(typeof(network.name) === "string" && typeof(network.chainId) === "number",
|
||||
"invalid network object name or chainId", "network", network);
|
||||
|
||||
const custom = new Network(<string>(network.name), <number>(network.chainId));
|
||||
|
||||
if ((<any>network).ensAddress || (<any>network).ensNetwork != null) {
|
||||
custom.attachPlugin(new EnsPlugin((<any>network).ensAddress, (<any>network).ensNetwork));
|
||||
}
|
||||
|
||||
//if ((<any>network).layerOneConnection) {
|
||||
// custom.attachPlugin(new LayerOneConnectionPlugin((<any>network).layerOneConnection));
|
||||
//}
|
||||
|
||||
return custom;
|
||||
}
|
||||
|
||||
assertArgument(false, "invalid network", "network", network);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register %%nameOrChainId%% with a function which returns
|
||||
* an instance of a Network representing that chain.
|
||||
*/
|
||||
static register(nameOrChainId: string | number | bigint, networkFunc: () => Network): void {
|
||||
if (typeof(nameOrChainId) === "number") { nameOrChainId = BigInt(nameOrChainId); }
|
||||
const existing = Networks.get(nameOrChainId);
|
||||
if (existing) {
|
||||
assertArgument(false, `conflicting network for ${ JSON.stringify(existing.name) }`, "nameOrChainId", nameOrChainId);
|
||||
}
|
||||
Networks.set(nameOrChainId, networkFunc);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
type Options = {
|
||||
ensNetwork?: number;
|
||||
altNames?: Array<string>;
|
||||
plugins?: Array<NetworkPlugin>;
|
||||
};
|
||||
|
||||
// We don't want to bring in formatUnits because it is backed by
|
||||
// FixedNumber and we want to keep Networks tiny. The values
|
||||
// included by the Gas Stations are also IEEE 754 with lots of
|
||||
// rounding issues and exceed the strict checks formatUnits has.
|
||||
function parseUnits(_value: number | string, decimals: number): bigint {
|
||||
const value = String(_value);
|
||||
if (!value.match(/^[0-9.]+$/)) {
|
||||
throw new Error(`invalid gwei value: ${ _value }`);
|
||||
}
|
||||
|
||||
// Break into [ whole, fraction ]
|
||||
const comps = value.split(".");
|
||||
if (comps.length === 1) { comps.push(""); }
|
||||
|
||||
// More than 1 decimal point or too many fractional positions
|
||||
if (comps.length !== 2) {
|
||||
throw new Error(`invalid gwei value: ${ _value }`);
|
||||
}
|
||||
|
||||
// Pad the fraction to 9 decimalplaces
|
||||
while (comps[1].length < decimals) { comps[1] += "0"; }
|
||||
|
||||
// Too many decimals and some non-zero ending, take the ceiling
|
||||
if (comps[1].length > 9) {
|
||||
let frac = BigInt(comps[1].substring(0, 9));
|
||||
if (!comps[1].substring(9).match(/^0+$/)) { frac++; }
|
||||
comps[1] = frac.toString();
|
||||
}
|
||||
|
||||
return BigInt(comps[0] + comps[1]);
|
||||
}
|
||||
|
||||
// Used by Polygon to use a gas station for fee data
|
||||
function getGasStationPlugin(url: string) {
|
||||
return new FetchUrlFeeDataNetworkPlugin(url, async (fetchFeeData, provider, request) => {
|
||||
|
||||
// Prevent Cloudflare from blocking our request in node.js
|
||||
request.setHeader("User-Agent", "ethers");
|
||||
|
||||
let response;
|
||||
try {
|
||||
const [ _response, _feeData ] = await Promise.all([
|
||||
request.send(), fetchFeeData()
|
||||
]);
|
||||
response = _response;
|
||||
const payload = response.bodyJson.standard;
|
||||
const feeData = {
|
||||
gasPrice: _feeData.gasPrice,
|
||||
maxFeePerGas: parseUnits(payload.maxFee, 9),
|
||||
maxPriorityFeePerGas: parseUnits(payload.maxPriorityFee, 9),
|
||||
};
|
||||
return feeData;
|
||||
} catch (error: any) {
|
||||
assert(false, `error encountered with polygon gas station (${ JSON.stringify(request.url) })`, "SERVER_ERROR", { request, response, error });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// See: https://chainlist.org
|
||||
let injected = false;
|
||||
function injectCommonNetworks(): void {
|
||||
if (injected) { return; }
|
||||
injected = true;
|
||||
|
||||
/// Register popular Ethereum networks
|
||||
function registerEth(name: string, chainId: number, options: Options): void {
|
||||
const func = function() {
|
||||
const network = new Network(name, chainId);
|
||||
|
||||
// We use 0 to disable ENS
|
||||
if (options.ensNetwork != null) {
|
||||
network.attachPlugin(new EnsPlugin(null, options.ensNetwork));
|
||||
}
|
||||
|
||||
network.attachPlugin(new GasCostPlugin());
|
||||
|
||||
(options.plugins || []).forEach((plugin) => {
|
||||
network.attachPlugin(plugin);
|
||||
});
|
||||
|
||||
return network;
|
||||
};
|
||||
|
||||
// Register the network by name and chain ID
|
||||
Network.register(name, func);
|
||||
Network.register(chainId, func);
|
||||
|
||||
if (options.altNames) {
|
||||
options.altNames.forEach((name) => {
|
||||
Network.register(name, func);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
registerEth("mainnet", 1, { ensNetwork: 1, altNames: [ "homestead" ] });
|
||||
registerEth("ropsten", 3, { ensNetwork: 3 });
|
||||
registerEth("rinkeby", 4, { ensNetwork: 4 });
|
||||
registerEth("goerli", 5, { ensNetwork: 5 });
|
||||
registerEth("kovan", 42, { ensNetwork: 42 });
|
||||
registerEth("sepolia", 11155111, { ensNetwork: 11155111 });
|
||||
registerEth("holesky", 17000, { ensNetwork: 17000 });
|
||||
|
||||
registerEth("classic", 61, { });
|
||||
registerEth("classicKotti", 6, { });
|
||||
|
||||
registerEth("arbitrum", 42161, {
|
||||
ensNetwork: 1,
|
||||
});
|
||||
registerEth("arbitrum-goerli", 421613, { });
|
||||
registerEth("arbitrum-sepolia", 421614, { });
|
||||
|
||||
registerEth("base", 8453, { ensNetwork: 1 });
|
||||
registerEth("base-goerli", 84531, { });
|
||||
registerEth("base-sepolia", 84532, { });
|
||||
|
||||
registerEth("bnb", 56, { ensNetwork: 1 });
|
||||
registerEth("bnbt", 97, { });
|
||||
|
||||
registerEth("filecoin", 314, { });
|
||||
registerEth("filecoin-calibration", 314159, { });
|
||||
|
||||
registerEth("linea", 59144, { ensNetwork: 1 });
|
||||
registerEth("linea-goerli", 59140, { });
|
||||
registerEth("linea-sepolia", 59141, { });
|
||||
|
||||
registerEth("matic", 137, {
|
||||
ensNetwork: 1,
|
||||
plugins: [
|
||||
getGasStationPlugin("https:/\/gasstation.polygon.technology/v2")
|
||||
]
|
||||
});
|
||||
registerEth("matic-amoy", 80002, { });
|
||||
registerEth("matic-mumbai", 80001, {
|
||||
altNames: [ "maticMumbai", "maticmum" ], // @TODO: Future remove these alts
|
||||
plugins: [
|
||||
getGasStationPlugin("https:/\/gasstation-testnet.polygon.technology/v2")
|
||||
]
|
||||
});
|
||||
|
||||
registerEth("optimism", 10, {
|
||||
ensNetwork: 1,
|
||||
plugins: [ ]
|
||||
});
|
||||
registerEth("optimism-goerli", 420, { });
|
||||
registerEth("optimism-sepolia", 11155420, { });
|
||||
|
||||
registerEth("xdai", 100, { ensNetwork: 1 });
|
||||
}
|
||||
8
dev/env/node_modules/ethers/src.ts/providers/pagination.ts
generated
vendored
Executable file
8
dev/env/node_modules/ethers/src.ts/providers/pagination.ts
generated
vendored
Executable file
@@ -0,0 +1,8 @@
|
||||
export interface PaginationResult<R> extends Array<R> {
|
||||
next(): Promise<PaginationResult<R>>;
|
||||
|
||||
// The total number of results available or null if unknown
|
||||
totalResults: null | number;
|
||||
|
||||
done: boolean;
|
||||
}
|
||||
35
dev/env/node_modules/ethers/src.ts/providers/plugin-fallback.ts
generated
vendored
Executable file
35
dev/env/node_modules/ethers/src.ts/providers/plugin-fallback.ts
generated
vendored
Executable file
@@ -0,0 +1,35 @@
|
||||
|
||||
import { AbstractProviderPlugin } from "./abstract-provider.js";
|
||||
import { defineProperties } from "../utils/index.js";
|
||||
|
||||
import type { AbstractProvider, PerformActionRequest } from "./abstract-provider.js";
|
||||
|
||||
|
||||
export const PluginIdFallbackProvider = "org.ethers.plugins.provider.QualifiedPlugin";
|
||||
|
||||
export class CheckQualifiedPlugin implements AbstractProviderPlugin {
|
||||
declare name: string;
|
||||
|
||||
constructor() {
|
||||
defineProperties<CheckQualifiedPlugin>(this, { name: PluginIdFallbackProvider });
|
||||
}
|
||||
|
||||
connect(provider: AbstractProvider): CheckQualifiedPlugin {
|
||||
return this;
|
||||
}
|
||||
|
||||
// Retruns true if this value should be considered qualified for
|
||||
// inclusion in the quorum.
|
||||
isQualified(action: PerformActionRequest, result: any): boolean {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export class PossiblyPrunedTransactionPlugin extends CheckQualifiedPlugin {
|
||||
isQualified(action: PerformActionRequest, result: any): boolean {
|
||||
if (action.method === "getTransaction" || action.method === "getTransactionReceipt") {
|
||||
if (result == null) { return false; }
|
||||
}
|
||||
return super.isQualified(action, result);
|
||||
}
|
||||
}
|
||||
281
dev/env/node_modules/ethers/src.ts/providers/plugins-network.ts
generated
vendored
Executable file
281
dev/env/node_modules/ethers/src.ts/providers/plugins-network.ts
generated
vendored
Executable file
@@ -0,0 +1,281 @@
|
||||
import { defineProperties } from "../utils/properties.js";
|
||||
|
||||
import { assertArgument } from "../utils/index.js";
|
||||
|
||||
import type { FeeData, Provider } from "./provider.js";
|
||||
import type { FetchRequest } from "../utils/fetch.js";
|
||||
|
||||
|
||||
const EnsAddress = "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e";
|
||||
|
||||
/**
|
||||
* A **NetworkPlugin** provides additional functionality on a [[Network]].
|
||||
*/
|
||||
export class NetworkPlugin {
|
||||
/**
|
||||
* The name of the plugin.
|
||||
*
|
||||
* It is recommended to use reverse-domain-notation, which permits
|
||||
* unique names with a known authority as well as hierarchal entries.
|
||||
*/
|
||||
readonly name!: string;
|
||||
|
||||
/**
|
||||
* Creates a new **NetworkPlugin**.
|
||||
*/
|
||||
constructor(name: string) {
|
||||
defineProperties<NetworkPlugin>(this, { name });
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a copy of this plugin.
|
||||
*/
|
||||
clone(): NetworkPlugin {
|
||||
return new NetworkPlugin(this.name);
|
||||
}
|
||||
|
||||
// validate(network: Network): NetworkPlugin {
|
||||
// return this;
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The gas cost parameters for a [[GasCostPlugin]].
|
||||
*/
|
||||
export type GasCostParameters = {
|
||||
/**
|
||||
* The transactions base fee.
|
||||
*/
|
||||
txBase?: number;
|
||||
|
||||
/**
|
||||
* The fee for creating a new account.
|
||||
*/
|
||||
txCreate?: number;
|
||||
|
||||
/**
|
||||
* The fee per zero-byte in the data.
|
||||
*/
|
||||
txDataZero?: number;
|
||||
|
||||
/**
|
||||
* The fee per non-zero-byte in the data.
|
||||
*/
|
||||
txDataNonzero?: number;
|
||||
|
||||
/**
|
||||
* The fee per storage key in the [[link-eip-2930]] access list.
|
||||
*/
|
||||
txAccessListStorageKey?: number;
|
||||
|
||||
/**
|
||||
* The fee per address in the [[link-eip-2930]] access list.
|
||||
*/
|
||||
txAccessListAddress?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* A **GasCostPlugin** allows a network to provide alternative values when
|
||||
* computing the intrinsic gas required for a transaction.
|
||||
*/
|
||||
export class GasCostPlugin extends NetworkPlugin implements GasCostParameters {
|
||||
/**
|
||||
* The block number to treat these values as valid from.
|
||||
*
|
||||
* This allows a hardfork to have updated values included as well as
|
||||
* mulutiple hardforks to be supported.
|
||||
*/
|
||||
readonly effectiveBlock!: number;
|
||||
|
||||
/**
|
||||
* The transactions base fee.
|
||||
*/
|
||||
readonly txBase!: number;
|
||||
|
||||
/**
|
||||
* The fee for creating a new account.
|
||||
*/
|
||||
readonly txCreate!: number;
|
||||
|
||||
/**
|
||||
* The fee per zero-byte in the data.
|
||||
*/
|
||||
readonly txDataZero!: number;
|
||||
|
||||
/**
|
||||
* The fee per non-zero-byte in the data.
|
||||
*/
|
||||
readonly txDataNonzero!: number;
|
||||
|
||||
/**
|
||||
* The fee per storage key in the [[link-eip-2930]] access list.
|
||||
*/
|
||||
readonly txAccessListStorageKey!: number;
|
||||
|
||||
/**
|
||||
* The fee per address in the [[link-eip-2930]] access list.
|
||||
*/
|
||||
readonly txAccessListAddress!: number;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new GasCostPlugin from %%effectiveBlock%% until the
|
||||
* latest block or another GasCostPlugin supercedes that block number,
|
||||
* with the associated %%costs%%.
|
||||
*/
|
||||
constructor(effectiveBlock?: number, costs?: GasCostParameters) {
|
||||
if (effectiveBlock == null) { effectiveBlock = 0; }
|
||||
super(`org.ethers.network.plugins.GasCost#${ (effectiveBlock || 0) }`);
|
||||
|
||||
const props: Record<string, number> = { effectiveBlock };
|
||||
function set(name: keyof GasCostParameters, nullish: number): void {
|
||||
let value = (costs || { })[name];
|
||||
if (value == null) { value = nullish; }
|
||||
assertArgument(typeof(value) === "number", `invalud value for ${ name }`, "costs", costs);
|
||||
props[name] = value;
|
||||
}
|
||||
|
||||
set("txBase", 21000);
|
||||
set("txCreate", 32000);
|
||||
set("txDataZero", 4);
|
||||
set("txDataNonzero", 16);
|
||||
set("txAccessListStorageKey", 1900);
|
||||
set("txAccessListAddress", 2400);
|
||||
|
||||
defineProperties<GasCostPlugin>(this, props);
|
||||
}
|
||||
|
||||
clone(): GasCostPlugin {
|
||||
return new GasCostPlugin(this.effectiveBlock, this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An **EnsPlugin** allows a [[Network]] to specify the ENS Registry
|
||||
* Contract address and the target network to use when using that
|
||||
* contract.
|
||||
*
|
||||
* Various testnets have their own instance of the contract to use, but
|
||||
* in general, the mainnet instance supports multi-chain addresses and
|
||||
* should be used.
|
||||
*/
|
||||
export class EnsPlugin extends NetworkPlugin {
|
||||
|
||||
/**
|
||||
* The ENS Registrty Contract address.
|
||||
*/
|
||||
readonly address!: string;
|
||||
|
||||
/**
|
||||
* The chain ID that the ENS contract lives on.
|
||||
*/
|
||||
readonly targetNetwork!: number;
|
||||
|
||||
/**
|
||||
* Creates a new **EnsPlugin** connected to %%address%% on the
|
||||
* %%targetNetwork%%. The default ENS address and mainnet is used
|
||||
* if unspecified.
|
||||
*/
|
||||
constructor(address?: null | string, targetNetwork?: null | number) {
|
||||
super("org.ethers.plugins.network.Ens");
|
||||
defineProperties<EnsPlugin>(this, {
|
||||
address: (address || EnsAddress),
|
||||
targetNetwork: ((targetNetwork == null) ? 1: targetNetwork)
|
||||
});
|
||||
}
|
||||
|
||||
clone(): EnsPlugin {
|
||||
return new EnsPlugin(this.address, this.targetNetwork);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A **FeeDataNetworkPlugin** allows a network to provide and alternate
|
||||
* means to specify its fee data.
|
||||
*
|
||||
* For example, a network which does not support [[link-eip-1559]] may
|
||||
* choose to use a Gas Station site to approximate the gas price.
|
||||
*/
|
||||
export class FeeDataNetworkPlugin extends NetworkPlugin {
|
||||
readonly #feeDataFunc: (provider: Provider) => Promise<FeeData>;
|
||||
|
||||
/**
|
||||
* The fee data function provided to the constructor.
|
||||
*/
|
||||
get feeDataFunc(): (provider: Provider) => Promise<FeeData> {
|
||||
return this.#feeDataFunc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new **FeeDataNetworkPlugin**.
|
||||
*/
|
||||
constructor(feeDataFunc: (provider: Provider) => Promise<FeeData>) {
|
||||
super("org.ethers.plugins.network.FeeData");
|
||||
this.#feeDataFunc = feeDataFunc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves to the fee data.
|
||||
*/
|
||||
async getFeeData(provider: Provider): Promise<FeeData> {
|
||||
return await this.#feeDataFunc(provider);
|
||||
}
|
||||
|
||||
clone(): FeeDataNetworkPlugin {
|
||||
return new FeeDataNetworkPlugin(this.#feeDataFunc);
|
||||
}
|
||||
}
|
||||
|
||||
export class FetchUrlFeeDataNetworkPlugin extends NetworkPlugin {
|
||||
readonly #url: string;
|
||||
readonly #processFunc: (f: () => Promise<FeeData>, p: Provider, r: FetchRequest) => Promise<{ gasPrice?: null | bigint, maxFeePerGas?: null | bigint, maxPriorityFeePerGas?: null | bigint }>;
|
||||
|
||||
/**
|
||||
* The URL to initialize the FetchRequest with in %%processFunc%%.
|
||||
*/
|
||||
get url(): string { return this.#url; }
|
||||
|
||||
/**
|
||||
* The callback to use when computing the FeeData.
|
||||
*/
|
||||
get processFunc(): (f: () => Promise<FeeData>, p: Provider, r: FetchRequest) => Promise<{ gasPrice?: null | bigint, maxFeePerGas?: null | bigint, maxPriorityFeePerGas?: null | bigint }> { return this.#processFunc; }
|
||||
|
||||
/**
|
||||
* Creates a new **FetchUrlFeeDataNetworkPlugin** which will
|
||||
* be used when computing the fee data for the network.
|
||||
*/
|
||||
constructor(url: string, processFunc: (f: () => Promise<FeeData>, p: Provider, r: FetchRequest) => Promise<{ gasPrice?: null | bigint, maxFeePerGas?: null | bigint, maxPriorityFeePerGas?: null | bigint }>) {
|
||||
super("org.ethers.plugins.network.FetchUrlFeeDataPlugin");
|
||||
this.#url = url;
|
||||
this.#processFunc = processFunc;
|
||||
}
|
||||
|
||||
// We are immutable, so we can serve as our own clone
|
||||
clone(): FetchUrlFeeDataNetworkPlugin { return this; }
|
||||
}
|
||||
|
||||
/*
|
||||
export class CustomBlockNetworkPlugin extends NetworkPlugin {
|
||||
readonly #blockFunc: (provider: Provider, block: BlockParams<string>) => Block<string>;
|
||||
readonly #blockWithTxsFunc: (provider: Provider, block: BlockParams<TransactionResponseParams>) => Block<TransactionResponse>;
|
||||
|
||||
constructor(blockFunc: (provider: Provider, block: BlockParams<string>) => Block<string>, blockWithTxsFunc: (provider: Provider, block: BlockParams<TransactionResponseParams>) => Block<TransactionResponse>) {
|
||||
super("org.ethers.network-plugins.custom-block");
|
||||
this.#blockFunc = blockFunc;
|
||||
this.#blockWithTxsFunc = blockWithTxsFunc;
|
||||
}
|
||||
|
||||
async getBlock(provider: Provider, block: BlockParams<string>): Promise<Block<string>> {
|
||||
return await this.#blockFunc(provider, block);
|
||||
}
|
||||
|
||||
async getBlockions(provider: Provider, block: BlockParams<TransactionResponseParams>): Promise<Block<TransactionResponse>> {
|
||||
return await this.#blockWithTxsFunc(provider, block);
|
||||
}
|
||||
|
||||
clone(): CustomBlockNetworkPlugin {
|
||||
return new CustomBlockNetworkPlugin(this.#blockFunc, this.#blockWithTxsFunc);
|
||||
}
|
||||
}
|
||||
*/
|
||||
166
dev/env/node_modules/ethers/src.ts/providers/provider-alchemy.ts
generated
vendored
Executable file
166
dev/env/node_modules/ethers/src.ts/providers/provider-alchemy.ts
generated
vendored
Executable file
@@ -0,0 +1,166 @@
|
||||
/**
|
||||
* [[link-alchemy]] provides a third-party service for connecting to
|
||||
* various blockchains over JSON-RPC.
|
||||
*
|
||||
* **Supported Networks**
|
||||
*
|
||||
* - Ethereum Mainnet (``mainnet``)
|
||||
* - Goerli Testnet (``goerli``)
|
||||
* - Sepolia Testnet (``sepolia``)
|
||||
* - Arbitrum (``arbitrum``)
|
||||
* - Arbitrum Goerli Testnet (``arbitrum-goerli``)
|
||||
* - Arbitrum Sepolia Testnet (``arbitrum-sepolia``)
|
||||
* - Base (``base``)
|
||||
* - Base Goerlia Testnet (``base-goerli``)
|
||||
* - Base Sepolia Testnet (``base-sepolia``)
|
||||
* - Optimism (``optimism``)
|
||||
* - Optimism Goerli Testnet (``optimism-goerli``)
|
||||
* - Optimism Sepolia Testnet (``optimism-sepolia``)
|
||||
* - Polygon (``matic``)
|
||||
* - Polygon Amoy Testnet (``matic-amoy``)
|
||||
* - Polygon Mumbai Testnet (``matic-mumbai``)
|
||||
*
|
||||
* @_subsection: api/providers/thirdparty:Alchemy [providers-alchemy]
|
||||
*/
|
||||
|
||||
import {
|
||||
defineProperties, resolveProperties, assert, assertArgument,
|
||||
FetchRequest
|
||||
} from "../utils/index.js";
|
||||
|
||||
import { showThrottleMessage } from "./community.js";
|
||||
import { Network } from "./network.js";
|
||||
import { JsonRpcProvider } from "./provider-jsonrpc.js";
|
||||
|
||||
import type { AbstractProvider, PerformActionRequest } from "./abstract-provider.js";
|
||||
import type { CommunityResourcable } from "./community.js";
|
||||
import type { Networkish } from "./network.js";
|
||||
|
||||
|
||||
const defaultApiKey = "_gg7wSSi0KMBsdKnGVfHDueq6xMB9EkC"
|
||||
|
||||
function getHost(name: string): string {
|
||||
switch(name) {
|
||||
case "mainnet":
|
||||
return "eth-mainnet.g.alchemy.com";
|
||||
case "goerli":
|
||||
return "eth-goerli.g.alchemy.com";
|
||||
case "sepolia":
|
||||
return "eth-sepolia.g.alchemy.com";
|
||||
|
||||
case "arbitrum":
|
||||
return "arb-mainnet.g.alchemy.com";
|
||||
case "arbitrum-goerli":
|
||||
return "arb-goerli.g.alchemy.com";
|
||||
case "arbitrum-sepolia":
|
||||
return "arb-sepolia.g.alchemy.com";
|
||||
case "base":
|
||||
return "base-mainnet.g.alchemy.com";
|
||||
case "base-goerli":
|
||||
return "base-goerli.g.alchemy.com";
|
||||
case "base-sepolia":
|
||||
return "base-sepolia.g.alchemy.com";
|
||||
case "matic":
|
||||
return "polygon-mainnet.g.alchemy.com";
|
||||
case "matic-amoy":
|
||||
return "polygon-amoy.g.alchemy.com";
|
||||
case "matic-mumbai":
|
||||
return "polygon-mumbai.g.alchemy.com";
|
||||
case "optimism":
|
||||
return "opt-mainnet.g.alchemy.com";
|
||||
case "optimism-goerli":
|
||||
return "opt-goerli.g.alchemy.com";
|
||||
case "optimism-sepolia":
|
||||
return "opt-sepolia.g.alchemy.com";
|
||||
}
|
||||
|
||||
assertArgument(false, "unsupported network", "network", name);
|
||||
}
|
||||
|
||||
/**
|
||||
* The **AlchemyProvider** connects to the [[link-alchemy]]
|
||||
* JSON-RPC end-points.
|
||||
*
|
||||
* By default, a highly-throttled API key is used, which is
|
||||
* appropriate for quick prototypes and simple scripts. To
|
||||
* gain access to an increased rate-limit, it is highly
|
||||
* recommended to [sign up here](link-alchemy-signup).
|
||||
*
|
||||
* @_docloc: api/providers/thirdparty
|
||||
*/
|
||||
export class AlchemyProvider extends JsonRpcProvider implements CommunityResourcable {
|
||||
readonly apiKey!: string;
|
||||
|
||||
constructor(_network?: Networkish, apiKey?: null | string) {
|
||||
if (_network == null) { _network = "mainnet"; }
|
||||
const network = Network.from(_network);
|
||||
if (apiKey == null) { apiKey = defaultApiKey; }
|
||||
|
||||
const request = AlchemyProvider.getRequest(network, apiKey);
|
||||
super(request, network, { staticNetwork: network });
|
||||
|
||||
defineProperties<AlchemyProvider>(this, { apiKey });
|
||||
}
|
||||
|
||||
_getProvider(chainId: number): AbstractProvider {
|
||||
try {
|
||||
return new AlchemyProvider(chainId, this.apiKey);
|
||||
} catch (error) { }
|
||||
return super._getProvider(chainId);
|
||||
}
|
||||
|
||||
async _perform(req: PerformActionRequest): Promise<any> {
|
||||
|
||||
// https://docs.alchemy.com/reference/trace-transaction
|
||||
if (req.method === "getTransactionResult") {
|
||||
const { trace, tx } = await resolveProperties({
|
||||
trace: this.send("trace_transaction", [ req.hash ]),
|
||||
tx: this.getTransaction(req.hash)
|
||||
});
|
||||
if (trace == null || tx == null) { return null; }
|
||||
|
||||
let data: undefined | string;
|
||||
let error = false;
|
||||
try {
|
||||
data = trace[0].result.output;
|
||||
error = (trace[0].error === "Reverted");
|
||||
} catch (error) { }
|
||||
|
||||
if (data) {
|
||||
assert(!error, "an error occurred during transaction executions", "CALL_EXCEPTION", {
|
||||
action: "getTransactionResult",
|
||||
data,
|
||||
reason: null,
|
||||
transaction: tx,
|
||||
invocation: null,
|
||||
revert: null // @TODO
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
assert(false, "could not parse trace result", "BAD_DATA", { value: trace });
|
||||
}
|
||||
|
||||
return await super._perform(req);
|
||||
}
|
||||
|
||||
isCommunityResource(): boolean {
|
||||
return (this.apiKey === defaultApiKey);
|
||||
}
|
||||
|
||||
static getRequest(network: Network, apiKey?: string): FetchRequest {
|
||||
if (apiKey == null) { apiKey = defaultApiKey; }
|
||||
|
||||
const request = new FetchRequest(`https:/\/${ getHost(network.name) }/v2/${ apiKey }`);
|
||||
request.allowGzip = true;
|
||||
|
||||
if (apiKey === defaultApiKey) {
|
||||
request.retryFunc = async (request, response, attempt) => {
|
||||
showThrottleMessage("alchemy");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
}
|
||||
159
dev/env/node_modules/ethers/src.ts/providers/provider-ankr.ts
generated
vendored
Executable file
159
dev/env/node_modules/ethers/src.ts/providers/provider-ankr.ts
generated
vendored
Executable file
@@ -0,0 +1,159 @@
|
||||
/**
|
||||
* [[link-ankr]] provides a third-party service for connecting to
|
||||
* various blockchains over JSON-RPC.
|
||||
*
|
||||
* **Supported Networks**
|
||||
*
|
||||
* - Ethereum Mainnet (``mainnet``)
|
||||
* - Goerli Testnet (``goerli``)
|
||||
* - Sepolia Testnet (``sepolia``)
|
||||
* - Arbitrum (``arbitrum``)
|
||||
* - Base (``base``)
|
||||
* - Base Goerlia Testnet (``base-goerli``)
|
||||
* - Base Sepolia Testnet (``base-sepolia``)
|
||||
* - BNB (``bnb``)
|
||||
* - BNB Testnet (``bnbt``)
|
||||
* - Filecoin (``filecoin``)
|
||||
* - Filecoin Calibration Testnet (``filecoin-calibration``)
|
||||
* - Optimism (``optimism``)
|
||||
* - Optimism Goerli Testnet (``optimism-goerli``)
|
||||
* - Optimism Sepolia Testnet (``optimism-sepolia``)
|
||||
* - Polygon (``matic``)
|
||||
* - Polygon Mumbai Testnet (``matic-mumbai``)
|
||||
*
|
||||
* @_subsection: api/providers/thirdparty:Ankr [providers-ankr]
|
||||
*/
|
||||
import {
|
||||
defineProperties, FetchRequest, assertArgument
|
||||
} from "../utils/index.js";
|
||||
|
||||
import { AbstractProvider } from "./abstract-provider.js";
|
||||
import { showThrottleMessage } from "./community.js";
|
||||
import { Network } from "./network.js";
|
||||
import { JsonRpcProvider } from "./provider-jsonrpc.js";
|
||||
|
||||
import type { CommunityResourcable } from "./community.js";
|
||||
import type { Networkish } from "./network.js";
|
||||
import type { JsonRpcError, JsonRpcPayload } from "./provider-jsonrpc.js";
|
||||
|
||||
|
||||
const defaultApiKey = "9f7d929b018cdffb338517efa06f58359e86ff1ffd350bc889738523659e7972";
|
||||
|
||||
function getHost(name: string): string {
|
||||
switch (name) {
|
||||
case "mainnet":
|
||||
return "rpc.ankr.com/eth";
|
||||
case "goerli":
|
||||
return "rpc.ankr.com/eth_goerli";
|
||||
case "sepolia":
|
||||
return "rpc.ankr.com/eth_sepolia";
|
||||
|
||||
case "arbitrum":
|
||||
return "rpc.ankr.com/arbitrum";
|
||||
case "base":
|
||||
return "rpc.ankr.com/base";
|
||||
case "base-goerli":
|
||||
return "rpc.ankr.com/base_goerli";
|
||||
case "base-sepolia":
|
||||
return "rpc.ankr.com/base_sepolia";
|
||||
case "bnb":
|
||||
return "rpc.ankr.com/bsc";
|
||||
case "bnbt":
|
||||
return "rpc.ankr.com/bsc_testnet_chapel";
|
||||
case "filecoin":
|
||||
return "rpc.ankr.com/filecoin";
|
||||
case "filecoin-calibration":
|
||||
return "rpc.ankr.com/filecoin_testnet";
|
||||
case "matic":
|
||||
return "rpc.ankr.com/polygon";
|
||||
case "matic-mumbai":
|
||||
return "rpc.ankr.com/polygon_mumbai";
|
||||
case "optimism":
|
||||
return "rpc.ankr.com/optimism";
|
||||
case "optimism-goerli":
|
||||
return "rpc.ankr.com/optimism_testnet";
|
||||
case "optimism-sepolia":
|
||||
return "rpc.ankr.com/optimism_sepolia";
|
||||
}
|
||||
|
||||
assertArgument(false, "unsupported network", "network", name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The **AnkrProvider** connects to the [[link-ankr]]
|
||||
* JSON-RPC end-points.
|
||||
*
|
||||
* By default, a highly-throttled API key is used, which is
|
||||
* appropriate for quick prototypes and simple scripts. To
|
||||
* gain access to an increased rate-limit, it is highly
|
||||
* recommended to [sign up here](link-ankr-signup).
|
||||
*/
|
||||
export class AnkrProvider extends JsonRpcProvider implements CommunityResourcable {
|
||||
|
||||
/**
|
||||
* The API key for the Ankr connection.
|
||||
*/
|
||||
readonly apiKey!: string;
|
||||
|
||||
/**
|
||||
* Create a new **AnkrProvider**.
|
||||
*
|
||||
* By default connecting to ``mainnet`` with a highly throttled
|
||||
* API key.
|
||||
*/
|
||||
constructor(_network?: Networkish, apiKey?: null | string) {
|
||||
if (_network == null) { _network = "mainnet"; }
|
||||
const network = Network.from(_network);
|
||||
if (apiKey == null) { apiKey = defaultApiKey; }
|
||||
|
||||
// Ankr does not support filterId, so we force polling
|
||||
const options = { polling: true, staticNetwork: network };
|
||||
|
||||
const request = AnkrProvider.getRequest(network, apiKey);
|
||||
super(request, network, options);
|
||||
|
||||
defineProperties<AnkrProvider>(this, { apiKey });
|
||||
}
|
||||
|
||||
_getProvider(chainId: number): AbstractProvider {
|
||||
try {
|
||||
return new AnkrProvider(chainId, this.apiKey);
|
||||
} catch (error) { }
|
||||
return super._getProvider(chainId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a prepared request for connecting to %%network%% with
|
||||
* %%apiKey%%.
|
||||
*/
|
||||
static getRequest(network: Network, apiKey?: null | string): FetchRequest {
|
||||
if (apiKey == null) { apiKey = defaultApiKey; }
|
||||
|
||||
const request = new FetchRequest(`https:/\/${ getHost(network.name) }/${ apiKey }`);
|
||||
request.allowGzip = true;
|
||||
|
||||
if (apiKey === defaultApiKey) {
|
||||
request.retryFunc = async (request, response, attempt) => {
|
||||
showThrottleMessage("AnkrProvider");
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
getRpcError(payload: JsonRpcPayload, error: JsonRpcError): Error {
|
||||
if (payload.method === "eth_sendRawTransaction") {
|
||||
if (error && error.error && error.error.message === "INTERNAL_ERROR: could not replace existing tx") {
|
||||
error.error.message = "replacement transaction underpriced";
|
||||
}
|
||||
}
|
||||
|
||||
return super.getRpcError(payload, error);
|
||||
}
|
||||
|
||||
isCommunityResource(): boolean {
|
||||
return (this.apiKey === defaultApiKey);
|
||||
}
|
||||
}
|
||||
167
dev/env/node_modules/ethers/src.ts/providers/provider-blockscout.ts
generated
vendored
Executable file
167
dev/env/node_modules/ethers/src.ts/providers/provider-blockscout.ts
generated
vendored
Executable file
@@ -0,0 +1,167 @@
|
||||
|
||||
/**
|
||||
* [[link-blockscout]] provides a third-party service for connecting to
|
||||
* various blockchains over JSON-RPC.
|
||||
*
|
||||
* **Supported Networks**
|
||||
*
|
||||
* - Ethereum Mainnet (``mainnet``)
|
||||
* - Sepolia Testnet (``sepolia``)
|
||||
* - Holesky Testnet (``holesky``)
|
||||
* - Ethereum Classic (``classic``)
|
||||
* - Arbitrum (``arbitrum``)
|
||||
* - Base (``base``)
|
||||
* - Base Sepolia Testnet (``base-sepolia``)
|
||||
* - Gnosis (``xdai``)
|
||||
* - Optimism (``optimism``)
|
||||
* - Optimism Sepolia Testnet (``optimism-sepolia``)
|
||||
* - Polygon (``matic``)
|
||||
*
|
||||
* @_subsection: api/providers/thirdparty:Blockscout [providers-blockscout]
|
||||
*/
|
||||
import {
|
||||
assertArgument, defineProperties, FetchRequest, isHexString
|
||||
} from "../utils/index.js";
|
||||
|
||||
import { Network } from "./network.js";
|
||||
import { JsonRpcProvider } from "./provider-jsonrpc.js";
|
||||
|
||||
import type { AbstractProvider, PerformActionRequest } from "./abstract-provider.js";
|
||||
import type { CommunityResourcable } from "./community.js";
|
||||
import type { Networkish } from "./network.js";
|
||||
import type { JsonRpcPayload, JsonRpcError } from "./provider-jsonrpc.js";
|
||||
|
||||
|
||||
function getUrl(name: string): string {
|
||||
switch(name) {
|
||||
case "mainnet":
|
||||
return "https:/\/eth.blockscout.com/api/eth-rpc";
|
||||
case "sepolia":
|
||||
return "https:/\/eth-sepolia.blockscout.com/api/eth-rpc";
|
||||
case "holesky":
|
||||
return "https:/\/eth-holesky.blockscout.com/api/eth-rpc";
|
||||
|
||||
case "classic":
|
||||
return "https:/\/etc.blockscout.com/api/eth-rpc";
|
||||
|
||||
case "arbitrum":
|
||||
return "https:/\/arbitrum.blockscout.com/api/eth-rpc";
|
||||
|
||||
case "base":
|
||||
return "https:/\/base.blockscout.com/api/eth-rpc";
|
||||
case "base-sepolia":
|
||||
return "https:/\/base-sepolia.blockscout.com/api/eth-rpc";
|
||||
|
||||
case "matic":
|
||||
return "https:/\/polygon.blockscout.com/api/eth-rpc";
|
||||
|
||||
case "optimism":
|
||||
return "https:/\/optimism.blockscout.com/api/eth-rpc";
|
||||
case "optimism-sepolia":
|
||||
return "https:/\/optimism-sepolia.blockscout.com/api/eth-rpc";
|
||||
|
||||
case "xdai":
|
||||
return "https:/\/gnosis.blockscout.com/api/eth-rpc";
|
||||
}
|
||||
|
||||
assertArgument(false, "unsupported network", "network", name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The **BlockscoutProvider** connects to the [[link-blockscout]]
|
||||
* JSON-RPC end-points.
|
||||
*
|
||||
* By default, a highly-throttled API key is used, which is
|
||||
* appropriate for quick prototypes and simple scripts. To
|
||||
* gain access to an increased rate-limit, it is highly
|
||||
* recommended to [sign up here](link-blockscout).
|
||||
*/
|
||||
export class BlockscoutProvider extends JsonRpcProvider implements CommunityResourcable {
|
||||
/**
|
||||
* The API key.
|
||||
*/
|
||||
readonly apiKey!: null | string;
|
||||
|
||||
/**
|
||||
* Creates a new **BlockscoutProvider**.
|
||||
*/
|
||||
constructor(_network?: Networkish, apiKey?: null | string) {
|
||||
if (_network == null) { _network = "mainnet"; }
|
||||
const network = Network.from(_network);
|
||||
|
||||
if (apiKey == null) { apiKey = null; }
|
||||
|
||||
const request = BlockscoutProvider.getRequest(network);
|
||||
super(request, network, { staticNetwork: network });
|
||||
|
||||
defineProperties<BlockscoutProvider>(this, { apiKey });
|
||||
}
|
||||
|
||||
_getProvider(chainId: number): AbstractProvider {
|
||||
try {
|
||||
return new BlockscoutProvider(chainId, this.apiKey);
|
||||
} catch (error) { }
|
||||
return super._getProvider(chainId);
|
||||
}
|
||||
|
||||
isCommunityResource(): boolean {
|
||||
return (this.apiKey === null);
|
||||
}
|
||||
|
||||
getRpcRequest(req: PerformActionRequest): null | { method: string, args: Array<any> } {
|
||||
// Blockscout enforces the TAG argument for estimateGas
|
||||
const resp = super.getRpcRequest(req);
|
||||
if (resp && resp.method === "eth_estimateGas" && resp.args.length == 1) {
|
||||
resp.args = resp.args.slice();
|
||||
resp.args.push("latest");
|
||||
}
|
||||
return resp;
|
||||
}
|
||||
|
||||
getRpcError(payload: JsonRpcPayload, _error: JsonRpcError): Error {
|
||||
const error = _error ? _error.error: null;
|
||||
|
||||
// Blockscout currently drops the VM result and replaces it with a
|
||||
// human-readable string, so we need to make it machine-readable.
|
||||
if (error && error.code === -32015 && !isHexString(error.data || "", true)) {
|
||||
const panicCodes = <Record<string, string>>{
|
||||
"assert(false)": "01",
|
||||
"arithmetic underflow or overflow": "11",
|
||||
"division or modulo by zero": "12",
|
||||
"out-of-bounds array access; popping on an empty array": "31",
|
||||
"out-of-bounds access of an array or bytesN": "32"
|
||||
};
|
||||
|
||||
let panicCode = "";
|
||||
if (error.message === "VM execution error.") {
|
||||
// eth_call passes this message
|
||||
panicCode = panicCodes[error.data] || "";
|
||||
} else if (panicCodes[error.message || ""]) {
|
||||
panicCode = panicCodes[error.message || ""];
|
||||
}
|
||||
|
||||
if (panicCode) {
|
||||
error.message += ` (reverted: ${ error.data })`;
|
||||
error.data = "0x4e487b7100000000000000000000000000000000000000000000000000000000000000" + panicCode;
|
||||
}
|
||||
|
||||
} else if (error && error.code === -32000) {
|
||||
if (error.message === "wrong transaction nonce") {
|
||||
error.message += " (nonce too low)";
|
||||
}
|
||||
}
|
||||
|
||||
return super.getRpcError(payload, _error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a prepared request for connecting to %%network%%
|
||||
* with %%apiKey%%.
|
||||
*/
|
||||
static getRequest(network: Network): FetchRequest {
|
||||
const request = new FetchRequest(getUrl(network.name));
|
||||
request.allowGzip = true;
|
||||
return request;
|
||||
}
|
||||
}
|
||||
334
dev/env/node_modules/ethers/src.ts/providers/provider-browser.ts
generated
vendored
Executable file
334
dev/env/node_modules/ethers/src.ts/providers/provider-browser.ts
generated
vendored
Executable file
@@ -0,0 +1,334 @@
|
||||
|
||||
import { assertArgument, makeError } from "../utils/index.js";
|
||||
|
||||
import { JsonRpcApiPollingProvider } from "./provider-jsonrpc.js";
|
||||
|
||||
import type {
|
||||
JsonRpcApiProviderOptions,
|
||||
JsonRpcError, JsonRpcPayload, JsonRpcResult,
|
||||
JsonRpcSigner
|
||||
} from "./provider-jsonrpc.js";
|
||||
import type { Network, Networkish } from "./network.js";
|
||||
|
||||
/**
|
||||
* The interface to an [[link-eip-1193]] provider, which is a standard
|
||||
* used by most injected providers, which the [[BrowserProvider]] accepts
|
||||
* and exposes the API of.
|
||||
*/
|
||||
export interface Eip1193Provider {
|
||||
/**
|
||||
* See [[link-eip-1193]] for details on this method.
|
||||
*/
|
||||
request(request: { method: string, params?: Array<any> | Record<string, any> }): Promise<any>;
|
||||
};
|
||||
|
||||
/**
|
||||
* The possible additional events dispatched when using the ``"debug"``
|
||||
* event on a [[BrowserProvider]].
|
||||
*/
|
||||
export type DebugEventBrowserProvider = {
|
||||
action: "sendEip1193Payload",
|
||||
payload: { method: string, params: Array<any> }
|
||||
} | {
|
||||
action: "receiveEip1193Result",
|
||||
result: any
|
||||
} | {
|
||||
action: "receiveEip1193Error",
|
||||
error: Error
|
||||
};
|
||||
|
||||
/**
|
||||
* Provider info provided by the [[link-eip-6963]] discovery mechanism.
|
||||
*/
|
||||
export interface Eip6963ProviderInfo {
|
||||
uuid: string;
|
||||
name: string;
|
||||
icon: string;
|
||||
rdns: string;
|
||||
}
|
||||
|
||||
interface Eip6963ProviderDetail {
|
||||
info: Eip6963ProviderInfo;
|
||||
provider: Eip1193Provider;
|
||||
}
|
||||
|
||||
interface Eip6963Announcement {
|
||||
type: "eip6963:announceProvider";
|
||||
detail: Eip6963ProviderDetail
|
||||
}
|
||||
|
||||
export type BrowserProviderOptions = {
|
||||
polling?: boolean;
|
||||
staticNetwork?: null | boolean | Network;
|
||||
|
||||
cacheTimeout?: number;
|
||||
pollingInterval?: number;
|
||||
|
||||
providerInfo?: Eip6963ProviderInfo;
|
||||
};
|
||||
|
||||
/**
|
||||
* Specifies how [[link-eip-6963]] discovery should proceed.
|
||||
*
|
||||
* See: [[BrowserProvider-discover]]
|
||||
*/
|
||||
export interface BrowserDiscoverOptions {
|
||||
/**
|
||||
* Override provider detection with this provider.
|
||||
*/
|
||||
provider?: Eip1193Provider;
|
||||
|
||||
/**
|
||||
* Duration to wait to detect providers. (default: 300ms)
|
||||
*/
|
||||
timeout?: number;
|
||||
|
||||
/**
|
||||
* Return the first detected provider. Otherwise wait for %%timeout%%
|
||||
* and allowing filtering before selecting the desired provider.
|
||||
*/
|
||||
anyProvider?: boolean;
|
||||
|
||||
/**
|
||||
* Use the provided window context. Useful in non-standard
|
||||
* environments or to hijack where a provider comes from.
|
||||
*/
|
||||
window?: any;
|
||||
|
||||
/**
|
||||
* Explicitly choose which provider to used once scanning is complete.
|
||||
*/
|
||||
filter?: (found: Array<Eip6963ProviderInfo>) => null | BrowserProvider |
|
||||
Eip6963ProviderInfo;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A **BrowserProvider** is intended to wrap an injected provider which
|
||||
* adheres to the [[link-eip-1193]] standard, which most (if not all)
|
||||
* currently do.
|
||||
*/
|
||||
export class BrowserProvider extends JsonRpcApiPollingProvider {
|
||||
#request: (method: string, params: Array<any> | Record<string, any>) => Promise<any>;
|
||||
|
||||
#providerInfo: null | Eip6963ProviderInfo;
|
||||
|
||||
/**
|
||||
* Connect to the %%ethereum%% provider, optionally forcing the
|
||||
* %%network%%.
|
||||
*/
|
||||
constructor(ethereum: Eip1193Provider, network?: Networkish, _options?: BrowserProviderOptions) {
|
||||
|
||||
// Copy the options
|
||||
const options: JsonRpcApiProviderOptions = Object.assign({ },
|
||||
((_options != null) ? _options: { }),
|
||||
{ batchMaxCount: 1 });
|
||||
|
||||
assertArgument(ethereum && ethereum.request, "invalid EIP-1193 provider", "ethereum", ethereum);
|
||||
|
||||
super(network, options);
|
||||
|
||||
this.#providerInfo = null;
|
||||
if (_options && _options.providerInfo) {
|
||||
this.#providerInfo = _options.providerInfo;
|
||||
}
|
||||
|
||||
this.#request = async (method: string, params: Array<any> | Record<string, any>) => {
|
||||
const payload = { method, params };
|
||||
this.emit("debug", { action: "sendEip1193Request", payload });
|
||||
try {
|
||||
const result = await ethereum.request(payload);
|
||||
this.emit("debug", { action: "receiveEip1193Result", result });
|
||||
return result;
|
||||
} catch (e: any) {
|
||||
const error = new Error(e.message);
|
||||
(<any>error).code = e.code;
|
||||
(<any>error).data = e.data;
|
||||
(<any>error).payload = payload;
|
||||
this.emit("debug", { action: "receiveEip1193Error", error });
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
get providerInfo(): null | Eip6963ProviderInfo {
|
||||
return this.#providerInfo;
|
||||
}
|
||||
|
||||
async send(method: string, params: Array<any> | Record<string, any>): Promise<any> {
|
||||
await this._start();
|
||||
|
||||
return await super.send(method, params);
|
||||
}
|
||||
|
||||
async _send(payload: JsonRpcPayload | Array<JsonRpcPayload>): Promise<Array<JsonRpcResult | JsonRpcError>> {
|
||||
assertArgument(!Array.isArray(payload), "EIP-1193 does not support batch request", "payload", payload);
|
||||
|
||||
try {
|
||||
const result = await this.#request(payload.method, payload.params || [ ]);
|
||||
return [ { id: payload.id, result } ];
|
||||
} catch (e: any) {
|
||||
return [ {
|
||||
id: payload.id,
|
||||
error: { code: e.code, data: e.data, message: e.message }
|
||||
} ];
|
||||
}
|
||||
}
|
||||
|
||||
getRpcError(payload: JsonRpcPayload, error: JsonRpcError): Error {
|
||||
|
||||
error = JSON.parse(JSON.stringify(error));
|
||||
|
||||
// EIP-1193 gives us some machine-readable error codes, so rewrite
|
||||
// them into Ethers standard errors.
|
||||
switch (error.error.code || -1) {
|
||||
case 4001:
|
||||
error.error.message = `ethers-user-denied: ${ error.error.message }`;
|
||||
break;
|
||||
case 4200:
|
||||
error.error.message = `ethers-unsupported: ${ error.error.message }`;
|
||||
break;
|
||||
}
|
||||
|
||||
return super.getRpcError(payload, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves to ``true`` if the provider manages the %%address%%.
|
||||
*/
|
||||
async hasSigner(address: number | string): Promise<boolean> {
|
||||
if (address == null) { address = 0; }
|
||||
|
||||
const accounts = await this.send("eth_accounts", [ ]);
|
||||
if (typeof(address) === "number") {
|
||||
return (accounts.length > address);
|
||||
}
|
||||
|
||||
address = address.toLowerCase();
|
||||
return accounts.filter((a: string) => (a.toLowerCase() === address)).length !== 0;
|
||||
}
|
||||
|
||||
async getSigner(address?: number | string): Promise<JsonRpcSigner> {
|
||||
if (address == null) { address = 0; }
|
||||
|
||||
if (!(await this.hasSigner(address))) {
|
||||
try {
|
||||
await this.#request("eth_requestAccounts", [ ]);
|
||||
|
||||
} catch (error: any) {
|
||||
const payload = error.payload;
|
||||
throw this.getRpcError(payload, { id: payload.id, error });
|
||||
}
|
||||
}
|
||||
|
||||
return await super.getSigner(address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Discover and connect to a Provider in the Browser using the
|
||||
* [[link-eip-6963]] discovery mechanism. If no providers are
|
||||
* present, ``null`` is resolved.
|
||||
*/
|
||||
static async discover(options?: BrowserDiscoverOptions): Promise<null | BrowserProvider> {
|
||||
if (options == null) { options = { }; }
|
||||
|
||||
if (options.provider) {
|
||||
return new BrowserProvider(options.provider);
|
||||
}
|
||||
|
||||
const context = options.window ? options.window:
|
||||
(typeof(window) !== "undefined") ? window: null;
|
||||
|
||||
if (context == null) { return null; }
|
||||
|
||||
const anyProvider = options.anyProvider;
|
||||
if (anyProvider && context.ethereum) {
|
||||
return new BrowserProvider(context.ethereum);
|
||||
}
|
||||
|
||||
if (!("addEventListener" in context && "dispatchEvent" in context
|
||||
&& "removeEventListener" in context)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const timeout = options.timeout ? options.timeout: 300;
|
||||
if (timeout === 0) { return null; }
|
||||
|
||||
return await (new Promise((resolve, reject) => {
|
||||
let found: Array<Eip6963ProviderDetail> = [ ];
|
||||
|
||||
const addProvider = (event: Eip6963Announcement) => {
|
||||
found.push(event.detail);
|
||||
if (anyProvider) { finalize(); }
|
||||
};
|
||||
|
||||
const finalize = () => {
|
||||
clearTimeout(timer);
|
||||
|
||||
if (found.length) {
|
||||
|
||||
// If filtering is provided:
|
||||
if (options && options.filter) {
|
||||
|
||||
// Call filter, with a copies of found provider infos
|
||||
const filtered = options.filter(found.map(i =>
|
||||
Object.assign({ }, (i.info))));
|
||||
|
||||
if (filtered == null) {
|
||||
// No provider selected
|
||||
resolve(null);
|
||||
|
||||
} else if (filtered instanceof BrowserProvider) {
|
||||
// Custom provider created
|
||||
resolve(filtered);
|
||||
|
||||
} else {
|
||||
// Find the matching provider
|
||||
let match: null | Eip6963ProviderDetail = null;
|
||||
if (filtered.uuid) {
|
||||
const matches = found.filter(f =>
|
||||
(filtered.uuid === f.info.uuid));
|
||||
// @TODO: What should happen if multiple values
|
||||
// for the same UUID?
|
||||
match = matches[0];
|
||||
}
|
||||
|
||||
if (match) {
|
||||
const { provider, info } = match;
|
||||
resolve(new BrowserProvider(provider, undefined, {
|
||||
providerInfo: info
|
||||
}));
|
||||
} else {
|
||||
reject(makeError("filter returned unknown info", "UNSUPPORTED_OPERATION", {
|
||||
value: filtered
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// Pick the first found provider
|
||||
const { provider, info } = found[0];
|
||||
resolve(new BrowserProvider(provider, undefined, {
|
||||
providerInfo: info
|
||||
}));
|
||||
}
|
||||
|
||||
} else {
|
||||
// Nothing found
|
||||
resolve(null);
|
||||
}
|
||||
|
||||
context.removeEventListener(<any>"eip6963:announceProvider",
|
||||
addProvider);
|
||||
};
|
||||
|
||||
const timer = setTimeout(() => { finalize(); }, timeout);
|
||||
|
||||
context.addEventListener(<any>"eip6963:announceProvider",
|
||||
addProvider);
|
||||
|
||||
context.dispatchEvent(new Event("eip6963:requestProvider"));
|
||||
}));
|
||||
}
|
||||
}
|
||||
113
dev/env/node_modules/ethers/src.ts/providers/provider-chainstack.ts
generated
vendored
Executable file
113
dev/env/node_modules/ethers/src.ts/providers/provider-chainstack.ts
generated
vendored
Executable file
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* [[link-chainstack]] provides a third-party service for connecting to
|
||||
* various blockchains over JSON-RPC.
|
||||
*
|
||||
* **Supported Networks**
|
||||
*
|
||||
* - Ethereum Mainnet (``mainnet``)
|
||||
* - Arbitrum (``arbitrum``)
|
||||
* - BNB Smart Chain Mainnet (``bnb``)
|
||||
* - Polygon (``matic``)
|
||||
*
|
||||
* @_subsection: api/providers/thirdparty:Chainstack [providers-chainstack]
|
||||
*/
|
||||
import {
|
||||
defineProperties, FetchRequest, assertArgument
|
||||
} from "../utils/index.js";
|
||||
|
||||
import { showThrottleMessage } from "./community.js";
|
||||
import { Network } from "./network.js";
|
||||
import { JsonRpcProvider } from "./provider-jsonrpc.js";
|
||||
|
||||
import type { AbstractProvider } from "./abstract-provider.js";
|
||||
import type { CommunityResourcable } from "./community.js";
|
||||
import type { Networkish } from "./network.js";
|
||||
|
||||
|
||||
function getApiKey(name: string): string {
|
||||
switch (name) {
|
||||
case "mainnet": return "39f1d67cedf8b7831010a665328c9197";
|
||||
case "arbitrum": return "0550c209db33c3abf4cc927e1e18cea1"
|
||||
case "bnb": return "98b5a77e531614387366f6fc5da097f8";
|
||||
case "matic": return "cd9d4d70377471aa7c142ec4a4205249";
|
||||
}
|
||||
|
||||
assertArgument(false, "unsupported network", "network", name);
|
||||
}
|
||||
|
||||
function getHost(name: string): string {
|
||||
switch(name) {
|
||||
case "mainnet":
|
||||
return "ethereum-mainnet.core.chainstack.com";
|
||||
case "arbitrum":
|
||||
return "arbitrum-mainnet.core.chainstack.com";
|
||||
case "bnb":
|
||||
return "bsc-mainnet.core.chainstack.com";
|
||||
case "matic":
|
||||
return "polygon-mainnet.core.chainstack.com";
|
||||
}
|
||||
|
||||
assertArgument(false, "unsupported network", "network", name);
|
||||
}
|
||||
|
||||
/**
|
||||
* The **ChainstackProvider** connects to the [[link-chainstack]]
|
||||
* JSON-RPC end-points.
|
||||
*
|
||||
* By default, a highly-throttled API key is used, which is
|
||||
* appropriate for quick prototypes and simple scripts. To
|
||||
* gain access to an increased rate-limit, it is highly
|
||||
* recommended to [sign up here](link-chainstack).
|
||||
*/
|
||||
export class ChainstackProvider extends JsonRpcProvider implements CommunityResourcable {
|
||||
/**
|
||||
* The API key for the Chainstack connection.
|
||||
*/
|
||||
readonly apiKey!: string;
|
||||
|
||||
/**
|
||||
* Creates a new **ChainstackProvider**.
|
||||
*/
|
||||
constructor(_network?: Networkish, apiKey?: null | string) {
|
||||
if (_network == null) { _network = "mainnet"; }
|
||||
const network = Network.from(_network);
|
||||
|
||||
if (apiKey == null) { apiKey = getApiKey(network.name); }
|
||||
|
||||
const request = ChainstackProvider.getRequest(network, apiKey);
|
||||
super(request, network, { staticNetwork: network });
|
||||
|
||||
defineProperties<ChainstackProvider>(this, { apiKey });
|
||||
}
|
||||
|
||||
_getProvider(chainId: number): AbstractProvider {
|
||||
try {
|
||||
return new ChainstackProvider(chainId, this.apiKey);
|
||||
} catch (error) { }
|
||||
return super._getProvider(chainId);
|
||||
}
|
||||
|
||||
isCommunityResource(): boolean {
|
||||
return (this.apiKey === getApiKey(this._network.name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a prepared request for connecting to %%network%%
|
||||
* with %%apiKey%% and %%projectSecret%%.
|
||||
*/
|
||||
static getRequest(network: Network, apiKey?: null | string): FetchRequest {
|
||||
if (apiKey == null) { apiKey = getApiKey(network.name); }
|
||||
|
||||
const request = new FetchRequest(`https:/\/${ getHost(network.name) }/${ apiKey }`);
|
||||
request.allowGzip = true;
|
||||
|
||||
if (apiKey === getApiKey(network.name)) {
|
||||
request.retryFunc = async (request, response, attempt) => {
|
||||
showThrottleMessage("ChainstackProvider");
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
}
|
||||
24
dev/env/node_modules/ethers/src.ts/providers/provider-cloudflare.ts
generated
vendored
Executable file
24
dev/env/node_modules/ethers/src.ts/providers/provider-cloudflare.ts
generated
vendored
Executable file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* About Cloudflare
|
||||
*
|
||||
* @_subsection: api/providers/thirdparty:Cloudflare [providers-cloudflare]
|
||||
*/
|
||||
|
||||
import { assertArgument } from "../utils/index.js";
|
||||
|
||||
import { Network } from "./network.js";
|
||||
import { JsonRpcProvider } from "./provider-jsonrpc.js";
|
||||
|
||||
import type { Networkish } from "./network.js";
|
||||
|
||||
/**
|
||||
* About Cloudflare...
|
||||
*/
|
||||
export class CloudflareProvider extends JsonRpcProvider {
|
||||
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 });
|
||||
}
|
||||
}
|
||||
687
dev/env/node_modules/ethers/src.ts/providers/provider-etherscan.ts
generated
vendored
Executable file
687
dev/env/node_modules/ethers/src.ts/providers/provider-etherscan.ts
generated
vendored
Executable file
@@ -0,0 +1,687 @@
|
||||
/**
|
||||
* [[link-etherscan]] provides a third-party service for connecting to
|
||||
* various blockchains over a combination of JSON-RPC and custom API
|
||||
* endpoints.
|
||||
*
|
||||
* **Supported Networks**
|
||||
*
|
||||
* - Ethereum Mainnet (``mainnet``)
|
||||
* - Goerli Testnet (``goerli``)
|
||||
* - Sepolia Testnet (``sepolia``)
|
||||
* - Holesky Testnet (``holesky``)
|
||||
* - Arbitrum (``arbitrum``)
|
||||
* - Arbitrum Goerli Testnet (``arbitrum-goerli``)
|
||||
* - Base (``base``)
|
||||
* - Base Sepolia Testnet (``base-sepolia``)
|
||||
* - BNB Smart Chain Mainnet (``bnb``)
|
||||
* - BNB Smart Chain Testnet (``bnbt``)
|
||||
* - Optimism (``optimism``)
|
||||
* - Optimism Goerli Testnet (``optimism-goerli``)
|
||||
* - Polygon (``matic``)
|
||||
* - Polygon Mumbai Testnet (``matic-mumbai``)
|
||||
* - Polygon Amoy Testnet (``matic-amoy``)
|
||||
*
|
||||
* @_subsection api/providers/thirdparty:Etherscan [providers-etherscan]
|
||||
*/
|
||||
|
||||
import { AbiCoder } from "../abi/index.js";
|
||||
import { Contract } from "../contract/index.js";
|
||||
import { accessListify, Transaction } from "../transaction/index.js";
|
||||
import {
|
||||
defineProperties,
|
||||
hexlify, toQuantity,
|
||||
FetchRequest,
|
||||
assert, assertArgument, isError,
|
||||
// parseUnits,
|
||||
toUtf8String
|
||||
} from "../utils/index.js";
|
||||
|
||||
import { AbstractProvider } from "./abstract-provider.js";
|
||||
import { Network } from "./network.js";
|
||||
import { NetworkPlugin } from "./plugins-network.js";
|
||||
import { showThrottleMessage } from "./community.js";
|
||||
|
||||
import { PerformActionRequest } from "./abstract-provider.js";
|
||||
import type { Networkish } from "./network.js";
|
||||
//import type { } from "./pagination";
|
||||
import type { TransactionRequest } from "./provider.js";
|
||||
|
||||
// See: https://docs.etherscan.io/supported-chains
|
||||
const Supported = (
|
||||
"1 11155111 17000 560048 2741 11124 33111 33139 42170 " +
|
||||
"42161 421614 43114 43113 8453 84532 80069 80094 199 1029 81457 " +
|
||||
"168587773 56 97 42220 11142220 252 2523 100 999 737373 747474 " +
|
||||
"59144 59141 5000 5003 43521 143 10143 1287 1284 1285 10 " +
|
||||
"11155420 204 5611 80002 137 534352 534351 1329 1328 146 14601 " +
|
||||
"988 2201 1923 1924 167013 167000 130 1301 480 4801 51 50 324 300"
|
||||
).split(/ /g);
|
||||
|
||||
const THROTTLE = 2000;
|
||||
|
||||
function isPromise<T = any>(value: any): value is Promise<T> {
|
||||
return (value && typeof(value.then) === "function");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* When subscribing to the ``"debug"`` event on an Etherscan-based
|
||||
* provider, the events receive a **DebugEventEtherscanProvider**
|
||||
* payload.
|
||||
*
|
||||
* @_docloc: api/providers/thirdparty:Etherscan
|
||||
*/
|
||||
export type DebugEventEtherscanProvider = {
|
||||
action: "sendRequest",
|
||||
id: number,
|
||||
url: string,
|
||||
payload: Record<string, any>
|
||||
} | {
|
||||
action: "receiveRequest",
|
||||
id: number,
|
||||
result: any
|
||||
} | {
|
||||
action: "receiveError",
|
||||
id: number,
|
||||
error: any
|
||||
};
|
||||
|
||||
const EtherscanPluginId = "org.ethers.plugins.provider.Etherscan";
|
||||
|
||||
/**
|
||||
* A Network can include an **EtherscanPlugin** to provide
|
||||
* a custom base URL.
|
||||
*
|
||||
* @_docloc: api/providers/thirdparty:Etherscan
|
||||
*/
|
||||
export class EtherscanPlugin extends NetworkPlugin {
|
||||
/**
|
||||
* The Etherscan API base URL.
|
||||
*/
|
||||
readonly baseUrl!: string;
|
||||
|
||||
/**
|
||||
* Creates a new **EtherscanProvider** which will use
|
||||
* %%baseUrl%%.
|
||||
*/
|
||||
constructor(baseUrl: string) {
|
||||
super(EtherscanPluginId);
|
||||
defineProperties<EtherscanPlugin>(this, { baseUrl });
|
||||
}
|
||||
|
||||
clone(): EtherscanPlugin {
|
||||
return new EtherscanPlugin(this.baseUrl);
|
||||
}
|
||||
}
|
||||
|
||||
const skipKeys = [ "enableCcipRead" ];
|
||||
|
||||
let nextId = 1;
|
||||
|
||||
/**
|
||||
* The **EtherscanBaseProvider** is the super-class of
|
||||
* [[EtherscanProvider]], which should generally be used instead.
|
||||
*
|
||||
* Since the **EtherscanProvider** includes additional code for
|
||||
* [[Contract]] access, in //rare cases// that contracts are not
|
||||
* used, this class can reduce code size.
|
||||
*
|
||||
* @_docloc: api/providers/thirdparty:Etherscan
|
||||
*/
|
||||
export class EtherscanProvider extends AbstractProvider {
|
||||
|
||||
/**
|
||||
* The connected network.
|
||||
*/
|
||||
readonly network!: Network;
|
||||
|
||||
/**
|
||||
* The API key or null if using the community provided bandwidth.
|
||||
*/
|
||||
readonly apiKey!: null | string;
|
||||
|
||||
readonly #plugin: null | EtherscanPlugin;
|
||||
|
||||
/**
|
||||
* Creates a new **EtherscanBaseProvider**.
|
||||
*/
|
||||
constructor(_network?: Networkish, _apiKey?: string) {
|
||||
|
||||
const apiKey = (_apiKey != null) ? _apiKey: null;
|
||||
|
||||
super();
|
||||
|
||||
const network = Network.from(_network);
|
||||
|
||||
assertArgument(Supported.indexOf(`${ network.chainId }`) >= 0,
|
||||
"unsupported network", "network", network);
|
||||
|
||||
this.#plugin = network.getPlugin<EtherscanPlugin>(EtherscanPluginId);
|
||||
|
||||
defineProperties<EtherscanProvider>(this, { apiKey, network });
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the base URL.
|
||||
*
|
||||
* If an [[EtherscanPlugin]] is configured on the
|
||||
* [[EtherscanBaseProvider_network]], returns the plugin's
|
||||
* baseUrl.
|
||||
*
|
||||
* Deprecated; for Etherscan v2 the base is no longer a simply
|
||||
* host, but instead a URL including a chainId parameter. Changing
|
||||
* this to return a URL prefix could break some libraries, so it
|
||||
* is left intact but will be removed in the future as it is unused.
|
||||
*/
|
||||
getBaseUrl(): string {
|
||||
if (this.#plugin) { return this.#plugin.baseUrl; }
|
||||
|
||||
switch(this.network.name) {
|
||||
case "mainnet":
|
||||
return "https:/\/api.etherscan.io";
|
||||
case "goerli":
|
||||
return "https:/\/api-goerli.etherscan.io";
|
||||
case "sepolia":
|
||||
return "https:/\/api-sepolia.etherscan.io";
|
||||
case "holesky":
|
||||
return "https:/\/api-holesky.etherscan.io";
|
||||
|
||||
case "arbitrum":
|
||||
return "https:/\/api.arbiscan.io";
|
||||
case "arbitrum-goerli":
|
||||
return "https:/\/api-goerli.arbiscan.io";
|
||||
case "base":
|
||||
return "https:/\/api.basescan.org";
|
||||
case "base-sepolia":
|
||||
return "https:/\/api-sepolia.basescan.org";
|
||||
case "bnb":
|
||||
return "https:/\/api.bscscan.com";
|
||||
case "bnbt":
|
||||
return "https:/\/api-testnet.bscscan.com";
|
||||
case "matic":
|
||||
return "https:/\/api.polygonscan.com";
|
||||
case "matic-amoy":
|
||||
return "https:/\/api-amoy.polygonscan.com";
|
||||
case "matic-mumbai":
|
||||
return "https:/\/api-testnet.polygonscan.com";
|
||||
case "optimism":
|
||||
return "https:/\/api-optimistic.etherscan.io";
|
||||
case "optimism-goerli":
|
||||
return "https:/\/api-goerli-optimistic.etherscan.io";
|
||||
|
||||
default:
|
||||
}
|
||||
|
||||
assertArgument(false, "unsupported network", "network", this.network);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URL for the %%module%% and %%params%%.
|
||||
*/
|
||||
getUrl(module: string, params: Record<string, string>): string {
|
||||
let query = Object.keys(params).reduce((accum, key) => {
|
||||
const value = params[key];
|
||||
if (value != null) {
|
||||
accum += `&${ key }=${ value }`
|
||||
}
|
||||
return accum
|
||||
}, "");
|
||||
if (this.apiKey) { query += `&apikey=${ this.apiKey }`; }
|
||||
return `https:/\/api.etherscan.io/v2/api?chainid=${ this.network.chainId }&module=${ module }${ query }`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URL for using POST requests.
|
||||
*/
|
||||
getPostUrl(): string {
|
||||
return `https:/\/api.etherscan.io/v2/api?chainid=${ this.network.chainId }`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parameters for using POST requests.
|
||||
*/
|
||||
getPostData(module: string, params: Record<string, any>): Record<string, any> {
|
||||
params.module = module;
|
||||
params.apikey = this.apiKey;
|
||||
params.chainid = this.network.chainId;
|
||||
return params;
|
||||
}
|
||||
|
||||
async detectNetwork(): Promise<Network> {
|
||||
return this.network;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves to the result of calling %%module%% with %%params%%.
|
||||
*
|
||||
* If %%post%%, the request is made as a POST request.
|
||||
*/
|
||||
async fetch(module: string, params: Record<string, any>, post?: boolean): Promise<any> {
|
||||
const id = nextId++;
|
||||
|
||||
const url = (post ? this.getPostUrl(): this.getUrl(module, params));
|
||||
const payload = (post ? this.getPostData(module, params): null);
|
||||
|
||||
this.emit("debug", { action: "sendRequest", id, url, payload: payload });
|
||||
|
||||
const request = new FetchRequest(url);
|
||||
request.setThrottleParams({ slotInterval: 1000 });
|
||||
request.retryFunc = (req, resp, attempt: number) => {
|
||||
if (this.isCommunityResource()) {
|
||||
showThrottleMessage("Etherscan");
|
||||
}
|
||||
return Promise.resolve(true);
|
||||
};
|
||||
request.processFunc = async (request, response) => {
|
||||
const result = response.hasBody() ? JSON.parse(toUtf8String(response.body)): { };
|
||||
const throttle = ((typeof(result.result) === "string") ? result.result: "").toLowerCase().indexOf("rate limit") >= 0;
|
||||
if (module === "proxy") {
|
||||
// This JSON response indicates we are being throttled
|
||||
if (result && result.status == 0 && result.message == "NOTOK" && throttle) {
|
||||
this.emit("debug", { action: "receiveError", id, reason: "proxy-NOTOK", error: result });
|
||||
response.throwThrottleError(result.result, THROTTLE);
|
||||
}
|
||||
} else {
|
||||
if (throttle) {
|
||||
this.emit("debug", { action: "receiveError", id, reason: "null result", error: result.result });
|
||||
response.throwThrottleError(result.result, THROTTLE);
|
||||
}
|
||||
}
|
||||
return response;
|
||||
};
|
||||
|
||||
if (payload) {
|
||||
request.setHeader("content-type", "application/x-www-form-urlencoded; charset=UTF-8");
|
||||
request.body = Object.keys(payload).map((k) => `${ k }=${ payload[k] }`).join("&");
|
||||
}
|
||||
|
||||
const response = await request.send();
|
||||
try {
|
||||
response.assertOk();
|
||||
} catch (error) {
|
||||
this.emit("debug", { action: "receiveError", id, error, reason: "assertOk" });
|
||||
assert(false, "response error", "SERVER_ERROR", { request, response });
|
||||
}
|
||||
|
||||
if (!response.hasBody()) {
|
||||
this.emit("debug", { action: "receiveError", id, error: "missing body", reason: "null body" });
|
||||
assert(false, "missing response", "SERVER_ERROR", { request, response });
|
||||
}
|
||||
|
||||
const result = JSON.parse(toUtf8String(response.body));
|
||||
if (module === "proxy") {
|
||||
if (result.jsonrpc != "2.0") {
|
||||
this.emit("debug", { action: "receiveError", id, result, reason: "invalid JSON-RPC" });
|
||||
assert(false, "invalid JSON-RPC response (missing jsonrpc='2.0')", "SERVER_ERROR", { request, response, info: { result } });
|
||||
}
|
||||
|
||||
if (result.error) {
|
||||
this.emit("debug", { action: "receiveError", id, result, reason: "JSON-RPC error" });
|
||||
assert(false, "error response", "SERVER_ERROR", { request, response, info: { result } });
|
||||
}
|
||||
|
||||
this.emit("debug", { action: "receiveRequest", id, result });
|
||||
|
||||
return result.result;
|
||||
|
||||
} else {
|
||||
// getLogs, getHistory have weird success responses
|
||||
if (result.status == 0 && (result.message === "No records found" || result.message === "No transactions found")) {
|
||||
this.emit("debug", { action: "receiveRequest", id, result });
|
||||
return result.result;
|
||||
}
|
||||
|
||||
if (result.status != 1 || (typeof(result.message) === "string" && !result.message.match(/^OK/))) {
|
||||
this.emit("debug", { action: "receiveError", id, result });
|
||||
assert(false, "error response", "SERVER_ERROR", { request, response, info: { result } });
|
||||
}
|
||||
|
||||
this.emit("debug", { action: "receiveRequest", id, result });
|
||||
|
||||
return result.result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns %%transaction%% normalized for the Etherscan API.
|
||||
*/
|
||||
_getTransactionPostData(transaction: TransactionRequest): Record<string, string> {
|
||||
const result: Record<string, string> = { };
|
||||
for (let key in transaction) {
|
||||
if (skipKeys.indexOf(key) >= 0) { continue; }
|
||||
|
||||
if ((<any>transaction)[key] == null) { continue; }
|
||||
let value = (<any>transaction)[key];
|
||||
if (key === "type" && value === 0) { continue; }
|
||||
if (key === "blockTag" && value === "latest") { continue; }
|
||||
|
||||
// Quantity-types require no leading zero, unless 0
|
||||
if ((<any>{ type: true, gasLimit: true, gasPrice: true, maxFeePerGs: true, maxPriorityFeePerGas: true, nonce: true, value: true })[key]) {
|
||||
value = toQuantity(value);
|
||||
|
||||
} else if (key === "accessList") {
|
||||
value = "[" + accessListify(value).map((set) => {
|
||||
return `{address:"${ set.address }",storageKeys:["${ set.storageKeys.join('","') }"]}`;
|
||||
}).join(",") + "]";
|
||||
|
||||
} else if (key === "blobVersionedHashes") {
|
||||
if (value.length === 0) { continue; }
|
||||
|
||||
// @TODO: update this once the API supports blobs
|
||||
assert(false, "Etherscan API does not support blobVersionedHashes", "UNSUPPORTED_OPERATION", {
|
||||
operation: "_getTransactionPostData",
|
||||
info: { transaction }
|
||||
});
|
||||
|
||||
} else {
|
||||
value = hexlify(value);
|
||||
}
|
||||
result[key] = value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws the normalized Etherscan error.
|
||||
*/
|
||||
_checkError(req: PerformActionRequest, error: Error, transaction: any): never {
|
||||
// Pull any message out if, possible
|
||||
let message = "";
|
||||
if (isError(error, "SERVER_ERROR")) {
|
||||
// Check for an error emitted by a proxy call
|
||||
try {
|
||||
message = (<any>error).info.result.error.message;
|
||||
} catch (e) { }
|
||||
|
||||
if (!message) {
|
||||
try {
|
||||
message = (<any>error).info.message;
|
||||
} catch (e) { }
|
||||
}
|
||||
}
|
||||
|
||||
if (req.method === "estimateGas") {
|
||||
if (!message.match(/revert/i) && message.match(/insufficient funds/i)) {
|
||||
assert(false, "insufficient funds", "INSUFFICIENT_FUNDS", {
|
||||
transaction: req.transaction
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (req.method === "call" || req.method === "estimateGas") {
|
||||
if (message.match(/execution reverted/i)) {
|
||||
let data = "";
|
||||
try {
|
||||
data = (<any>error).info.result.error.data;
|
||||
} catch (error) { }
|
||||
|
||||
const e = AbiCoder.getBuiltinCallException(req.method, <any>req.transaction, data);
|
||||
e.info = { request: req, error }
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
if (message) {
|
||||
if (req.method === "broadcastTransaction") {
|
||||
const transaction = Transaction.from(req.signedTransaction);
|
||||
if (message.match(/replacement/i) && message.match(/underpriced/i)) {
|
||||
assert(false, "replacement fee too low", "REPLACEMENT_UNDERPRICED", {
|
||||
transaction
|
||||
});
|
||||
}
|
||||
|
||||
if (message.match(/insufficient funds/)) {
|
||||
assert(false, "insufficient funds for intrinsic transaction cost", "INSUFFICIENT_FUNDS", {
|
||||
transaction
|
||||
});
|
||||
}
|
||||
|
||||
if (message.match(/same hash was already imported|transaction nonce is too low|nonce too low/)) {
|
||||
assert(false, "nonce has already been used", "NONCE_EXPIRED", {
|
||||
transaction
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Something we could not process
|
||||
throw error;
|
||||
}
|
||||
|
||||
async _detectNetwork(): Promise<Network> {
|
||||
return this.network;
|
||||
}
|
||||
|
||||
async _perform(req: PerformActionRequest): Promise<any> {
|
||||
switch (req.method) {
|
||||
case "chainId":
|
||||
return this.network.chainId;
|
||||
|
||||
case "getBlockNumber":
|
||||
return this.fetch("proxy", { action: "eth_blockNumber" });
|
||||
|
||||
case "getGasPrice":
|
||||
return this.fetch("proxy", { action: "eth_gasPrice" });
|
||||
|
||||
case "getPriorityFee":
|
||||
// This is temporary until Etherscan completes support
|
||||
if (this.network.name === "mainnet") {
|
||||
return "1000000000";
|
||||
} else if (this.network.name === "optimism") {
|
||||
return "1000000";
|
||||
} else {
|
||||
throw new Error("fallback onto the AbstractProvider default");
|
||||
}
|
||||
/* Working with Etherscan to get this added:
|
||||
try {
|
||||
const test = await this.fetch("proxy", {
|
||||
action: "eth_maxPriorityFeePerGas"
|
||||
});
|
||||
console.log(test);
|
||||
return test;
|
||||
} catch (e) {
|
||||
console.log("DEBUG", e);
|
||||
throw e;
|
||||
}
|
||||
*/
|
||||
/* This might be safe; but due to rounding neither myself
|
||||
or Etherscan are necessarily comfortable with this. :)
|
||||
try {
|
||||
const result = await this.fetch("gastracker", { action: "gasoracle" });
|
||||
console.log(result);
|
||||
const gasPrice = parseUnits(result.SafeGasPrice, "gwei");
|
||||
const baseFee = parseUnits(result.suggestBaseFee, "gwei");
|
||||
const priorityFee = gasPrice - baseFee;
|
||||
if (priorityFee < 0) { throw new Error("negative priority fee; defer to abstract provider default"); }
|
||||
return priorityFee;
|
||||
} catch (error) {
|
||||
console.log("DEBUG", error);
|
||||
throw error;
|
||||
}
|
||||
*/
|
||||
|
||||
case "getBalance":
|
||||
// Returns base-10 result
|
||||
return this.fetch("account", {
|
||||
action: "balance",
|
||||
address: req.address,
|
||||
tag: req.blockTag
|
||||
});
|
||||
|
||||
case "getTransactionCount":
|
||||
return this.fetch("proxy", {
|
||||
action: "eth_getTransactionCount",
|
||||
address: req.address,
|
||||
tag: req.blockTag
|
||||
});
|
||||
|
||||
case "getCode":
|
||||
return this.fetch("proxy", {
|
||||
action: "eth_getCode",
|
||||
address: req.address,
|
||||
tag: req.blockTag
|
||||
});
|
||||
|
||||
case "getStorage":
|
||||
return this.fetch("proxy", {
|
||||
action: "eth_getStorageAt",
|
||||
address: req.address,
|
||||
position: req.position,
|
||||
tag: req.blockTag
|
||||
});
|
||||
|
||||
case "broadcastTransaction":
|
||||
return this.fetch("proxy", {
|
||||
action: "eth_sendRawTransaction",
|
||||
hex: req.signedTransaction
|
||||
}, true).catch((error) => {
|
||||
return this._checkError(req, <Error>error, req.signedTransaction);
|
||||
});
|
||||
|
||||
case "getBlock":
|
||||
if ("blockTag" in req) {
|
||||
return this.fetch("proxy", {
|
||||
action: "eth_getBlockByNumber",
|
||||
tag: req.blockTag,
|
||||
boolean: (req.includeTransactions ? "true": "false")
|
||||
});
|
||||
}
|
||||
|
||||
assert(false, "getBlock by blockHash not supported by Etherscan", "UNSUPPORTED_OPERATION", {
|
||||
operation: "getBlock(blockHash)"
|
||||
});
|
||||
|
||||
case "getTransaction":
|
||||
return this.fetch("proxy", {
|
||||
action: "eth_getTransactionByHash",
|
||||
txhash: req.hash
|
||||
});
|
||||
|
||||
case "getTransactionReceipt":
|
||||
return this.fetch("proxy", {
|
||||
action: "eth_getTransactionReceipt",
|
||||
txhash: req.hash
|
||||
});
|
||||
|
||||
case "call": {
|
||||
if (req.blockTag !== "latest") {
|
||||
throw new Error("EtherscanProvider does not support blockTag for call");
|
||||
}
|
||||
|
||||
const postData = this._getTransactionPostData(req.transaction);
|
||||
postData.module = "proxy";
|
||||
postData.action = "eth_call";
|
||||
|
||||
try {
|
||||
return await this.fetch("proxy", postData, true);
|
||||
} catch (error) {
|
||||
return this._checkError(req, <Error>error, req.transaction);
|
||||
}
|
||||
}
|
||||
|
||||
case "estimateGas": {
|
||||
const postData = this._getTransactionPostData(req.transaction);
|
||||
postData.module = "proxy";
|
||||
postData.action = "eth_estimateGas";
|
||||
|
||||
try {
|
||||
return await this.fetch("proxy", postData, true);
|
||||
} catch (error) {
|
||||
return this._checkError(req, <Error>error, req.transaction);
|
||||
}
|
||||
}
|
||||
/*
|
||||
case "getLogs": {
|
||||
// Needs to complain if more than one address is passed in
|
||||
const args: Record<string, any> = { action: "getLogs" }
|
||||
|
||||
if (params.filter.fromBlock) {
|
||||
args.fromBlock = checkLogTag(params.filter.fromBlock);
|
||||
}
|
||||
|
||||
if (params.filter.toBlock) {
|
||||
args.toBlock = checkLogTag(params.filter.toBlock);
|
||||
}
|
||||
|
||||
if (params.filter.address) {
|
||||
args.address = params.filter.address;
|
||||
}
|
||||
|
||||
// @TODO: We can handle slightly more complicated logs using the logs API
|
||||
if (params.filter.topics && params.filter.topics.length > 0) {
|
||||
if (params.filter.topics.length > 1) {
|
||||
logger.throwError("unsupported topic count", Logger.Errors.UNSUPPORTED_OPERATION, { topics: params.filter.topics });
|
||||
}
|
||||
if (params.filter.topics.length === 1) {
|
||||
const topic0 = params.filter.topics[0];
|
||||
if (typeof(topic0) !== "string" || topic0.length !== 66) {
|
||||
logger.throwError("unsupported topic format", Logger.Errors.UNSUPPORTED_OPERATION, { topic0: topic0 });
|
||||
}
|
||||
args.topic0 = topic0;
|
||||
}
|
||||
}
|
||||
|
||||
const logs: Array<any> = await this.fetch("logs", args);
|
||||
|
||||
// Cache txHash => blockHash
|
||||
let blocks: { [tag: string]: string } = {};
|
||||
|
||||
// Add any missing blockHash to the logs
|
||||
for (let i = 0; i < logs.length; i++) {
|
||||
const log = logs[i];
|
||||
if (log.blockHash != null) { continue; }
|
||||
if (blocks[log.blockNumber] == null) {
|
||||
const block = await this.getBlock(log.blockNumber);
|
||||
if (block) {
|
||||
blocks[log.blockNumber] = block.hash;
|
||||
}
|
||||
}
|
||||
|
||||
log.blockHash = blocks[log.blockNumber];
|
||||
}
|
||||
|
||||
return logs;
|
||||
}
|
||||
*/
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return super._perform(req);
|
||||
}
|
||||
|
||||
async getNetwork(): Promise<Network> {
|
||||
return this.network;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves to the current price of ether.
|
||||
*
|
||||
* This returns ``0`` on any network other than ``mainnet``.
|
||||
*/
|
||||
async getEtherPrice(): Promise<number> {
|
||||
if (this.network.name !== "mainnet") { return 0.0; }
|
||||
return parseFloat((await this.fetch("stats", { action: "ethprice" })).ethusd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves to a [Contract]] for %%address%%, using the
|
||||
* Etherscan API to retreive the Contract ABI.
|
||||
*/
|
||||
async getContract(_address: string): Promise<null | Contract> {
|
||||
let address = this._getAddress(_address);
|
||||
if (isPromise(address)) { address = await address; }
|
||||
|
||||
try {
|
||||
const resp = await this.fetch("contract", {
|
||||
action: "getabi", address });
|
||||
const abi = JSON.parse(resp);
|
||||
return new Contract(address, abi, this);
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
isCommunityResource(): boolean {
|
||||
return (this.apiKey == null);
|
||||
}
|
||||
}
|
||||
801
dev/env/node_modules/ethers/src.ts/providers/provider-fallback.ts
generated
vendored
Executable file
801
dev/env/node_modules/ethers/src.ts/providers/provider-fallback.ts
generated
vendored
Executable file
@@ -0,0 +1,801 @@
|
||||
/**
|
||||
* A **FallbackProvider** provides resilience, security and performance
|
||||
* in a way that is customizable and configurable.
|
||||
*
|
||||
* @_section: api/providers/fallback-provider:Fallback Provider [about-fallback-provider]
|
||||
*/
|
||||
import {
|
||||
assert, assertArgument, getBigInt, getNumber, isError
|
||||
} from "../utils/index.js";
|
||||
|
||||
import { AbstractProvider } from "./abstract-provider.js";
|
||||
import { Network } from "./network.js"
|
||||
|
||||
import type { PerformActionRequest } from "./abstract-provider.js";
|
||||
import type { Networkish } from "./network.js"
|
||||
|
||||
const BN_1 = BigInt("1");
|
||||
const BN_2 = BigInt("2");
|
||||
|
||||
function shuffle<T = any>(array: Array<T>): void {
|
||||
for (let i = array.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
const tmp = array[i];
|
||||
array[i] = array[j];
|
||||
array[j] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
function stall(duration: number): Promise<void> {
|
||||
return new Promise((resolve) => { setTimeout(resolve, duration); });
|
||||
}
|
||||
|
||||
function getTime(): number { return (new Date()).getTime(); }
|
||||
|
||||
function stringify(value: any): string {
|
||||
return JSON.stringify(value, (key, value) => {
|
||||
if (typeof(value) === "bigint") {
|
||||
return { type: "bigint", value: value.toString() };
|
||||
}
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* A configuration entry for how to use a [[Provider]].
|
||||
*/
|
||||
export interface FallbackProviderConfig {
|
||||
|
||||
/**
|
||||
* The provider.
|
||||
*/
|
||||
provider: AbstractProvider;
|
||||
|
||||
/**
|
||||
* The amount of time to wait before kicking off the next provider.
|
||||
*
|
||||
* Any providers that have not responded can still respond and be
|
||||
* counted, but this ensures new providers start.
|
||||
*/
|
||||
stallTimeout?: number;
|
||||
|
||||
/**
|
||||
* The priority. Lower priority providers are dispatched first.
|
||||
*/
|
||||
priority?: number;
|
||||
|
||||
/**
|
||||
* The amount of weight a provider is given against the quorum.
|
||||
*/
|
||||
weight?: number;
|
||||
};
|
||||
|
||||
const defaultConfig = { stallTimeout: 400, priority: 1, weight: 1 };
|
||||
|
||||
// We track a bunch of extra stuff that might help debug problems or
|
||||
// optimize infrastructure later on.
|
||||
/**
|
||||
* The statistics and state maintained for a [[Provider]].
|
||||
*/
|
||||
export interface FallbackProviderState extends Required<FallbackProviderConfig> {
|
||||
|
||||
/**
|
||||
* The most recent blockNumber this provider has reported (-2 if none).
|
||||
*/
|
||||
blockNumber: number;
|
||||
|
||||
/**
|
||||
* The number of total requests ever sent to this provider.
|
||||
*/
|
||||
requests: number;
|
||||
|
||||
/**
|
||||
* The number of responses that errored.
|
||||
*/
|
||||
errorResponses: number;
|
||||
|
||||
/**
|
||||
* The number of responses that occured after the result resolved.
|
||||
*/
|
||||
lateResponses: number;
|
||||
|
||||
/**
|
||||
* How many times syncing was required to catch up the expected block.
|
||||
*/
|
||||
outOfSync: number;
|
||||
|
||||
/**
|
||||
* The number of requests which reported unsupported operation.
|
||||
*/
|
||||
unsupportedEvents: number;
|
||||
|
||||
/**
|
||||
* A rolling average (5% current duration) for response time.
|
||||
*/
|
||||
rollingDuration: number;
|
||||
|
||||
/**
|
||||
* The ratio of quorum-agreed results to total.
|
||||
*/
|
||||
score: number;
|
||||
}
|
||||
|
||||
interface Config extends FallbackProviderState {
|
||||
_updateNumber: null | Promise<any>;
|
||||
_network: null | Network;
|
||||
_totalTime: number;
|
||||
_lastFatalError: null | Error;
|
||||
_lastFatalErrorTimestamp: number;
|
||||
}
|
||||
|
||||
const defaultState = {
|
||||
blockNumber: -2, requests: 0, lateResponses: 0, errorResponses: 0,
|
||||
outOfSync: -1, unsupportedEvents: 0, rollingDuration: 0, score: 0,
|
||||
_network: null, _updateNumber: null, _totalTime: 0,
|
||||
_lastFatalError: null, _lastFatalErrorTimestamp: 0
|
||||
};
|
||||
|
||||
|
||||
async function waitForSync(config: Config, blockNumber: number): Promise<void> {
|
||||
while (config.blockNumber < 0 || config.blockNumber < blockNumber) {
|
||||
if (!config._updateNumber) {
|
||||
config._updateNumber = (async () => {
|
||||
try {
|
||||
const blockNumber = await config.provider.getBlockNumber();
|
||||
if (blockNumber > config.blockNumber) {
|
||||
config.blockNumber = blockNumber;
|
||||
}
|
||||
} catch (error: any) {
|
||||
config.blockNumber = -2;
|
||||
config._lastFatalError = error;
|
||||
config._lastFatalErrorTimestamp = getTime();
|
||||
}
|
||||
config._updateNumber = null;
|
||||
})();
|
||||
}
|
||||
await config._updateNumber;
|
||||
config.outOfSync++;
|
||||
if (config._lastFatalError) { break; }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional options to configure a [[FallbackProvider]].
|
||||
*/
|
||||
export type FallbackProviderOptions = {
|
||||
// How many providers must agree on a value before reporting
|
||||
// back the response
|
||||
quorum?: number;
|
||||
|
||||
// How many providers must have reported the same event
|
||||
// for it to be emitted (currently unimplmented)
|
||||
eventQuorum?: number;
|
||||
|
||||
// How many providers to dispatch each event to simultaneously.
|
||||
// Set this to 0 to use getLog polling, which implies eventQuorum
|
||||
// is equal to quorum. (currently unimplemented)
|
||||
eventWorkers?: number;
|
||||
|
||||
cacheTimeout?: number;
|
||||
|
||||
pollingInterval?: number;
|
||||
};
|
||||
|
||||
type RunnerResult = { result: any } | { error: Error };
|
||||
|
||||
type RunnerState = {
|
||||
config: Config;
|
||||
staller: null | Promise<void>;
|
||||
didBump: boolean;
|
||||
perform: null | Promise<any>;
|
||||
result: null | RunnerResult;
|
||||
}
|
||||
|
||||
function _normalize(value: any): string {
|
||||
if (value == null) { return "null"; }
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
return "[" + (value.map(_normalize)).join(",") + "]";
|
||||
}
|
||||
|
||||
if (typeof(value) === "object" && typeof(value.toJSON) === "function") {
|
||||
return _normalize(value.toJSON());
|
||||
}
|
||||
|
||||
switch (typeof(value)) {
|
||||
case "boolean": case "symbol":
|
||||
return value.toString();
|
||||
case "bigint": case "number":
|
||||
return BigInt(value).toString();
|
||||
case "string":
|
||||
return JSON.stringify(value);
|
||||
case "object": {
|
||||
const keys = Object.keys(value);
|
||||
keys.sort();
|
||||
return "{" + keys.map((k) => `${ JSON.stringify(k) }:${ _normalize(value[k]) }`).join(",") + "}";
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Could not serialize", value);
|
||||
throw new Error("Hmm...");
|
||||
}
|
||||
|
||||
function normalizeResult(method: string, value: RunnerResult): { tag: string, value: any } {
|
||||
|
||||
if ("error" in value) {
|
||||
const error = value.error;
|
||||
|
||||
let tag: string;
|
||||
if (isError(error, "CALL_EXCEPTION")) {
|
||||
tag = _normalize(Object.assign({ }, error, {
|
||||
shortMessage: undefined, reason: undefined, info: undefined
|
||||
}));
|
||||
} else {
|
||||
tag = _normalize(error)
|
||||
}
|
||||
|
||||
return { tag, value: error };
|
||||
}
|
||||
|
||||
const result = value.result;
|
||||
return { tag: _normalize(result), value: result };
|
||||
}
|
||||
|
||||
type TallyResult = {
|
||||
tag: string;
|
||||
value: any;
|
||||
weight: number;
|
||||
};
|
||||
|
||||
// This strategy picks the highest weight result, as long as the weight is
|
||||
// equal to or greater than quorum
|
||||
function checkQuorum(quorum: number, results: Array<TallyResult>): any | Error {
|
||||
const tally: Map<string, { value: any, weight: number }> = new Map();
|
||||
for (const { value, tag, weight } of results) {
|
||||
const t = tally.get(tag) || { value, weight: 0 };
|
||||
t.weight += weight;
|
||||
tally.set(tag, t);
|
||||
}
|
||||
|
||||
let best: null | { value: any, weight: number } = null;
|
||||
for (const r of tally.values()) {
|
||||
if (r.weight >= quorum && (!best || r.weight > best.weight)) {
|
||||
best = r;
|
||||
}
|
||||
}
|
||||
|
||||
if (best) { return best.value; }
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getMedian(quorum: number, results: Array<TallyResult>): undefined | bigint | Error {
|
||||
let resultWeight = 0;
|
||||
|
||||
const errorMap: Map<string, { weight: number, value: Error }> = new Map();
|
||||
let bestError: null | { weight: number, value: Error } = null;
|
||||
|
||||
const values: Array<bigint> = [ ];
|
||||
for (const { value, tag, weight } of results) {
|
||||
if (value instanceof Error) {
|
||||
const e = errorMap.get(tag) || { value, weight: 0 };
|
||||
e.weight += weight;
|
||||
errorMap.set(tag, e);
|
||||
|
||||
if (bestError == null || e.weight > bestError.weight) { bestError = e; }
|
||||
} else {
|
||||
values.push(BigInt(value));
|
||||
resultWeight += weight;
|
||||
}
|
||||
}
|
||||
|
||||
if (resultWeight < quorum) {
|
||||
// We have quorum for an error
|
||||
if (bestError && bestError.weight >= quorum) { return bestError.value; }
|
||||
|
||||
// We do not have quorum for a result
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Get the sorted values
|
||||
values.sort((a, b) => ((a < b) ? -1: (b > a) ? 1: 0));
|
||||
|
||||
const mid = Math.floor(values.length / 2);
|
||||
|
||||
// Odd-length; take the middle value
|
||||
if (values.length % 2) { return values[mid]; }
|
||||
|
||||
// Even length; take the ceiling of the mean of the center two values
|
||||
return (values[mid - 1] + values[mid] + BN_1) / BN_2;
|
||||
}
|
||||
|
||||
function getAnyResult(quorum: number, results: Array<TallyResult>): undefined | any | Error {
|
||||
// If any value or error meets quorum, that is our preferred result
|
||||
const result = checkQuorum(quorum, results);
|
||||
if (result !== undefined) { return result; }
|
||||
|
||||
// Otherwise, do we have any result?
|
||||
for (const r of results) {
|
||||
if (r.value) { return r.value; }
|
||||
}
|
||||
|
||||
// Nope!
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getFuzzyMode(quorum: number, results: Array<TallyResult>): undefined | number {
|
||||
if (quorum === 1) { return getNumber(<bigint>getMedian(quorum, results), "%internal"); }
|
||||
|
||||
const tally: Map<number, { result: number, weight: number }> = new Map();
|
||||
const add = (result: number, weight: number) => {
|
||||
const t = tally.get(result) || { result, weight: 0 };
|
||||
t.weight += weight;
|
||||
tally.set(result, t);
|
||||
};
|
||||
|
||||
for (const { weight, value } of results) {
|
||||
const r = getNumber(value);
|
||||
add(r - 1, weight);
|
||||
add(r, weight);
|
||||
add(r + 1, weight);
|
||||
}
|
||||
|
||||
let bestWeight = 0;
|
||||
let bestResult: undefined | number = undefined;
|
||||
|
||||
for (const { weight, result } of tally.values()) {
|
||||
// Use this result, if this result meets quorum and has either:
|
||||
// - a better weight
|
||||
// - or equal weight, but the result is larger
|
||||
if (weight >= quorum && (weight > bestWeight || (bestResult != null && weight === bestWeight && result > bestResult))) {
|
||||
bestWeight = weight;
|
||||
bestResult = result;
|
||||
}
|
||||
}
|
||||
|
||||
return bestResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* A **FallbackProvider** manages several [[Providers]] providing
|
||||
* resilience by switching between slow or misbehaving nodes, security
|
||||
* by requiring multiple backends to aggree and performance by allowing
|
||||
* faster backends to respond earlier.
|
||||
*
|
||||
*/
|
||||
export class FallbackProvider extends AbstractProvider {
|
||||
|
||||
/**
|
||||
* The number of backends that must agree on a value before it is
|
||||
* accpeted.
|
||||
*/
|
||||
readonly quorum: number;
|
||||
|
||||
/**
|
||||
* @_ignore:
|
||||
*/
|
||||
readonly eventQuorum: number;
|
||||
|
||||
/**
|
||||
* @_ignore:
|
||||
*/
|
||||
readonly eventWorkers: number;
|
||||
|
||||
readonly #configs: Array<Config>;
|
||||
|
||||
#height: number;
|
||||
#initialSyncPromise: null | Promise<void>;
|
||||
|
||||
/**
|
||||
* Creates a new **FallbackProvider** with %%providers%% connected to
|
||||
* %%network%%.
|
||||
*
|
||||
* If a [[Provider]] is included in %%providers%%, defaults are used
|
||||
* for the configuration.
|
||||
*/
|
||||
constructor(providers: Array<AbstractProvider | FallbackProviderConfig>, network?: Networkish, options?: FallbackProviderOptions) {
|
||||
super(network, options);
|
||||
|
||||
this.#configs = providers.map((p) => {
|
||||
if (p instanceof AbstractProvider) {
|
||||
return Object.assign({ provider: p }, defaultConfig, defaultState );
|
||||
} else {
|
||||
return Object.assign({ }, defaultConfig, p, defaultState );
|
||||
}
|
||||
});
|
||||
|
||||
this.#height = -2;
|
||||
this.#initialSyncPromise = null;
|
||||
|
||||
if (options && options.quorum != null) {
|
||||
this.quorum = options.quorum;
|
||||
} else {
|
||||
this.quorum = Math.ceil(this.#configs.reduce((accum, config) => {
|
||||
accum += config.weight;
|
||||
return accum;
|
||||
}, 0) / 2);
|
||||
}
|
||||
|
||||
this.eventQuorum = 1;
|
||||
this.eventWorkers = 1;
|
||||
|
||||
assertArgument(this.quorum <= this.#configs.reduce((a, c) => (a + c.weight), 0),
|
||||
"quorum exceed provider weight", "quorum", this.quorum);
|
||||
}
|
||||
|
||||
get providerConfigs(): Array<FallbackProviderState> {
|
||||
return this.#configs.map((c) => {
|
||||
const result: any = Object.assign({ }, c);
|
||||
for (const key in result) {
|
||||
if (key[0] === "_") { delete result[key]; }
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
async _detectNetwork(): Promise<Network> {
|
||||
return Network.from(getBigInt(await this._perform({ method: "chainId" })));
|
||||
}
|
||||
|
||||
// @TODO: Add support to select providers to be the event subscriber
|
||||
//_getSubscriber(sub: Subscription): Subscriber {
|
||||
// throw new Error("@TODO");
|
||||
//}
|
||||
|
||||
/**
|
||||
* Transforms a %%req%% into the correct method call on %%provider%%.
|
||||
*/
|
||||
async _translatePerform(provider: AbstractProvider, req: PerformActionRequest): Promise<any> {
|
||||
switch (req.method) {
|
||||
case "broadcastTransaction":
|
||||
return await provider.broadcastTransaction(req.signedTransaction);
|
||||
case "call":
|
||||
return await provider.call(Object.assign({ }, req.transaction, { blockTag: req.blockTag }));
|
||||
case "chainId":
|
||||
return (await provider.getNetwork()).chainId;
|
||||
case "estimateGas":
|
||||
return await provider.estimateGas(req.transaction);
|
||||
case "getBalance":
|
||||
return await provider.getBalance(req.address, req.blockTag);
|
||||
case "getBlock": {
|
||||
const block = ("blockHash" in req) ? req.blockHash: req.blockTag;
|
||||
return await provider.getBlock(block, req.includeTransactions);
|
||||
}
|
||||
case "getBlockNumber":
|
||||
return await provider.getBlockNumber();
|
||||
case "getCode":
|
||||
return await provider.getCode(req.address, req.blockTag);
|
||||
case "getGasPrice":
|
||||
return (await provider.getFeeData()).gasPrice;
|
||||
case "getPriorityFee":
|
||||
return (await provider.getFeeData()).maxPriorityFeePerGas;
|
||||
case "getLogs":
|
||||
return await provider.getLogs(req.filter);
|
||||
case "getStorage":
|
||||
return await provider.getStorage(req.address, req.position, req.blockTag);
|
||||
case "getTransaction":
|
||||
return await provider.getTransaction(req.hash);
|
||||
case "getTransactionCount":
|
||||
return await provider.getTransactionCount(req.address, req.blockTag);
|
||||
case "getTransactionReceipt":
|
||||
return await provider.getTransactionReceipt(req.hash);
|
||||
case "getTransactionResult":
|
||||
return await provider.getTransactionResult(req.hash);
|
||||
}
|
||||
}
|
||||
|
||||
// Grab the next (random) config that is not already part of
|
||||
// the running set
|
||||
#getNextConfig(running: Set<RunnerState>): null | Config {
|
||||
// @TODO: Maybe do a check here to favour (heavily) providers that
|
||||
// do not require waitForSync and disfavour providers that
|
||||
// seem down-ish or are behaving slowly
|
||||
|
||||
const configs = Array.from(running).map((r) => r.config)
|
||||
|
||||
// Shuffle the states, sorted by priority
|
||||
const allConfigs = this.#configs.slice();
|
||||
shuffle(allConfigs);
|
||||
allConfigs.sort((a, b) => (a.priority - b.priority));
|
||||
|
||||
for (const config of allConfigs) {
|
||||
if (config._lastFatalError) { continue; }
|
||||
if (configs.indexOf(config) === -1) { return config; }
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Adds a new runner (if available) to running.
|
||||
#addRunner(running: Set<RunnerState>, req: PerformActionRequest): null | RunnerState {
|
||||
const config = this.#getNextConfig(running);
|
||||
|
||||
// No runners available
|
||||
if (config == null) { return null; }
|
||||
|
||||
// Create a new runner
|
||||
const runner: RunnerState = {
|
||||
config, result: null, didBump: false,
|
||||
perform: null, staller: null
|
||||
};
|
||||
|
||||
const now = getTime();
|
||||
|
||||
// Start performing this operation
|
||||
runner.perform = (async () => {
|
||||
try {
|
||||
config.requests++;
|
||||
const result = await this._translatePerform(config.provider, req);
|
||||
runner.result = { result };
|
||||
} catch (error: any) {
|
||||
config.errorResponses++;
|
||||
runner.result = { error };
|
||||
}
|
||||
|
||||
const dt = (getTime() - now);
|
||||
config._totalTime += dt;
|
||||
|
||||
config.rollingDuration = 0.95 * config.rollingDuration + 0.05 * dt;
|
||||
|
||||
runner.perform = null;
|
||||
})();
|
||||
|
||||
// Start a staller; when this times out, it's time to force
|
||||
// kicking off another runner because we are taking too long
|
||||
runner.staller = (async () => {
|
||||
await stall(config.stallTimeout);
|
||||
runner.staller = null;
|
||||
})();
|
||||
|
||||
running.add(runner);
|
||||
return runner;
|
||||
}
|
||||
|
||||
// Initializes the blockNumber and network for each runner and
|
||||
// blocks until initialized
|
||||
async #initialSync(): Promise<void> {
|
||||
let initialSync = this.#initialSyncPromise;
|
||||
if (!initialSync) {
|
||||
const promises: Array<Promise<any>> = [ ];
|
||||
this.#configs.forEach((config) => {
|
||||
promises.push((async () => {
|
||||
await waitForSync(config, 0);
|
||||
if (!config._lastFatalError) {
|
||||
config._network = await config.provider.getNetwork();
|
||||
}
|
||||
})());
|
||||
});
|
||||
|
||||
this.#initialSyncPromise = initialSync = (async () => {
|
||||
// Wait for all providers to have a block number and network
|
||||
await Promise.all(promises);
|
||||
|
||||
// Check all the networks match
|
||||
let chainId: null | bigint = null;
|
||||
for (const config of this.#configs) {
|
||||
if (config._lastFatalError) { continue; }
|
||||
const network = <Network>(config._network);
|
||||
if (chainId == null) {
|
||||
chainId = network.chainId;
|
||||
} else if (network.chainId !== chainId) {
|
||||
assert(false, "cannot mix providers on different networks", "UNSUPPORTED_OPERATION", {
|
||||
operation: "new FallbackProvider"
|
||||
});
|
||||
}
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
await initialSync
|
||||
}
|
||||
|
||||
|
||||
async #checkQuorum(running: Set<RunnerState>, req: PerformActionRequest): Promise<any> {
|
||||
// Get all the result objects
|
||||
const results: Array<TallyResult> = [ ];
|
||||
for (const runner of running) {
|
||||
if (runner.result != null) {
|
||||
const { tag, value } = normalizeResult(req.method, runner.result);
|
||||
results.push({ tag, value, weight: runner.config.weight });
|
||||
}
|
||||
}
|
||||
|
||||
// Are there enough results to event meet quorum?
|
||||
if (results.reduce((a, r) => (a + r.weight), 0) < this.quorum) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
switch (req.method) {
|
||||
case "getBlockNumber": {
|
||||
// We need to get the bootstrap block height
|
||||
if (this.#height === -2) {
|
||||
this.#height = Math.ceil(getNumber(<bigint>getMedian(this.quorum, this.#configs.filter((c) => (!c._lastFatalError)).map((c) => ({
|
||||
value: c.blockNumber,
|
||||
tag: getNumber(c.blockNumber).toString(),
|
||||
weight: c.weight
|
||||
})))));
|
||||
}
|
||||
|
||||
// Find the mode across all the providers, allowing for
|
||||
// a little drift between block heights
|
||||
const mode = getFuzzyMode(this.quorum, results);
|
||||
if (mode === undefined) { return undefined; }
|
||||
if (mode > this.#height) { this.#height = mode; }
|
||||
return this.#height;
|
||||
}
|
||||
|
||||
case "getGasPrice":
|
||||
case "getPriorityFee":
|
||||
case "estimateGas":
|
||||
return getMedian(this.quorum, results);
|
||||
|
||||
case "getBlock":
|
||||
// Pending blocks are in the mempool and already
|
||||
// quite untrustworthy; just grab anything
|
||||
if ("blockTag" in req && req.blockTag === "pending") {
|
||||
return getAnyResult(this.quorum, results);
|
||||
}
|
||||
return checkQuorum(this.quorum, results);
|
||||
|
||||
case "call":
|
||||
case "chainId":
|
||||
case "getBalance":
|
||||
case "getTransactionCount":
|
||||
case "getCode":
|
||||
case "getStorage":
|
||||
case "getTransaction":
|
||||
case "getTransactionReceipt":
|
||||
case "getLogs":
|
||||
return checkQuorum(this.quorum, results);
|
||||
|
||||
case "broadcastTransaction":
|
||||
return getAnyResult(this.quorum, results);
|
||||
}
|
||||
|
||||
assert(false, "unsupported method", "UNSUPPORTED_OPERATION", {
|
||||
operation: `_perform(${ stringify((<any>req).method) })`
|
||||
});
|
||||
}
|
||||
|
||||
async #waitForQuorum(running: Set<RunnerState>, req: PerformActionRequest): Promise<any> {
|
||||
if (running.size === 0) { throw new Error("no runners?!"); }
|
||||
|
||||
// Any promises that are interesting to watch for; an expired stall
|
||||
// or a successful perform
|
||||
const interesting: Array<Promise<void>> = [ ];
|
||||
|
||||
let newRunners = 0;
|
||||
for (const runner of running) {
|
||||
|
||||
// No responses, yet; keep an eye on it
|
||||
if (runner.perform) {
|
||||
interesting.push(runner.perform);
|
||||
}
|
||||
|
||||
// Still stalling...
|
||||
if (runner.staller) {
|
||||
interesting.push(runner.staller);
|
||||
continue;
|
||||
}
|
||||
|
||||
// This runner has already triggered another runner
|
||||
if (runner.didBump) { continue; }
|
||||
|
||||
// Got a response (result or error) or stalled; kick off another runner
|
||||
runner.didBump = true;
|
||||
newRunners++;
|
||||
}
|
||||
|
||||
// Check if we have reached quorum on a result (or error)
|
||||
const value = await this.#checkQuorum(running, req);
|
||||
if (value !== undefined) {
|
||||
if (value instanceof Error) { throw value; }
|
||||
return value;
|
||||
}
|
||||
|
||||
// Add any new runners, because a staller timed out or a result
|
||||
// or error response came in.
|
||||
for (let i = 0; i < newRunners; i++) {
|
||||
this.#addRunner(running, req);
|
||||
}
|
||||
|
||||
// All providers have returned, and we have no result
|
||||
|
||||
assert(interesting.length > 0, "quorum not met", "SERVER_ERROR", {
|
||||
request: "%sub-requests",
|
||||
info: { request: req, results: Array.from(running).map((r) => stringify(r.result)) }
|
||||
});
|
||||
|
||||
// Wait for someone to either complete its perform or stall out
|
||||
await Promise.race(interesting);
|
||||
|
||||
// This is recursive, but at worst case the depth is 2x the
|
||||
// number of providers (each has a perform and a staller)
|
||||
return await this.#waitForQuorum(running, req);
|
||||
}
|
||||
|
||||
async _perform<T = any>(req: PerformActionRequest): Promise<T> {
|
||||
// Broadcasting a transaction is rare (ish) and already incurs
|
||||
// a cost on the user, so spamming is safe-ish. Just send it to
|
||||
// every backend.
|
||||
if (req.method === "broadcastTransaction") {
|
||||
// Once any broadcast provides a positive result, use it. No
|
||||
// need to wait for anyone else
|
||||
const results: Array<null | TallyResult> = this.#configs.map((c) => null);
|
||||
const broadcasts = this.#configs.map(async ({ provider, weight }, index) => {
|
||||
try {
|
||||
const result = await provider._perform(req);
|
||||
results[index] = Object.assign(normalizeResult(req.method, { result }), { weight });
|
||||
} catch (error: any) {
|
||||
results[index] = Object.assign(normalizeResult(req.method, { error }), { weight });
|
||||
}
|
||||
});
|
||||
|
||||
// As each promise finishes...
|
||||
while (true) {
|
||||
// Check for a valid broadcast result
|
||||
const done = <Array<any>>results.filter((r) => (r != null));
|
||||
for (const { value } of done) {
|
||||
if (!(value instanceof Error)) { return value; }
|
||||
}
|
||||
|
||||
// Check for a legit broadcast error (one which we cannot
|
||||
// recover from; some nodes may return the following red
|
||||
// herring events:
|
||||
// - alredy seend (UNKNOWN_ERROR)
|
||||
// - NONCE_EXPIRED
|
||||
// - REPLACEMENT_UNDERPRICED
|
||||
const result = checkQuorum(this.quorum, <Array<any>>results.filter((r) => (r != null)));
|
||||
if (isError(result, "INSUFFICIENT_FUNDS")) {
|
||||
throw result;
|
||||
}
|
||||
|
||||
// Kick off the next provider (if any)
|
||||
const waiting = broadcasts.filter((b, i) => (results[i] == null));
|
||||
if (waiting.length === 0) { break; }
|
||||
await Promise.race(waiting);
|
||||
}
|
||||
|
||||
// Use standard quorum results; any result was returned above,
|
||||
// so this will find any error that met quorum if any
|
||||
const result = getAnyResult(this.quorum, <Array<any>>results);
|
||||
assert(result !== undefined, "problem multi-broadcasting", "SERVER_ERROR", {
|
||||
request: "%sub-requests",
|
||||
info: { request: req, results: results.map(stringify) }
|
||||
})
|
||||
if (result instanceof Error) { throw result; }
|
||||
return result;
|
||||
}
|
||||
|
||||
await this.#initialSync();
|
||||
|
||||
// Bootstrap enough runners to meet quorum
|
||||
const running: Set<RunnerState> = new Set();
|
||||
let inflightQuorum = 0;
|
||||
while (true) {
|
||||
const runner = this.#addRunner(running, req);
|
||||
if (runner == null) { break; }
|
||||
inflightQuorum += runner.config.weight;
|
||||
if (inflightQuorum >= this.quorum) { break; }
|
||||
}
|
||||
|
||||
const result = await this.#waitForQuorum(running, req);
|
||||
|
||||
// Track requests sent to a provider that are still
|
||||
// outstanding after quorum has been otherwise found
|
||||
for (const runner of running) {
|
||||
if (runner.perform && runner.result == null) {
|
||||
runner.config.lateResponses++;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async destroy(): Promise<void> {
|
||||
for (const { provider } of this.#configs) {
|
||||
provider.destroy();
|
||||
}
|
||||
super.destroy();
|
||||
}
|
||||
}
|
||||
220
dev/env/node_modules/ethers/src.ts/providers/provider-infura.ts
generated
vendored
Executable file
220
dev/env/node_modules/ethers/src.ts/providers/provider-infura.ts
generated
vendored
Executable file
@@ -0,0 +1,220 @@
|
||||
/**
|
||||
* [[link-infura]] provides a third-party service for connecting to
|
||||
* various blockchains over JSON-RPC.
|
||||
*
|
||||
* **Supported Networks**
|
||||
*
|
||||
* - Ethereum Mainnet (``mainnet``)
|
||||
* - Goerli Testnet (``goerli``)
|
||||
* - Sepolia Testnet (``sepolia``)
|
||||
* - Arbitrum (``arbitrum``)
|
||||
* - Arbitrum Goerli Testnet (``arbitrum-goerli``)
|
||||
* - Arbitrum Sepolia Testnet (``arbitrum-sepolia``)
|
||||
* - Base (``base``)
|
||||
* - Base Goerlia Testnet (``base-goerli``)
|
||||
* - Base Sepolia Testnet (``base-sepolia``)
|
||||
* - BNB Smart Chain Mainnet (``bnb``)
|
||||
* - BNB Smart Chain Testnet (``bnbt``)
|
||||
* - Linea (``linea``)
|
||||
* - Linea Goerli Testnet (``linea-goerli``)
|
||||
* - Linea Sepolia Testnet (``linea-sepolia``)
|
||||
* - Optimism (``optimism``)
|
||||
* - Optimism Goerli Testnet (``optimism-goerli``)
|
||||
* - Optimism Sepolia Testnet (``optimism-sepolia``)
|
||||
* - Polygon (``matic``)
|
||||
* - Polygon Amoy Testnet (``matic-amoy``)
|
||||
* - Polygon Mumbai Testnet (``matic-mumbai``)
|
||||
*
|
||||
* @_subsection: api/providers/thirdparty:INFURA [providers-infura]
|
||||
*/
|
||||
import {
|
||||
defineProperties, FetchRequest, assert, assertArgument
|
||||
} from "../utils/index.js";
|
||||
|
||||
import { showThrottleMessage } from "./community.js";
|
||||
import { Network } from "./network.js";
|
||||
import { JsonRpcProvider } from "./provider-jsonrpc.js";
|
||||
import { WebSocketProvider } from "./provider-websocket.js";
|
||||
|
||||
import type { AbstractProvider } from "./abstract-provider.js";
|
||||
import type { CommunityResourcable } from "./community.js";
|
||||
import type { Networkish } from "./network.js";
|
||||
|
||||
|
||||
const defaultProjectId = "84842078b09946638c03157f83405213";
|
||||
|
||||
function getHost(name: string): string {
|
||||
switch(name) {
|
||||
case "mainnet":
|
||||
return "mainnet.infura.io";
|
||||
case "goerli":
|
||||
return "goerli.infura.io";
|
||||
case "sepolia":
|
||||
return "sepolia.infura.io";
|
||||
|
||||
case "arbitrum":
|
||||
return "arbitrum-mainnet.infura.io";
|
||||
case "arbitrum-goerli":
|
||||
return "arbitrum-goerli.infura.io";
|
||||
case "arbitrum-sepolia":
|
||||
return "arbitrum-sepolia.infura.io";
|
||||
case "base":
|
||||
return "base-mainnet.infura.io";
|
||||
case "base-goerlia": // @TODO: Remove this typo in the future!
|
||||
case "base-goerli":
|
||||
return "base-goerli.infura.io";
|
||||
case "base-sepolia":
|
||||
return "base-sepolia.infura.io";
|
||||
case "bnb":
|
||||
return "bsc-mainnet.infura.io";
|
||||
case "bnbt":
|
||||
return "bsc-testnet.infura.io";
|
||||
case "linea":
|
||||
return "linea-mainnet.infura.io";
|
||||
case "linea-goerli":
|
||||
return "linea-goerli.infura.io";
|
||||
case "linea-sepolia":
|
||||
return "linea-sepolia.infura.io";
|
||||
case "matic":
|
||||
return "polygon-mainnet.infura.io";
|
||||
case "matic-amoy":
|
||||
return "polygon-amoy.infura.io";
|
||||
case "matic-mumbai":
|
||||
return "polygon-mumbai.infura.io";
|
||||
case "optimism":
|
||||
return "optimism-mainnet.infura.io";
|
||||
case "optimism-goerli":
|
||||
return "optimism-goerli.infura.io";
|
||||
case "optimism-sepolia":
|
||||
return "optimism-sepolia.infura.io";
|
||||
}
|
||||
|
||||
assertArgument(false, "unsupported network", "network", name);
|
||||
}
|
||||
|
||||
/**
|
||||
* The **InfuraWebSocketProvider** connects to the [[link-infura]]
|
||||
* WebSocket end-points.
|
||||
*
|
||||
* By default, a highly-throttled API key is used, which is
|
||||
* appropriate for quick prototypes and simple scripts. To
|
||||
* gain access to an increased rate-limit, it is highly
|
||||
* recommended to [sign up here](link-infura-signup).
|
||||
*/
|
||||
export class InfuraWebSocketProvider extends WebSocketProvider implements CommunityResourcable {
|
||||
|
||||
/**
|
||||
* The Project ID for the INFURA connection.
|
||||
*/
|
||||
readonly projectId!: string;
|
||||
|
||||
/**
|
||||
* The Project Secret.
|
||||
*
|
||||
* If null, no authenticated requests are made. This should not
|
||||
* be used outside of private contexts.
|
||||
*/
|
||||
readonly projectSecret!: null | string;
|
||||
|
||||
/**
|
||||
* Creates a new **InfuraWebSocketProvider**.
|
||||
*/
|
||||
constructor(network?: Networkish, projectId?: string) {
|
||||
const provider = new InfuraProvider(network, projectId);
|
||||
|
||||
const req = provider._getConnection();
|
||||
assert(!req.credentials, "INFURA WebSocket project secrets unsupported",
|
||||
"UNSUPPORTED_OPERATION", { operation: "InfuraProvider.getWebSocketProvider()" });
|
||||
|
||||
const url = req.url.replace(/^http/i, "ws").replace("/v3/", "/ws/v3/");
|
||||
super(url, provider._network);
|
||||
|
||||
defineProperties<InfuraWebSocketProvider>(this, {
|
||||
projectId: provider.projectId,
|
||||
projectSecret: provider.projectSecret
|
||||
});
|
||||
}
|
||||
|
||||
isCommunityResource(): boolean {
|
||||
return (this.projectId === defaultProjectId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The **InfuraProvider** connects to the [[link-infura]]
|
||||
* JSON-RPC end-points.
|
||||
*
|
||||
* By default, a highly-throttled API key is used, which is
|
||||
* appropriate for quick prototypes and simple scripts. To
|
||||
* gain access to an increased rate-limit, it is highly
|
||||
* recommended to [sign up here](link-infura-signup).
|
||||
*/
|
||||
export class InfuraProvider extends JsonRpcProvider implements CommunityResourcable {
|
||||
/**
|
||||
* The Project ID for the INFURA connection.
|
||||
*/
|
||||
readonly projectId!: string;
|
||||
|
||||
/**
|
||||
* The Project Secret.
|
||||
*
|
||||
* If null, no authenticated requests are made. This should not
|
||||
* be used outside of private contexts.
|
||||
*/
|
||||
readonly projectSecret!: null | string;
|
||||
|
||||
/**
|
||||
* Creates a new **InfuraProvider**.
|
||||
*/
|
||||
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; }
|
||||
|
||||
const request = InfuraProvider.getRequest(network, projectId, projectSecret);
|
||||
super(request, network, { staticNetwork: network });
|
||||
|
||||
defineProperties<InfuraProvider>(this, { projectId, projectSecret });
|
||||
}
|
||||
|
||||
_getProvider(chainId: number): AbstractProvider {
|
||||
try {
|
||||
return new InfuraProvider(chainId, this.projectId, this.projectSecret);
|
||||
} catch (error) { }
|
||||
return super._getProvider(chainId);
|
||||
}
|
||||
|
||||
isCommunityResource(): boolean {
|
||||
return (this.projectId === defaultProjectId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new **InfuraWebSocketProvider**.
|
||||
*/
|
||||
static getWebSocketProvider(network?: Networkish, projectId?: string): InfuraWebSocketProvider {
|
||||
return new InfuraWebSocketProvider(network, projectId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a prepared request for connecting to %%network%%
|
||||
* with %%projectId%% and %%projectSecret%%.
|
||||
*/
|
||||
static getRequest(network: Network, projectId?: null | string, projectSecret?: null | string): FetchRequest {
|
||||
if (projectId == null) { projectId = defaultProjectId; }
|
||||
if (projectSecret == null) { projectSecret = null; }
|
||||
|
||||
const request = new FetchRequest(`https:/\/${ getHost(network.name) }/v3/${ projectId }`);
|
||||
request.allowGzip = true;
|
||||
if (projectSecret) { request.setCredentials("", projectSecret); }
|
||||
|
||||
if (projectId === defaultProjectId) {
|
||||
request.retryFunc = async (request, response, attempt) => {
|
||||
showThrottleMessage("InfuraProvider");
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
}
|
||||
3
dev/env/node_modules/ethers/src.ts/providers/provider-ipcsocket-browser.ts
generated
vendored
Executable file
3
dev/env/node_modules/ethers/src.ts/providers/provider-ipcsocket-browser.ts
generated
vendored
Executable file
@@ -0,0 +1,3 @@
|
||||
const IpcSocketProvider = undefined;
|
||||
|
||||
export { IpcSocketProvider };
|
||||
81
dev/env/node_modules/ethers/src.ts/providers/provider-ipcsocket.ts
generated
vendored
Executable file
81
dev/env/node_modules/ethers/src.ts/providers/provider-ipcsocket.ts
generated
vendored
Executable file
@@ -0,0 +1,81 @@
|
||||
|
||||
import { connect } from "net";
|
||||
import { SocketProvider } from "./provider-socket.js";
|
||||
|
||||
import type { Socket } from "net";
|
||||
|
||||
import type { JsonRpcApiProviderOptions } from "./provider-jsonrpc.js";
|
||||
import type { Networkish } from "./network.js";
|
||||
|
||||
|
||||
// @TODO: Is this sufficient? Is this robust? Will newlines occur between
|
||||
// all payloads and only between payloads?
|
||||
function splitBuffer(data: Buffer): { messages: Array<string>, remaining: Buffer } {
|
||||
const messages: Array<string> = [ ];
|
||||
|
||||
let lastStart = 0;
|
||||
while (true) {
|
||||
const nl = data.indexOf(10, lastStart);
|
||||
if (nl === -1) { break; }
|
||||
messages.push(data.subarray(lastStart, nl).toString().trim());
|
||||
lastStart = nl + 1;
|
||||
}
|
||||
|
||||
return { messages, remaining: data.subarray(lastStart) };
|
||||
}
|
||||
|
||||
/**
|
||||
* An **IpcSocketProvider** connects over an IPC socket on the host
|
||||
* which provides fast access to the node, but requires the node and
|
||||
* the script run on the same machine.
|
||||
*/
|
||||
export class IpcSocketProvider extends SocketProvider {
|
||||
#socket: Socket;
|
||||
|
||||
/**
|
||||
* The connected socket.
|
||||
*/
|
||||
get socket(): Socket { return this.#socket; }
|
||||
|
||||
constructor(path: string, network?: Networkish, options?: JsonRpcApiProviderOptions) {
|
||||
super(network, options);
|
||||
this.#socket = connect(path);
|
||||
|
||||
this.socket.on("ready", async () => {
|
||||
try {
|
||||
await this._start();
|
||||
} catch (error) {
|
||||
console.log("failed to start IpcSocketProvider", error);
|
||||
// @TODO: Now what? Restart?
|
||||
}
|
||||
});
|
||||
|
||||
let response = Buffer.alloc(0);
|
||||
this.socket.on("data", (data) => {
|
||||
response = Buffer.concat([ response, data ]);
|
||||
const { messages, remaining } = splitBuffer(response);
|
||||
messages.forEach((message) => {
|
||||
this._processMessage(message);
|
||||
});
|
||||
response = remaining;
|
||||
});
|
||||
|
||||
this.socket.on("end", () => {
|
||||
this.emit("close");
|
||||
this.socket.destroy();
|
||||
this.socket.end();
|
||||
});
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
this.socket.destroy();
|
||||
this.socket.end();
|
||||
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
async _write(message: string): Promise<void> {
|
||||
if (!message.endsWith("\n")) { message += "\n"; }
|
||||
this.socket.write(message);
|
||||
}
|
||||
}
|
||||
1335
dev/env/node_modules/ethers/src.ts/providers/provider-jsonrpc.ts
generated
vendored
Executable file
1335
dev/env/node_modules/ethers/src.ts/providers/provider-jsonrpc.ts
generated
vendored
Executable file
File diff suppressed because it is too large
Load Diff
121
dev/env/node_modules/ethers/src.ts/providers/provider-pocket.ts
generated
vendored
Executable file
121
dev/env/node_modules/ethers/src.ts/providers/provider-pocket.ts
generated
vendored
Executable file
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* [[link-pocket]] provides a third-party service for connecting to
|
||||
* various blockchains over JSON-RPC.
|
||||
*
|
||||
* **Supported Networks**
|
||||
*
|
||||
* - Ethereum Mainnet (``mainnet``)
|
||||
* - Goerli Testnet (``goerli``)
|
||||
* - Polygon (``matic``)
|
||||
* - Arbitrum (``arbitrum``)
|
||||
*
|
||||
* @_subsection: api/providers/thirdparty:Pocket [providers-pocket]
|
||||
*/
|
||||
import {
|
||||
defineProperties, FetchRequest, assertArgument
|
||||
} from "../utils/index.js";
|
||||
|
||||
import { AbstractProvider } from "./abstract-provider.js";
|
||||
import { showThrottleMessage } from "./community.js";
|
||||
import { Network } from "./network.js";
|
||||
import { JsonRpcProvider } from "./provider-jsonrpc.js";
|
||||
|
||||
import type { CommunityResourcable } from "./community.js";
|
||||
import type { Networkish } from "./network.js";
|
||||
|
||||
const defaultApplicationId = "62e1ad51b37b8e00394bda3b";
|
||||
|
||||
function getHost(name: string): string {
|
||||
switch (name) {
|
||||
case "mainnet":
|
||||
return "eth-mainnet.gateway.pokt.network";
|
||||
case "goerli":
|
||||
return "eth-goerli.gateway.pokt.network";
|
||||
|
||||
case "matic":
|
||||
return "poly-mainnet.gateway.pokt.network";
|
||||
case "matic-mumbai":
|
||||
return "polygon-mumbai-rpc.gateway.pokt.network";
|
||||
}
|
||||
|
||||
assertArgument(false, "unsupported network", "network", name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The **PocketProvider** connects to the [[link-pocket]]
|
||||
* JSON-RPC end-points.
|
||||
*
|
||||
* By default, a highly-throttled API key is used, which is
|
||||
* appropriate for quick prototypes and simple scripts. To
|
||||
* gain access to an increased rate-limit, it is highly
|
||||
* recommended to [sign up here](link-pocket-signup).
|
||||
*/
|
||||
export class PocketProvider extends JsonRpcProvider implements CommunityResourcable {
|
||||
|
||||
/**
|
||||
* The Application ID for the Pocket connection.
|
||||
*/
|
||||
readonly applicationId!: string;
|
||||
|
||||
/**
|
||||
* The Application Secret for making authenticated requests
|
||||
* to the Pocket connection.
|
||||
*/
|
||||
readonly applicationSecret!: null | string;
|
||||
|
||||
/**
|
||||
* Create a new **PocketProvider**.
|
||||
*
|
||||
* By default connecting to ``mainnet`` with a highly throttled
|
||||
* API key.
|
||||
*/
|
||||
constructor(_network?: Networkish, applicationId?: null | string, applicationSecret?: null | string) {
|
||||
if (_network == null) { _network = "mainnet"; }
|
||||
const network = Network.from(_network);
|
||||
if (applicationId == null) { applicationId = defaultApplicationId; }
|
||||
if (applicationSecret == null) { applicationSecret = null; }
|
||||
|
||||
const options = { staticNetwork: network };
|
||||
|
||||
const request = PocketProvider.getRequest(network, applicationId, applicationSecret);
|
||||
super(request, network, options);
|
||||
|
||||
defineProperties<PocketProvider>(this, { applicationId, applicationSecret });
|
||||
}
|
||||
|
||||
_getProvider(chainId: number): AbstractProvider {
|
||||
try {
|
||||
return new PocketProvider(chainId, this.applicationId, this.applicationSecret);
|
||||
} catch (error) { }
|
||||
return super._getProvider(chainId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a prepared request for connecting to %%network%% with
|
||||
* %%applicationId%%.
|
||||
*/
|
||||
static getRequest(network: Network, applicationId?: null | string, applicationSecret?: null | string): FetchRequest {
|
||||
if (applicationId == null) { applicationId = defaultApplicationId; }
|
||||
|
||||
const request = new FetchRequest(`https:/\/${ getHost(network.name) }/v1/lb/${ applicationId }`);
|
||||
request.allowGzip = true;
|
||||
|
||||
if (applicationSecret) {
|
||||
request.setCredentials("", applicationSecret);
|
||||
}
|
||||
|
||||
if (applicationId === defaultApplicationId) {
|
||||
request.retryFunc = async (request, response, attempt) => {
|
||||
showThrottleMessage("PocketProvider");
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
isCommunityResource(): boolean {
|
||||
return (this.applicationId === defaultApplicationId);
|
||||
}
|
||||
}
|
||||
177
dev/env/node_modules/ethers/src.ts/providers/provider-quicknode.ts
generated
vendored
Executable file
177
dev/env/node_modules/ethers/src.ts/providers/provider-quicknode.ts
generated
vendored
Executable file
@@ -0,0 +1,177 @@
|
||||
/**
|
||||
* [[link-quicknode]] provides a third-party service for connecting to
|
||||
* various blockchains over JSON-RPC.
|
||||
*
|
||||
* **Supported Networks**
|
||||
*
|
||||
* - Ethereum Mainnet (``mainnet``)
|
||||
* - Goerli Testnet (``goerli``)
|
||||
* - Sepolia Testnet (``sepolia``)
|
||||
* - Holesky Testnet (``holesky``)
|
||||
* - Arbitrum (``arbitrum``)
|
||||
* - Arbitrum Goerli Testnet (``arbitrum-goerli``)
|
||||
* - Arbitrum Sepolia Testnet (``arbitrum-sepolia``)
|
||||
* - Base Mainnet (``base``);
|
||||
* - Base Goerli Testnet (``base-goerli``);
|
||||
* - Base Sepolia Testnet (``base-sepolia``);
|
||||
* - BNB Smart Chain Mainnet (``bnb``)
|
||||
* - BNB Smart Chain Testnet (``bnbt``)
|
||||
* - Optimism (``optimism``)
|
||||
* - Optimism Goerli Testnet (``optimism-goerli``)
|
||||
* - Optimism Sepolia Testnet (``optimism-sepolia``)
|
||||
* - Polygon (``matic``)
|
||||
* - Polygon Mumbai Testnet (``matic-mumbai``)
|
||||
*
|
||||
* @_subsection: api/providers/thirdparty:QuickNode [providers-quicknode]
|
||||
*/
|
||||
|
||||
import {
|
||||
defineProperties, FetchRequest, assertArgument
|
||||
} from "../utils/index.js";
|
||||
|
||||
import { showThrottleMessage } from "./community.js";
|
||||
import { Network } from "./network.js";
|
||||
import { JsonRpcProvider } from "./provider-jsonrpc.js";
|
||||
|
||||
import type { AbstractProvider } from "./abstract-provider.js";
|
||||
import type { CommunityResourcable } from "./community.js";
|
||||
import type { Networkish } from "./network.js";
|
||||
|
||||
|
||||
const defaultToken = "919b412a057b5e9c9b6dce193c5a60242d6efadb";
|
||||
|
||||
function getHost(name: string): string {
|
||||
switch(name) {
|
||||
case "mainnet":
|
||||
return "ethers.quiknode.pro";
|
||||
case "goerli":
|
||||
return "ethers.ethereum-goerli.quiknode.pro";
|
||||
case "sepolia":
|
||||
return "ethers.ethereum-sepolia.quiknode.pro";
|
||||
case "holesky":
|
||||
return "ethers.ethereum-holesky.quiknode.pro";
|
||||
|
||||
case "arbitrum":
|
||||
return "ethers.arbitrum-mainnet.quiknode.pro";
|
||||
case "arbitrum-goerli":
|
||||
return "ethers.arbitrum-goerli.quiknode.pro";
|
||||
case "arbitrum-sepolia":
|
||||
return "ethers.arbitrum-sepolia.quiknode.pro";
|
||||
case "base":
|
||||
return "ethers.base-mainnet.quiknode.pro";
|
||||
case "base-goerli":
|
||||
return "ethers.base-goerli.quiknode.pro";
|
||||
case "base-spolia":
|
||||
return "ethers.base-sepolia.quiknode.pro";
|
||||
case "bnb":
|
||||
return "ethers.bsc.quiknode.pro";
|
||||
case "bnbt":
|
||||
return "ethers.bsc-testnet.quiknode.pro";
|
||||
case "matic":
|
||||
return "ethers.matic.quiknode.pro";
|
||||
case "matic-mumbai":
|
||||
return "ethers.matic-testnet.quiknode.pro";
|
||||
case "optimism":
|
||||
return "ethers.optimism.quiknode.pro";
|
||||
case "optimism-goerli":
|
||||
return "ethers.optimism-goerli.quiknode.pro";
|
||||
case "optimism-sepolia":
|
||||
return "ethers.optimism-sepolia.quiknode.pro";
|
||||
case "xdai":
|
||||
return "ethers.xdai.quiknode.pro";
|
||||
}
|
||||
|
||||
assertArgument(false, "unsupported network", "network", name);
|
||||
}
|
||||
|
||||
/*
|
||||
@TODO:
|
||||
These networks are not currently present in the Network
|
||||
default included networks. Research them and ensure they
|
||||
are EVM compatible and work with ethers
|
||||
|
||||
http://ethers.matic-amoy.quiknode.pro
|
||||
|
||||
http://ethers.avalanche-mainnet.quiknode.pro
|
||||
http://ethers.avalanche-testnet.quiknode.pro
|
||||
http://ethers.blast-sepolia.quiknode.pro
|
||||
http://ethers.celo-mainnet.quiknode.pro
|
||||
http://ethers.fantom.quiknode.pro
|
||||
http://ethers.imx-demo.quiknode.pro
|
||||
http://ethers.imx-mainnet.quiknode.pro
|
||||
http://ethers.imx-testnet.quiknode.pro
|
||||
http://ethers.near-mainnet.quiknode.pro
|
||||
http://ethers.near-testnet.quiknode.pro
|
||||
http://ethers.nova-mainnet.quiknode.pro
|
||||
http://ethers.scroll-mainnet.quiknode.pro
|
||||
http://ethers.scroll-testnet.quiknode.pro
|
||||
http://ethers.tron-mainnet.quiknode.pro
|
||||
http://ethers.zkevm-mainnet.quiknode.pro
|
||||
http://ethers.zkevm-testnet.quiknode.pro
|
||||
http://ethers.zksync-mainnet.quiknode.pro
|
||||
http://ethers.zksync-testnet.quiknode.pro
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The **QuickNodeProvider** connects to the [[link-quicknode]]
|
||||
* JSON-RPC end-points.
|
||||
*
|
||||
* By default, a highly-throttled API token is used, which is
|
||||
* appropriate for quick prototypes and simple scripts. To
|
||||
* gain access to an increased rate-limit, it is highly
|
||||
* recommended to [sign up here](link-quicknode).
|
||||
*/
|
||||
export class QuickNodeProvider extends JsonRpcProvider implements CommunityResourcable {
|
||||
/**
|
||||
* The API token.
|
||||
*/
|
||||
readonly token!: string;
|
||||
|
||||
/**
|
||||
* Creates a new **QuickNodeProvider**.
|
||||
*/
|
||||
constructor(_network?: Networkish, token?: null | string) {
|
||||
if (_network == null) { _network = "mainnet"; }
|
||||
const network = Network.from(_network);
|
||||
if (token == null) { token = defaultToken; }
|
||||
|
||||
const request = QuickNodeProvider.getRequest(network, token);
|
||||
super(request, network, { staticNetwork: network });
|
||||
|
||||
defineProperties<QuickNodeProvider>(this, { token });
|
||||
}
|
||||
|
||||
_getProvider(chainId: number): AbstractProvider {
|
||||
try {
|
||||
return new QuickNodeProvider(chainId, this.token);
|
||||
} catch (error) { }
|
||||
return super._getProvider(chainId);
|
||||
}
|
||||
|
||||
isCommunityResource(): boolean {
|
||||
return (this.token === defaultToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new request prepared for %%network%% and the
|
||||
* %%token%%.
|
||||
*/
|
||||
static getRequest(network: Network, token?: null | string): FetchRequest {
|
||||
if (token == null) { token = defaultToken; }
|
||||
|
||||
const request = new FetchRequest(`https:/\/${ getHost(network.name) }/${ token }`);
|
||||
request.allowGzip = true;
|
||||
//if (projectSecret) { request.setCredentials("", projectSecret); }
|
||||
|
||||
if (token === defaultToken) {
|
||||
request.retryFunc = async (request, response, attempt) => {
|
||||
showThrottleMessage("QuickNodeProvider");
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
}
|
||||
352
dev/env/node_modules/ethers/src.ts/providers/provider-socket.ts
generated
vendored
Executable file
352
dev/env/node_modules/ethers/src.ts/providers/provider-socket.ts
generated
vendored
Executable file
@@ -0,0 +1,352 @@
|
||||
/**
|
||||
* Generic long-lived socket provider.
|
||||
*
|
||||
* Sub-classing notes
|
||||
* - a sub-class MUST call the `_start()` method once connected
|
||||
* - a sub-class MUST override the `_write(string)` method
|
||||
* - a sub-class MUST call `_processMessage(string)` for each message
|
||||
*
|
||||
* @_subsection: api/providers/abstract-provider:Socket Providers [about-socketProvider]
|
||||
*/
|
||||
|
||||
import { UnmanagedSubscriber } from "./abstract-provider.js";
|
||||
import { assert, assertArgument, makeError } from "../utils/index.js";
|
||||
import { JsonRpcApiProvider } from "./provider-jsonrpc.js";
|
||||
|
||||
import type { Subscriber, Subscription } from "./abstract-provider.js";
|
||||
import type { EventFilter } from "./provider.js";
|
||||
import type {
|
||||
JsonRpcApiProviderOptions, JsonRpcError, JsonRpcPayload, JsonRpcResult
|
||||
} from "./provider-jsonrpc.js";
|
||||
import type { Networkish } from "./network.js";
|
||||
|
||||
|
||||
type JsonRpcSubscription = {
|
||||
method: string,
|
||||
params: {
|
||||
result: any,
|
||||
subscription: string
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A **SocketSubscriber** uses a socket transport to handle events and
|
||||
* should use [[_emit]] to manage the events.
|
||||
*/
|
||||
export class SocketSubscriber implements Subscriber {
|
||||
#provider: SocketProvider;
|
||||
|
||||
#filter: string;
|
||||
|
||||
/**
|
||||
* The filter.
|
||||
*/
|
||||
get filter(): Array<any> { return JSON.parse(this.#filter); }
|
||||
|
||||
#filterId: null | Promise<string |number>;
|
||||
#paused: null | boolean;
|
||||
|
||||
#emitPromise: null | Promise<void>;
|
||||
|
||||
/**
|
||||
* Creates a new **SocketSubscriber** attached to %%provider%% listening
|
||||
* to %%filter%%.
|
||||
*/
|
||||
constructor(provider: SocketProvider, filter: Array<any>) {
|
||||
this.#provider = provider;
|
||||
this.#filter = JSON.stringify(filter);
|
||||
this.#filterId = null;
|
||||
this.#paused = null;
|
||||
this.#emitPromise = null;
|
||||
}
|
||||
|
||||
start(): void {
|
||||
this.#filterId = this.#provider.send("eth_subscribe", this.filter).then((filterId) => {;
|
||||
this.#provider._register(filterId, this);
|
||||
return filterId;
|
||||
});
|
||||
}
|
||||
|
||||
stop(): void {
|
||||
(<Promise<number>>(this.#filterId)).then((filterId) => {
|
||||
if (this.#provider.destroyed) { return; }
|
||||
this.#provider.send("eth_unsubscribe", [ filterId ]);
|
||||
});
|
||||
this.#filterId = null;
|
||||
}
|
||||
|
||||
// @TODO: pause should trap the current blockNumber, unsub, and on resume use getLogs
|
||||
// and resume
|
||||
pause(dropWhilePaused?: boolean): void {
|
||||
assert(dropWhilePaused, "preserve logs while paused not supported by SocketSubscriber yet",
|
||||
"UNSUPPORTED_OPERATION", { operation: "pause(false)" });
|
||||
this.#paused = !!dropWhilePaused;
|
||||
}
|
||||
|
||||
resume(): void {
|
||||
this.#paused = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @_ignore:
|
||||
*/
|
||||
_handleMessage(message: any): void {
|
||||
if (this.#filterId == null) { return; }
|
||||
if (this.#paused === null) {
|
||||
let emitPromise: null | Promise<void> = this.#emitPromise;
|
||||
if (emitPromise == null) {
|
||||
emitPromise = this._emit(this.#provider, message);
|
||||
} else {
|
||||
emitPromise = emitPromise.then(async () => {
|
||||
await this._emit(this.#provider, message);
|
||||
});
|
||||
}
|
||||
this.#emitPromise = emitPromise.then(() => {
|
||||
if (this.#emitPromise === emitPromise) {
|
||||
this.#emitPromise = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sub-classes **must** override this to emit the events on the
|
||||
* provider.
|
||||
*/
|
||||
async _emit(provider: SocketProvider, message: any): Promise<void> {
|
||||
throw new Error("sub-classes must implemente this; _emit");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A **SocketBlockSubscriber** listens for ``newHeads`` events and emits
|
||||
* ``"block"`` events.
|
||||
*/
|
||||
export class SocketBlockSubscriber extends SocketSubscriber {
|
||||
/**
|
||||
* @_ignore:
|
||||
*/
|
||||
constructor(provider: SocketProvider) {
|
||||
super(provider, [ "newHeads" ]);
|
||||
}
|
||||
|
||||
async _emit(provider: SocketProvider, message: any): Promise<void> {
|
||||
provider.emit("block", parseInt(message.number));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A **SocketPendingSubscriber** listens for pending transacitons and emits
|
||||
* ``"pending"`` events.
|
||||
*/
|
||||
export class SocketPendingSubscriber extends SocketSubscriber {
|
||||
|
||||
/**
|
||||
* @_ignore:
|
||||
*/
|
||||
constructor(provider: SocketProvider) {
|
||||
super(provider, [ "newPendingTransactions" ]);
|
||||
}
|
||||
|
||||
async _emit(provider: SocketProvider, message: any): Promise<void> {
|
||||
provider.emit("pending", message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A **SocketEventSubscriber** listens for event logs.
|
||||
*/
|
||||
export class SocketEventSubscriber extends SocketSubscriber {
|
||||
#logFilter: string;
|
||||
|
||||
/**
|
||||
* The filter.
|
||||
*/
|
||||
get logFilter(): EventFilter { return JSON.parse(this.#logFilter); }
|
||||
|
||||
/**
|
||||
* @_ignore:
|
||||
*/
|
||||
constructor(provider: SocketProvider, filter: EventFilter) {
|
||||
super(provider, [ "logs", filter ]);
|
||||
this.#logFilter = JSON.stringify(filter);
|
||||
}
|
||||
|
||||
async _emit(provider: SocketProvider, message: any): Promise<void> {
|
||||
provider.emit(this.logFilter, provider._wrapLog(message, provider._network));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A **SocketProvider** is backed by a long-lived connection over a
|
||||
* socket, which can subscribe and receive real-time messages over
|
||||
* its communication channel.
|
||||
*/
|
||||
export class SocketProvider extends JsonRpcApiProvider {
|
||||
#callbacks: Map<number, { payload: JsonRpcPayload, resolve: (r: any) => void, reject: (e: Error) => void }>;
|
||||
|
||||
// Maps each filterId to its subscriber
|
||||
#subs: Map<number | string, SocketSubscriber>;
|
||||
|
||||
// If any events come in before a subscriber has finished
|
||||
// registering, queue them
|
||||
#pending: Map<number | string, Array<any>>;
|
||||
|
||||
/**
|
||||
* Creates a new **SocketProvider** connected to %%network%%.
|
||||
*
|
||||
* If unspecified, the network will be discovered.
|
||||
*/
|
||||
constructor(network?: Networkish, _options?: JsonRpcApiProviderOptions) {
|
||||
// Copy the options
|
||||
const options = Object.assign({ }, (_options != null) ? _options: { });
|
||||
|
||||
// Support for batches is generally not supported for
|
||||
// connection-base providers; if this changes in the future
|
||||
// the _send should be updated to reflect this
|
||||
assertArgument(options.batchMaxCount == null || options.batchMaxCount === 1,
|
||||
"sockets-based providers do not support batches", "options.batchMaxCount", _options);
|
||||
options.batchMaxCount = 1;
|
||||
|
||||
// Socket-based Providers (generally) cannot change their network,
|
||||
// since they have a long-lived connection; but let people override
|
||||
// this if they have just cause.
|
||||
if (options.staticNetwork == null) { options.staticNetwork = true; }
|
||||
|
||||
super(network, options);
|
||||
this.#callbacks = new Map();
|
||||
this.#subs = new Map();
|
||||
this.#pending = new Map();
|
||||
}
|
||||
|
||||
// This value is only valid after _start has been called
|
||||
/*
|
||||
get _network(): Network {
|
||||
if (this.#network == null) {
|
||||
throw new Error("this shouldn't happen");
|
||||
}
|
||||
return this.#network.clone();
|
||||
}
|
||||
*/
|
||||
|
||||
_getSubscriber(sub: Subscription): Subscriber {
|
||||
switch (sub.type) {
|
||||
case "close":
|
||||
return new UnmanagedSubscriber("close");
|
||||
case "block":
|
||||
return new SocketBlockSubscriber(this);
|
||||
case "pending":
|
||||
return new SocketPendingSubscriber(this);
|
||||
case "event":
|
||||
return new SocketEventSubscriber(this, sub.filter);
|
||||
case "orphan":
|
||||
// Handled auto-matically within AbstractProvider
|
||||
// when the log.removed = true
|
||||
if (sub.filter.orphan === "drop-log") {
|
||||
return new UnmanagedSubscriber("drop-log");
|
||||
}
|
||||
}
|
||||
return super._getSubscriber(sub);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new subscriber. This is used internalled by Subscribers
|
||||
* and generally is unecessary unless extending capabilities.
|
||||
*/
|
||||
_register(filterId: number | string, subscriber: SocketSubscriber): void {
|
||||
this.#subs.set(filterId, subscriber);
|
||||
const pending = this.#pending.get(filterId);
|
||||
if (pending) {
|
||||
for (const message of pending) {
|
||||
subscriber._handleMessage(message);
|
||||
}
|
||||
this.#pending.delete(filterId);
|
||||
}
|
||||
}
|
||||
|
||||
async _send(payload: JsonRpcPayload | Array<JsonRpcPayload>): Promise<Array<JsonRpcResult | JsonRpcError>> {
|
||||
// WebSocket provider doesn't accept batches
|
||||
assertArgument(!Array.isArray(payload), "WebSocket does not support batch send", "payload", payload);
|
||||
|
||||
// @TODO: stringify payloads here and store to prevent mutations
|
||||
|
||||
// Prepare a promise to respond to
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
this.#callbacks.set(payload.id, { payload, resolve, reject });
|
||||
});
|
||||
|
||||
// Wait until the socket is connected before writing to it
|
||||
await this._waitUntilReady();
|
||||
|
||||
// Write the request to the socket
|
||||
await this._write(JSON.stringify(payload));
|
||||
|
||||
return <Array<JsonRpcResult | JsonRpcError>>[ await promise ];
|
||||
}
|
||||
|
||||
// Sub-classes must call this once they are connected
|
||||
/*
|
||||
async _start(): Promise<void> {
|
||||
if (this.#ready) { return; }
|
||||
|
||||
for (const { payload } of this.#callbacks.values()) {
|
||||
await this._write(JSON.stringify(payload));
|
||||
}
|
||||
|
||||
this.#ready = (async function() {
|
||||
await super._start();
|
||||
})();
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sub-classes **must** call this with messages received over their
|
||||
* transport to be processed and dispatched.
|
||||
*/
|
||||
async _processMessage(message: string): Promise<void> {
|
||||
const result = <JsonRpcResult | JsonRpcError | JsonRpcSubscription>(JSON.parse(message));
|
||||
|
||||
if (result && typeof(result) === "object" && "id" in result) {
|
||||
const callback = this.#callbacks.get(result.id);
|
||||
if (callback == null) {
|
||||
this.emit("error", makeError("received result for unknown id", "UNKNOWN_ERROR", {
|
||||
reasonCode: "UNKNOWN_ID",
|
||||
result
|
||||
}));
|
||||
return;
|
||||
}
|
||||
this.#callbacks.delete(result.id);
|
||||
|
||||
callback.resolve(result);
|
||||
|
||||
} else if (result && result.method === "eth_subscription") {
|
||||
const filterId = result.params.subscription;
|
||||
const subscriber = this.#subs.get(filterId);
|
||||
if (subscriber) {
|
||||
subscriber._handleMessage(result.params.result);
|
||||
} else {
|
||||
let pending = this.#pending.get(filterId);
|
||||
if (pending == null) {
|
||||
pending = [ ];
|
||||
this.#pending.set(filterId, pending);
|
||||
}
|
||||
pending.push(result.params.result);
|
||||
}
|
||||
|
||||
} else {
|
||||
this.emit("error", makeError("received unexpected message", "UNKNOWN_ERROR", {
|
||||
reasonCode: "UNEXPECTED_MESSAGE",
|
||||
result
|
||||
}));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sub-classes **must** override this to send %%message%% over their
|
||||
* transport.
|
||||
*/
|
||||
async _write(message: string): Promise<void> {
|
||||
throw new Error("sub-classes must override this");
|
||||
}
|
||||
}
|
||||
103
dev/env/node_modules/ethers/src.ts/providers/provider-websocket.ts
generated
vendored
Executable file
103
dev/env/node_modules/ethers/src.ts/providers/provider-websocket.ts
generated
vendored
Executable file
@@ -0,0 +1,103 @@
|
||||
|
||||
|
||||
import { WebSocket as _WebSocket } from "./ws.js"; /*-browser*/
|
||||
|
||||
import { SocketProvider } from "./provider-socket.js";
|
||||
|
||||
import type { JsonRpcApiProviderOptions} from "./provider-jsonrpc.js";
|
||||
import type { Networkish } from "./network.js";
|
||||
|
||||
/**
|
||||
* A generic interface to a Websocket-like object.
|
||||
*/
|
||||
export interface WebSocketLike {
|
||||
onopen: null | ((...args: Array<any>) => any);
|
||||
onmessage: null | ((...args: Array<any>) => any);
|
||||
onerror: null | ((...args: Array<any>) => any);
|
||||
|
||||
readyState: number;
|
||||
|
||||
send(payload: any): void;
|
||||
close(code?: number, reason?: string): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* A function which can be used to re-create a WebSocket connection
|
||||
* on disconnect.
|
||||
*/
|
||||
export type WebSocketCreator = () => WebSocketLike;
|
||||
|
||||
/**
|
||||
* A JSON-RPC provider which is backed by a WebSocket.
|
||||
*
|
||||
* WebSockets are often preferred because they retain a live connection
|
||||
* to a server, which permits more instant access to events.
|
||||
*
|
||||
* However, this incurs higher server infrasturture costs, so additional
|
||||
* resources may be required to host your own WebSocket nodes and many
|
||||
* third-party services charge additional fees for WebSocket endpoints.
|
||||
*/
|
||||
export class WebSocketProvider extends SocketProvider {
|
||||
#connect: null | WebSocketCreator;
|
||||
|
||||
#websocket: null | WebSocketLike;
|
||||
get websocket(): WebSocketLike {
|
||||
if (this.#websocket == null) { throw new Error("websocket closed"); }
|
||||
return this.#websocket;
|
||||
}
|
||||
|
||||
constructor(url: string | WebSocketLike | WebSocketCreator, network?: Networkish, options?: JsonRpcApiProviderOptions) {
|
||||
super(network, options);
|
||||
if (typeof(url) === "string") {
|
||||
this.#connect = () => { return new _WebSocket(url); };
|
||||
this.#websocket = this.#connect();
|
||||
} else if (typeof(url) === "function") {
|
||||
this.#connect = url;
|
||||
this.#websocket = url();
|
||||
} else {
|
||||
this.#connect = null;
|
||||
this.#websocket = url;
|
||||
}
|
||||
|
||||
this.websocket.onopen = async () => {
|
||||
try {
|
||||
await this._start()
|
||||
this.resume();
|
||||
} catch (error) {
|
||||
console.log("failed to start WebsocketProvider", error);
|
||||
// @TODO: now what? Attempt reconnect?
|
||||
}
|
||||
};
|
||||
|
||||
this.websocket.onmessage = (message: { data: string }) => {
|
||||
this._processMessage(message.data);
|
||||
};
|
||||
/*
|
||||
this.websocket.onclose = (event) => {
|
||||
// @TODO: What event.code should we reconnect on?
|
||||
const reconnect = false;
|
||||
if (reconnect) {
|
||||
this.pause(true);
|
||||
if (this.#connect) {
|
||||
this.#websocket = this.#connect();
|
||||
this.#websocket.onopen = ...
|
||||
// @TODO: this requires the super class to rebroadcast; move it there
|
||||
}
|
||||
this._reconnect();
|
||||
}
|
||||
};
|
||||
*/
|
||||
}
|
||||
|
||||
async _write(message: string): Promise<void> {
|
||||
this.websocket.send(message);
|
||||
}
|
||||
|
||||
async destroy(): Promise<void> {
|
||||
if (this.#websocket != null) {
|
||||
this.#websocket.close();
|
||||
this.#websocket = null;
|
||||
}
|
||||
super.destroy();
|
||||
}
|
||||
}
|
||||
2146
dev/env/node_modules/ethers/src.ts/providers/provider.ts
generated
vendored
Executable file
2146
dev/env/node_modules/ethers/src.ts/providers/provider.ts
generated
vendored
Executable file
File diff suppressed because it is too large
Load Diff
98
dev/env/node_modules/ethers/src.ts/providers/signer-noncemanager.ts
generated
vendored
Executable file
98
dev/env/node_modules/ethers/src.ts/providers/signer-noncemanager.ts
generated
vendored
Executable file
@@ -0,0 +1,98 @@
|
||||
import { defineProperties } from "../utils/index.js";
|
||||
import { AbstractSigner } from "./abstract-signer.js";
|
||||
|
||||
import type { TypedDataDomain, TypedDataField } from "../hash/index.js";
|
||||
|
||||
import type {
|
||||
BlockTag, Provider, TransactionRequest, TransactionResponse
|
||||
} from "./provider.js";
|
||||
import type { Signer } from "./signer.js";
|
||||
|
||||
|
||||
/**
|
||||
* A **NonceManager** wraps another [[Signer]] and automatically manages
|
||||
* the nonce, ensuring serialized and sequential nonces are used during
|
||||
* transaction.
|
||||
*/
|
||||
export class NonceManager extends AbstractSigner {
|
||||
/**
|
||||
* The Signer being managed.
|
||||
*/
|
||||
signer!: Signer;
|
||||
|
||||
#noncePromise: null | Promise<number>;
|
||||
#delta: number;
|
||||
|
||||
/**
|
||||
* Creates a new **NonceManager** to manage %%signer%%.
|
||||
*/
|
||||
constructor(signer: Signer) {
|
||||
super(signer.provider);
|
||||
defineProperties<NonceManager>(this, { signer });
|
||||
|
||||
this.#noncePromise = null;
|
||||
this.#delta = 0;
|
||||
}
|
||||
|
||||
async getAddress(): Promise<string> {
|
||||
return this.signer.getAddress();
|
||||
}
|
||||
|
||||
connect(provider: null | Provider): NonceManager {
|
||||
return new NonceManager(this.signer.connect(provider));
|
||||
}
|
||||
|
||||
async getNonce(blockTag?: BlockTag): Promise<number> {
|
||||
if (blockTag === "pending") {
|
||||
if (this.#noncePromise == null) {
|
||||
this.#noncePromise = super.getNonce("pending");
|
||||
}
|
||||
|
||||
const delta = this.#delta;
|
||||
return (await this.#noncePromise) + delta;
|
||||
}
|
||||
|
||||
return super.getNonce(blockTag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually increment the nonce. This may be useful when managng
|
||||
* offline transactions.
|
||||
*/
|
||||
increment(): void {
|
||||
this.#delta++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the nonce, causing the **NonceManager** to reload the current
|
||||
* nonce from the blockchain on the next transaction.
|
||||
*/
|
||||
reset(): void {
|
||||
this.#delta = 0;
|
||||
this.#noncePromise = null;
|
||||
}
|
||||
|
||||
async sendTransaction(tx: TransactionRequest): Promise<TransactionResponse> {
|
||||
const noncePromise = this.getNonce("pending");
|
||||
this.increment();
|
||||
|
||||
tx = await this.signer.populateTransaction(tx);
|
||||
tx.nonce = await noncePromise;
|
||||
|
||||
// @TODO: Maybe handle interesting/recoverable errors?
|
||||
// Like don't increment if the tx was certainly not sent
|
||||
return await this.signer.sendTransaction(tx);
|
||||
}
|
||||
|
||||
signTransaction(tx: TransactionRequest): Promise<string> {
|
||||
return this.signer.signTransaction(tx);
|
||||
}
|
||||
|
||||
signMessage(message: string | Uint8Array): Promise<string> {
|
||||
return this.signer.signMessage(message);
|
||||
}
|
||||
|
||||
signTypedData(domain: TypedDataDomain, types: Record<string, Array<TypedDataField>>, value: Record<string, any>): Promise<string> {
|
||||
return this.signer.signTypedData(domain, types, value);
|
||||
}
|
||||
}
|
||||
166
dev/env/node_modules/ethers/src.ts/providers/signer.ts
generated
vendored
Executable file
166
dev/env/node_modules/ethers/src.ts/providers/signer.ts
generated
vendored
Executable file
@@ -0,0 +1,166 @@
|
||||
|
||||
import type { Addressable, NameResolver } from "../address/index.js";
|
||||
import type {
|
||||
AuthorizationRequest, TypedDataDomain, TypedDataField
|
||||
} from "../hash/index.js";
|
||||
import type { Authorization, TransactionLike } from "../transaction/index.js";
|
||||
|
||||
import type { ContractRunner } from "./contracts.js";
|
||||
import type { BlockTag, Provider, TransactionRequest, TransactionResponse } from "./provider.js";
|
||||
|
||||
/**
|
||||
* A Signer represents an account on the Ethereum Blockchain, and is most often
|
||||
* backed by a private key represented by a mnemonic or residing on a Hardware Wallet.
|
||||
*
|
||||
* The API remains abstract though, so that it can deal with more advanced exotic
|
||||
* Signing entities, such as Smart Contract Wallets or Virtual Wallets (where the
|
||||
* private key may not be known).
|
||||
*/
|
||||
export interface Signer extends Addressable, ContractRunner, NameResolver {
|
||||
|
||||
/**
|
||||
* The [[Provider]] attached to this Signer (if any).
|
||||
*/
|
||||
provider: null | Provider;
|
||||
|
||||
/**
|
||||
* Returns a new instance of this Signer connected to //provider// or detached
|
||||
* from any Provider if null.
|
||||
*/
|
||||
connect(provider: null | Provider): Signer;
|
||||
|
||||
|
||||
////////////////////
|
||||
// State
|
||||
|
||||
/**
|
||||
* Get the address of the Signer.
|
||||
*/
|
||||
getAddress(): Promise<string>;
|
||||
|
||||
/**
|
||||
* Gets the next nonce required for this Signer to send a transaction.
|
||||
*
|
||||
* @param blockTag - The blocktag to base the transaction count on, keep in mind
|
||||
* many nodes do not honour this value and silently ignore it [default: ``"latest"``]
|
||||
*/
|
||||
getNonce(blockTag?: BlockTag): Promise<number>;
|
||||
|
||||
|
||||
////////////////////
|
||||
// Preparation
|
||||
|
||||
/**
|
||||
* Prepares a {@link TransactionRequest} for calling:
|
||||
* - resolves ``to`` and ``from`` addresses
|
||||
* - if ``from`` is specified , check that it matches this Signer
|
||||
*
|
||||
* @param tx - The call to prepare
|
||||
*/
|
||||
populateCall(tx: TransactionRequest): Promise<TransactionLike<string>>;
|
||||
|
||||
/**
|
||||
* Prepares a {@link TransactionRequest} for sending to the network by
|
||||
* populating any missing properties:
|
||||
* - resolves ``to`` and ``from`` addresses
|
||||
* - if ``from`` is specified , check that it matches this Signer
|
||||
* - populates ``nonce`` via ``signer.getNonce("pending")``
|
||||
* - populates ``gasLimit`` via ``signer.estimateGas(tx)``
|
||||
* - populates ``chainId`` via ``signer.provider.getNetwork()``
|
||||
* - populates ``type`` and relevant fee data for that type (``gasPrice``
|
||||
* for legacy transactions, ``maxFeePerGas`` for EIP-1559, etc)
|
||||
*
|
||||
* @note Some Signer implementations may skip populating properties that
|
||||
* are populated downstream; for example JsonRpcSigner defers to the
|
||||
* node to populate the nonce and fee data.
|
||||
*
|
||||
* @param tx - The call to prepare
|
||||
*/
|
||||
populateTransaction(tx: TransactionRequest): Promise<TransactionLike<string>>;
|
||||
|
||||
|
||||
////////////////////
|
||||
// Execution
|
||||
|
||||
/**
|
||||
* Estimates the required gas required to execute //tx// on the Blockchain. This
|
||||
* will be the expected amount a transaction will require as its ``gasLimit``
|
||||
* to successfully run all the necessary computations and store the needed state
|
||||
* that the transaction intends.
|
||||
*
|
||||
* Keep in mind that this is **best efforts**, since the state of the Blockchain
|
||||
* is in flux, which could affect transaction gas requirements.
|
||||
*
|
||||
* @throws UNPREDICTABLE_GAS_LIMIT A transaction that is believed by the node to likely
|
||||
* fail will throw an error during gas estimation. This could indicate that it
|
||||
* will actually fail or that the circumstances are simply too complex for the
|
||||
* node to take into account. In these cases, a manually determined ``gasLimit``
|
||||
* will need to be made.
|
||||
*/
|
||||
estimateGas(tx: TransactionRequest): Promise<bigint>;
|
||||
|
||||
/**
|
||||
* Evaluates the //tx// by running it against the current Blockchain state. This
|
||||
* cannot change state and has no cost in ether, as it is effectively simulating
|
||||
* execution.
|
||||
*
|
||||
* This can be used to have the Blockchain perform computations based on its state
|
||||
* (e.g. running a Contract's getters) or to simulate the effect of a transaction
|
||||
* before actually performing an operation.
|
||||
*/
|
||||
call(tx: TransactionRequest): Promise<string>;
|
||||
|
||||
/**
|
||||
* Resolves an ENS Name to an address.
|
||||
*/
|
||||
resolveName(name: string): Promise<null | string>;
|
||||
|
||||
|
||||
////////////////////
|
||||
// Signing
|
||||
|
||||
/**
|
||||
* Signs %%tx%%, returning the fully signed transaction. This does not
|
||||
* populate any additional properties within the transaction.
|
||||
*/
|
||||
signTransaction(tx: TransactionRequest): Promise<string>;
|
||||
|
||||
/**
|
||||
* Sends %%tx%% to the Network. The ``signer.populateTransaction(tx)``
|
||||
* is called first to ensure all necessary properties for the
|
||||
* transaction to be valid have been popualted first.
|
||||
*/
|
||||
sendTransaction(tx: TransactionRequest): Promise<TransactionResponse>;
|
||||
|
||||
/**
|
||||
* Signs an [[link-eip-191]] prefixed personal message.
|
||||
*
|
||||
* If the %%message%% is a string, it is signed as UTF-8 encoded bytes. It is **not**
|
||||
* interpretted as a [[BytesLike]]; so the string ``"0x1234"`` is signed as six
|
||||
* characters, **not** two bytes.
|
||||
*
|
||||
* To sign that example as two bytes, the Uint8Array should be used
|
||||
* (i.e. ``new Uint8Array([ 0x12, 0x34 ])``).
|
||||
*/
|
||||
signMessage(message: string | Uint8Array): Promise<string>;
|
||||
|
||||
/**
|
||||
* Signs the [[link-eip-712]] typed data.
|
||||
*/
|
||||
signTypedData(domain: TypedDataDomain, types: Record<string, Array<TypedDataField>>, value: Record<string, any>): Promise<string>;
|
||||
|
||||
/**
|
||||
* Prepares an [[AuthorizationRequest]] for authorization by
|
||||
* populating any missing properties:
|
||||
* - resolves ``address`` (if an Addressable or ENS name)
|
||||
* - populates ``nonce`` via ``signer.getNonce("pending")``
|
||||
* - populates ``chainId`` via ``signer.provider.getNetwork()``
|
||||
*/
|
||||
populateAuthorization(auth: AuthorizationRequest): Promise<AuthorizationRequest>;
|
||||
|
||||
/**
|
||||
* Signs an %%authorization%% to be used in [[link-eip-7702]]
|
||||
* transactions.
|
||||
*/
|
||||
authorize(authorization: AuthorizationRequest): Promise<Authorization>;
|
||||
}
|
||||
74
dev/env/node_modules/ethers/src.ts/providers/subscriber-connection.ts
generated
vendored
Executable file
74
dev/env/node_modules/ethers/src.ts/providers/subscriber-connection.ts
generated
vendored
Executable file
@@ -0,0 +1,74 @@
|
||||
|
||||
import { getNumber } from "../utils/index.js";
|
||||
|
||||
import type { Subscriber } from "./abstract-provider.js";
|
||||
|
||||
|
||||
//#TODO: Temp
|
||||
import type { Provider } from "./provider.js";
|
||||
|
||||
/**
|
||||
* @TODO
|
||||
*
|
||||
* @_docloc: api/providers/abstract-provider
|
||||
*/
|
||||
export interface ConnectionRpcProvider extends Provider {
|
||||
//send(method: string, params: Array<any>): Promise<any>;
|
||||
_subscribe(param: Array<any>, processFunc: (result: any) => void): number;
|
||||
_unsubscribe(filterId: number): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* @TODO
|
||||
*
|
||||
* @_docloc: api/providers/abstract-provider
|
||||
*/
|
||||
export class BlockConnectionSubscriber implements Subscriber {
|
||||
#provider: ConnectionRpcProvider;
|
||||
#blockNumber: number;
|
||||
|
||||
#running: boolean;
|
||||
|
||||
#filterId: null | number;
|
||||
|
||||
constructor(provider: ConnectionRpcProvider) {
|
||||
this.#provider = provider;
|
||||
this.#blockNumber = -2;
|
||||
this.#running = false;
|
||||
this.#filterId = null;
|
||||
}
|
||||
|
||||
start(): void {
|
||||
if (this.#running) { return; }
|
||||
this.#running = true;
|
||||
|
||||
this.#filterId = this.#provider._subscribe([ "newHeads" ], (result: any) => {
|
||||
const blockNumber = getNumber(result.number);
|
||||
const initial = (this.#blockNumber === -2) ? blockNumber: (this.#blockNumber + 1)
|
||||
for (let b = initial; b <= blockNumber; b++) {
|
||||
this.#provider.emit("block", b);
|
||||
}
|
||||
this.#blockNumber = blockNumber;
|
||||
});
|
||||
}
|
||||
|
||||
stop(): void {
|
||||
if (!this.#running) { return; }
|
||||
this.#running = false;
|
||||
|
||||
if (this.#filterId != null) {
|
||||
this.#provider._unsubscribe(this.#filterId);
|
||||
this.#filterId = null;
|
||||
}
|
||||
}
|
||||
|
||||
pause(dropWhilePaused?: boolean): void {
|
||||
if (dropWhilePaused) { this.#blockNumber = -2; }
|
||||
this.stop();
|
||||
}
|
||||
|
||||
resume(): void {
|
||||
this.start();
|
||||
}
|
||||
}
|
||||
|
||||
199
dev/env/node_modules/ethers/src.ts/providers/subscriber-filterid.ts
generated
vendored
Executable file
199
dev/env/node_modules/ethers/src.ts/providers/subscriber-filterid.ts
generated
vendored
Executable file
@@ -0,0 +1,199 @@
|
||||
import { isError } from "../utils/index.js";
|
||||
|
||||
import { PollingEventSubscriber } from "./subscriber-polling.js";
|
||||
|
||||
import type { AbstractProvider, Subscriber } from "./abstract-provider.js";
|
||||
import type { Network } from "./network.js";
|
||||
import type { EventFilter } from "./provider.js";
|
||||
import type { JsonRpcApiProvider } from "./provider-jsonrpc.js";
|
||||
|
||||
function copy(obj: any): any {
|
||||
return JSON.parse(JSON.stringify(obj));
|
||||
}
|
||||
|
||||
/**
|
||||
* Some backends support subscribing to events using a Filter ID.
|
||||
*
|
||||
* When subscribing with this technique, the node issues a unique
|
||||
* //Filter ID//. At this point the node dedicates resources to
|
||||
* the filter, so that periodic calls to follow up on the //Filter ID//
|
||||
* will receive any events since the last call.
|
||||
*
|
||||
* @_docloc: api/providers/abstract-provider
|
||||
*/
|
||||
export class FilterIdSubscriber implements Subscriber {
|
||||
#provider: JsonRpcApiProvider;
|
||||
|
||||
#filterIdPromise: null | Promise<string>;
|
||||
#poller: (b: number) => Promise<void>;
|
||||
|
||||
#running: boolean;
|
||||
|
||||
#network: null | Network;
|
||||
|
||||
#hault: boolean;
|
||||
|
||||
/**
|
||||
* Creates a new **FilterIdSubscriber** which will used [[_subscribe]]
|
||||
* and [[_emitResults]] to setup the subscription and provide the event
|
||||
* to the %%provider%%.
|
||||
*/
|
||||
constructor(provider: JsonRpcApiProvider) {
|
||||
this.#provider = provider;
|
||||
|
||||
this.#filterIdPromise = null;
|
||||
this.#poller = this.#poll.bind(this);
|
||||
|
||||
this.#running = false;
|
||||
|
||||
this.#network = null;
|
||||
|
||||
this.#hault = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sub-classes **must** override this to begin the subscription.
|
||||
*/
|
||||
_subscribe(provider: JsonRpcApiProvider): Promise<string> {
|
||||
throw new Error("subclasses must override this");
|
||||
}
|
||||
|
||||
/**
|
||||
* Sub-classes **must** override this handle the events.
|
||||
*/
|
||||
_emitResults(provider: AbstractProvider, result: Array<any>): Promise<void> {
|
||||
throw new Error("subclasses must override this");
|
||||
}
|
||||
|
||||
/**
|
||||
* Sub-classes **must** override this handle recovery on errors.
|
||||
*/
|
||||
_recover(provider: AbstractProvider): Subscriber {
|
||||
throw new Error("subclasses must override this");
|
||||
}
|
||||
|
||||
async #poll(blockNumber: number): Promise<void> {
|
||||
try {
|
||||
// Subscribe if necessary
|
||||
if (this.#filterIdPromise == null) {
|
||||
this.#filterIdPromise = this._subscribe(this.#provider);
|
||||
}
|
||||
|
||||
// Get the Filter ID
|
||||
let filterId: null | string = null;
|
||||
try {
|
||||
filterId = await this.#filterIdPromise;
|
||||
} catch (error) {
|
||||
if (!isError(error, "UNSUPPORTED_OPERATION") || error.operation !== "eth_newFilter") {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// The backend does not support Filter ID; downgrade to
|
||||
// polling
|
||||
if (filterId == null) {
|
||||
this.#filterIdPromise = null;
|
||||
this.#provider._recoverSubscriber(this, this._recover(this.#provider));
|
||||
return;
|
||||
}
|
||||
|
||||
const network = await this.#provider.getNetwork();
|
||||
if (!this.#network) { this.#network = network; }
|
||||
|
||||
if ((this.#network as Network).chainId !== network.chainId) {
|
||||
throw new Error("chaid changed");
|
||||
}
|
||||
|
||||
if (this.#hault) { return; }
|
||||
|
||||
const result = await this.#provider.send("eth_getFilterChanges", [ filterId ]);
|
||||
await this._emitResults(this.#provider, result);
|
||||
} catch (error) { console.log("@TODO", error); }
|
||||
|
||||
this.#provider.once("block", this.#poller);
|
||||
}
|
||||
|
||||
#teardown(): void {
|
||||
const filterIdPromise = this.#filterIdPromise;
|
||||
if (filterIdPromise) {
|
||||
this.#filterIdPromise = null;
|
||||
filterIdPromise.then((filterId) => {
|
||||
if (this.#provider.destroyed) { return; }
|
||||
this.#provider.send("eth_uninstallFilter", [ filterId ]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
start(): void {
|
||||
if (this.#running) { return; }
|
||||
this.#running = true;
|
||||
|
||||
this.#poll(-2);
|
||||
}
|
||||
|
||||
stop(): void {
|
||||
if (!this.#running) { return; }
|
||||
this.#running = false;
|
||||
|
||||
this.#hault = true;
|
||||
this.#teardown();
|
||||
this.#provider.off("block", this.#poller);
|
||||
}
|
||||
|
||||
pause(dropWhilePaused?: boolean): void {
|
||||
if (dropWhilePaused){ this.#teardown(); }
|
||||
this.#provider.off("block", this.#poller);
|
||||
}
|
||||
|
||||
resume(): void { this.start(); }
|
||||
}
|
||||
|
||||
/**
|
||||
* A **FilterIdSubscriber** for receiving contract events.
|
||||
*
|
||||
* @_docloc: api/providers/abstract-provider
|
||||
*/
|
||||
export class FilterIdEventSubscriber extends FilterIdSubscriber {
|
||||
#event: EventFilter;
|
||||
|
||||
/**
|
||||
* Creates a new **FilterIdEventSubscriber** attached to %%provider%%
|
||||
* listening for %%filter%%.
|
||||
*/
|
||||
constructor(provider: JsonRpcApiProvider, filter: EventFilter) {
|
||||
super(provider);
|
||||
this.#event = copy(filter);
|
||||
}
|
||||
|
||||
_recover(provider: AbstractProvider): Subscriber {
|
||||
return new PollingEventSubscriber(provider, this.#event);
|
||||
}
|
||||
|
||||
async _subscribe(provider: JsonRpcApiProvider): Promise<string> {
|
||||
const filterId = await provider.send("eth_newFilter", [ this.#event ]);
|
||||
return filterId;
|
||||
}
|
||||
|
||||
async _emitResults(provider: JsonRpcApiProvider, results: Array<any>): Promise<void> {
|
||||
for (const result of results) {
|
||||
provider.emit(this.#event, provider._wrapLog(result, provider._network));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A **FilterIdSubscriber** for receiving pending transactions events.
|
||||
*
|
||||
* @_docloc: api/providers/abstract-provider
|
||||
*/
|
||||
export class FilterIdPendingSubscriber extends FilterIdSubscriber {
|
||||
async _subscribe(provider: JsonRpcApiProvider): Promise<string> {
|
||||
return await provider.send("eth_newPendingTransactionFilter", [ ]);
|
||||
}
|
||||
|
||||
async _emitResults(provider: JsonRpcApiProvider, results: Array<any>): Promise<void> {
|
||||
for (const result of results) {
|
||||
provider.emit("pending", result);
|
||||
}
|
||||
}
|
||||
}
|
||||
321
dev/env/node_modules/ethers/src.ts/providers/subscriber-polling.ts
generated
vendored
Executable file
321
dev/env/node_modules/ethers/src.ts/providers/subscriber-polling.ts
generated
vendored
Executable file
@@ -0,0 +1,321 @@
|
||||
import { assert, isHexString } from "../utils/index.js";
|
||||
|
||||
import type { AbstractProvider, Subscriber } from "./abstract-provider.js";
|
||||
import type { EventFilter, OrphanFilter, ProviderEvent } from "./provider.js";
|
||||
|
||||
function copy(obj: any): any {
|
||||
return JSON.parse(JSON.stringify(obj));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the polling subscriber for common events.
|
||||
*
|
||||
* @_docloc: api/providers/abstract-provider
|
||||
*/
|
||||
export function getPollingSubscriber(provider: AbstractProvider, event: ProviderEvent): Subscriber {
|
||||
if (event === "block") { return new PollingBlockSubscriber(provider); }
|
||||
if (isHexString(event, 32)) { return new PollingTransactionSubscriber(provider, event); }
|
||||
|
||||
assert(false, "unsupported polling event", "UNSUPPORTED_OPERATION", {
|
||||
operation: "getPollingSubscriber", info: { event }
|
||||
});
|
||||
}
|
||||
|
||||
// @TODO: refactor this
|
||||
|
||||
/**
|
||||
* A **PollingBlockSubscriber** polls at a regular interval for a change
|
||||
* in the block number.
|
||||
*
|
||||
* @_docloc: api/providers/abstract-provider
|
||||
*/
|
||||
export class PollingBlockSubscriber implements Subscriber {
|
||||
#provider: AbstractProvider;
|
||||
#poller: null | number;
|
||||
|
||||
#interval: number;
|
||||
|
||||
// The most recent block we have scanned for events. The value -2
|
||||
// indicates we still need to fetch an initial block number
|
||||
#blockNumber: number;
|
||||
|
||||
/**
|
||||
* Create a new **PollingBlockSubscriber** attached to %%provider%%.
|
||||
*/
|
||||
constructor(provider: AbstractProvider) {
|
||||
this.#provider = provider;
|
||||
this.#poller = null;
|
||||
this.#interval = 4000;
|
||||
|
||||
this.#blockNumber = -2;
|
||||
}
|
||||
|
||||
/**
|
||||
* The polling interval.
|
||||
*/
|
||||
get pollingInterval(): number { return this.#interval; }
|
||||
set pollingInterval(value: number) { this.#interval = value; }
|
||||
|
||||
async #poll(): Promise<void> {
|
||||
try {
|
||||
const blockNumber = await this.#provider.getBlockNumber();
|
||||
|
||||
// Bootstrap poll to setup our initial block number
|
||||
if (this.#blockNumber === -2) {
|
||||
this.#blockNumber = blockNumber;
|
||||
return;
|
||||
}
|
||||
|
||||
// @TODO: Put a cap on the maximum number of events per loop?
|
||||
|
||||
if (blockNumber !== this.#blockNumber) {
|
||||
for (let b = this.#blockNumber + 1; b <= blockNumber; b++) {
|
||||
// We have been stopped
|
||||
if (this.#poller == null) { return; }
|
||||
|
||||
await this.#provider.emit("block", b);
|
||||
}
|
||||
|
||||
this.#blockNumber = blockNumber;
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
// @TODO: Minor bump, add an "error" event to let subscribers
|
||||
// know things went awry.
|
||||
//console.log(error);
|
||||
}
|
||||
|
||||
// We have been stopped
|
||||
if (this.#poller == null) { return; }
|
||||
|
||||
this.#poller = this.#provider._setTimeout(this.#poll.bind(this), this.#interval);
|
||||
}
|
||||
|
||||
start(): void {
|
||||
if (this.#poller) { return; }
|
||||
this.#poller = this.#provider._setTimeout(this.#poll.bind(this), this.#interval);
|
||||
this.#poll();
|
||||
}
|
||||
|
||||
stop(): void {
|
||||
if (!this.#poller) { return; }
|
||||
this.#provider._clearTimeout(this.#poller);
|
||||
this.#poller = null;
|
||||
}
|
||||
|
||||
pause(dropWhilePaused?: boolean): void {
|
||||
this.stop();
|
||||
if (dropWhilePaused) { this.#blockNumber = -2; }
|
||||
}
|
||||
|
||||
resume(): void {
|
||||
this.start();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* An **OnBlockSubscriber** can be sub-classed, with a [[_poll]]
|
||||
* implmentation which will be called on every new block.
|
||||
*
|
||||
* @_docloc: api/providers/abstract-provider
|
||||
*/
|
||||
export class OnBlockSubscriber implements Subscriber {
|
||||
#provider: AbstractProvider;
|
||||
#poll: (b: number) => void;
|
||||
#running: boolean;
|
||||
|
||||
/**
|
||||
* Create a new **OnBlockSubscriber** attached to %%provider%%.
|
||||
*/
|
||||
constructor(provider: AbstractProvider) {
|
||||
this.#provider = provider;
|
||||
this.#running = false;
|
||||
this.#poll = (blockNumber: number) => {
|
||||
this._poll(blockNumber, this.#provider);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called on every new block.
|
||||
*/
|
||||
async _poll(blockNumber: number, provider: AbstractProvider): Promise<void> {
|
||||
throw new Error("sub-classes must override this");
|
||||
}
|
||||
|
||||
start(): void {
|
||||
if (this.#running) { return; }
|
||||
this.#running = true;
|
||||
|
||||
this.#poll(-2);
|
||||
this.#provider.on("block", this.#poll);
|
||||
}
|
||||
|
||||
stop(): void {
|
||||
if (!this.#running) { return; }
|
||||
this.#running = false;
|
||||
|
||||
this.#provider.off("block", this.#poll);
|
||||
}
|
||||
|
||||
pause(dropWhilePaused?: boolean): void { this.stop(); }
|
||||
resume(): void { this.start(); }
|
||||
}
|
||||
|
||||
export class PollingBlockTagSubscriber extends OnBlockSubscriber {
|
||||
readonly #tag: string;
|
||||
#lastBlock: number;
|
||||
|
||||
constructor(provider: AbstractProvider, tag: string) {
|
||||
super(provider);
|
||||
this.#tag = tag;
|
||||
this.#lastBlock = -2;
|
||||
}
|
||||
|
||||
pause(dropWhilePaused?: boolean): void {
|
||||
if (dropWhilePaused) { this.#lastBlock = -2; }
|
||||
super.pause(dropWhilePaused);
|
||||
}
|
||||
|
||||
async _poll(blockNumber: number, provider: AbstractProvider): Promise<void> {
|
||||
const block = await provider.getBlock(this.#tag);
|
||||
if (block == null) { return; }
|
||||
|
||||
if (this.#lastBlock === -2) {
|
||||
this.#lastBlock = block.number;
|
||||
} else if (block.number > this.#lastBlock) {
|
||||
provider.emit(this.#tag, block.number);
|
||||
this.#lastBlock = block.number;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @_ignore:
|
||||
*
|
||||
* @_docloc: api/providers/abstract-provider
|
||||
*/
|
||||
export class PollingOrphanSubscriber extends OnBlockSubscriber {
|
||||
#filter: OrphanFilter;
|
||||
|
||||
constructor(provider: AbstractProvider, filter: OrphanFilter) {
|
||||
super(provider);
|
||||
this.#filter = copy(filter);
|
||||
}
|
||||
|
||||
async _poll(blockNumber: number, provider: AbstractProvider): Promise<void> {
|
||||
throw new Error("@TODO");
|
||||
console.log(this.#filter);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A **PollingTransactionSubscriber** will poll for a given transaction
|
||||
* hash for its receipt.
|
||||
*
|
||||
* @_docloc: api/providers/abstract-provider
|
||||
*/
|
||||
export class PollingTransactionSubscriber extends OnBlockSubscriber {
|
||||
#hash: string;
|
||||
|
||||
/**
|
||||
* Create a new **PollingTransactionSubscriber** attached to
|
||||
* %%provider%%, listening for %%hash%%.
|
||||
*/
|
||||
constructor(provider: AbstractProvider, hash: string) {
|
||||
super(provider);
|
||||
this.#hash = hash;
|
||||
}
|
||||
|
||||
async _poll(blockNumber: number, provider: AbstractProvider): Promise<void> {
|
||||
const tx = await provider.getTransactionReceipt(this.#hash);
|
||||
if (tx) { provider.emit(this.#hash, tx); }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A **PollingEventSubscriber** will poll for a given filter for its logs.
|
||||
*
|
||||
* @_docloc: api/providers/abstract-provider
|
||||
*/
|
||||
export class PollingEventSubscriber implements Subscriber {
|
||||
#provider: AbstractProvider;
|
||||
#filter: EventFilter;
|
||||
#poller: (b: number) => void;
|
||||
|
||||
#running: boolean;
|
||||
|
||||
// The most recent block we have scanned for events. The value -2
|
||||
// indicates we still need to fetch an initial block number
|
||||
#blockNumber: number;
|
||||
|
||||
/**
|
||||
* Create a new **PollingTransactionSubscriber** attached to
|
||||
* %%provider%%, listening for %%filter%%.
|
||||
*/
|
||||
constructor(provider: AbstractProvider, filter: EventFilter) {
|
||||
this.#provider = provider;
|
||||
this.#filter = copy(filter);
|
||||
this.#poller = this.#poll.bind(this);
|
||||
this.#running = false;
|
||||
this.#blockNumber = -2;
|
||||
}
|
||||
|
||||
async #poll(blockNumber: number): Promise<void> {
|
||||
// The initial block hasn't been determined yet
|
||||
if (this.#blockNumber === -2) { return; }
|
||||
|
||||
const filter = copy(this.#filter);
|
||||
filter.fromBlock = this.#blockNumber + 1;
|
||||
filter.toBlock = blockNumber;
|
||||
|
||||
const logs = await this.#provider.getLogs(filter);
|
||||
|
||||
// No logs could just mean the node has not indexed them yet,
|
||||
// so we keep a sliding window of 60 blocks to keep scanning
|
||||
if (logs.length === 0) {
|
||||
if (this.#blockNumber < blockNumber - 60) {
|
||||
this.#blockNumber = blockNumber - 60;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (const log of logs) {
|
||||
this.#provider.emit(this.#filter, log);
|
||||
|
||||
// Only advance the block number when logs were found to
|
||||
// account for networks (like BNB and Polygon) which may
|
||||
// sacrifice event consistency for block event speed
|
||||
this.#blockNumber = log.blockNumber;
|
||||
}
|
||||
}
|
||||
|
||||
start(): void {
|
||||
if (this.#running) { return; }
|
||||
this.#running = true;
|
||||
|
||||
if (this.#blockNumber === -2) {
|
||||
this.#provider.getBlockNumber().then((blockNumber) => {
|
||||
this.#blockNumber = blockNumber;
|
||||
});
|
||||
}
|
||||
this.#provider.on("block", this.#poller);
|
||||
}
|
||||
|
||||
stop(): void {
|
||||
if (!this.#running) { return; }
|
||||
this.#running = false;
|
||||
|
||||
this.#provider.off("block", this.#poller);
|
||||
}
|
||||
|
||||
pause(dropWhilePaused?: boolean): void {
|
||||
this.stop();
|
||||
if (dropWhilePaused) { this.#blockNumber = -2; }
|
||||
}
|
||||
|
||||
resume(): void {
|
||||
this.start();
|
||||
}
|
||||
}
|
||||
11
dev/env/node_modules/ethers/src.ts/providers/ws-browser.ts
generated
vendored
Executable file
11
dev/env/node_modules/ethers/src.ts/providers/ws-browser.ts
generated
vendored
Executable file
@@ -0,0 +1,11 @@
|
||||
|
||||
function getGlobal(): any {
|
||||
if (typeof self !== 'undefined') { return self; }
|
||||
if (typeof window !== 'undefined') { return window; }
|
||||
if (typeof global !== 'undefined') { return global; }
|
||||
throw new Error('unable to locate global object');
|
||||
};
|
||||
|
||||
const _WebSocket = getGlobal().WebSocket;
|
||||
|
||||
export { _WebSocket as WebSocket };
|
||||
3
dev/env/node_modules/ethers/src.ts/providers/ws.ts
generated
vendored
Executable file
3
dev/env/node_modules/ethers/src.ts/providers/ws.ts
generated
vendored
Executable file
@@ -0,0 +1,3 @@
|
||||
export { WebSocket } from "ws";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user