abstract-provider.ts 59 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761
  1. /**
  2. * The available providers should suffice for most developers purposes,
  3. * but the [[AbstractProvider]] class has many features which enable
  4. * sub-classing it for specific purposes.
  5. *
  6. * @_section: api/providers/abstract-provider: Subclassing Provider [abstract-provider]
  7. */
  8. // @TODO
  9. // Event coalescence
  10. // When we register an event with an async value (e.g. address is a Signer
  11. // or ENS name), we need to add it immeidately for the Event API, but also
  12. // need time to resolve the address. Upon resolving the address, we need to
  13. // migrate the listener to the static event. We also need to maintain a map
  14. // of Signer/ENS name to address so we can sync respond to listenerCount.
  15. import { getAddress, resolveAddress } from "../address/index.js";
  16. import { ZeroAddress } from "../constants/index.js";
  17. import { Contract } from "../contract/index.js";
  18. import { namehash } from "../hash/index.js";
  19. import { Transaction } from "../transaction/index.js";
  20. import {
  21. concat, dataLength, dataSlice, hexlify, isHexString,
  22. getBigInt, getBytes, getNumber,
  23. isCallException, isError, makeError, assert, assertArgument,
  24. FetchRequest,
  25. toBeArray, toQuantity,
  26. defineProperties, EventPayload, resolveProperties,
  27. toUtf8String
  28. } from "../utils/index.js";
  29. import { EnsResolver } from "./ens-resolver.js";
  30. import {
  31. formatBlock, formatLog, formatTransactionReceipt, formatTransactionResponse
  32. } from "./format.js";
  33. import { Network } from "./network.js";
  34. import { copyRequest, Block, FeeData, Log, TransactionReceipt, TransactionResponse } from "./provider.js";
  35. import {
  36. PollingBlockSubscriber, PollingBlockTagSubscriber, PollingEventSubscriber,
  37. PollingOrphanSubscriber, PollingTransactionSubscriber
  38. } from "./subscriber-polling.js";
  39. import type { Addressable, AddressLike } from "../address/index.js";
  40. import type { BigNumberish, BytesLike } from "../utils/index.js";
  41. import type { FetchResponse, Listener } from "../utils/index.js";
  42. import type { Networkish } from "./network.js";
  43. import type { FetchUrlFeeDataNetworkPlugin } from "./plugins-network.js";
  44. //import type { MaxPriorityFeePlugin } from "./plugins-network.js";
  45. import type {
  46. BlockParams, LogParams, TransactionReceiptParams,
  47. TransactionResponseParams
  48. } from "./formatting.js";
  49. import type {
  50. BlockTag, EventFilter, Filter, FilterByBlockHash, OrphanFilter,
  51. PreparedTransactionRequest, Provider, ProviderEvent,
  52. TransactionRequest
  53. } from "./provider.js";
  54. type Timer = ReturnType<typeof setTimeout>;
  55. // Constants
  56. const BN_2 = BigInt(2);
  57. const MAX_CCIP_REDIRECTS = 10;
  58. function isPromise<T = any>(value: any): value is Promise<T> {
  59. return (value && typeof(value.then) === "function");
  60. }
  61. function getTag(prefix: string, value: any): string {
  62. return prefix + ":" + JSON.stringify(value, (k, v) => {
  63. if (v == null) { return "null"; }
  64. if (typeof(v) === "bigint") { return `bigint:${ v.toString() }`}
  65. if (typeof(v) === "string") { return v.toLowerCase(); }
  66. // Sort object keys
  67. if (typeof(v) === "object" && !Array.isArray(v)) {
  68. const keys = Object.keys(v);
  69. keys.sort();
  70. return keys.reduce((accum, key) => {
  71. accum[key] = v[key];
  72. return accum;
  73. }, <any>{ });
  74. }
  75. return v;
  76. });
  77. }
  78. /**
  79. * The types of additional event values that can be emitted for the
  80. * ``"debug"`` event.
  81. */
  82. export type DebugEventAbstractProvider = {
  83. action: "sendCcipReadFetchRequest",
  84. request: FetchRequest
  85. index: number
  86. urls: Array<string>
  87. } | {
  88. action: "receiveCcipReadFetchResult",
  89. request: FetchRequest,
  90. result: any
  91. } | {
  92. action: "receiveCcipReadFetchError",
  93. request: FetchRequest,
  94. result: any
  95. } | {
  96. action: "sendCcipReadCall",
  97. transaction: { to: string, data: string }
  98. } | {
  99. action: "receiveCcipReadCallResult",
  100. transaction: { to: string, data: string }
  101. result: string
  102. } | {
  103. action: "receiveCcipReadCallError",
  104. transaction: { to: string, data: string }
  105. error: Error
  106. };
  107. /**
  108. * The value passed to the [[AbstractProvider-_getSubscriber]] method.
  109. *
  110. * Only developers sub-classing [[AbstractProvider[[ will care about this,
  111. * if they are modifying a low-level feature of how subscriptions operate.
  112. */
  113. export type Subscription = {
  114. type: "block" | "close" | "debug" | "error" | "finalized" | "network" | "pending" | "safe",
  115. tag: string
  116. } | {
  117. type: "transaction",
  118. tag: string,
  119. hash: string
  120. } | {
  121. type: "event",
  122. tag: string,
  123. filter: EventFilter
  124. } | {
  125. type: "orphan",
  126. tag: string,
  127. filter: OrphanFilter
  128. };
  129. /**
  130. * A **Subscriber** manages a subscription.
  131. *
  132. * Only developers sub-classing [[AbstractProvider[[ will care about this,
  133. * if they are modifying a low-level feature of how subscriptions operate.
  134. */
  135. export interface Subscriber {
  136. /**
  137. * Called initially when a subscriber is added the first time.
  138. */
  139. start(): void;
  140. /**
  141. * Called when there are no more subscribers to the event.
  142. */
  143. stop(): void;
  144. /**
  145. * Called when the subscription should pause.
  146. *
  147. * If %%dropWhilePaused%%, events that occur while paused should not
  148. * be emitted [[resume]].
  149. */
  150. pause(dropWhilePaused?: boolean): void;
  151. /**
  152. * Resume a paused subscriber.
  153. */
  154. resume(): void;
  155. /**
  156. * The frequency (in ms) to poll for events, if polling is used by
  157. * the subscriber.
  158. *
  159. * For non-polling subscribers, this must return ``undefined``.
  160. */
  161. pollingInterval?: number;
  162. }
  163. /**
  164. * An **UnmanagedSubscriber** is useful for events which do not require
  165. * any additional management, such as ``"debug"`` which only requires
  166. * emit in synchronous event loop triggered calls.
  167. */
  168. export class UnmanagedSubscriber implements Subscriber {
  169. /**
  170. * The name fof the event.
  171. */
  172. name!: string;
  173. /**
  174. * Create a new UnmanagedSubscriber with %%name%%.
  175. */
  176. constructor(name: string) { defineProperties<UnmanagedSubscriber>(this, { name }); }
  177. start(): void { }
  178. stop(): void { }
  179. pause(dropWhilePaused?: boolean): void { }
  180. resume(): void { }
  181. }
  182. type Sub = {
  183. tag: string;
  184. nameMap: Map<string, string>
  185. addressableMap: WeakMap<Addressable, string>;
  186. listeners: Array<{ listener: Listener, once: boolean }>;
  187. // @TODO: get rid of this, as it is (and has to be)
  188. // tracked in subscriber
  189. started: boolean;
  190. subscriber: Subscriber;
  191. };
  192. function copy<T = any>(value: T): T {
  193. return JSON.parse(JSON.stringify(value));
  194. }
  195. function concisify(items: Array<string>): Array<string> {
  196. items = Array.from((new Set(items)).values())
  197. items.sort();
  198. return items;
  199. }
  200. async function getSubscription(_event: ProviderEvent, provider: AbstractProvider): Promise<Subscription> {
  201. if (_event == null) { throw new Error("invalid event"); }
  202. // Normalize topic array info an EventFilter
  203. if (Array.isArray(_event)) { _event = { topics: _event }; }
  204. if (typeof(_event) === "string") {
  205. switch (_event) {
  206. case "block":
  207. case "debug":
  208. case "error":
  209. case "finalized":
  210. case "network":
  211. case "pending":
  212. case "safe": {
  213. return { type: _event, tag: _event };
  214. }
  215. }
  216. }
  217. if (isHexString(_event, 32)) {
  218. const hash = _event.toLowerCase();
  219. return { type: "transaction", tag: getTag("tx", { hash }), hash };
  220. }
  221. if ((<any>_event).orphan) {
  222. const event = <OrphanFilter>_event;
  223. // @TODO: Should lowercase and whatnot things here instead of copy...
  224. return { type: "orphan", tag: getTag("orphan", event), filter: copy(event) };
  225. }
  226. if (((<any>_event).address || (<any>_event).topics)) {
  227. const event = <EventFilter>_event;
  228. const filter: any = {
  229. topics: ((event.topics || []).map((t) => {
  230. if (t == null) { return null; }
  231. if (Array.isArray(t)) {
  232. return concisify(t.map((t) => t.toLowerCase()));
  233. }
  234. return t.toLowerCase();
  235. }))
  236. };
  237. if (event.address) {
  238. const addresses: Array<string> = [ ];
  239. const promises: Array<Promise<void>> = [ ];
  240. const addAddress = (addr: AddressLike) => {
  241. if (isHexString(addr)) {
  242. addresses.push(addr);
  243. } else {
  244. promises.push((async () => {
  245. addresses.push(await resolveAddress(addr, provider));
  246. })());
  247. }
  248. }
  249. if (Array.isArray(event.address)) {
  250. event.address.forEach(addAddress);
  251. } else {
  252. addAddress(event.address);
  253. }
  254. if (promises.length) { await Promise.all(promises); }
  255. filter.address = concisify(addresses.map((a) => a.toLowerCase()));
  256. }
  257. return { filter, tag: getTag("event", filter), type: "event" };
  258. }
  259. assertArgument(false, "unknown ProviderEvent", "event", _event);
  260. }
  261. function getTime(): number { return (new Date()).getTime(); }
  262. /**
  263. * An **AbstractPlugin** is used to provide additional internal services
  264. * to an [[AbstractProvider]] without adding backwards-incompatible changes
  265. * to method signatures or other internal and complex logic.
  266. */
  267. export interface AbstractProviderPlugin {
  268. /**
  269. * The reverse domain notation of the plugin.
  270. */
  271. readonly name: string;
  272. /**
  273. * Creates a new instance of the plugin, connected to %%provider%%.
  274. */
  275. connect(provider: AbstractProvider): AbstractProviderPlugin;
  276. }
  277. /**
  278. * A normalized filter used for [[PerformActionRequest]] objects.
  279. */
  280. export type PerformActionFilter = {
  281. address?: string | Array<string>;
  282. topics?: Array<null | string | Array<string>>;
  283. fromBlock?: BlockTag;
  284. toBlock?: BlockTag;
  285. } | {
  286. address?: string | Array<string>;
  287. topics?: Array<null | string | Array<string>>;
  288. blockHash?: string;
  289. };
  290. /**
  291. * A normalized transactions used for [[PerformActionRequest]] objects.
  292. */
  293. export interface PerformActionTransaction extends PreparedTransactionRequest {
  294. /**
  295. * The ``to`` address of the transaction.
  296. */
  297. to?: string;
  298. /**
  299. * The sender of the transaction.
  300. */
  301. from?: string;
  302. }
  303. /**
  304. * The [[AbstractProvider]] methods will normalize all values and pass this
  305. * type to [[AbstractProvider-_perform]].
  306. */
  307. export type PerformActionRequest = {
  308. method: "broadcastTransaction",
  309. signedTransaction: string
  310. } | {
  311. method: "call",
  312. transaction: PerformActionTransaction, blockTag: BlockTag
  313. } | {
  314. method: "chainId"
  315. } | {
  316. method: "estimateGas",
  317. transaction: PerformActionTransaction
  318. } | {
  319. method: "getBalance",
  320. address: string, blockTag: BlockTag
  321. } | {
  322. method: "getBlock",
  323. blockTag: BlockTag, includeTransactions: boolean
  324. } | {
  325. method: "getBlock",
  326. blockHash: string, includeTransactions: boolean
  327. } | {
  328. method: "getBlockNumber"
  329. } | {
  330. method: "getCode",
  331. address: string, blockTag: BlockTag
  332. } | {
  333. method: "getGasPrice"
  334. } | {
  335. method: "getLogs",
  336. filter: PerformActionFilter
  337. } | {
  338. method: "getPriorityFee"
  339. } | {
  340. method: "getStorage",
  341. address: string, position: bigint, blockTag: BlockTag
  342. } | {
  343. method: "getTransaction",
  344. hash: string
  345. } | {
  346. method: "getTransactionCount",
  347. address: string, blockTag: BlockTag
  348. } | {
  349. method: "getTransactionReceipt",
  350. hash: string
  351. } | {
  352. method: "getTransactionResult",
  353. hash: string
  354. };
  355. type _PerformAccountRequest = {
  356. method: "getBalance" | "getTransactionCount" | "getCode"
  357. } | {
  358. method: "getStorage", position: bigint
  359. }
  360. /**
  361. * Options for configuring some internal aspects of an [[AbstractProvider]].
  362. *
  363. * **``cacheTimeout``** - how long to cache a low-level ``_perform``
  364. * for, based on input parameters. This reduces the number of calls
  365. * to getChainId and getBlockNumber, but may break test chains which
  366. * can perform operations (internally) synchronously. Use ``-1`` to
  367. * disable, ``0`` will only buffer within the same event loop and
  368. * any other value is in ms. (default: ``250``)
  369. */
  370. export type AbstractProviderOptions = {
  371. cacheTimeout?: number;
  372. pollingInterval?: number;
  373. };
  374. const defaultOptions = {
  375. cacheTimeout: 250,
  376. pollingInterval: 4000
  377. };
  378. type CcipArgs = {
  379. sender: string;
  380. urls: Array<string>;
  381. calldata: string;
  382. selector: string;
  383. extraData: string;
  384. errorArgs: Array<any>
  385. };
  386. /**
  387. * An **AbstractProvider** provides a base class for other sub-classes to
  388. * implement the [[Provider]] API by normalizing input arguments and
  389. * formatting output results as well as tracking events for consistent
  390. * behaviour on an eventually-consistent network.
  391. */
  392. export class AbstractProvider implements Provider {
  393. #subs: Map<string, Sub>;
  394. #plugins: Map<string, AbstractProviderPlugin>;
  395. // null=unpaused, true=paused+dropWhilePaused, false=paused
  396. #pausedState: null | boolean;
  397. #destroyed: boolean;
  398. #networkPromise: null | Promise<Network>;
  399. readonly #anyNetwork: boolean;
  400. #performCache: Map<string, Promise<any>>;
  401. // The most recent block number if running an event or -1 if no "block" event
  402. #lastBlockNumber: number;
  403. #nextTimer: number;
  404. #timers: Map<number, { timer: null | Timer, func: () => void, time: number }>;
  405. #disableCcipRead: boolean;
  406. #options: Required<AbstractProviderOptions>;
  407. /**
  408. * Create a new **AbstractProvider** connected to %%network%%, or
  409. * use the various network detection capabilities to discover the
  410. * [[Network]] if necessary.
  411. */
  412. constructor(_network?: "any" | Networkish, options?: AbstractProviderOptions) {
  413. this.#options = Object.assign({ }, defaultOptions, options || { });
  414. if (_network === "any") {
  415. this.#anyNetwork = true;
  416. this.#networkPromise = null;
  417. } else if (_network) {
  418. const network = Network.from(_network);
  419. this.#anyNetwork = false;
  420. this.#networkPromise = Promise.resolve(network);
  421. setTimeout(() => { this.emit("network", network, null); }, 0);
  422. } else {
  423. this.#anyNetwork = false;
  424. this.#networkPromise = null;
  425. }
  426. this.#lastBlockNumber = -1;
  427. this.#performCache = new Map();
  428. this.#subs = new Map();
  429. this.#plugins = new Map();
  430. this.#pausedState = null;
  431. this.#destroyed = false;
  432. this.#nextTimer = 1;
  433. this.#timers = new Map();
  434. this.#disableCcipRead = false;
  435. }
  436. get pollingInterval(): number { return this.#options.pollingInterval; }
  437. /**
  438. * Returns ``this``, to allow an **AbstractProvider** to implement
  439. * the [[ContractRunner]] interface.
  440. */
  441. get provider(): this { return this; }
  442. /**
  443. * Returns all the registered plug-ins.
  444. */
  445. get plugins(): Array<AbstractProviderPlugin> {
  446. return Array.from(this.#plugins.values());
  447. }
  448. /**
  449. * Attach a new plug-in.
  450. */
  451. attachPlugin(plugin: AbstractProviderPlugin): this {
  452. if (this.#plugins.get(plugin.name)) {
  453. throw new Error(`cannot replace existing plugin: ${ plugin.name } `);
  454. }
  455. this.#plugins.set(plugin.name, plugin.connect(this));
  456. return this;
  457. }
  458. /**
  459. * Get a plugin by name.
  460. */
  461. getPlugin<T extends AbstractProviderPlugin = AbstractProviderPlugin>(name: string): null | T {
  462. return <T>(this.#plugins.get(name)) || null;
  463. }
  464. /**
  465. * Prevent any CCIP-read operation, regardless of whether requested
  466. * in a [[call]] using ``enableCcipRead``.
  467. */
  468. get disableCcipRead(): boolean { return this.#disableCcipRead; }
  469. set disableCcipRead(value: boolean) { this.#disableCcipRead = !!value; }
  470. // Shares multiple identical requests made during the same 250ms
  471. async #perform<T = any>(req: PerformActionRequest): Promise<T> {
  472. const timeout = this.#options.cacheTimeout;
  473. // Caching disabled
  474. if (timeout < 0) { return await this._perform(req); }
  475. // Create a tag
  476. const tag = getTag(req.method, req);
  477. let perform = this.#performCache.get(tag);
  478. if (!perform) {
  479. perform = this._perform(req);
  480. this.#performCache.set(tag, perform);
  481. setTimeout(() => {
  482. if (this.#performCache.get(tag) === perform) {
  483. this.#performCache.delete(tag);
  484. }
  485. }, timeout);
  486. }
  487. return await perform;
  488. }
  489. /**
  490. * Resolves to the data for executing the CCIP-read operations.
  491. */
  492. async ccipReadFetch(tx: PerformActionTransaction, calldata: string, urls: Array<string>): Promise<null | string> {
  493. if (this.disableCcipRead || urls.length === 0 || tx.to == null) { return null; }
  494. const sender = tx.to.toLowerCase();
  495. const data = calldata.toLowerCase();
  496. const errorMessages: Array<string> = [ ];
  497. for (let i = 0; i < urls.length; i++) {
  498. const url = urls[i];
  499. // URL expansion
  500. const href = url.replace("{sender}", sender).replace("{data}", data);
  501. // If no {data} is present, use POST; otherwise GET
  502. //const json: string | null = (url.indexOf("{data}") >= 0) ? null: JSON.stringify({ data, sender });
  503. //const result = await fetchJson({ url: href, errorPassThrough: true }, json, (value, response) => {
  504. // value.status = response.statusCode;
  505. // return value;
  506. //});
  507. const request = new FetchRequest(href);
  508. if (url.indexOf("{data}") === -1) {
  509. request.body = { data, sender };
  510. }
  511. this.emit("debug", { action: "sendCcipReadFetchRequest", request, index: i, urls });
  512. let errorMessage = "unknown error";
  513. // Fetch the resource...
  514. let resp: FetchResponse;
  515. try {
  516. resp = await request.send();
  517. } catch (error: any) {
  518. // ...low-level fetch error (missing host, bad SSL, etc.),
  519. // so try next URL
  520. errorMessages.push(error.message);
  521. this.emit("debug", { action: "receiveCcipReadFetchError", request, result: { error } });
  522. continue;
  523. }
  524. try {
  525. const result = resp.bodyJson;
  526. if (result.data) {
  527. this.emit("debug", { action: "receiveCcipReadFetchResult", request, result });
  528. return result.data;
  529. }
  530. if (result.message) { errorMessage = result.message; }
  531. this.emit("debug", { action: "receiveCcipReadFetchError", request, result });
  532. } catch (error) { }
  533. // 4xx indicates the result is not present; stop
  534. assert(resp.statusCode < 400 || resp.statusCode >= 500, `response not found during CCIP fetch: ${ errorMessage }`,
  535. "OFFCHAIN_FAULT", { reason: "404_MISSING_RESOURCE", transaction: tx, info: { url, errorMessage } });
  536. // 5xx indicates server issue; try the next url
  537. errorMessages.push(errorMessage);
  538. }
  539. assert(false, `error encountered during CCIP fetch: ${ errorMessages.map((m) => JSON.stringify(m)).join(", ") }`, "OFFCHAIN_FAULT", {
  540. reason: "500_SERVER_ERROR",
  541. transaction: tx, info: { urls, errorMessages }
  542. });
  543. }
  544. /**
  545. * Provides the opportunity for a sub-class to wrap a block before
  546. * returning it, to add additional properties or an alternate
  547. * sub-class of [[Block]].
  548. */
  549. _wrapBlock(value: BlockParams, network: Network): Block {
  550. return new Block(formatBlock(value), this);
  551. }
  552. /**
  553. * Provides the opportunity for a sub-class to wrap a log before
  554. * returning it, to add additional properties or an alternate
  555. * sub-class of [[Log]].
  556. */
  557. _wrapLog(value: LogParams, network: Network): Log {
  558. return new Log(formatLog(value), this);
  559. }
  560. /**
  561. * Provides the opportunity for a sub-class to wrap a transaction
  562. * receipt before returning it, to add additional properties or an
  563. * alternate sub-class of [[TransactionReceipt]].
  564. */
  565. _wrapTransactionReceipt(value: TransactionReceiptParams, network: Network): TransactionReceipt {
  566. return new TransactionReceipt(formatTransactionReceipt(value), this);
  567. }
  568. /**
  569. * Provides the opportunity for a sub-class to wrap a transaction
  570. * response before returning it, to add additional properties or an
  571. * alternate sub-class of [[TransactionResponse]].
  572. */
  573. _wrapTransactionResponse(tx: TransactionResponseParams, network: Network): TransactionResponse {
  574. return new TransactionResponse(formatTransactionResponse(tx), this);
  575. }
  576. /**
  577. * Resolves to the Network, forcing a network detection using whatever
  578. * technique the sub-class requires.
  579. *
  580. * Sub-classes **must** override this.
  581. */
  582. _detectNetwork(): Promise<Network> {
  583. assert(false, "sub-classes must implement this", "UNSUPPORTED_OPERATION", {
  584. operation: "_detectNetwork"
  585. });
  586. }
  587. /**
  588. * Sub-classes should use this to perform all built-in operations. All
  589. * methods sanitizes and normalizes the values passed into this.
  590. *
  591. * Sub-classes **must** override this.
  592. */
  593. async _perform<T = any>(req: PerformActionRequest): Promise<T> {
  594. assert(false, `unsupported method: ${ req.method }`, "UNSUPPORTED_OPERATION", {
  595. operation: req.method,
  596. info: req
  597. });
  598. }
  599. // State
  600. async getBlockNumber(): Promise<number> {
  601. const blockNumber = getNumber(await this.#perform({ method: "getBlockNumber" }), "%response");
  602. if (this.#lastBlockNumber >= 0) { this.#lastBlockNumber = blockNumber; }
  603. return blockNumber;
  604. }
  605. /**
  606. * Returns or resolves to the address for %%address%%, resolving ENS
  607. * names and [[Addressable]] objects and returning if already an
  608. * address.
  609. */
  610. _getAddress(address: AddressLike): string | Promise<string> {
  611. return resolveAddress(address, this);
  612. }
  613. /**
  614. * Returns or resolves to a valid block tag for %%blockTag%%, resolving
  615. * negative values and returning if already a valid block tag.
  616. */
  617. _getBlockTag(blockTag?: BlockTag): string | Promise<string> {
  618. if (blockTag == null) { return "latest"; }
  619. switch (blockTag) {
  620. case "earliest":
  621. return "0x0";
  622. case "finalized":
  623. case "latest":
  624. case "pending":
  625. case "safe":
  626. return blockTag;
  627. }
  628. if (isHexString(blockTag)) {
  629. if (isHexString(blockTag, 32)) { return blockTag; }
  630. return toQuantity(blockTag);
  631. }
  632. if (typeof(blockTag) === "bigint") {
  633. blockTag = getNumber(blockTag, "blockTag");
  634. }
  635. if (typeof(blockTag) === "number") {
  636. if (blockTag >= 0) { return toQuantity(blockTag); }
  637. if (this.#lastBlockNumber >= 0) { return toQuantity(this.#lastBlockNumber + blockTag); }
  638. return this.getBlockNumber().then((b) => toQuantity(b + <number>blockTag));
  639. }
  640. assertArgument(false, "invalid blockTag", "blockTag", blockTag);
  641. }
  642. /**
  643. * Returns or resolves to a filter for %%filter%%, resolving any ENS
  644. * names or [[Addressable]] object and returning if already a valid
  645. * filter.
  646. */
  647. _getFilter(filter: Filter | FilterByBlockHash): PerformActionFilter | Promise<PerformActionFilter> {
  648. // Create a canonical representation of the topics
  649. const topics = (filter.topics || [ ]).map((t) => {
  650. if (t == null) { return null; }
  651. if (Array.isArray(t)) {
  652. return concisify(t.map((t) => t.toLowerCase()));
  653. }
  654. return t.toLowerCase();
  655. });
  656. const blockHash = ("blockHash" in filter) ? filter.blockHash: undefined;
  657. const resolve = (_address: Array<string>, fromBlock?: string, toBlock?: string) => {
  658. let address: undefined | string | Array<string> = undefined;
  659. switch (_address.length) {
  660. case 0: break;
  661. case 1:
  662. address = _address[0];
  663. break;
  664. default:
  665. _address.sort();
  666. address = _address;
  667. }
  668. if (blockHash) {
  669. if (fromBlock != null || toBlock != null) {
  670. throw new Error("invalid filter");
  671. }
  672. }
  673. const filter = <any>{ };
  674. if (address) { filter.address = address; }
  675. if (topics.length) { filter.topics = topics; }
  676. if (fromBlock) { filter.fromBlock = fromBlock; }
  677. if (toBlock) { filter.toBlock = toBlock; }
  678. if (blockHash) { filter.blockHash = blockHash; }
  679. return filter;
  680. };
  681. // Addresses could be async (ENS names or Addressables)
  682. let address: Array<string | Promise<string>> = [ ];
  683. if (filter.address) {
  684. if (Array.isArray(filter.address)) {
  685. for (const addr of filter.address) { address.push(this._getAddress(addr)); }
  686. } else {
  687. address.push(this._getAddress(filter.address));
  688. }
  689. }
  690. let fromBlock: undefined | string | Promise<string> = undefined;
  691. if ("fromBlock" in filter) { fromBlock = this._getBlockTag(filter.fromBlock); }
  692. let toBlock: undefined | string | Promise<string> = undefined;
  693. if ("toBlock" in filter) { toBlock = this._getBlockTag(filter.toBlock); }
  694. if (address.filter((a) => (typeof(a) !== "string")).length ||
  695. (fromBlock != null && typeof(fromBlock) !== "string") ||
  696. (toBlock != null && typeof(toBlock) !== "string")) {
  697. return Promise.all([ Promise.all(address), fromBlock, toBlock ]).then((result) => {
  698. return resolve(result[0], result[1], result[2]);
  699. });
  700. }
  701. return resolve(<Array<string>>address, fromBlock, toBlock);
  702. }
  703. /**
  704. * Returns or resolves to a transaction for %%request%%, resolving
  705. * any ENS names or [[Addressable]] and returning if already a valid
  706. * transaction.
  707. */
  708. _getTransactionRequest(_request: TransactionRequest): PerformActionTransaction | Promise<PerformActionTransaction> {
  709. const request = <PerformActionTransaction>copyRequest(_request);
  710. const promises: Array<Promise<void>> = [ ];
  711. [ "to", "from" ].forEach((key) => {
  712. if ((<any>request)[key] == null) { return; }
  713. const addr = resolveAddress((<any>request)[key], this);
  714. if (isPromise(addr)) {
  715. promises.push((async function() { (<any>request)[key] = await addr; })());
  716. } else {
  717. (<any>request)[key] = addr;
  718. }
  719. });
  720. if (request.blockTag != null) {
  721. const blockTag = this._getBlockTag(request.blockTag);
  722. if (isPromise(blockTag)) {
  723. promises.push((async function() { request.blockTag = await blockTag; })());
  724. } else {
  725. request.blockTag = blockTag;
  726. }
  727. }
  728. if (promises.length) {
  729. return (async function() {
  730. await Promise.all(promises);
  731. return request;
  732. })();
  733. }
  734. return request;
  735. }
  736. async getNetwork(): Promise<Network> {
  737. // No explicit network was set and this is our first time
  738. if (this.#networkPromise == null) {
  739. // Detect the current network (shared with all calls)
  740. const detectNetwork = (async () => {
  741. try {
  742. const network = await this._detectNetwork();
  743. this.emit("network", network, null);
  744. return network;
  745. } catch (error) {
  746. if (this.#networkPromise === detectNetwork!) {
  747. this.#networkPromise = null;
  748. }
  749. throw error;
  750. }
  751. })();
  752. this.#networkPromise = detectNetwork;
  753. return (await detectNetwork).clone();
  754. }
  755. const networkPromise = this.#networkPromise;
  756. const [ expected, actual ] = await Promise.all([
  757. networkPromise, // Possibly an explicit Network
  758. this._detectNetwork() // The actual connected network
  759. ]);
  760. if (expected.chainId !== actual.chainId) {
  761. if (this.#anyNetwork) {
  762. // The "any" network can change, so notify listeners
  763. this.emit("network", actual, expected);
  764. // Update the network if something else hasn't already changed it
  765. if (this.#networkPromise === networkPromise) {
  766. this.#networkPromise = Promise.resolve(actual);
  767. }
  768. } else {
  769. // Otherwise, we do not allow changes to the underlying network
  770. assert(false, `network changed: ${ expected.chainId } => ${ actual.chainId } `, "NETWORK_ERROR", {
  771. event: "changed"
  772. });
  773. }
  774. }
  775. return expected.clone();
  776. }
  777. async getFeeData(): Promise<FeeData> {
  778. const network = await this.getNetwork();
  779. const getFeeDataFunc = async () => {
  780. const { _block, gasPrice, priorityFee } = await resolveProperties({
  781. _block: this.#getBlock("latest", false),
  782. gasPrice: ((async () => {
  783. try {
  784. const value = await this.#perform({ method: "getGasPrice" });
  785. return getBigInt(value, "%response");
  786. } catch (error) { }
  787. return null
  788. })()),
  789. priorityFee: ((async () => {
  790. try {
  791. const value = await this.#perform({ method: "getPriorityFee" });
  792. return getBigInt(value, "%response");
  793. } catch (error) { }
  794. return null;
  795. })())
  796. });
  797. let maxFeePerGas: null | bigint = null;
  798. let maxPriorityFeePerGas: null | bigint = null;
  799. // These are the recommended EIP-1559 heuristics for fee data
  800. const block = this._wrapBlock(_block, network);
  801. if (block && block.baseFeePerGas) {
  802. maxPriorityFeePerGas = (priorityFee != null) ? priorityFee: BigInt("1000000000");
  803. maxFeePerGas = (block.baseFeePerGas * BN_2) + maxPriorityFeePerGas;
  804. }
  805. return new FeeData(gasPrice, maxFeePerGas, maxPriorityFeePerGas);
  806. };
  807. // Check for a FeeDataNetWorkPlugin
  808. const plugin = <FetchUrlFeeDataNetworkPlugin>network.getPlugin("org.ethers.plugins.network.FetchUrlFeeDataPlugin");
  809. if (plugin) {
  810. const req = new FetchRequest(plugin.url);
  811. const feeData = await plugin.processFunc(getFeeDataFunc, this, req);
  812. return new FeeData(feeData.gasPrice, feeData.maxFeePerGas, feeData.maxPriorityFeePerGas);
  813. }
  814. return await getFeeDataFunc();
  815. }
  816. async estimateGas(_tx: TransactionRequest): Promise<bigint> {
  817. let tx = this._getTransactionRequest(_tx);
  818. if (isPromise(tx)) { tx = await tx; }
  819. return getBigInt(await this.#perform({
  820. method: "estimateGas", transaction: tx
  821. }), "%response");
  822. }
  823. async #call(tx: PerformActionTransaction, blockTag: string, attempt: number): Promise<string> {
  824. assert (attempt < MAX_CCIP_REDIRECTS, "CCIP read exceeded maximum redirections", "OFFCHAIN_FAULT", {
  825. reason: "TOO_MANY_REDIRECTS",
  826. transaction: Object.assign({ }, tx, { blockTag, enableCcipRead: true })
  827. });
  828. // This came in as a PerformActionTransaction, so to/from are safe; we can cast
  829. const transaction = <PerformActionTransaction>copyRequest(tx);
  830. try {
  831. return hexlify(await this._perform({ method: "call", transaction, blockTag }));
  832. } catch (error: any) {
  833. // CCIP Read OffchainLookup
  834. if (!this.disableCcipRead && isCallException(error) && error.data && attempt >= 0 && blockTag === "latest" && transaction.to != null && dataSlice(error.data, 0, 4) === "0x556f1830") {
  835. const data = error.data;
  836. const txSender = await resolveAddress(transaction.to, this);
  837. // Parse the CCIP Read Arguments
  838. let ccipArgs: CcipArgs;
  839. try {
  840. ccipArgs = parseOffchainLookup(dataSlice(error.data, 4));
  841. } catch (error: any) {
  842. assert(false, error.message, "OFFCHAIN_FAULT", {
  843. reason: "BAD_DATA", transaction, info: { data } });
  844. }
  845. // Check the sender of the OffchainLookup matches the transaction
  846. assert(ccipArgs.sender.toLowerCase() === txSender.toLowerCase(),
  847. "CCIP Read sender mismatch", "CALL_EXCEPTION", {
  848. action: "call",
  849. data,
  850. reason: "OffchainLookup",
  851. transaction: <any>transaction, // @TODO: populate data?
  852. invocation: null,
  853. revert: {
  854. signature: "OffchainLookup(address,string[],bytes,bytes4,bytes)",
  855. name: "OffchainLookup",
  856. args: ccipArgs.errorArgs
  857. }
  858. });
  859. const ccipResult = await this.ccipReadFetch(transaction, ccipArgs.calldata, ccipArgs.urls);
  860. assert(ccipResult != null, "CCIP Read failed to fetch data", "OFFCHAIN_FAULT", {
  861. reason: "FETCH_FAILED", transaction, info: { data: error.data, errorArgs: ccipArgs.errorArgs } });
  862. const tx = {
  863. to: txSender,
  864. data: concat([ ccipArgs.selector, encodeBytes([ ccipResult, ccipArgs.extraData ]) ])
  865. };
  866. this.emit("debug", { action: "sendCcipReadCall", transaction: tx });
  867. try {
  868. const result = await this.#call(tx, blockTag, attempt + 1);
  869. this.emit("debug", { action: "receiveCcipReadCallResult", transaction: Object.assign({ }, tx), result });
  870. return result;
  871. } catch (error) {
  872. this.emit("debug", { action: "receiveCcipReadCallError", transaction: Object.assign({ }, tx), error });
  873. throw error;
  874. }
  875. }
  876. throw error;
  877. }
  878. }
  879. async #checkNetwork<T>(promise: Promise<T>): Promise<T> {
  880. const { value } = await resolveProperties({
  881. network: this.getNetwork(),
  882. value: promise
  883. });
  884. return value;
  885. }
  886. async call(_tx: TransactionRequest): Promise<string> {
  887. const { tx, blockTag } = await resolveProperties({
  888. tx: this._getTransactionRequest(_tx),
  889. blockTag: this._getBlockTag(_tx.blockTag)
  890. });
  891. return await this.#checkNetwork(this.#call(tx, blockTag, _tx.enableCcipRead ? 0: -1));
  892. }
  893. // Account
  894. async #getAccountValue(request: _PerformAccountRequest, _address: AddressLike, _blockTag?: BlockTag): Promise<any> {
  895. let address: string | Promise<string> = this._getAddress(_address);
  896. let blockTag: string | Promise<string> = this._getBlockTag(_blockTag);
  897. if (typeof(address) !== "string" || typeof(blockTag) !== "string") {
  898. [ address, blockTag ] = await Promise.all([ address, blockTag ]);
  899. }
  900. return await this.#checkNetwork(this.#perform(Object.assign(request, { address, blockTag })));
  901. }
  902. async getBalance(address: AddressLike, blockTag?: BlockTag): Promise<bigint> {
  903. return getBigInt(await this.#getAccountValue({ method: "getBalance" }, address, blockTag), "%response");
  904. }
  905. async getTransactionCount(address: AddressLike, blockTag?: BlockTag): Promise<number> {
  906. return getNumber(await this.#getAccountValue({ method: "getTransactionCount" }, address, blockTag), "%response");
  907. }
  908. async getCode(address: AddressLike, blockTag?: BlockTag): Promise<string> {
  909. return hexlify(await this.#getAccountValue({ method: "getCode" }, address, blockTag));
  910. }
  911. async getStorage(address: AddressLike, _position: BigNumberish, blockTag?: BlockTag): Promise<string> {
  912. const position = getBigInt(_position, "position");
  913. return hexlify(await this.#getAccountValue({ method: "getStorage", position }, address, blockTag));
  914. }
  915. // Write
  916. async broadcastTransaction(signedTx: string): Promise<TransactionResponse> {
  917. const { blockNumber, hash, network } = await resolveProperties({
  918. blockNumber: this.getBlockNumber(),
  919. hash: this._perform({
  920. method: "broadcastTransaction",
  921. signedTransaction: signedTx
  922. }),
  923. network: this.getNetwork()
  924. });
  925. const tx = Transaction.from(signedTx);
  926. if (tx.hash !== hash) {
  927. throw new Error("@TODO: the returned hash did not match");
  928. }
  929. return this._wrapTransactionResponse(<any>tx, network).replaceableTransaction(blockNumber);
  930. }
  931. async #getBlock(block: BlockTag | string, includeTransactions: boolean): Promise<any> {
  932. // @TODO: Add CustomBlockPlugin check
  933. if (isHexString(block, 32)) {
  934. return await this.#perform({
  935. method: "getBlock", blockHash: block, includeTransactions
  936. });
  937. }
  938. let blockTag = this._getBlockTag(block);
  939. if (typeof(blockTag) !== "string") { blockTag = await blockTag; }
  940. return await this.#perform({
  941. method: "getBlock", blockTag, includeTransactions
  942. });
  943. }
  944. // Queries
  945. async getBlock(block: BlockTag | string, prefetchTxs?: boolean): Promise<null | Block> {
  946. const { network, params } = await resolveProperties({
  947. network: this.getNetwork(),
  948. params: this.#getBlock(block, !!prefetchTxs)
  949. });
  950. if (params == null) { return null; }
  951. return this._wrapBlock(params, network);
  952. }
  953. async getTransaction(hash: string): Promise<null | TransactionResponse> {
  954. const { network, params } = await resolveProperties({
  955. network: this.getNetwork(),
  956. params: this.#perform({ method: "getTransaction", hash })
  957. });
  958. if (params == null) { return null; }
  959. return this._wrapTransactionResponse(params, network);
  960. }
  961. async getTransactionReceipt(hash: string): Promise<null | TransactionReceipt> {
  962. const { network, params } = await resolveProperties({
  963. network: this.getNetwork(),
  964. params: this.#perform({ method: "getTransactionReceipt", hash })
  965. });
  966. if (params == null) { return null; }
  967. // Some backends did not backfill the effectiveGasPrice into old transactions
  968. // in the receipt, so we look it up manually and inject it.
  969. if (params.gasPrice == null && params.effectiveGasPrice == null) {
  970. const tx = await this.#perform({ method: "getTransaction", hash });
  971. if (tx == null) { throw new Error("report this; could not find tx or effectiveGasPrice"); }
  972. params.effectiveGasPrice = tx.gasPrice;
  973. }
  974. return this._wrapTransactionReceipt(params, network);
  975. }
  976. async getTransactionResult(hash: string): Promise<null | string> {
  977. const { result } = await resolveProperties({
  978. network: this.getNetwork(),
  979. result: this.#perform({ method: "getTransactionResult", hash })
  980. });
  981. if (result == null) { return null; }
  982. return hexlify(result);
  983. }
  984. // Bloom-filter Queries
  985. async getLogs(_filter: Filter | FilterByBlockHash): Promise<Array<Log>> {
  986. let filter = this._getFilter(_filter);
  987. if (isPromise(filter)) { filter = await filter; }
  988. const { network, params } = await resolveProperties({
  989. network: this.getNetwork(),
  990. params: this.#perform<Array<LogParams>>({ method: "getLogs", filter })
  991. });
  992. return params.map((p) => this._wrapLog(p, network));
  993. }
  994. // ENS
  995. _getProvider(chainId: number): AbstractProvider {
  996. assert(false, "provider cannot connect to target network", "UNSUPPORTED_OPERATION", {
  997. operation: "_getProvider()"
  998. });
  999. }
  1000. async getResolver(name: string): Promise<null | EnsResolver> {
  1001. return await EnsResolver.fromName(this, name);
  1002. }
  1003. async getAvatar(name: string): Promise<null | string> {
  1004. const resolver = await this.getResolver(name);
  1005. if (resolver) { return await resolver.getAvatar(); }
  1006. return null;
  1007. }
  1008. async resolveName(name: string): Promise<null | string>{
  1009. const resolver = await this.getResolver(name);
  1010. if (resolver) { return await resolver.getAddress(); }
  1011. return null;
  1012. }
  1013. async lookupAddress(address: string): Promise<null | string> {
  1014. address = getAddress(address);
  1015. const node = namehash(address.substring(2).toLowerCase() + ".addr.reverse");
  1016. try {
  1017. const ensAddr = await EnsResolver.getEnsAddress(this);
  1018. const ensContract = new Contract(ensAddr, [
  1019. "function resolver(bytes32) view returns (address)"
  1020. ], this);
  1021. const resolver = await ensContract.resolver(node);
  1022. if (resolver == null || resolver === ZeroAddress) { return null; }
  1023. const resolverContract = new Contract(resolver, [
  1024. "function name(bytes32) view returns (string)"
  1025. ], this);
  1026. const name = await resolverContract.name(node);
  1027. // Failed forward resolution
  1028. const check = await this.resolveName(name);
  1029. if (check !== address) { return null; }
  1030. return name;
  1031. } catch (error) {
  1032. // No data was returned from the resolver
  1033. if (isError(error, "BAD_DATA") && error.value === "0x") {
  1034. return null;
  1035. }
  1036. // Something reerted
  1037. if (isError(error, "CALL_EXCEPTION")) { return null; }
  1038. throw error;
  1039. }
  1040. return null;
  1041. }
  1042. async waitForTransaction(hash: string, _confirms?: null | number, timeout?: null | number): Promise<null | TransactionReceipt> {
  1043. const confirms = (_confirms != null) ? _confirms: 1;
  1044. if (confirms === 0) { return this.getTransactionReceipt(hash); }
  1045. return new Promise(async (resolve, reject) => {
  1046. let timer: null | Timer = null;
  1047. const listener = (async (blockNumber: number) => {
  1048. try {
  1049. const receipt = await this.getTransactionReceipt(hash);
  1050. if (receipt != null) {
  1051. if (blockNumber - receipt.blockNumber + 1 >= confirms) {
  1052. resolve(receipt);
  1053. //this.off("block", listener);
  1054. if (timer) {
  1055. clearTimeout(timer);
  1056. timer = null;
  1057. }
  1058. return;
  1059. }
  1060. }
  1061. } catch (error) {
  1062. console.log("EEE", error);
  1063. }
  1064. this.once("block", listener);
  1065. });
  1066. if (timeout != null) {
  1067. timer = setTimeout(() => {
  1068. if (timer == null) { return; }
  1069. timer = null;
  1070. this.off("block", listener);
  1071. reject(makeError("timeout", "TIMEOUT", { reason: "timeout" }));
  1072. }, timeout);
  1073. }
  1074. listener(await this.getBlockNumber());
  1075. });
  1076. }
  1077. async waitForBlock(blockTag?: BlockTag): Promise<Block> {
  1078. assert(false, "not implemented yet", "NOT_IMPLEMENTED", {
  1079. operation: "waitForBlock"
  1080. });
  1081. }
  1082. /**
  1083. * Clear a timer created using the [[_setTimeout]] method.
  1084. */
  1085. _clearTimeout(timerId: number): void {
  1086. const timer = this.#timers.get(timerId);
  1087. if (!timer) { return; }
  1088. if (timer.timer) { clearTimeout(timer.timer); }
  1089. this.#timers.delete(timerId);
  1090. }
  1091. /**
  1092. * Create a timer that will execute %%func%% after at least %%timeout%%
  1093. * (in ms). If %%timeout%% is unspecified, then %%func%% will execute
  1094. * in the next event loop.
  1095. *
  1096. * [Pausing](AbstractProvider-paused) the provider will pause any
  1097. * associated timers.
  1098. */
  1099. _setTimeout(_func: () => void, timeout?: number): number {
  1100. if (timeout == null) { timeout = 0; }
  1101. const timerId = this.#nextTimer++;
  1102. const func = () => {
  1103. this.#timers.delete(timerId);
  1104. _func();
  1105. };
  1106. if (this.paused) {
  1107. this.#timers.set(timerId, { timer: null, func, time: timeout });
  1108. } else {
  1109. const timer = setTimeout(func, timeout);
  1110. this.#timers.set(timerId, { timer, func, time: getTime() });
  1111. }
  1112. return timerId;
  1113. }
  1114. /**
  1115. * Perform %%func%% on each subscriber.
  1116. */
  1117. _forEachSubscriber(func: (s: Subscriber) => void): void {
  1118. for (const sub of this.#subs.values()) {
  1119. func(sub.subscriber);
  1120. }
  1121. }
  1122. /**
  1123. * Sub-classes may override this to customize subscription
  1124. * implementations.
  1125. */
  1126. _getSubscriber(sub: Subscription): Subscriber {
  1127. switch (sub.type) {
  1128. case "debug":
  1129. case "error":
  1130. case "network":
  1131. return new UnmanagedSubscriber(sub.type);
  1132. case "block": {
  1133. const subscriber = new PollingBlockSubscriber(this);
  1134. subscriber.pollingInterval = this.pollingInterval;
  1135. return subscriber;
  1136. }
  1137. case "safe": case "finalized":
  1138. return new PollingBlockTagSubscriber(this, sub.type);
  1139. case "event":
  1140. return new PollingEventSubscriber(this, sub.filter);
  1141. case "transaction":
  1142. return new PollingTransactionSubscriber(this, sub.hash);
  1143. case "orphan":
  1144. return new PollingOrphanSubscriber(this, sub.filter);
  1145. }
  1146. throw new Error(`unsupported event: ${ sub.type }`);
  1147. }
  1148. /**
  1149. * If a [[Subscriber]] fails and needs to replace itself, this
  1150. * method may be used.
  1151. *
  1152. * For example, this is used for providers when using the
  1153. * ``eth_getFilterChanges`` method, which can return null if state
  1154. * filters are not supported by the backend, allowing the Subscriber
  1155. * to swap in a [[PollingEventSubscriber]].
  1156. */
  1157. _recoverSubscriber(oldSub: Subscriber, newSub: Subscriber): void {
  1158. for (const sub of this.#subs.values()) {
  1159. if (sub.subscriber === oldSub) {
  1160. if (sub.started) { sub.subscriber.stop(); }
  1161. sub.subscriber = newSub;
  1162. if (sub.started) { newSub.start(); }
  1163. if (this.#pausedState != null) { newSub.pause(this.#pausedState); }
  1164. break;
  1165. }
  1166. }
  1167. }
  1168. async #hasSub(event: ProviderEvent, emitArgs?: Array<any>): Promise<null | Sub> {
  1169. let sub = await getSubscription(event, this);
  1170. // This is a log that is removing an existing log; we actually want
  1171. // to emit an orphan event for the removed log
  1172. if (sub.type === "event" && emitArgs && emitArgs.length > 0 && emitArgs[0].removed === true) {
  1173. sub = await getSubscription({ orphan: "drop-log", log: emitArgs[0] }, this);
  1174. }
  1175. return this.#subs.get(sub.tag) || null;
  1176. }
  1177. async #getSub(event: ProviderEvent): Promise<Sub> {
  1178. const subscription = await getSubscription(event, this);
  1179. // Prevent tampering with our tag in any subclass' _getSubscriber
  1180. const tag = subscription.tag;
  1181. let sub = this.#subs.get(tag);
  1182. if (!sub) {
  1183. const subscriber = this._getSubscriber(subscription);
  1184. const addressableMap = new WeakMap();
  1185. const nameMap = new Map();
  1186. sub = { subscriber, tag, addressableMap, nameMap, started: false, listeners: [ ] };
  1187. this.#subs.set(tag, sub);
  1188. }
  1189. return sub;
  1190. }
  1191. async on(event: ProviderEvent, listener: Listener): Promise<this> {
  1192. const sub = await this.#getSub(event);
  1193. sub.listeners.push({ listener, once: false });
  1194. if (!sub.started) {
  1195. sub.subscriber.start();
  1196. sub.started = true;
  1197. if (this.#pausedState != null) { sub.subscriber.pause(this.#pausedState); }
  1198. }
  1199. return this;
  1200. }
  1201. async once(event: ProviderEvent, listener: Listener): Promise<this> {
  1202. const sub = await this.#getSub(event);
  1203. sub.listeners.push({ listener, once: true });
  1204. if (!sub.started) {
  1205. sub.subscriber.start();
  1206. sub.started = true;
  1207. if (this.#pausedState != null) { sub.subscriber.pause(this.#pausedState); }
  1208. }
  1209. return this;
  1210. }
  1211. async emit(event: ProviderEvent, ...args: Array<any>): Promise<boolean> {
  1212. const sub = await this.#hasSub(event, args);
  1213. // If there is not subscription or if a recent emit removed
  1214. // the last of them (which also deleted the sub) do nothing
  1215. if (!sub || sub.listeners.length === 0) { return false; };
  1216. const count = sub.listeners.length;
  1217. sub.listeners = sub.listeners.filter(({ listener, once }) => {
  1218. const payload = new EventPayload(this, (once ? null: listener), event);
  1219. try {
  1220. listener.call(this, ...args, payload);
  1221. } catch(error) { }
  1222. return !once;
  1223. });
  1224. if (sub.listeners.length === 0) {
  1225. if (sub.started) { sub.subscriber.stop(); }
  1226. this.#subs.delete(sub.tag);
  1227. }
  1228. return (count > 0);
  1229. }
  1230. async listenerCount(event?: ProviderEvent): Promise<number> {
  1231. if (event) {
  1232. const sub = await this.#hasSub(event);
  1233. if (!sub) { return 0; }
  1234. return sub.listeners.length;
  1235. }
  1236. let total = 0;
  1237. for (const { listeners } of this.#subs.values()) {
  1238. total += listeners.length;
  1239. }
  1240. return total;
  1241. }
  1242. async listeners(event?: ProviderEvent): Promise<Array<Listener>> {
  1243. if (event) {
  1244. const sub = await this.#hasSub(event);
  1245. if (!sub) { return [ ]; }
  1246. return sub.listeners.map(({ listener }) => listener);
  1247. }
  1248. let result: Array<Listener> = [ ];
  1249. for (const { listeners } of this.#subs.values()) {
  1250. result = result.concat(listeners.map(({ listener }) => listener));
  1251. }
  1252. return result;
  1253. }
  1254. async off(event: ProviderEvent, listener?: Listener): Promise<this> {
  1255. const sub = await this.#hasSub(event);
  1256. if (!sub) { return this; }
  1257. if (listener) {
  1258. const index = sub.listeners.map(({ listener }) => listener).indexOf(listener);
  1259. if (index >= 0) { sub.listeners.splice(index, 1); }
  1260. }
  1261. if (!listener || sub.listeners.length === 0) {
  1262. if (sub.started) { sub.subscriber.stop(); }
  1263. this.#subs.delete(sub.tag);
  1264. }
  1265. return this;
  1266. }
  1267. async removeAllListeners(event?: ProviderEvent): Promise<this> {
  1268. if (event) {
  1269. const { tag, started, subscriber } = await this.#getSub(event);
  1270. if (started) { subscriber.stop(); }
  1271. this.#subs.delete(tag);
  1272. } else {
  1273. for (const [ tag, { started, subscriber } ] of this.#subs) {
  1274. if (started) { subscriber.stop(); }
  1275. this.#subs.delete(tag);
  1276. }
  1277. }
  1278. return this;
  1279. }
  1280. // Alias for "on"
  1281. async addListener(event: ProviderEvent, listener: Listener): Promise<this> {
  1282. return await this.on(event, listener);
  1283. }
  1284. // Alias for "off"
  1285. async removeListener(event: ProviderEvent, listener: Listener): Promise<this> {
  1286. return this.off(event, listener);
  1287. }
  1288. /**
  1289. * If this provider has been destroyed using the [[destroy]] method.
  1290. *
  1291. * Once destroyed, all resources are reclaimed, internal event loops
  1292. * and timers are cleaned up and no further requests may be sent to
  1293. * the provider.
  1294. */
  1295. get destroyed(): boolean {
  1296. return this.#destroyed;
  1297. }
  1298. /**
  1299. * Sub-classes may use this to shutdown any sockets or release their
  1300. * resources and reject any pending requests.
  1301. *
  1302. * Sub-classes **must** call ``super.destroy()``.
  1303. */
  1304. destroy(): void {
  1305. // Stop all listeners
  1306. this.removeAllListeners();
  1307. // Shut down all tiemrs
  1308. for (const timerId of this.#timers.keys()) {
  1309. this._clearTimeout(timerId);
  1310. }
  1311. this.#destroyed = true;
  1312. }
  1313. /**
  1314. * Whether the provider is currently paused.
  1315. *
  1316. * A paused provider will not emit any events, and generally should
  1317. * not make any requests to the network, but that is up to sub-classes
  1318. * to manage.
  1319. *
  1320. * Setting ``paused = true`` is identical to calling ``.pause(false)``,
  1321. * which will buffer any events that occur while paused until the
  1322. * provider is unpaused.
  1323. */
  1324. get paused(): boolean { return (this.#pausedState != null); }
  1325. set paused(pause: boolean) {
  1326. if (!!pause === this.paused) { return; }
  1327. if (this.paused) {
  1328. this.resume();
  1329. } else {
  1330. this.pause(false);
  1331. }
  1332. }
  1333. /**
  1334. * Pause the provider. If %%dropWhilePaused%%, any events that occur
  1335. * while paused are dropped, otherwise all events will be emitted once
  1336. * the provider is unpaused.
  1337. */
  1338. pause(dropWhilePaused?: boolean): void {
  1339. this.#lastBlockNumber = -1;
  1340. if (this.#pausedState != null) {
  1341. if (this.#pausedState == !!dropWhilePaused) { return; }
  1342. assert(false, "cannot change pause type; resume first", "UNSUPPORTED_OPERATION", {
  1343. operation: "pause"
  1344. });
  1345. }
  1346. this._forEachSubscriber((s) => s.pause(dropWhilePaused));
  1347. this.#pausedState = !!dropWhilePaused;
  1348. for (const timer of this.#timers.values()) {
  1349. // Clear the timer
  1350. if (timer.timer) { clearTimeout(timer.timer); }
  1351. // Remaining time needed for when we become unpaused
  1352. timer.time = getTime() - timer.time;
  1353. }
  1354. }
  1355. /**
  1356. * Resume the provider.
  1357. */
  1358. resume(): void {
  1359. if (this.#pausedState == null) { return; }
  1360. this._forEachSubscriber((s) => s.resume());
  1361. this.#pausedState = null;
  1362. for (const timer of this.#timers.values()) {
  1363. // Remaining time when we were paused
  1364. let timeout = timer.time;
  1365. if (timeout < 0) { timeout = 0; }
  1366. // Start time (in cause paused, so we con compute remaininf time)
  1367. timer.time = getTime();
  1368. // Start the timer
  1369. setTimeout(timer.func, timeout);
  1370. }
  1371. }
  1372. }
  1373. function _parseString(result: string, start: number): null | string {
  1374. try {
  1375. const bytes = _parseBytes(result, start);
  1376. if (bytes) { return toUtf8String(bytes); }
  1377. } catch(error) { }
  1378. return null;
  1379. }
  1380. function _parseBytes(result: string, start: number): null | string {
  1381. if (result === "0x") { return null; }
  1382. try {
  1383. const offset = getNumber(dataSlice(result, start, start + 32));
  1384. const length = getNumber(dataSlice(result, offset, offset + 32));
  1385. return dataSlice(result, offset + 32, offset + 32 + length);
  1386. } catch (error) { }
  1387. return null;
  1388. }
  1389. function numPad(value: number): Uint8Array {
  1390. const result = toBeArray(value);
  1391. if (result.length > 32) { throw new Error("internal; should not happen"); }
  1392. const padded = new Uint8Array(32);
  1393. padded.set(result, 32 - result.length);
  1394. return padded;
  1395. }
  1396. function bytesPad(value: Uint8Array): Uint8Array {
  1397. if ((value.length % 32) === 0) { return value; }
  1398. const result = new Uint8Array(Math.ceil(value.length / 32) * 32);
  1399. result.set(value);
  1400. return result;
  1401. }
  1402. const empty: Uint8Array = new Uint8Array([ ]);
  1403. // ABI Encodes a series of (bytes, bytes, ...)
  1404. function encodeBytes(datas: Array<BytesLike>): string {
  1405. const result: Array<Uint8Array> = [ ];
  1406. let byteCount = 0;
  1407. // Add place-holders for pointers as we add items
  1408. for (let i = 0; i < datas.length; i++) {
  1409. result.push(empty);
  1410. byteCount += 32;
  1411. }
  1412. for (let i = 0; i < datas.length; i++) {
  1413. const data = getBytes(datas[i]);
  1414. // Update the bytes offset
  1415. result[i] = numPad(byteCount);
  1416. // The length and padded value of data
  1417. result.push(numPad(data.length));
  1418. result.push(bytesPad(data));
  1419. byteCount += 32 + Math.ceil(data.length / 32) * 32;
  1420. }
  1421. return concat(result);
  1422. }
  1423. const zeros = "0x0000000000000000000000000000000000000000000000000000000000000000"
  1424. function parseOffchainLookup(data: string): CcipArgs {
  1425. const result: CcipArgs = {
  1426. sender: "", urls: [ ], calldata: "", selector: "", extraData: "", errorArgs: [ ]
  1427. };
  1428. assert(dataLength(data) >= 5 * 32, "insufficient OffchainLookup data", "OFFCHAIN_FAULT", {
  1429. reason: "insufficient OffchainLookup data"
  1430. });
  1431. const sender = dataSlice(data, 0, 32);
  1432. assert(dataSlice(sender, 0, 12) === dataSlice(zeros, 0, 12), "corrupt OffchainLookup sender", "OFFCHAIN_FAULT", {
  1433. reason: "corrupt OffchainLookup sender"
  1434. });
  1435. result.sender = dataSlice(sender, 12);
  1436. // Read the URLs from the response
  1437. try {
  1438. const urls: Array<string> = [];
  1439. const urlsOffset = getNumber(dataSlice(data, 32, 64));
  1440. const urlsLength = getNumber(dataSlice(data, urlsOffset, urlsOffset + 32));
  1441. const urlsData = dataSlice(data, urlsOffset + 32);
  1442. for (let u = 0; u < urlsLength; u++) {
  1443. const url = _parseString(urlsData, u * 32);
  1444. if (url == null) { throw new Error("abort"); }
  1445. urls.push(url);
  1446. }
  1447. result.urls = urls;
  1448. } catch (error) {
  1449. assert(false, "corrupt OffchainLookup urls", "OFFCHAIN_FAULT", {
  1450. reason: "corrupt OffchainLookup urls"
  1451. });
  1452. }
  1453. // Get the CCIP calldata to forward
  1454. try {
  1455. const calldata = _parseBytes(data, 64);
  1456. if (calldata == null) { throw new Error("abort"); }
  1457. result.calldata = calldata;
  1458. } catch (error) {
  1459. assert(false, "corrupt OffchainLookup calldata", "OFFCHAIN_FAULT", {
  1460. reason: "corrupt OffchainLookup calldata"
  1461. });
  1462. }
  1463. // Get the callbackSelector (bytes4)
  1464. assert(dataSlice(data, 100, 128) === dataSlice(zeros, 0, 28), "corrupt OffchainLookup callbaackSelector", "OFFCHAIN_FAULT", {
  1465. reason: "corrupt OffchainLookup callbaackSelector"
  1466. });
  1467. result.selector = dataSlice(data, 96, 100);
  1468. // Get the extra data to send back to the contract as context
  1469. try {
  1470. const extraData = _parseBytes(data, 128);
  1471. if (extraData == null) { throw new Error("abort"); }
  1472. result.extraData = extraData;
  1473. } catch (error) {
  1474. assert(false, "corrupt OffchainLookup extraData", "OFFCHAIN_FAULT", {
  1475. reason: "corrupt OffchainLookup extraData"
  1476. });
  1477. }
  1478. result.errorArgs = "sender,urls,calldata,selector,extraData".split(/,/).map((k) => (<any>result)[k])
  1479. return result;
  1480. }