provider-alchemy.ts 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. /**
  2. * [[link-alchemy]] provides a third-party service for connecting to
  3. * various blockchains over JSON-RPC.
  4. *
  5. * **Supported Networks**
  6. *
  7. * - Ethereum Mainnet (``mainnet``)
  8. * - Goerli Testnet (``goerli``)
  9. * - Sepolia Testnet (``sepolia``)
  10. * - Arbitrum (``arbitrum``)
  11. * - Arbitrum Goerli Testnet (``arbitrum-goerli``)
  12. * - Arbitrum Sepolia Testnet (``arbitrum-sepolia``)
  13. * - Base (``base``)
  14. * - Base Goerlia Testnet (``base-goerli``)
  15. * - Base Sepolia Testnet (``base-sepolia``)
  16. * - Optimism (``optimism``)
  17. * - Optimism Goerli Testnet (``optimism-goerli``)
  18. * - Optimism Sepolia Testnet (``optimism-sepolia``)
  19. * - Polygon (``matic``)
  20. * - Polygon Amoy Testnet (``matic-amoy``)
  21. * - Polygon Mumbai Testnet (``matic-mumbai``)
  22. *
  23. * @_subsection: api/providers/thirdparty:Alchemy [providers-alchemy]
  24. */
  25. import {
  26. defineProperties, resolveProperties, assert, assertArgument,
  27. FetchRequest
  28. } from "../utils/index.js";
  29. import { showThrottleMessage } from "./community.js";
  30. import { Network } from "./network.js";
  31. import { JsonRpcProvider } from "./provider-jsonrpc.js";
  32. import type { AbstractProvider, PerformActionRequest } from "./abstract-provider.js";
  33. import type { CommunityResourcable } from "./community.js";
  34. import type { Networkish } from "./network.js";
  35. const defaultApiKey = "_gg7wSSi0KMBsdKnGVfHDueq6xMB9EkC"
  36. function getHost(name: string): string {
  37. switch(name) {
  38. case "mainnet":
  39. return "eth-mainnet.alchemyapi.io";
  40. case "goerli":
  41. return "eth-goerli.g.alchemy.com";
  42. case "sepolia":
  43. return "eth-sepolia.g.alchemy.com";
  44. case "arbitrum":
  45. return "arb-mainnet.g.alchemy.com";
  46. case "arbitrum-goerli":
  47. return "arb-goerli.g.alchemy.com";
  48. case "arbitrum-sepolia":
  49. return "arb-sepolia.g.alchemy.com";
  50. case "base":
  51. return "base-mainnet.g.alchemy.com";
  52. case "base-goerli":
  53. return "base-goerli.g.alchemy.com";
  54. case "base-sepolia":
  55. return "base-sepolia.g.alchemy.com";
  56. case "matic":
  57. return "polygon-mainnet.g.alchemy.com";
  58. case "matic-amoy":
  59. return "polygon-amoy.g.alchemy.com";
  60. case "matic-mumbai":
  61. return "polygon-mumbai.g.alchemy.com";
  62. case "optimism":
  63. return "opt-mainnet.g.alchemy.com";
  64. case "optimism-goerli":
  65. return "opt-goerli.g.alchemy.com";
  66. case "optimism-sepolia":
  67. return "opt-sepolia.g.alchemy.com";
  68. }
  69. assertArgument(false, "unsupported network", "network", name);
  70. }
  71. /**
  72. * The **AlchemyProvider** connects to the [[link-alchemy]]
  73. * JSON-RPC end-points.
  74. *
  75. * By default, a highly-throttled API key is used, which is
  76. * appropriate for quick prototypes and simple scripts. To
  77. * gain access to an increased rate-limit, it is highly
  78. * recommended to [sign up here](link-alchemy-signup).
  79. *
  80. * @_docloc: api/providers/thirdparty
  81. */
  82. export class AlchemyProvider extends JsonRpcProvider implements CommunityResourcable {
  83. readonly apiKey!: string;
  84. constructor(_network?: Networkish, apiKey?: null | string) {
  85. if (_network == null) { _network = "mainnet"; }
  86. const network = Network.from(_network);
  87. if (apiKey == null) { apiKey = defaultApiKey; }
  88. const request = AlchemyProvider.getRequest(network, apiKey);
  89. super(request, network, { staticNetwork: network });
  90. defineProperties<AlchemyProvider>(this, { apiKey });
  91. }
  92. _getProvider(chainId: number): AbstractProvider {
  93. try {
  94. return new AlchemyProvider(chainId, this.apiKey);
  95. } catch (error) { }
  96. return super._getProvider(chainId);
  97. }
  98. async _perform(req: PerformActionRequest): Promise<any> {
  99. // https://docs.alchemy.com/reference/trace-transaction
  100. if (req.method === "getTransactionResult") {
  101. const { trace, tx } = await resolveProperties({
  102. trace: this.send("trace_transaction", [ req.hash ]),
  103. tx: this.getTransaction(req.hash)
  104. });
  105. if (trace == null || tx == null) { return null; }
  106. let data: undefined | string;
  107. let error = false;
  108. try {
  109. data = trace[0].result.output;
  110. error = (trace[0].error === "Reverted");
  111. } catch (error) { }
  112. if (data) {
  113. assert(!error, "an error occurred during transaction executions", "CALL_EXCEPTION", {
  114. action: "getTransactionResult",
  115. data,
  116. reason: null,
  117. transaction: tx,
  118. invocation: null,
  119. revert: null // @TODO
  120. });
  121. return data;
  122. }
  123. assert(false, "could not parse trace result", "BAD_DATA", { value: trace });
  124. }
  125. return await super._perform(req);
  126. }
  127. isCommunityResource(): boolean {
  128. return (this.apiKey === defaultApiKey);
  129. }
  130. static getRequest(network: Network, apiKey?: string): FetchRequest {
  131. if (apiKey == null) { apiKey = defaultApiKey; }
  132. const request = new FetchRequest(`https:/\/${ getHost(network.name) }/v2/${ apiKey }`);
  133. request.allowGzip = true;
  134. if (apiKey === defaultApiKey) {
  135. request.retryFunc = async (request, response, attempt) => {
  136. showThrottleMessage("alchemy");
  137. return true;
  138. }
  139. }
  140. return request;
  141. }
  142. }