abstract-provider.js 50 KB

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