provider-jsonrpc.js 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961
  1. "use strict";
  2. /**
  3. * One of the most common ways to interact with the blockchain is
  4. * by a node running a JSON-RPC interface which can be connected to,
  5. * based on the transport, using:
  6. *
  7. * - HTTP or HTTPS - [[JsonRpcProvider]]
  8. * - WebSocket - [[WebSocketProvider]]
  9. * - IPC - [[IpcSocketProvider]]
  10. *
  11. * @_section: api/providers/jsonrpc:JSON-RPC Provider [about-jsonrpcProvider]
  12. */
  13. Object.defineProperty(exports, "__esModule", { value: true });
  14. exports.JsonRpcProvider = exports.JsonRpcApiPollingProvider = exports.JsonRpcApiProvider = exports.JsonRpcSigner = void 0;
  15. // @TODO:
  16. // - Add the batching API
  17. // https://playground.open-rpc.org/?schemaUrl=https://raw.githubusercontent.com/ethereum/eth1.0-apis/assembled-spec/openrpc.json&uiSchema%5BappBar%5D%5Bui:splitView%5D=true&uiSchema%5BappBar%5D%5Bui:input%5D=false&uiSchema%5BappBar%5D%5Bui:examplesDropdown%5D=false
  18. const index_js_1 = require("../abi/index.js");
  19. const index_js_2 = require("../address/index.js");
  20. const index_js_3 = require("../hash/index.js");
  21. const index_js_4 = require("../transaction/index.js");
  22. const index_js_5 = require("../utils/index.js");
  23. const abstract_provider_js_1 = require("./abstract-provider.js");
  24. const abstract_signer_js_1 = require("./abstract-signer.js");
  25. const network_js_1 = require("./network.js");
  26. const subscriber_filterid_js_1 = require("./subscriber-filterid.js");
  27. const subscriber_polling_js_1 = require("./subscriber-polling.js");
  28. const Primitive = "bigint,boolean,function,number,string,symbol".split(/,/g);
  29. //const Methods = "getAddress,then".split(/,/g);
  30. function deepCopy(value) {
  31. if (value == null || Primitive.indexOf(typeof (value)) >= 0) {
  32. return value;
  33. }
  34. // Keep any Addressable
  35. if (typeof (value.getAddress) === "function") {
  36. return value;
  37. }
  38. if (Array.isArray(value)) {
  39. return (value.map(deepCopy));
  40. }
  41. if (typeof (value) === "object") {
  42. return Object.keys(value).reduce((accum, key) => {
  43. accum[key] = value[key];
  44. return accum;
  45. }, {});
  46. }
  47. throw new Error(`should not happen: ${value} (${typeof (value)})`);
  48. }
  49. function stall(duration) {
  50. return new Promise((resolve) => { setTimeout(resolve, duration); });
  51. }
  52. function getLowerCase(value) {
  53. if (value) {
  54. return value.toLowerCase();
  55. }
  56. return value;
  57. }
  58. function isPollable(value) {
  59. return (value && typeof (value.pollingInterval) === "number");
  60. }
  61. const defaultOptions = {
  62. polling: false,
  63. staticNetwork: null,
  64. batchStallTime: 10,
  65. batchMaxSize: (1 << 20),
  66. batchMaxCount: 100,
  67. cacheTimeout: 250,
  68. pollingInterval: 4000
  69. };
  70. // @TODO: Unchecked Signers
  71. class JsonRpcSigner extends abstract_signer_js_1.AbstractSigner {
  72. address;
  73. constructor(provider, address) {
  74. super(provider);
  75. address = (0, index_js_2.getAddress)(address);
  76. (0, index_js_5.defineProperties)(this, { address });
  77. }
  78. connect(provider) {
  79. (0, index_js_5.assert)(false, "cannot reconnect JsonRpcSigner", "UNSUPPORTED_OPERATION", {
  80. operation: "signer.connect"
  81. });
  82. }
  83. async getAddress() {
  84. return this.address;
  85. }
  86. // JSON-RPC will automatially fill in nonce, etc. so we just check from
  87. async populateTransaction(tx) {
  88. return await this.populateCall(tx);
  89. }
  90. // Returns just the hash of the transaction after sent, which is what
  91. // the bare JSON-RPC API does;
  92. async sendUncheckedTransaction(_tx) {
  93. const tx = deepCopy(_tx);
  94. const promises = [];
  95. // Make sure the from matches the sender
  96. if (tx.from) {
  97. const _from = tx.from;
  98. promises.push((async () => {
  99. const from = await (0, index_js_2.resolveAddress)(_from, this.provider);
  100. (0, index_js_5.assertArgument)(from != null && from.toLowerCase() === this.address.toLowerCase(), "from address mismatch", "transaction", _tx);
  101. tx.from = from;
  102. })());
  103. }
  104. else {
  105. tx.from = this.address;
  106. }
  107. // The JSON-RPC for eth_sendTransaction uses 90000 gas; if the user
  108. // wishes to use this, it is easy to specify explicitly, otherwise
  109. // we look it up for them.
  110. if (tx.gasLimit == null) {
  111. promises.push((async () => {
  112. tx.gasLimit = await this.provider.estimateGas({ ...tx, from: this.address });
  113. })());
  114. }
  115. // The address may be an ENS name or Addressable
  116. if (tx.to != null) {
  117. const _to = tx.to;
  118. promises.push((async () => {
  119. tx.to = await (0, index_js_2.resolveAddress)(_to, this.provider);
  120. })());
  121. }
  122. // Wait until all of our properties are filled in
  123. if (promises.length) {
  124. await Promise.all(promises);
  125. }
  126. const hexTx = this.provider.getRpcTransaction(tx);
  127. return this.provider.send("eth_sendTransaction", [hexTx]);
  128. }
  129. async sendTransaction(tx) {
  130. // This cannot be mined any earlier than any recent block
  131. const blockNumber = await this.provider.getBlockNumber();
  132. // Send the transaction
  133. const hash = await this.sendUncheckedTransaction(tx);
  134. // Unfortunately, JSON-RPC only provides and opaque transaction hash
  135. // for a response, and we need the actual transaction, so we poll
  136. // for it; it should show up very quickly
  137. return await (new Promise((resolve, reject) => {
  138. const timeouts = [1000, 100];
  139. let invalids = 0;
  140. const checkTx = async () => {
  141. try {
  142. // Try getting the transaction
  143. const tx = await this.provider.getTransaction(hash);
  144. if (tx != null) {
  145. resolve(tx.replaceableTransaction(blockNumber));
  146. return;
  147. }
  148. }
  149. catch (error) {
  150. // If we were cancelled: stop polling.
  151. // If the data is bad: the node returns bad transactions
  152. // If the network changed: calling again will also fail
  153. // If unsupported: likely destroyed
  154. if ((0, index_js_5.isError)(error, "CANCELLED") || (0, index_js_5.isError)(error, "BAD_DATA") ||
  155. (0, index_js_5.isError)(error, "NETWORK_ERROR") || (0, index_js_5.isError)(error, "UNSUPPORTED_OPERATION")) {
  156. if (error.info == null) {
  157. error.info = {};
  158. }
  159. error.info.sendTransactionHash = hash;
  160. reject(error);
  161. return;
  162. }
  163. // Stop-gap for misbehaving backends; see #4513
  164. if ((0, index_js_5.isError)(error, "INVALID_ARGUMENT")) {
  165. invalids++;
  166. if (error.info == null) {
  167. error.info = {};
  168. }
  169. error.info.sendTransactionHash = hash;
  170. if (invalids > 10) {
  171. reject(error);
  172. return;
  173. }
  174. }
  175. // Notify anyone that cares; but we will try again, since
  176. // it is likely an intermittent service error
  177. this.provider.emit("error", (0, index_js_5.makeError)("failed to fetch transation after sending (will try again)", "UNKNOWN_ERROR", { error }));
  178. }
  179. // Wait another 4 seconds
  180. this.provider._setTimeout(() => { checkTx(); }, timeouts.pop() || 4000);
  181. };
  182. checkTx();
  183. }));
  184. }
  185. async signTransaction(_tx) {
  186. const tx = deepCopy(_tx);
  187. // Make sure the from matches the sender
  188. if (tx.from) {
  189. const from = await (0, index_js_2.resolveAddress)(tx.from, this.provider);
  190. (0, index_js_5.assertArgument)(from != null && from.toLowerCase() === this.address.toLowerCase(), "from address mismatch", "transaction", _tx);
  191. tx.from = from;
  192. }
  193. else {
  194. tx.from = this.address;
  195. }
  196. const hexTx = this.provider.getRpcTransaction(tx);
  197. return await this.provider.send("eth_signTransaction", [hexTx]);
  198. }
  199. async signMessage(_message) {
  200. const message = ((typeof (_message) === "string") ? (0, index_js_5.toUtf8Bytes)(_message) : _message);
  201. return await this.provider.send("personal_sign", [
  202. (0, index_js_5.hexlify)(message), this.address.toLowerCase()
  203. ]);
  204. }
  205. async signTypedData(domain, types, _value) {
  206. const value = deepCopy(_value);
  207. // Populate any ENS names (in-place)
  208. const populated = await index_js_3.TypedDataEncoder.resolveNames(domain, types, value, async (value) => {
  209. const address = await (0, index_js_2.resolveAddress)(value);
  210. (0, index_js_5.assertArgument)(address != null, "TypedData does not support null address", "value", value);
  211. return address;
  212. });
  213. return await this.provider.send("eth_signTypedData_v4", [
  214. this.address.toLowerCase(),
  215. JSON.stringify(index_js_3.TypedDataEncoder.getPayload(populated.domain, types, populated.value))
  216. ]);
  217. }
  218. async unlock(password) {
  219. return this.provider.send("personal_unlockAccount", [
  220. this.address.toLowerCase(), password, null
  221. ]);
  222. }
  223. // https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign
  224. async _legacySignMessage(_message) {
  225. const message = ((typeof (_message) === "string") ? (0, index_js_5.toUtf8Bytes)(_message) : _message);
  226. return await this.provider.send("eth_sign", [
  227. this.address.toLowerCase(), (0, index_js_5.hexlify)(message)
  228. ]);
  229. }
  230. }
  231. exports.JsonRpcSigner = JsonRpcSigner;
  232. /**
  233. * The JsonRpcApiProvider is an abstract class and **MUST** be
  234. * sub-classed.
  235. *
  236. * It provides the base for all JSON-RPC-based Provider interaction.
  237. *
  238. * Sub-classing Notes:
  239. * - a sub-class MUST override _send
  240. * - a sub-class MUST call the `_start()` method once connected
  241. */
  242. class JsonRpcApiProvider extends abstract_provider_js_1.AbstractProvider {
  243. #options;
  244. // The next ID to use for the JSON-RPC ID field
  245. #nextId;
  246. // Payloads are queued and triggered in batches using the drainTimer
  247. #payloads;
  248. #drainTimer;
  249. #notReady;
  250. #network;
  251. #pendingDetectNetwork;
  252. #scheduleDrain() {
  253. if (this.#drainTimer) {
  254. return;
  255. }
  256. // If we aren't using batching, no harm in sending it immediately
  257. const stallTime = (this._getOption("batchMaxCount") === 1) ? 0 : this._getOption("batchStallTime");
  258. this.#drainTimer = setTimeout(() => {
  259. this.#drainTimer = null;
  260. const payloads = this.#payloads;
  261. this.#payloads = [];
  262. while (payloads.length) {
  263. // Create payload batches that satisfy our batch constraints
  264. const batch = [(payloads.shift())];
  265. while (payloads.length) {
  266. if (batch.length === this.#options.batchMaxCount) {
  267. break;
  268. }
  269. batch.push((payloads.shift()));
  270. const bytes = JSON.stringify(batch.map((p) => p.payload));
  271. if (bytes.length > this.#options.batchMaxSize) {
  272. payloads.unshift((batch.pop()));
  273. break;
  274. }
  275. }
  276. // Process the result to each payload
  277. (async () => {
  278. const payload = ((batch.length === 1) ? batch[0].payload : batch.map((p) => p.payload));
  279. this.emit("debug", { action: "sendRpcPayload", payload });
  280. try {
  281. const result = await this._send(payload);
  282. this.emit("debug", { action: "receiveRpcResult", result });
  283. // Process results in batch order
  284. for (const { resolve, reject, payload } of batch) {
  285. if (this.destroyed) {
  286. reject((0, index_js_5.makeError)("provider destroyed; cancelled request", "UNSUPPORTED_OPERATION", { operation: payload.method }));
  287. continue;
  288. }
  289. // Find the matching result
  290. const resp = result.filter((r) => (r.id === payload.id))[0];
  291. // No result; the node failed us in unexpected ways
  292. if (resp == null) {
  293. const error = (0, index_js_5.makeError)("missing response for request", "BAD_DATA", {
  294. value: result, info: { payload }
  295. });
  296. this.emit("error", error);
  297. reject(error);
  298. continue;
  299. }
  300. // The response is an error
  301. if ("error" in resp) {
  302. reject(this.getRpcError(payload, resp));
  303. continue;
  304. }
  305. // All good; send the result
  306. resolve(resp.result);
  307. }
  308. }
  309. catch (error) {
  310. this.emit("debug", { action: "receiveRpcError", error });
  311. for (const { reject } of batch) {
  312. // @TODO: augment the error with the payload
  313. reject(error);
  314. }
  315. }
  316. })();
  317. }
  318. }, stallTime);
  319. }
  320. constructor(network, options) {
  321. super(network, options);
  322. this.#nextId = 1;
  323. this.#options = Object.assign({}, defaultOptions, options || {});
  324. this.#payloads = [];
  325. this.#drainTimer = null;
  326. this.#network = null;
  327. this.#pendingDetectNetwork = null;
  328. {
  329. let resolve = null;
  330. const promise = new Promise((_resolve) => {
  331. resolve = _resolve;
  332. });
  333. this.#notReady = { promise, resolve };
  334. }
  335. const staticNetwork = this._getOption("staticNetwork");
  336. if (typeof (staticNetwork) === "boolean") {
  337. (0, index_js_5.assertArgument)(!staticNetwork || network !== "any", "staticNetwork cannot be used on special network 'any'", "options", options);
  338. if (staticNetwork && network != null) {
  339. this.#network = network_js_1.Network.from(network);
  340. }
  341. }
  342. else if (staticNetwork) {
  343. // Make sure any static network is compatbile with the provided netwrok
  344. (0, index_js_5.assertArgument)(network == null || staticNetwork.matches(network), "staticNetwork MUST match network object", "options", options);
  345. this.#network = staticNetwork;
  346. }
  347. }
  348. /**
  349. * Returns the value associated with the option %%key%%.
  350. *
  351. * Sub-classes can use this to inquire about configuration options.
  352. */
  353. _getOption(key) {
  354. return this.#options[key];
  355. }
  356. /**
  357. * Gets the [[Network]] this provider has committed to. On each call, the network
  358. * is detected, and if it has changed, the call will reject.
  359. */
  360. get _network() {
  361. (0, index_js_5.assert)(this.#network, "network is not available yet", "NETWORK_ERROR");
  362. return this.#network;
  363. }
  364. /**
  365. * Resolves to the non-normalized value by performing %%req%%.
  366. *
  367. * Sub-classes may override this to modify behavior of actions,
  368. * and should generally call ``super._perform`` as a fallback.
  369. */
  370. async _perform(req) {
  371. // Legacy networks do not like the type field being passed along (which
  372. // is fair), so we delete type if it is 0 and a non-EIP-1559 network
  373. if (req.method === "call" || req.method === "estimateGas") {
  374. let tx = req.transaction;
  375. if (tx && tx.type != null && (0, index_js_5.getBigInt)(tx.type)) {
  376. // If there are no EIP-1559 or newer properties, it might be pre-EIP-1559
  377. if (tx.maxFeePerGas == null && tx.maxPriorityFeePerGas == null) {
  378. const feeData = await this.getFeeData();
  379. if (feeData.maxFeePerGas == null && feeData.maxPriorityFeePerGas == null) {
  380. // Network doesn't know about EIP-1559 (and hence type)
  381. req = Object.assign({}, req, {
  382. transaction: Object.assign({}, tx, { type: undefined })
  383. });
  384. }
  385. }
  386. }
  387. }
  388. const request = this.getRpcRequest(req);
  389. if (request != null) {
  390. return await this.send(request.method, request.args);
  391. }
  392. return super._perform(req);
  393. }
  394. /**
  395. * Sub-classes may override this; it detects the *actual* network that
  396. * we are **currently** connected to.
  397. *
  398. * Keep in mind that [[send]] may only be used once [[ready]], otherwise the
  399. * _send primitive must be used instead.
  400. */
  401. async _detectNetwork() {
  402. const network = this._getOption("staticNetwork");
  403. if (network) {
  404. if (network === true) {
  405. if (this.#network) {
  406. return this.#network;
  407. }
  408. }
  409. else {
  410. return network;
  411. }
  412. }
  413. if (this.#pendingDetectNetwork) {
  414. return await this.#pendingDetectNetwork;
  415. }
  416. // If we are ready, use ``send``, which enabled requests to be batched
  417. if (this.ready) {
  418. this.#pendingDetectNetwork = (async () => {
  419. try {
  420. const result = network_js_1.Network.from((0, index_js_5.getBigInt)(await this.send("eth_chainId", [])));
  421. this.#pendingDetectNetwork = null;
  422. return result;
  423. }
  424. catch (error) {
  425. this.#pendingDetectNetwork = null;
  426. throw error;
  427. }
  428. })();
  429. return await this.#pendingDetectNetwork;
  430. }
  431. // We are not ready yet; use the primitive _send
  432. this.#pendingDetectNetwork = (async () => {
  433. const payload = {
  434. id: this.#nextId++, method: "eth_chainId", params: [], jsonrpc: "2.0"
  435. };
  436. this.emit("debug", { action: "sendRpcPayload", payload });
  437. let result;
  438. try {
  439. result = (await this._send(payload))[0];
  440. this.#pendingDetectNetwork = null;
  441. }
  442. catch (error) {
  443. this.#pendingDetectNetwork = null;
  444. this.emit("debug", { action: "receiveRpcError", error });
  445. throw error;
  446. }
  447. this.emit("debug", { action: "receiveRpcResult", result });
  448. if ("result" in result) {
  449. return network_js_1.Network.from((0, index_js_5.getBigInt)(result.result));
  450. }
  451. throw this.getRpcError(payload, result);
  452. })();
  453. return await this.#pendingDetectNetwork;
  454. }
  455. /**
  456. * Sub-classes **MUST** call this. Until [[_start]] has been called, no calls
  457. * will be passed to [[_send]] from [[send]]. If it is overridden, then
  458. * ``super._start()`` **MUST** be called.
  459. *
  460. * Calling it multiple times is safe and has no effect.
  461. */
  462. _start() {
  463. if (this.#notReady == null || this.#notReady.resolve == null) {
  464. return;
  465. }
  466. this.#notReady.resolve();
  467. this.#notReady = null;
  468. (async () => {
  469. // Bootstrap the network
  470. while (this.#network == null && !this.destroyed) {
  471. try {
  472. this.#network = await this._detectNetwork();
  473. }
  474. catch (error) {
  475. if (this.destroyed) {
  476. break;
  477. }
  478. console.log("JsonRpcProvider failed to detect network and cannot start up; retry in 1s (perhaps the URL is wrong or the node is not started)");
  479. this.emit("error", (0, index_js_5.makeError)("failed to bootstrap network detection", "NETWORK_ERROR", { event: "initial-network-discovery", info: { error } }));
  480. await stall(1000);
  481. }
  482. }
  483. // Start dispatching requests
  484. this.#scheduleDrain();
  485. })();
  486. }
  487. /**
  488. * Resolves once the [[_start]] has been called. This can be used in
  489. * sub-classes to defer sending data until the connection has been
  490. * established.
  491. */
  492. async _waitUntilReady() {
  493. if (this.#notReady == null) {
  494. return;
  495. }
  496. return await this.#notReady.promise;
  497. }
  498. /**
  499. * Return a Subscriber that will manage the %%sub%%.
  500. *
  501. * Sub-classes may override this to modify the behavior of
  502. * subscription management.
  503. */
  504. _getSubscriber(sub) {
  505. // Pending Filters aren't availble via polling
  506. if (sub.type === "pending") {
  507. return new subscriber_filterid_js_1.FilterIdPendingSubscriber(this);
  508. }
  509. if (sub.type === "event") {
  510. if (this._getOption("polling")) {
  511. return new subscriber_polling_js_1.PollingEventSubscriber(this, sub.filter);
  512. }
  513. return new subscriber_filterid_js_1.FilterIdEventSubscriber(this, sub.filter);
  514. }
  515. // Orphaned Logs are handled automatically, by the filter, since
  516. // logs with removed are emitted by it
  517. if (sub.type === "orphan" && sub.filter.orphan === "drop-log") {
  518. return new abstract_provider_js_1.UnmanagedSubscriber("orphan");
  519. }
  520. return super._getSubscriber(sub);
  521. }
  522. /**
  523. * Returns true only if the [[_start]] has been called.
  524. */
  525. get ready() { return this.#notReady == null; }
  526. /**
  527. * Returns %%tx%% as a normalized JSON-RPC transaction request,
  528. * which has all values hexlified and any numeric values converted
  529. * to Quantity values.
  530. */
  531. getRpcTransaction(tx) {
  532. const result = {};
  533. // JSON-RPC now requires numeric values to be "quantity" values
  534. ["chainId", "gasLimit", "gasPrice", "type", "maxFeePerGas", "maxPriorityFeePerGas", "nonce", "value"].forEach((key) => {
  535. if (tx[key] == null) {
  536. return;
  537. }
  538. let dstKey = key;
  539. if (key === "gasLimit") {
  540. dstKey = "gas";
  541. }
  542. result[dstKey] = (0, index_js_5.toQuantity)((0, index_js_5.getBigInt)(tx[key], `tx.${key}`));
  543. });
  544. // Make sure addresses and data are lowercase
  545. ["from", "to", "data"].forEach((key) => {
  546. if (tx[key] == null) {
  547. return;
  548. }
  549. result[key] = (0, index_js_5.hexlify)(tx[key]);
  550. });
  551. // Normalize the access list object
  552. if (tx.accessList) {
  553. result["accessList"] = (0, index_js_4.accessListify)(tx.accessList);
  554. }
  555. if (tx.blobVersionedHashes) {
  556. // @TODO: Remove this <any> case once EIP-4844 added to prepared tx
  557. result["blobVersionedHashes"] = tx.blobVersionedHashes.map(h => h.toLowerCase());
  558. }
  559. // @TODO: blobs should probably also be copied over, optionally
  560. // accounting for the kzg property to backfill blobVersionedHashes
  561. // using the commitment. Or should that be left as an exercise to
  562. // the caller?
  563. return result;
  564. }
  565. /**
  566. * Returns the request method and arguments required to perform
  567. * %%req%%.
  568. */
  569. getRpcRequest(req) {
  570. switch (req.method) {
  571. case "chainId":
  572. return { method: "eth_chainId", args: [] };
  573. case "getBlockNumber":
  574. return { method: "eth_blockNumber", args: [] };
  575. case "getGasPrice":
  576. return { method: "eth_gasPrice", args: [] };
  577. case "getPriorityFee":
  578. return { method: "eth_maxPriorityFeePerGas", args: [] };
  579. case "getBalance":
  580. return {
  581. method: "eth_getBalance",
  582. args: [getLowerCase(req.address), req.blockTag]
  583. };
  584. case "getTransactionCount":
  585. return {
  586. method: "eth_getTransactionCount",
  587. args: [getLowerCase(req.address), req.blockTag]
  588. };
  589. case "getCode":
  590. return {
  591. method: "eth_getCode",
  592. args: [getLowerCase(req.address), req.blockTag]
  593. };
  594. case "getStorage":
  595. return {
  596. method: "eth_getStorageAt",
  597. args: [
  598. getLowerCase(req.address),
  599. ("0x" + req.position.toString(16)),
  600. req.blockTag
  601. ]
  602. };
  603. case "broadcastTransaction":
  604. return {
  605. method: "eth_sendRawTransaction",
  606. args: [req.signedTransaction]
  607. };
  608. case "getBlock":
  609. if ("blockTag" in req) {
  610. return {
  611. method: "eth_getBlockByNumber",
  612. args: [req.blockTag, !!req.includeTransactions]
  613. };
  614. }
  615. else if ("blockHash" in req) {
  616. return {
  617. method: "eth_getBlockByHash",
  618. args: [req.blockHash, !!req.includeTransactions]
  619. };
  620. }
  621. break;
  622. case "getTransaction":
  623. return {
  624. method: "eth_getTransactionByHash",
  625. args: [req.hash]
  626. };
  627. case "getTransactionReceipt":
  628. return {
  629. method: "eth_getTransactionReceipt",
  630. args: [req.hash]
  631. };
  632. case "call":
  633. return {
  634. method: "eth_call",
  635. args: [this.getRpcTransaction(req.transaction), req.blockTag]
  636. };
  637. case "estimateGas": {
  638. return {
  639. method: "eth_estimateGas",
  640. args: [this.getRpcTransaction(req.transaction)]
  641. };
  642. }
  643. case "getLogs":
  644. if (req.filter && req.filter.address != null) {
  645. if (Array.isArray(req.filter.address)) {
  646. req.filter.address = req.filter.address.map(getLowerCase);
  647. }
  648. else {
  649. req.filter.address = getLowerCase(req.filter.address);
  650. }
  651. }
  652. return { method: "eth_getLogs", args: [req.filter] };
  653. }
  654. return null;
  655. }
  656. /**
  657. * Returns an ethers-style Error for the given JSON-RPC error
  658. * %%payload%%, coalescing the various strings and error shapes
  659. * that different nodes return, coercing them into a machine-readable
  660. * standardized error.
  661. */
  662. getRpcError(payload, _error) {
  663. const { method } = payload;
  664. const { error } = _error;
  665. if (method === "eth_estimateGas" && error.message) {
  666. const msg = error.message;
  667. if (!msg.match(/revert/i) && msg.match(/insufficient funds/i)) {
  668. return (0, index_js_5.makeError)("insufficient funds", "INSUFFICIENT_FUNDS", {
  669. transaction: (payload.params[0]),
  670. info: { payload, error }
  671. });
  672. }
  673. }
  674. if (method === "eth_call" || method === "eth_estimateGas") {
  675. const result = spelunkData(error);
  676. const e = index_js_1.AbiCoder.getBuiltinCallException((method === "eth_call") ? "call" : "estimateGas", (payload.params[0]), (result ? result.data : null));
  677. e.info = { error, payload };
  678. return e;
  679. }
  680. // Only estimateGas and call can return arbitrary contract-defined text, so now we
  681. // we can process text safely.
  682. const message = JSON.stringify(spelunkMessage(error));
  683. if (typeof (error.message) === "string" && error.message.match(/user denied|ethers-user-denied/i)) {
  684. const actionMap = {
  685. eth_sign: "signMessage",
  686. personal_sign: "signMessage",
  687. eth_signTypedData_v4: "signTypedData",
  688. eth_signTransaction: "signTransaction",
  689. eth_sendTransaction: "sendTransaction",
  690. eth_requestAccounts: "requestAccess",
  691. wallet_requestAccounts: "requestAccess",
  692. };
  693. return (0, index_js_5.makeError)(`user rejected action`, "ACTION_REJECTED", {
  694. action: (actionMap[method] || "unknown"),
  695. reason: "rejected",
  696. info: { payload, error }
  697. });
  698. }
  699. if (method === "eth_sendRawTransaction" || method === "eth_sendTransaction") {
  700. const transaction = (payload.params[0]);
  701. if (message.match(/insufficient funds|base fee exceeds gas limit/i)) {
  702. return (0, index_js_5.makeError)("insufficient funds for intrinsic transaction cost", "INSUFFICIENT_FUNDS", {
  703. transaction, info: { error }
  704. });
  705. }
  706. if (message.match(/nonce/i) && message.match(/too low/i)) {
  707. return (0, index_js_5.makeError)("nonce has already been used", "NONCE_EXPIRED", { transaction, info: { error } });
  708. }
  709. // "replacement transaction underpriced"
  710. if (message.match(/replacement transaction/i) && message.match(/underpriced/i)) {
  711. return (0, index_js_5.makeError)("replacement fee too low", "REPLACEMENT_UNDERPRICED", { transaction, info: { error } });
  712. }
  713. if (message.match(/only replay-protected/i)) {
  714. return (0, index_js_5.makeError)("legacy pre-eip-155 transactions not supported", "UNSUPPORTED_OPERATION", {
  715. operation: method, info: { transaction, info: { error } }
  716. });
  717. }
  718. }
  719. let unsupported = !!message.match(/the method .* does not exist/i);
  720. if (!unsupported) {
  721. if (error && error.details && error.details.startsWith("Unauthorized method:")) {
  722. unsupported = true;
  723. }
  724. }
  725. if (unsupported) {
  726. return (0, index_js_5.makeError)("unsupported operation", "UNSUPPORTED_OPERATION", {
  727. operation: payload.method, info: { error, payload }
  728. });
  729. }
  730. return (0, index_js_5.makeError)("could not coalesce error", "UNKNOWN_ERROR", { error, payload });
  731. }
  732. /**
  733. * Requests the %%method%% with %%params%% via the JSON-RPC protocol
  734. * over the underlying channel. This can be used to call methods
  735. * on the backend that do not have a high-level API within the Provider
  736. * API.
  737. *
  738. * This method queues requests according to the batch constraints
  739. * in the options, assigns the request a unique ID.
  740. *
  741. * **Do NOT override** this method in sub-classes; instead
  742. * override [[_send]] or force the options values in the
  743. * call to the constructor to modify this method's behavior.
  744. */
  745. send(method, params) {
  746. // @TODO: cache chainId?? purge on switch_networks
  747. // We have been destroyed; no operations are supported anymore
  748. if (this.destroyed) {
  749. return Promise.reject((0, index_js_5.makeError)("provider destroyed; cancelled request", "UNSUPPORTED_OPERATION", { operation: method }));
  750. }
  751. const id = this.#nextId++;
  752. const promise = new Promise((resolve, reject) => {
  753. this.#payloads.push({
  754. resolve, reject,
  755. payload: { method, params, id, jsonrpc: "2.0" }
  756. });
  757. });
  758. // If there is not a pending drainTimer, set one
  759. this.#scheduleDrain();
  760. return promise;
  761. }
  762. /**
  763. * Resolves to the [[Signer]] account for %%address%% managed by
  764. * the client.
  765. *
  766. * If the %%address%% is a number, it is used as an index in the
  767. * the accounts from [[listAccounts]].
  768. *
  769. * This can only be used on clients which manage accounts (such as
  770. * Geth with imported account or MetaMask).
  771. *
  772. * Throws if the account doesn't exist.
  773. */
  774. async getSigner(address) {
  775. if (address == null) {
  776. address = 0;
  777. }
  778. const accountsPromise = this.send("eth_accounts", []);
  779. // Account index
  780. if (typeof (address) === "number") {
  781. const accounts = (await accountsPromise);
  782. if (address >= accounts.length) {
  783. throw new Error("no such account");
  784. }
  785. return new JsonRpcSigner(this, accounts[address]);
  786. }
  787. const { accounts } = await (0, index_js_5.resolveProperties)({
  788. network: this.getNetwork(),
  789. accounts: accountsPromise
  790. });
  791. // Account address
  792. address = (0, index_js_2.getAddress)(address);
  793. for (const account of accounts) {
  794. if ((0, index_js_2.getAddress)(account) === address) {
  795. return new JsonRpcSigner(this, address);
  796. }
  797. }
  798. throw new Error("invalid account");
  799. }
  800. async listAccounts() {
  801. const accounts = await this.send("eth_accounts", []);
  802. return accounts.map((a) => new JsonRpcSigner(this, a));
  803. }
  804. destroy() {
  805. // Stop processing requests
  806. if (this.#drainTimer) {
  807. clearTimeout(this.#drainTimer);
  808. this.#drainTimer = null;
  809. }
  810. // Cancel all pending requests
  811. for (const { payload, reject } of this.#payloads) {
  812. reject((0, index_js_5.makeError)("provider destroyed; cancelled request", "UNSUPPORTED_OPERATION", { operation: payload.method }));
  813. }
  814. this.#payloads = [];
  815. // Parent clean-up
  816. super.destroy();
  817. }
  818. }
  819. exports.JsonRpcApiProvider = JsonRpcApiProvider;
  820. // @TODO: remove this in v7, it is not exported because this functionality
  821. // is exposed in the JsonRpcApiProvider by setting polling to true. It should
  822. // be safe to remove regardless, because it isn't reachable, but just in case.
  823. /**
  824. * @_ignore:
  825. */
  826. class JsonRpcApiPollingProvider extends JsonRpcApiProvider {
  827. #pollingInterval;
  828. constructor(network, options) {
  829. super(network, options);
  830. let pollingInterval = this._getOption("pollingInterval");
  831. if (pollingInterval == null) {
  832. pollingInterval = defaultOptions.pollingInterval;
  833. }
  834. this.#pollingInterval = pollingInterval;
  835. }
  836. _getSubscriber(sub) {
  837. const subscriber = super._getSubscriber(sub);
  838. if (isPollable(subscriber)) {
  839. subscriber.pollingInterval = this.#pollingInterval;
  840. }
  841. return subscriber;
  842. }
  843. /**
  844. * The polling interval (default: 4000 ms)
  845. */
  846. get pollingInterval() { return this.#pollingInterval; }
  847. set pollingInterval(value) {
  848. if (!Number.isInteger(value) || value < 0) {
  849. throw new Error("invalid interval");
  850. }
  851. this.#pollingInterval = value;
  852. this._forEachSubscriber((sub) => {
  853. if (isPollable(sub)) {
  854. sub.pollingInterval = this.#pollingInterval;
  855. }
  856. });
  857. }
  858. }
  859. exports.JsonRpcApiPollingProvider = JsonRpcApiPollingProvider;
  860. /**
  861. * The JsonRpcProvider is one of the most common Providers,
  862. * which performs all operations over HTTP (or HTTPS) requests.
  863. *
  864. * Events are processed by polling the backend for the current block
  865. * number; when it advances, all block-base events are then checked
  866. * for updates.
  867. */
  868. class JsonRpcProvider extends JsonRpcApiPollingProvider {
  869. #connect;
  870. constructor(url, network, options) {
  871. if (url == null) {
  872. url = "http:/\/localhost:8545";
  873. }
  874. super(network, options);
  875. if (typeof (url) === "string") {
  876. this.#connect = new index_js_5.FetchRequest(url);
  877. }
  878. else {
  879. this.#connect = url.clone();
  880. }
  881. }
  882. _getConnection() {
  883. return this.#connect.clone();
  884. }
  885. async send(method, params) {
  886. // All requests are over HTTP, so we can just start handling requests
  887. // We do this here rather than the constructor so that we don't send any
  888. // requests to the network (i.e. eth_chainId) until we absolutely have to.
  889. await this._start();
  890. return await super.send(method, params);
  891. }
  892. async _send(payload) {
  893. // Configure a POST connection for the requested method
  894. const request = this._getConnection();
  895. request.body = JSON.stringify(payload);
  896. request.setHeader("content-type", "application/json");
  897. const response = await request.send();
  898. response.assertOk();
  899. let resp = response.bodyJson;
  900. if (!Array.isArray(resp)) {
  901. resp = [resp];
  902. }
  903. return resp;
  904. }
  905. }
  906. exports.JsonRpcProvider = JsonRpcProvider;
  907. function spelunkData(value) {
  908. if (value == null) {
  909. return null;
  910. }
  911. // These *are* the droids we're looking for.
  912. if (typeof (value.message) === "string" && value.message.match(/revert/i) && (0, index_js_5.isHexString)(value.data)) {
  913. return { message: value.message, data: value.data };
  914. }
  915. // Spelunk further...
  916. if (typeof (value) === "object") {
  917. for (const key in value) {
  918. const result = spelunkData(value[key]);
  919. if (result) {
  920. return result;
  921. }
  922. }
  923. return null;
  924. }
  925. // Might be a JSON string we can further descend...
  926. if (typeof (value) === "string") {
  927. try {
  928. return spelunkData(JSON.parse(value));
  929. }
  930. catch (error) { }
  931. }
  932. return null;
  933. }
  934. function _spelunkMessage(value, result) {
  935. if (value == null) {
  936. return;
  937. }
  938. // These *are* the droids we're looking for.
  939. if (typeof (value.message) === "string") {
  940. result.push(value.message);
  941. }
  942. // Spelunk further...
  943. if (typeof (value) === "object") {
  944. for (const key in value) {
  945. _spelunkMessage(value[key], result);
  946. }
  947. }
  948. // Might be a JSON string we can further descend...
  949. if (typeof (value) === "string") {
  950. try {
  951. return _spelunkMessage(JSON.parse(value), result);
  952. }
  953. catch (error) { }
  954. }
  955. }
  956. function spelunkMessage(value) {
  957. const result = [];
  958. _spelunkMessage(value, result);
  959. return result;
  960. }
  961. //# sourceMappingURL=provider-jsonrpc.js.map