index.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. "use strict";
  2. import { BigNumber, BigNumberish } from "@ethersproject/bignumber";
  3. import { BytesLike, isHexString } from "@ethersproject/bytes";
  4. import { Network } from "@ethersproject/networks";
  5. import { Deferrable, Description, defineReadOnly, resolveProperties } from "@ethersproject/properties";
  6. import { AccessListish, Transaction } from "@ethersproject/transactions";
  7. import { OnceBlockable } from "@ethersproject/web";
  8. import { Logger } from "@ethersproject/logger";
  9. import { version } from "./_version";
  10. const logger = new Logger(version);
  11. ///////////////////////////////
  12. // Exported Types
  13. export type TransactionRequest = {
  14. to?: string,
  15. from?: string,
  16. nonce?: BigNumberish,
  17. gasLimit?: BigNumberish,
  18. gasPrice?: BigNumberish,
  19. data?: BytesLike,
  20. value?: BigNumberish,
  21. chainId?: number
  22. type?: number;
  23. accessList?: AccessListish;
  24. maxPriorityFeePerGas?: BigNumberish;
  25. maxFeePerGas?: BigNumberish;
  26. customData?: Record<string, any>;
  27. ccipReadEnabled?: boolean;
  28. }
  29. export interface TransactionResponse extends Transaction {
  30. hash: string;
  31. // Only if a transaction has been mined
  32. blockNumber?: number,
  33. blockHash?: string,
  34. timestamp?: number,
  35. confirmations: number,
  36. // Not optional (as it is in Transaction)
  37. from: string;
  38. // The raw transaction
  39. raw?: string,
  40. // This function waits until the transaction has been mined
  41. wait: (confirmations?: number) => Promise<TransactionReceipt>
  42. };
  43. export type BlockTag = string | number;
  44. export interface _Block {
  45. hash: string;
  46. parentHash: string;
  47. number: number;
  48. timestamp: number;
  49. nonce: string;
  50. difficulty: number;
  51. _difficulty: BigNumber;
  52. gasLimit: BigNumber;
  53. gasUsed: BigNumber;
  54. miner: string;
  55. extraData: string;
  56. baseFeePerGas?: null | BigNumber;
  57. }
  58. export interface Block extends _Block {
  59. transactions: Array<string>;
  60. }
  61. export interface BlockWithTransactions extends _Block {
  62. transactions: Array<TransactionResponse>;
  63. }
  64. export interface Log {
  65. blockNumber: number;
  66. blockHash: string;
  67. transactionIndex: number;
  68. removed: boolean;
  69. address: string;
  70. data: string;
  71. topics: Array<string>;
  72. transactionHash: string;
  73. logIndex: number;
  74. }
  75. export interface TransactionReceipt {
  76. to: string;
  77. from: string;
  78. contractAddress: string,
  79. transactionIndex: number,
  80. root?: string,
  81. gasUsed: BigNumber,
  82. logsBloom: string,
  83. blockHash: string,
  84. transactionHash: string,
  85. logs: Array<Log>,
  86. blockNumber: number,
  87. confirmations: number,
  88. cumulativeGasUsed: BigNumber,
  89. effectiveGasPrice: BigNumber,
  90. byzantium: boolean,
  91. type: number;
  92. status?: number
  93. };
  94. export interface FeeData {
  95. maxFeePerGas: null | BigNumber;
  96. maxPriorityFeePerGas: null | BigNumber;
  97. gasPrice: null | BigNumber;
  98. }
  99. export interface EventFilter {
  100. address?: string;
  101. topics?: Array<string | Array<string> | null>;
  102. }
  103. export interface Filter extends EventFilter {
  104. fromBlock?: BlockTag,
  105. toBlock?: BlockTag,
  106. }
  107. export interface FilterByBlockHash extends EventFilter {
  108. blockHash?: string;
  109. }
  110. //export type CallTransactionable = {
  111. // call(transaction: TransactionRequest): Promise<TransactionResponse>;
  112. //};
  113. export abstract class ForkEvent extends Description {
  114. readonly expiry: number;
  115. readonly _isForkEvent?: boolean;
  116. static isForkEvent(value: any): value is ForkEvent {
  117. return !!(value && value._isForkEvent);
  118. }
  119. }
  120. export class BlockForkEvent extends ForkEvent {
  121. readonly blockHash: string;
  122. readonly _isBlockForkEvent?: boolean;
  123. constructor(blockHash: string, expiry?: number) {
  124. if (!isHexString(blockHash, 32)) {
  125. logger.throwArgumentError("invalid blockHash", "blockHash", blockHash);
  126. }
  127. super({
  128. _isForkEvent: true,
  129. _isBlockForkEvent: true,
  130. expiry: (expiry || 0),
  131. blockHash: blockHash
  132. });
  133. }
  134. }
  135. export class TransactionForkEvent extends ForkEvent {
  136. readonly hash: string;
  137. readonly _isTransactionOrderForkEvent?: boolean;
  138. constructor(hash: string, expiry?: number) {
  139. if (!isHexString(hash, 32)) {
  140. logger.throwArgumentError("invalid transaction hash", "hash", hash);
  141. }
  142. super({
  143. _isForkEvent: true,
  144. _isTransactionForkEvent: true,
  145. expiry: (expiry || 0),
  146. hash: hash
  147. });
  148. }
  149. }
  150. export class TransactionOrderForkEvent extends ForkEvent {
  151. readonly beforeHash: string;
  152. readonly afterHash: string;
  153. constructor(beforeHash: string, afterHash: string, expiry?: number) {
  154. if (!isHexString(beforeHash, 32)) {
  155. logger.throwArgumentError("invalid transaction hash", "beforeHash", beforeHash);
  156. }
  157. if (!isHexString(afterHash, 32)) {
  158. logger.throwArgumentError("invalid transaction hash", "afterHash", afterHash);
  159. }
  160. super({
  161. _isForkEvent: true,
  162. _isTransactionOrderForkEvent: true,
  163. expiry: (expiry || 0),
  164. beforeHash: beforeHash,
  165. afterHash: afterHash
  166. });
  167. }
  168. }
  169. export type EventType = string | Array<string | Array<string>> | EventFilter | ForkEvent;
  170. export type Listener = (...args: Array<any>) => void;
  171. ///////////////////////////////
  172. // Exported Abstracts
  173. export abstract class Provider implements OnceBlockable {
  174. // Network
  175. abstract getNetwork(): Promise<Network>;
  176. // Latest State
  177. abstract getBlockNumber(): Promise<number>;
  178. abstract getGasPrice(): Promise<BigNumber>;
  179. async getFeeData(): Promise<FeeData> {
  180. const { block, gasPrice } = await resolveProperties({
  181. block: this.getBlock("latest"),
  182. gasPrice: this.getGasPrice().catch((error) => {
  183. // @TODO: Why is this now failing on Calaveras?
  184. //console.log(error);
  185. return null;
  186. })
  187. });
  188. let maxFeePerGas = null, maxPriorityFeePerGas = null;
  189. if (block && block.baseFeePerGas) {
  190. // We may want to compute this more accurately in the future,
  191. // using the formula "check if the base fee is correct".
  192. // See: https://eips.ethereum.org/EIPS/eip-1559
  193. maxPriorityFeePerGas = BigNumber.from("1500000000");
  194. maxFeePerGas = block.baseFeePerGas.mul(2).add(maxPriorityFeePerGas);
  195. }
  196. return { maxFeePerGas, maxPriorityFeePerGas, gasPrice };
  197. }
  198. // Account
  199. abstract getBalance(addressOrName: string | Promise<string>, blockTag?: BlockTag | Promise<BlockTag>): Promise<BigNumber>;
  200. abstract getTransactionCount(addressOrName: string | Promise<string>, blockTag?: BlockTag | Promise<BlockTag>): Promise<number>;
  201. abstract getCode(addressOrName: string | Promise<string>, blockTag?: BlockTag | Promise<BlockTag>): Promise<string> ;
  202. abstract getStorageAt(addressOrName: string | Promise<string>, position: BigNumberish | Promise<BigNumberish>, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
  203. // Execution
  204. abstract sendTransaction(signedTransaction: string | Promise<string>): Promise<TransactionResponse>;
  205. abstract call(transaction: Deferrable<TransactionRequest>, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
  206. abstract estimateGas(transaction: Deferrable<TransactionRequest>): Promise<BigNumber>;
  207. // Queries
  208. abstract getBlock(blockHashOrBlockTag: BlockTag | string | Promise<BlockTag | string>): Promise<Block>;
  209. abstract getBlockWithTransactions(blockHashOrBlockTag: BlockTag | string | Promise<BlockTag | string>): Promise<BlockWithTransactions>;
  210. abstract getTransaction(transactionHash: string): Promise<TransactionResponse>;
  211. abstract getTransactionReceipt(transactionHash: string): Promise<TransactionReceipt>;
  212. // Bloom-filter Queries
  213. abstract getLogs(filter: Filter): Promise<Array<Log>>;
  214. // ENS
  215. abstract resolveName(name: string | Promise<string>): Promise<null | string>;
  216. abstract lookupAddress(address: string | Promise<string>): Promise<null | string>;
  217. // Event Emitter (ish)
  218. abstract on(eventName: EventType, listener: Listener): Provider;
  219. abstract once(eventName: EventType, listener: Listener): Provider;
  220. abstract emit(eventName: EventType, ...args: Array<any>): boolean
  221. abstract listenerCount(eventName?: EventType): number;
  222. abstract listeners(eventName?: EventType): Array<Listener>;
  223. abstract off(eventName: EventType, listener?: Listener): Provider;
  224. abstract removeAllListeners(eventName?: EventType): Provider;
  225. // Alias for "on"
  226. addListener(eventName: EventType, listener: Listener): Provider {
  227. return this.on(eventName, listener);
  228. }
  229. // Alias for "off"
  230. removeListener(eventName: EventType, listener: Listener): Provider {
  231. return this.off(eventName, listener);
  232. }
  233. // @TODO: This *could* be implemented here, but would pull in events...
  234. abstract waitForTransaction(transactionHash: string, confirmations?: number, timeout?: number): Promise<TransactionReceipt>;
  235. readonly _isProvider: boolean;
  236. constructor() {
  237. logger.checkAbstract(new.target, Provider);
  238. defineReadOnly(this, "_isProvider", true);
  239. }
  240. static isProvider(value: any): value is Provider {
  241. return !!(value && value._isProvider);
  242. }
  243. /*
  244. static getResolver(network: Network, callable: CallTransactionable, namehash: string): string {
  245. // No ENS...
  246. if (!network.ensAddress) {
  247. errors.throwError(
  248. "network does support ENS",
  249. errors.UNSUPPORTED_OPERATION,
  250. { operation: "ENS", network: network.name }
  251. );
  252. }
  253. // Not a namehash
  254. if (!isHexString(namehash, 32)) {
  255. errors.throwArgumentError("invalid name hash", "namehash", namehash);
  256. }
  257. // keccak256("resolver(bytes32)")
  258. let data = "0x0178b8bf" + namehash.substring(2);
  259. let transaction = { to: network.ensAddress, data: data };
  260. return provider.call(transaction).then((data) => {
  261. return provider.formatter.callAddress(data);
  262. });
  263. }
  264. static resolveNamehash(network: Network, callable: CallTransactionable, namehash: string): string {
  265. return this.getResolver(network, callable, namehash).then((resolverAddress) => {
  266. if (!resolverAddress) { return null; }
  267. // keccak256("addr(bytes32)")
  268. let data = "0x3b3b57de" + namehash(name).substring(2);
  269. let transaction = { to: resolverAddress, data: data };
  270. return callable.call(transaction).then((data) => {
  271. return this.formatter.callAddress(data);
  272. });
  273. })
  274. }
  275. */
  276. }