"use strict"; import { BigNumber, BigNumberish } from "@ethersproject/bignumber"; import { BytesLike, isHexString } from "@ethersproject/bytes"; import { Network } from "@ethersproject/networks"; import { Deferrable, Description, defineReadOnly, resolveProperties } from "@ethersproject/properties"; import { AccessListish, Transaction } from "@ethersproject/transactions"; import { OnceBlockable } from "@ethersproject/web"; import { Logger } from "@ethersproject/logger"; import { version } from "./_version"; const logger = new Logger(version); /////////////////////////////// // Exported Types export type TransactionRequest = { to?: string, from?: string, nonce?: BigNumberish, gasLimit?: BigNumberish, gasPrice?: BigNumberish, data?: BytesLike, value?: BigNumberish, chainId?: number type?: number; accessList?: AccessListish; maxPriorityFeePerGas?: BigNumberish; maxFeePerGas?: BigNumberish; } export interface TransactionResponse extends Transaction { hash: string; // Only if a transaction has been mined blockNumber?: number, blockHash?: string, timestamp?: number, confirmations: number, // Not optional (as it is in Transaction) from: string; // The raw transaction raw?: string, // This function waits until the transaction has been mined wait: (confirmations?: number) => Promise }; export type BlockTag = string | number; interface _Block { hash: string; parentHash: string; number: number; timestamp: number; nonce: string; difficulty: number; gasLimit: BigNumber; gasUsed: BigNumber; miner: string; extraData: string; baseFeePerGas?: null | BigNumber; } export interface Block extends _Block { transactions: Array; } export interface BlockWithTransactions extends _Block { transactions: Array; } export interface Log { blockNumber: number; blockHash: string; transactionIndex: number; removed: boolean; address: string; data: string; topics: Array; transactionHash: string; logIndex: number; } export interface TransactionReceipt { to: string; from: string; contractAddress: string, transactionIndex: number, root?: string, gasUsed: BigNumber, logsBloom: string, blockHash: string, transactionHash: string, logs: Array, blockNumber: number, confirmations: number, cumulativeGasUsed: BigNumber, effectiveGasPrice: BigNumber, byzantium: boolean, type: number; status?: number }; export interface FeeData { maxFeePerGas: null | BigNumber; maxPriorityFeePerGas: null | BigNumber; gasPrice: null | BigNumber; } export interface EventFilter { address?: string; topics?: Array | null>; } export interface Filter extends EventFilter { fromBlock?: BlockTag, toBlock?: BlockTag, } export interface FilterByBlockHash extends EventFilter { blockHash?: string; } //export type CallTransactionable = { // call(transaction: TransactionRequest): Promise; //}; export abstract class ForkEvent extends Description { readonly expiry: number; readonly _isForkEvent?: boolean; static isForkEvent(value: any): value is ForkEvent { return !!(value && value._isForkEvent); } } export class BlockForkEvent extends ForkEvent { readonly blockHash: string; readonly _isBlockForkEvent?: boolean; constructor(blockHash: string, expiry?: number) { if (!isHexString(blockHash, 32)) { logger.throwArgumentError("invalid blockHash", "blockHash", blockHash); } super({ _isForkEvent: true, _isBlockForkEvent: true, expiry: (expiry || 0), blockHash: blockHash }); } } export class TransactionForkEvent extends ForkEvent { readonly hash: string; readonly _isTransactionOrderForkEvent?: boolean; constructor(hash: string, expiry?: number) { if (!isHexString(hash, 32)) { logger.throwArgumentError("invalid transaction hash", "hash", hash); } super({ _isForkEvent: true, _isTransactionForkEvent: true, expiry: (expiry || 0), hash: hash }); } } export class TransactionOrderForkEvent extends ForkEvent { readonly beforeHash: string; readonly afterHash: string; constructor(beforeHash: string, afterHash: string, expiry?: number) { if (!isHexString(beforeHash, 32)) { logger.throwArgumentError("invalid transaction hash", "beforeHash", beforeHash); } if (!isHexString(afterHash, 32)) { logger.throwArgumentError("invalid transaction hash", "afterHash", afterHash); } super({ _isForkEvent: true, _isTransactionOrderForkEvent: true, expiry: (expiry || 0), beforeHash: beforeHash, afterHash: afterHash }); } } export type EventType = string | Array> | EventFilter | ForkEvent; export type Listener = (...args: Array) => void; /////////////////////////////// // Exported Abstracts export abstract class Provider implements OnceBlockable { // Network abstract getNetwork(): Promise; // Latest State abstract getBlockNumber(): Promise; abstract getGasPrice(): Promise; async getFeeData(): Promise { const { block, gasPrice } = await resolveProperties({ block: this.getBlock("latest"), gasPrice: this.getGasPrice().catch((error) => { // @TODO: Why is this now failing on Calaveras? //console.log(error); return null; }) }); let maxFeePerGas = null, maxPriorityFeePerGas = null; if (block && block.baseFeePerGas) { // We may want to compute this more accurately in the future, // using the formula "check if the base fee is correct". // See: https://eips.ethereum.org/EIPS/eip-1559 maxPriorityFeePerGas = BigNumber.from("2500000000"); maxFeePerGas = block.baseFeePerGas.mul(2).add(maxPriorityFeePerGas); } return { maxFeePerGas, maxPriorityFeePerGas, gasPrice }; } // Account abstract getBalance(addressOrName: string | Promise, blockTag?: BlockTag | Promise): Promise; abstract getTransactionCount(addressOrName: string | Promise, blockTag?: BlockTag | Promise): Promise; abstract getCode(addressOrName: string | Promise, blockTag?: BlockTag | Promise): Promise ; abstract getStorageAt(addressOrName: string | Promise, position: BigNumberish | Promise, blockTag?: BlockTag | Promise): Promise; // Execution abstract sendTransaction(signedTransaction: string | Promise): Promise; abstract call(transaction: Deferrable, blockTag?: BlockTag | Promise): Promise; abstract estimateGas(transaction: Deferrable): Promise; // Queries abstract getBlock(blockHashOrBlockTag: BlockTag | string | Promise): Promise; abstract getBlockWithTransactions(blockHashOrBlockTag: BlockTag | string | Promise): Promise; abstract getTransaction(transactionHash: string): Promise; abstract getTransactionReceipt(transactionHash: string): Promise; // Bloom-filter Queries abstract getLogs(filter: Filter): Promise>; // ENS abstract resolveName(name: string | Promise): Promise; abstract lookupAddress(address: string | Promise): Promise; // Event Emitter (ish) abstract on(eventName: EventType, listener: Listener): Provider; abstract once(eventName: EventType, listener: Listener): Provider; abstract emit(eventName: EventType, ...args: Array): boolean abstract listenerCount(eventName?: EventType): number; abstract listeners(eventName?: EventType): Array; abstract off(eventName: EventType, listener?: Listener): Provider; abstract removeAllListeners(eventName?: EventType): Provider; // Alias for "on" addListener(eventName: EventType, listener: Listener): Provider { return this.on(eventName, listener); } // Alias for "off" removeListener(eventName: EventType, listener: Listener): Provider { return this.off(eventName, listener); } // @TODO: This *could* be implemented here, but would pull in events... abstract waitForTransaction(transactionHash: string, confirmations?: number, timeout?: number): Promise; readonly _isProvider: boolean; constructor() { logger.checkAbstract(new.target, Provider); defineReadOnly(this, "_isProvider", true); } static isProvider(value: any): value is Provider { return !!(value && value._isProvider); } /* static getResolver(network: Network, callable: CallTransactionable, namehash: string): string { // No ENS... if (!network.ensAddress) { errors.throwError( "network does support ENS", errors.UNSUPPORTED_OPERATION, { operation: "ENS", network: network.name } ); } // Not a namehash if (!isHexString(namehash, 32)) { errors.throwArgumentError("invalid name hash", "namehash", namehash); } // keccak256("resolver(bytes32)") let data = "0x0178b8bf" + namehash.substring(2); let transaction = { to: network.ensAddress, data: data }; return provider.call(transaction).then((data) => { return provider.formatter.callAddress(data); }); } static resolveNamehash(network: Network, callable: CallTransactionable, namehash: string): string { return this.getResolver(network, callable, namehash).then((resolverAddress) => { if (!resolverAddress) { return null; } // keccak256("addr(bytes32)") let data = "0x3b3b57de" + namehash(name).substring(2); let transaction = { to: resolverAddress, data: data }; return callable.call(transaction).then((data) => { return this.formatter.callAddress(data); }); }) } */ }