provider-browser.ts 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. import { assertArgument } from "../utils/index.js";
  2. import { JsonRpcApiPollingProvider } from "./provider-jsonrpc.js";
  3. import type {
  4. JsonRpcApiProviderOptions,
  5. JsonRpcError, JsonRpcPayload, JsonRpcResult,
  6. JsonRpcSigner
  7. } from "./provider-jsonrpc.js";
  8. import type { Network, Networkish } from "./network.js";
  9. /**
  10. * The interface to an [[link-eip-1193]] provider, which is a standard
  11. * used by most injected providers, which the [[BrowserProvider]] accepts
  12. * and exposes the API of.
  13. */
  14. export interface Eip1193Provider {
  15. /**
  16. * See [[link-eip-1193]] for details on this method.
  17. */
  18. request(request: { method: string, params?: Array<any> | Record<string, any> }): Promise<any>;
  19. };
  20. /**
  21. * The possible additional events dispatched when using the ``"debug"``
  22. * event on a [[BrowserProvider]].
  23. */
  24. export type DebugEventBrowserProvider = {
  25. action: "sendEip1193Payload",
  26. payload: { method: string, params: Array<any> }
  27. } | {
  28. action: "receiveEip1193Result",
  29. result: any
  30. } | {
  31. action: "receiveEip1193Error",
  32. error: Error
  33. };
  34. export type BrowserProviderOptions = {
  35. polling?: boolean;
  36. staticNetwork?: null | boolean | Network;
  37. cacheTimeout?: number;
  38. pollingInterval?: number;
  39. };
  40. /**
  41. * A **BrowserProvider** is intended to wrap an injected provider which
  42. * adheres to the [[link-eip-1193]] standard, which most (if not all)
  43. * currently do.
  44. */
  45. export class BrowserProvider extends JsonRpcApiPollingProvider {
  46. #request: (method: string, params: Array<any> | Record<string, any>) => Promise<any>;
  47. /**
  48. * Connect to the %%ethereum%% provider, optionally forcing the
  49. * %%network%%.
  50. */
  51. constructor(ethereum: Eip1193Provider, network?: Networkish, _options?: BrowserProviderOptions) {
  52. // Copy the options
  53. const options: JsonRpcApiProviderOptions = Object.assign({ },
  54. ((_options != null) ? _options: { }),
  55. { batchMaxCount: 1 });
  56. assertArgument(ethereum && ethereum.request, "invalid EIP-1193 provider", "ethereum", ethereum);
  57. super(network, options);
  58. this.#request = async (method: string, params: Array<any> | Record<string, any>) => {
  59. const payload = { method, params };
  60. this.emit("debug", { action: "sendEip1193Request", payload });
  61. try {
  62. const result = await ethereum.request(payload);
  63. this.emit("debug", { action: "receiveEip1193Result", result });
  64. return result;
  65. } catch (e: any) {
  66. const error = new Error(e.message);
  67. (<any>error).code = e.code;
  68. (<any>error).data = e.data;
  69. (<any>error).payload = payload;
  70. this.emit("debug", { action: "receiveEip1193Error", error });
  71. throw error;
  72. }
  73. };
  74. }
  75. async send(method: string, params: Array<any> | Record<string, any>): Promise<any> {
  76. await this._start();
  77. return await super.send(method, params);
  78. }
  79. async _send(payload: JsonRpcPayload | Array<JsonRpcPayload>): Promise<Array<JsonRpcResult | JsonRpcError>> {
  80. assertArgument(!Array.isArray(payload), "EIP-1193 does not support batch request", "payload", payload);
  81. try {
  82. const result = await this.#request(payload.method, payload.params || [ ]);
  83. return [ { id: payload.id, result } ];
  84. } catch (e: any) {
  85. return [ {
  86. id: payload.id,
  87. error: { code: e.code, data: e.data, message: e.message }
  88. } ];
  89. }
  90. }
  91. getRpcError(payload: JsonRpcPayload, error: JsonRpcError): Error {
  92. error = JSON.parse(JSON.stringify(error));
  93. // EIP-1193 gives us some machine-readable error codes, so rewrite
  94. // them into
  95. switch (error.error.code || -1) {
  96. case 4001:
  97. error.error.message = `ethers-user-denied: ${ error.error.message }`;
  98. break;
  99. case 4200:
  100. error.error.message = `ethers-unsupported: ${ error.error.message }`;
  101. break;
  102. }
  103. return super.getRpcError(payload, error);
  104. }
  105. /**
  106. * Resolves to ``true`` if the provider manages the %%address%%.
  107. */
  108. async hasSigner(address: number | string): Promise<boolean> {
  109. if (address == null) { address = 0; }
  110. const accounts = await this.send("eth_accounts", [ ]);
  111. if (typeof(address) === "number") {
  112. return (accounts.length > address);
  113. }
  114. address = address.toLowerCase();
  115. return accounts.filter((a: string) => (a.toLowerCase() === address)).length !== 0;
  116. }
  117. async getSigner(address?: number | string): Promise<JsonRpcSigner> {
  118. if (address == null) { address = 0; }
  119. if (!(await this.hasSigner(address))) {
  120. try {
  121. //const resp =
  122. await this.#request("eth_requestAccounts", [ ]);
  123. //console.log("RESP", resp);
  124. } catch (error: any) {
  125. const payload = error.payload;
  126. throw this.getRpcError(payload, { id: payload.id, error });
  127. }
  128. }
  129. return await super.getSigner(address);
  130. }
  131. }