@metamask-previews/eth-json-rpc-middleware 21.0.0-preview-468843ab

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/LICENSE +15 -0
  3. package/README.md +23 -0
  4. package/dist/block-cache.cjs +165 -0
  5. package/dist/block-cache.cjs.map +1 -0
  6. package/dist/block-cache.d.cts +9 -0
  7. package/dist/block-cache.d.cts.map +1 -0
  8. package/dist/block-cache.d.mts +9 -0
  9. package/dist/block-cache.d.mts.map +1 -0
  10. package/dist/block-cache.mjs +161 -0
  11. package/dist/block-cache.mjs.map +1 -0
  12. package/dist/block-ref-rewrite.cjs +33 -0
  13. package/dist/block-ref-rewrite.cjs.map +1 -0
  14. package/dist/block-ref-rewrite.d.cts +9 -0
  15. package/dist/block-ref-rewrite.d.cts.map +1 -0
  16. package/dist/block-ref-rewrite.d.mts +9 -0
  17. package/dist/block-ref-rewrite.d.mts.map +1 -0
  18. package/dist/block-ref-rewrite.mjs +29 -0
  19. package/dist/block-ref-rewrite.mjs.map +1 -0
  20. package/dist/block-ref.cjs +46 -0
  21. package/dist/block-ref.cjs.map +1 -0
  22. package/dist/block-ref.d.cts +11 -0
  23. package/dist/block-ref.d.cts.map +1 -0
  24. package/dist/block-ref.d.mts +11 -0
  25. package/dist/block-ref.d.mts.map +1 -0
  26. package/dist/block-ref.mjs +42 -0
  27. package/dist/block-ref.mjs.map +1 -0
  28. package/dist/block-tracker-inspector.cjs +58 -0
  29. package/dist/block-tracker-inspector.cjs.map +1 -0
  30. package/dist/block-tracker-inspector.d.cts +9 -0
  31. package/dist/block-tracker-inspector.d.cts.map +1 -0
  32. package/dist/block-tracker-inspector.d.mts +9 -0
  33. package/dist/block-tracker-inspector.d.mts.map +1 -0
  34. package/dist/block-tracker-inspector.mjs +54 -0
  35. package/dist/block-tracker-inspector.mjs.map +1 -0
  36. package/dist/fetch.cjs +48 -0
  37. package/dist/fetch.cjs.map +1 -0
  38. package/dist/fetch.d.cts +22 -0
  39. package/dist/fetch.d.cts.map +1 -0
  40. package/dist/fetch.d.mts +22 -0
  41. package/dist/fetch.d.mts.map +1 -0
  42. package/dist/fetch.mjs +44 -0
  43. package/dist/fetch.mjs.map +1 -0
  44. package/dist/index.cjs +28 -0
  45. package/dist/index.cjs.map +1 -0
  46. package/dist/index.d.cts +12 -0
  47. package/dist/index.d.cts.map +1 -0
  48. package/dist/index.d.mts +12 -0
  49. package/dist/index.d.mts.map +1 -0
  50. package/dist/index.mjs +10 -0
  51. package/dist/index.mjs.map +1 -0
  52. package/dist/inflight-cache.cjs +80 -0
  53. package/dist/inflight-cache.cjs.map +1 -0
  54. package/dist/inflight-cache.d.cts +4 -0
  55. package/dist/inflight-cache.d.cts.map +1 -0
  56. package/dist/inflight-cache.d.mts +4 -0
  57. package/dist/inflight-cache.d.mts.map +1 -0
  58. package/dist/inflight-cache.mjs +76 -0
  59. package/dist/inflight-cache.mjs.map +1 -0
  60. package/dist/logging-utils.cjs +7 -0
  61. package/dist/logging-utils.cjs.map +1 -0
  62. package/dist/logging-utils.d.cts +5 -0
  63. package/dist/logging-utils.d.cts.map +1 -0
  64. package/dist/logging-utils.d.mts +5 -0
  65. package/dist/logging-utils.d.mts.map +1 -0
  66. package/dist/logging-utils.mjs +4 -0
  67. package/dist/logging-utils.mjs.map +1 -0
  68. package/dist/methods/wallet-request-execution-permissions.cjs +41 -0
  69. package/dist/methods/wallet-request-execution-permissions.cjs.map +1 -0
  70. package/dist/methods/wallet-request-execution-permissions.d.cts +131 -0
  71. package/dist/methods/wallet-request-execution-permissions.d.cts.map +1 -0
  72. package/dist/methods/wallet-request-execution-permissions.d.mts +131 -0
  73. package/dist/methods/wallet-request-execution-permissions.d.mts.map +1 -0
  74. package/dist/methods/wallet-request-execution-permissions.mjs +37 -0
  75. package/dist/methods/wallet-request-execution-permissions.mjs.map +1 -0
  76. package/dist/methods/wallet-revoke-execution-permission.cjs +20 -0
  77. package/dist/methods/wallet-revoke-execution-permission.cjs.map +1 -0
  78. package/dist/methods/wallet-revoke-execution-permission.d.cts +13 -0
  79. package/dist/methods/wallet-revoke-execution-permission.d.cts.map +1 -0
  80. package/dist/methods/wallet-revoke-execution-permission.d.mts +13 -0
  81. package/dist/methods/wallet-revoke-execution-permission.d.mts.map +1 -0
  82. package/dist/methods/wallet-revoke-execution-permission.mjs +16 -0
  83. package/dist/methods/wallet-revoke-execution-permission.mjs.map +1 -0
  84. package/dist/providerAsMiddleware.cjs +11 -0
  85. package/dist/providerAsMiddleware.cjs.map +1 -0
  86. package/dist/providerAsMiddleware.d.cts +5 -0
  87. package/dist/providerAsMiddleware.d.cts.map +1 -0
  88. package/dist/providerAsMiddleware.d.mts +5 -0
  89. package/dist/providerAsMiddleware.d.mts.map +1 -0
  90. package/dist/providerAsMiddleware.mjs +7 -0
  91. package/dist/providerAsMiddleware.mjs.map +1 -0
  92. package/dist/retryOnEmpty.cjs +100 -0
  93. package/dist/retryOnEmpty.cjs.map +1 -0
  94. package/dist/retryOnEmpty.d.cts +11 -0
  95. package/dist/retryOnEmpty.d.cts.map +1 -0
  96. package/dist/retryOnEmpty.d.mts +11 -0
  97. package/dist/retryOnEmpty.d.mts.map +1 -0
  98. package/dist/retryOnEmpty.mjs +96 -0
  99. package/dist/retryOnEmpty.mjs.map +1 -0
  100. package/dist/types.cjs +3 -0
  101. package/dist/types.cjs.map +1 -0
  102. package/dist/types.d.cts +25 -0
  103. package/dist/types.d.cts.map +1 -0
  104. package/dist/types.d.mts +25 -0
  105. package/dist/types.d.mts.map +1 -0
  106. package/dist/types.mjs +2 -0
  107. package/dist/types.mjs.map +1 -0
  108. package/dist/utils/cache.cjs +186 -0
  109. package/dist/utils/cache.cjs.map +1 -0
  110. package/dist/utils/cache.d.cts +68 -0
  111. package/dist/utils/cache.d.cts.map +1 -0
  112. package/dist/utils/cache.d.mts +68 -0
  113. package/dist/utils/cache.d.mts.map +1 -0
  114. package/dist/utils/cache.mjs +178 -0
  115. package/dist/utils/cache.mjs.map +1 -0
  116. package/dist/utils/common.cjs +17 -0
  117. package/dist/utils/common.cjs.map +1 -0
  118. package/dist/utils/common.d.cts +8 -0
  119. package/dist/utils/common.d.cts.map +1 -0
  120. package/dist/utils/common.d.mts +8 -0
  121. package/dist/utils/common.d.mts.map +1 -0
  122. package/dist/utils/common.mjs +13 -0
  123. package/dist/utils/common.mjs.map +1 -0
  124. package/dist/utils/error.cjs +12 -0
  125. package/dist/utils/error.cjs.map +1 -0
  126. package/dist/utils/error.d.cts +3 -0
  127. package/dist/utils/error.d.cts.map +1 -0
  128. package/dist/utils/error.d.mts +3 -0
  129. package/dist/utils/error.d.mts.map +1 -0
  130. package/dist/utils/error.mjs +8 -0
  131. package/dist/utils/error.mjs.map +1 -0
  132. package/dist/utils/normalize.cjs +52 -0
  133. package/dist/utils/normalize.cjs.map +1 -0
  134. package/dist/utils/normalize.d.cts +26 -0
  135. package/dist/utils/normalize.d.cts.map +1 -0
  136. package/dist/utils/normalize.d.mts +26 -0
  137. package/dist/utils/normalize.d.mts.map +1 -0
  138. package/dist/utils/normalize.mjs +47 -0
  139. package/dist/utils/normalize.mjs.map +1 -0
  140. package/dist/utils/timeout.cjs +14 -0
  141. package/dist/utils/timeout.cjs.map +1 -0
  142. package/dist/utils/timeout.d.cts +8 -0
  143. package/dist/utils/timeout.d.cts.map +1 -0
  144. package/dist/utils/timeout.d.mts +8 -0
  145. package/dist/utils/timeout.d.mts.map +1 -0
  146. package/dist/utils/timeout.mjs +10 -0
  147. package/dist/utils/timeout.mjs.map +1 -0
  148. package/dist/utils/validation.cjs +43 -0
  149. package/dist/utils/validation.cjs.map +1 -0
  150. package/dist/utils/validation.d.cts +8 -0
  151. package/dist/utils/validation.d.cts.map +1 -0
  152. package/dist/utils/validation.d.mts +8 -0
  153. package/dist/utils/validation.d.mts.map +1 -0
  154. package/dist/utils/validation.mjs +37 -0
  155. package/dist/utils/validation.mjs.map +1 -0
  156. package/dist/wallet.cjs +327 -0
  157. package/dist/wallet.cjs.map +1 -0
  158. package/dist/wallet.d.cts +33 -0
  159. package/dist/wallet.d.cts.map +1 -0
  160. package/dist/wallet.d.mts +33 -0
  161. package/dist/wallet.d.mts.map +1 -0
  162. package/dist/wallet.mjs +300 -0
  163. package/dist/wallet.mjs.map +1 -0
  164. package/package.json +95 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"providerAsMiddleware.cjs","sourceRoot":"","sources":["../src/providerAsMiddleware.ts"],"names":[],"mappings":";;;AACA,+DAGmC;AAGnC,SAAgB,oBAAoB,CAClC,QAAkC;IAElC,OAAO,IAAA,uCAAqB,EAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QAC9C,GAAG,CAAC,MAAM,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;AACL,CAAC;AAND,oDAMC","sourcesContent":["import type { SafeEventEmitterProvider } from '@metamask/eth-json-rpc-provider';\nimport {\n createAsyncMiddleware,\n type JsonRpcMiddleware,\n} from '@metamask/json-rpc-engine';\nimport type { Json, JsonRpcParams } from '@metamask/utils';\n\nexport function providerAsMiddleware(\n provider: SafeEventEmitterProvider,\n): JsonRpcMiddleware<JsonRpcParams, Json> {\n return createAsyncMiddleware(async (req, res) => {\n res.result = await provider.request(req);\n });\n}\n"]}
@@ -0,0 +1,5 @@
1
+ import type { SafeEventEmitterProvider } from "@metamask/eth-json-rpc-provider";
2
+ import { type JsonRpcMiddleware } from "@metamask/json-rpc-engine";
3
+ import type { Json, JsonRpcParams } from "@metamask/utils";
4
+ export declare function providerAsMiddleware(provider: SafeEventEmitterProvider): JsonRpcMiddleware<JsonRpcParams, Json>;
5
+ //# sourceMappingURL=providerAsMiddleware.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"providerAsMiddleware.d.cts","sourceRoot":"","sources":["../src/providerAsMiddleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,wCAAwC;AAChF,OAAO,EAEL,KAAK,iBAAiB,EACvB,kCAAkC;AACnC,OAAO,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,wBAAwB;AAE3D,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,wBAAwB,GACjC,iBAAiB,CAAC,aAAa,EAAE,IAAI,CAAC,CAIxC"}
@@ -0,0 +1,5 @@
1
+ import type { SafeEventEmitterProvider } from "@metamask/eth-json-rpc-provider";
2
+ import { type JsonRpcMiddleware } from "@metamask/json-rpc-engine";
3
+ import type { Json, JsonRpcParams } from "@metamask/utils";
4
+ export declare function providerAsMiddleware(provider: SafeEventEmitterProvider): JsonRpcMiddleware<JsonRpcParams, Json>;
5
+ //# sourceMappingURL=providerAsMiddleware.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"providerAsMiddleware.d.mts","sourceRoot":"","sources":["../src/providerAsMiddleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,wCAAwC;AAChF,OAAO,EAEL,KAAK,iBAAiB,EACvB,kCAAkC;AACnC,OAAO,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,wBAAwB;AAE3D,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,wBAAwB,GACjC,iBAAiB,CAAC,aAAa,EAAE,IAAI,CAAC,CAIxC"}
@@ -0,0 +1,7 @@
1
+ import { createAsyncMiddleware } from "@metamask/json-rpc-engine";
2
+ export function providerAsMiddleware(provider) {
3
+ return createAsyncMiddleware(async (req, res) => {
4
+ res.result = await provider.request(req);
5
+ });
6
+ }
7
+ //# sourceMappingURL=providerAsMiddleware.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"providerAsMiddleware.mjs","sourceRoot":"","sources":["../src/providerAsMiddleware.ts"],"names":[],"mappings":"AACA,OAAO,EACL,qBAAqB,EAEtB,kCAAkC;AAGnC,MAAM,UAAU,oBAAoB,CAClC,QAAkC;IAElC,OAAO,qBAAqB,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QAC9C,GAAG,CAAC,MAAM,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import type { SafeEventEmitterProvider } from '@metamask/eth-json-rpc-provider';\nimport {\n createAsyncMiddleware,\n type JsonRpcMiddleware,\n} from '@metamask/json-rpc-engine';\nimport type { Json, JsonRpcParams } from '@metamask/utils';\n\nexport function providerAsMiddleware(\n provider: SafeEventEmitterProvider,\n): JsonRpcMiddleware<JsonRpcParams, Json> {\n return createAsyncMiddleware(async (req, res) => {\n res.result = await provider.request(req);\n });\n}\n"]}
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createRetryOnEmptyMiddleware = void 0;
4
+ const json_rpc_engine_1 = require("@metamask/json-rpc-engine");
5
+ const full_1 = require("klona/full");
6
+ const logging_utils_1 = require("./logging-utils.cjs");
7
+ const cache_1 = require("./utils/cache.cjs");
8
+ const error_1 = require("./utils/error.cjs");
9
+ const timeout_1 = require("./utils/timeout.cjs");
10
+ //
11
+ // RetryOnEmptyMiddleware will retry any request with an empty response that has
12
+ // a numbered block reference at or lower than the blockTracker's latest block.
13
+ // Its useful for dealing with load-balanced ethereum JSON RPC
14
+ // nodes that are not always in sync with each other.
15
+ //
16
+ const log = (0, logging_utils_1.createModuleLogger)(logging_utils_1.projectLogger, 'retry-on-empty');
17
+ // empty values used to determine if a request should be retried
18
+ // `<nil>` comes from https://github.com/ethereum/go-ethereum/issues/16925
19
+ const emptyValues = [
20
+ undefined,
21
+ null,
22
+ '\u003cnil\u003e',
23
+ ];
24
+ function createRetryOnEmptyMiddleware({ provider, blockTracker, } = {}) {
25
+ if (!provider) {
26
+ throw Error('RetryOnEmptyMiddleware - mandatory "provider" option is missing.');
27
+ }
28
+ if (!blockTracker) {
29
+ throw Error('RetryOnEmptyMiddleware - mandatory "blockTracker" option is missing.');
30
+ }
31
+ return (0, json_rpc_engine_1.createAsyncMiddleware)(async (req, res, next) => {
32
+ const blockRefIndex = (0, cache_1.blockTagParamIndex)(req.method);
33
+ // skip if method does not include blockRef
34
+ if (blockRefIndex === undefined) {
35
+ return next();
36
+ }
37
+ // skip if not exact block references
38
+ let blockRef = Array.isArray(req.params) && req.params[blockRefIndex]
39
+ ? req.params[blockRefIndex]
40
+ : undefined;
41
+ // omitted blockRef implies "latest"
42
+ if (blockRef === undefined) {
43
+ blockRef = 'latest';
44
+ }
45
+ // skip if non-number block reference
46
+ if (['latest', 'pending'].includes(blockRef)) {
47
+ return next();
48
+ }
49
+ // skip if block refernce is not a valid number
50
+ const blockRefNumber = Number.parseInt(blockRef.slice(2), 16);
51
+ if (Number.isNaN(blockRefNumber)) {
52
+ return next();
53
+ }
54
+ // lookup latest block
55
+ const latestBlockNumberHex = await blockTracker.getLatestBlock();
56
+ const latestBlockNumber = Number.parseInt(latestBlockNumberHex.slice(2), 16);
57
+ // skip if request block number is higher than current
58
+ if (blockRefNumber > latestBlockNumber) {
59
+ log('Requested block number %o is higher than latest block number %o, falling through to original request', blockRefNumber, latestBlockNumber);
60
+ return next();
61
+ }
62
+ log('Requested block number %o is not higher than latest block number %o, trying request until non-empty response is received', blockRefNumber, latestBlockNumber);
63
+ // create child request with specific block-ref
64
+ const childRequest = (0, full_1.klona)(req);
65
+ // attempt child request until non-empty response is received
66
+ const childResult = await retry(10, async () => {
67
+ log('Performing request %o', childRequest);
68
+ const attemptResult = await provider.request(childRequest);
69
+ log('Result is %o', attemptResult);
70
+ // verify result
71
+ const allEmptyValues = emptyValues;
72
+ if (allEmptyValues.includes(attemptResult)) {
73
+ throw new Error(`RetryOnEmptyMiddleware - empty result "${JSON.stringify(attemptResult)}" for request "${JSON.stringify(childRequest)}"`);
74
+ }
75
+ return attemptResult;
76
+ });
77
+ log('Copying result %o', childResult);
78
+ // copy child result onto original response
79
+ res.result = childResult;
80
+ return undefined;
81
+ });
82
+ }
83
+ exports.createRetryOnEmptyMiddleware = createRetryOnEmptyMiddleware;
84
+ async function retry(maxRetries, asyncFn) {
85
+ for (let index = 0; index < maxRetries; index++) {
86
+ try {
87
+ return await asyncFn();
88
+ }
89
+ catch (err) {
90
+ if ((0, error_1.isExecutionRevertedError)(err)) {
91
+ throw err;
92
+ }
93
+ log('(call %i) Request failed, waiting 1s to retry again...', index + 1);
94
+ await (0, timeout_1.timeout)(1000);
95
+ }
96
+ }
97
+ log('Retries exhausted');
98
+ throw new Error('RetryOnEmptyMiddleware - retries exhausted');
99
+ }
100
+ //# sourceMappingURL=retryOnEmpty.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retryOnEmpty.cjs","sourceRoot":"","sources":["../src/retryOnEmpty.ts"],"names":[],"mappings":"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","sourcesContent":["import type { PollingBlockTracker } from '@metamask/eth-block-tracker';\nimport type { SafeEventEmitterProvider } from '@metamask/eth-json-rpc-provider';\nimport type { JsonRpcMiddleware } from '@metamask/json-rpc-engine';\nimport { createAsyncMiddleware } from '@metamask/json-rpc-engine';\nimport type { Json, JsonRpcParams } from '@metamask/utils';\nimport { klona } from 'klona/full';\n\nimport { projectLogger, createModuleLogger } from './logging-utils';\nimport type { Block } from './types';\nimport { blockTagParamIndex } from './utils/cache';\nimport { isExecutionRevertedError } from './utils/error';\nimport { timeout } from './utils/timeout';\n\n//\n// RetryOnEmptyMiddleware will retry any request with an empty response that has\n// a numbered block reference at or lower than the blockTracker's latest block.\n// Its useful for dealing with load-balanced ethereum JSON RPC\n// nodes that are not always in sync with each other.\n//\n\nconst log = createModuleLogger(projectLogger, 'retry-on-empty');\n// empty values used to determine if a request should be retried\n// `<nil>` comes from https://github.com/ethereum/go-ethereum/issues/16925\nconst emptyValues: (string | null | undefined)[] = [\n undefined,\n null,\n '\\u003cnil\\u003e',\n];\n\ninterface RetryOnEmptyMiddlewareOptions {\n provider?: SafeEventEmitterProvider;\n blockTracker?: PollingBlockTracker;\n}\n\nexport function createRetryOnEmptyMiddleware({\n provider,\n blockTracker,\n}: RetryOnEmptyMiddlewareOptions = {}): JsonRpcMiddleware<JsonRpcParams, Json> {\n if (!provider) {\n throw Error(\n 'RetryOnEmptyMiddleware - mandatory \"provider\" option is missing.',\n );\n }\n\n if (!blockTracker) {\n throw Error(\n 'RetryOnEmptyMiddleware - mandatory \"blockTracker\" option is missing.',\n );\n }\n\n return createAsyncMiddleware(async (req, res, next) => {\n const blockRefIndex: number | undefined = blockTagParamIndex(req.method);\n // skip if method does not include blockRef\n if (blockRefIndex === undefined) {\n return next();\n }\n // skip if not exact block references\n let blockRef: string | undefined =\n Array.isArray(req.params) && req.params[blockRefIndex]\n ? (req.params[blockRefIndex] as string)\n : undefined;\n // omitted blockRef implies \"latest\"\n if (blockRef === undefined) {\n blockRef = 'latest';\n }\n\n // skip if non-number block reference\n if (['latest', 'pending'].includes(blockRef)) {\n return next();\n }\n // skip if block refernce is not a valid number\n const blockRefNumber: number = Number.parseInt(blockRef.slice(2), 16);\n if (Number.isNaN(blockRefNumber)) {\n return next();\n }\n // lookup latest block\n const latestBlockNumberHex: string = await blockTracker.getLatestBlock();\n const latestBlockNumber: number = Number.parseInt(\n latestBlockNumberHex.slice(2),\n 16,\n );\n // skip if request block number is higher than current\n if (blockRefNumber > latestBlockNumber) {\n log(\n 'Requested block number %o is higher than latest block number %o, falling through to original request',\n blockRefNumber,\n latestBlockNumber,\n );\n return next();\n }\n\n log(\n 'Requested block number %o is not higher than latest block number %o, trying request until non-empty response is received',\n blockRefNumber,\n latestBlockNumber,\n );\n\n // create child request with specific block-ref\n const childRequest = klona(req);\n // attempt child request until non-empty response is received\n const childResult = await retry(10, async () => {\n log('Performing request %o', childRequest);\n const attemptResult = await provider.request<JsonRpcParams, Block>(\n childRequest,\n );\n log('Result is %o', attemptResult);\n // verify result\n const allEmptyValues: unknown[] = emptyValues;\n if (allEmptyValues.includes(attemptResult)) {\n throw new Error(\n `RetryOnEmptyMiddleware - empty result \"${JSON.stringify(\n attemptResult,\n )}\" for request \"${JSON.stringify(childRequest)}\"`,\n );\n }\n return attemptResult;\n });\n log('Copying result %o', childResult);\n // copy child result onto original response\n res.result = childResult;\n return undefined;\n });\n}\n\nasync function retry<Result>(\n maxRetries: number,\n asyncFn: () => Promise<Result>,\n): Promise<Result> {\n for (let index = 0; index < maxRetries; index++) {\n try {\n return await asyncFn();\n } catch (err: unknown) {\n if (isExecutionRevertedError(err)) {\n throw err as unknown;\n }\n log('(call %i) Request failed, waiting 1s to retry again...', index + 1);\n await timeout(1000);\n }\n }\n log('Retries exhausted');\n throw new Error('RetryOnEmptyMiddleware - retries exhausted');\n}\n"]}
@@ -0,0 +1,11 @@
1
+ import type { PollingBlockTracker } from "@metamask/eth-block-tracker";
2
+ import type { SafeEventEmitterProvider } from "@metamask/eth-json-rpc-provider";
3
+ import type { JsonRpcMiddleware } from "@metamask/json-rpc-engine";
4
+ import type { Json, JsonRpcParams } from "@metamask/utils";
5
+ interface RetryOnEmptyMiddlewareOptions {
6
+ provider?: SafeEventEmitterProvider;
7
+ blockTracker?: PollingBlockTracker;
8
+ }
9
+ export declare function createRetryOnEmptyMiddleware({ provider, blockTracker, }?: RetryOnEmptyMiddlewareOptions): JsonRpcMiddleware<JsonRpcParams, Json>;
10
+ export {};
11
+ //# sourceMappingURL=retryOnEmpty.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retryOnEmpty.d.cts","sourceRoot":"","sources":["../src/retryOnEmpty.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,oCAAoC;AACvE,OAAO,KAAK,EAAE,wBAAwB,EAAE,wCAAwC;AAChF,OAAO,KAAK,EAAE,iBAAiB,EAAE,kCAAkC;AAEnE,OAAO,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,wBAAwB;AAyB3D,UAAU,6BAA6B;IACrC,QAAQ,CAAC,EAAE,wBAAwB,CAAC;IACpC,YAAY,CAAC,EAAE,mBAAmB,CAAC;CACpC;AAED,wBAAgB,4BAA4B,CAAC,EAC3C,QAAQ,EACR,YAAY,GACb,GAAE,6BAAkC,GAAG,iBAAiB,CAAC,aAAa,EAAE,IAAI,CAAC,CAqF7E"}
@@ -0,0 +1,11 @@
1
+ import type { PollingBlockTracker } from "@metamask/eth-block-tracker";
2
+ import type { SafeEventEmitterProvider } from "@metamask/eth-json-rpc-provider";
3
+ import type { JsonRpcMiddleware } from "@metamask/json-rpc-engine";
4
+ import type { Json, JsonRpcParams } from "@metamask/utils";
5
+ interface RetryOnEmptyMiddlewareOptions {
6
+ provider?: SafeEventEmitterProvider;
7
+ blockTracker?: PollingBlockTracker;
8
+ }
9
+ export declare function createRetryOnEmptyMiddleware({ provider, blockTracker, }?: RetryOnEmptyMiddlewareOptions): JsonRpcMiddleware<JsonRpcParams, Json>;
10
+ export {};
11
+ //# sourceMappingURL=retryOnEmpty.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retryOnEmpty.d.mts","sourceRoot":"","sources":["../src/retryOnEmpty.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,oCAAoC;AACvE,OAAO,KAAK,EAAE,wBAAwB,EAAE,wCAAwC;AAChF,OAAO,KAAK,EAAE,iBAAiB,EAAE,kCAAkC;AAEnE,OAAO,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,wBAAwB;AAyB3D,UAAU,6BAA6B;IACrC,QAAQ,CAAC,EAAE,wBAAwB,CAAC;IACpC,YAAY,CAAC,EAAE,mBAAmB,CAAC;CACpC;AAED,wBAAgB,4BAA4B,CAAC,EAC3C,QAAQ,EACR,YAAY,GACb,GAAE,6BAAkC,GAAG,iBAAiB,CAAC,aAAa,EAAE,IAAI,CAAC,CAqF7E"}
@@ -0,0 +1,96 @@
1
+ import { createAsyncMiddleware } from "@metamask/json-rpc-engine";
2
+ import { klona } from "klona/full";
3
+ import { projectLogger, createModuleLogger } from "./logging-utils.mjs";
4
+ import { blockTagParamIndex } from "./utils/cache.mjs";
5
+ import { isExecutionRevertedError } from "./utils/error.mjs";
6
+ import { timeout } from "./utils/timeout.mjs";
7
+ //
8
+ // RetryOnEmptyMiddleware will retry any request with an empty response that has
9
+ // a numbered block reference at or lower than the blockTracker's latest block.
10
+ // Its useful for dealing with load-balanced ethereum JSON RPC
11
+ // nodes that are not always in sync with each other.
12
+ //
13
+ const log = createModuleLogger(projectLogger, 'retry-on-empty');
14
+ // empty values used to determine if a request should be retried
15
+ // `<nil>` comes from https://github.com/ethereum/go-ethereum/issues/16925
16
+ const emptyValues = [
17
+ undefined,
18
+ null,
19
+ '\u003cnil\u003e',
20
+ ];
21
+ export function createRetryOnEmptyMiddleware({ provider, blockTracker, } = {}) {
22
+ if (!provider) {
23
+ throw Error('RetryOnEmptyMiddleware - mandatory "provider" option is missing.');
24
+ }
25
+ if (!blockTracker) {
26
+ throw Error('RetryOnEmptyMiddleware - mandatory "blockTracker" option is missing.');
27
+ }
28
+ return createAsyncMiddleware(async (req, res, next) => {
29
+ const blockRefIndex = blockTagParamIndex(req.method);
30
+ // skip if method does not include blockRef
31
+ if (blockRefIndex === undefined) {
32
+ return next();
33
+ }
34
+ // skip if not exact block references
35
+ let blockRef = Array.isArray(req.params) && req.params[blockRefIndex]
36
+ ? req.params[blockRefIndex]
37
+ : undefined;
38
+ // omitted blockRef implies "latest"
39
+ if (blockRef === undefined) {
40
+ blockRef = 'latest';
41
+ }
42
+ // skip if non-number block reference
43
+ if (['latest', 'pending'].includes(blockRef)) {
44
+ return next();
45
+ }
46
+ // skip if block refernce is not a valid number
47
+ const blockRefNumber = Number.parseInt(blockRef.slice(2), 16);
48
+ if (Number.isNaN(blockRefNumber)) {
49
+ return next();
50
+ }
51
+ // lookup latest block
52
+ const latestBlockNumberHex = await blockTracker.getLatestBlock();
53
+ const latestBlockNumber = Number.parseInt(latestBlockNumberHex.slice(2), 16);
54
+ // skip if request block number is higher than current
55
+ if (blockRefNumber > latestBlockNumber) {
56
+ log('Requested block number %o is higher than latest block number %o, falling through to original request', blockRefNumber, latestBlockNumber);
57
+ return next();
58
+ }
59
+ log('Requested block number %o is not higher than latest block number %o, trying request until non-empty response is received', blockRefNumber, latestBlockNumber);
60
+ // create child request with specific block-ref
61
+ const childRequest = klona(req);
62
+ // attempt child request until non-empty response is received
63
+ const childResult = await retry(10, async () => {
64
+ log('Performing request %o', childRequest);
65
+ const attemptResult = await provider.request(childRequest);
66
+ log('Result is %o', attemptResult);
67
+ // verify result
68
+ const allEmptyValues = emptyValues;
69
+ if (allEmptyValues.includes(attemptResult)) {
70
+ throw new Error(`RetryOnEmptyMiddleware - empty result "${JSON.stringify(attemptResult)}" for request "${JSON.stringify(childRequest)}"`);
71
+ }
72
+ return attemptResult;
73
+ });
74
+ log('Copying result %o', childResult);
75
+ // copy child result onto original response
76
+ res.result = childResult;
77
+ return undefined;
78
+ });
79
+ }
80
+ async function retry(maxRetries, asyncFn) {
81
+ for (let index = 0; index < maxRetries; index++) {
82
+ try {
83
+ return await asyncFn();
84
+ }
85
+ catch (err) {
86
+ if (isExecutionRevertedError(err)) {
87
+ throw err;
88
+ }
89
+ log('(call %i) Request failed, waiting 1s to retry again...', index + 1);
90
+ await timeout(1000);
91
+ }
92
+ }
93
+ log('Retries exhausted');
94
+ throw new Error('RetryOnEmptyMiddleware - retries exhausted');
95
+ }
96
+ //# sourceMappingURL=retryOnEmpty.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retryOnEmpty.mjs","sourceRoot":"","sources":["../src/retryOnEmpty.ts"],"names":[],"mappings":"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","sourcesContent":["import type { PollingBlockTracker } from '@metamask/eth-block-tracker';\nimport type { SafeEventEmitterProvider } from '@metamask/eth-json-rpc-provider';\nimport type { JsonRpcMiddleware } from '@metamask/json-rpc-engine';\nimport { createAsyncMiddleware } from '@metamask/json-rpc-engine';\nimport type { Json, JsonRpcParams } from '@metamask/utils';\nimport { klona } from 'klona/full';\n\nimport { projectLogger, createModuleLogger } from './logging-utils';\nimport type { Block } from './types';\nimport { blockTagParamIndex } from './utils/cache';\nimport { isExecutionRevertedError } from './utils/error';\nimport { timeout } from './utils/timeout';\n\n//\n// RetryOnEmptyMiddleware will retry any request with an empty response that has\n// a numbered block reference at or lower than the blockTracker's latest block.\n// Its useful for dealing with load-balanced ethereum JSON RPC\n// nodes that are not always in sync with each other.\n//\n\nconst log = createModuleLogger(projectLogger, 'retry-on-empty');\n// empty values used to determine if a request should be retried\n// `<nil>` comes from https://github.com/ethereum/go-ethereum/issues/16925\nconst emptyValues: (string | null | undefined)[] = [\n undefined,\n null,\n '\\u003cnil\\u003e',\n];\n\ninterface RetryOnEmptyMiddlewareOptions {\n provider?: SafeEventEmitterProvider;\n blockTracker?: PollingBlockTracker;\n}\n\nexport function createRetryOnEmptyMiddleware({\n provider,\n blockTracker,\n}: RetryOnEmptyMiddlewareOptions = {}): JsonRpcMiddleware<JsonRpcParams, Json> {\n if (!provider) {\n throw Error(\n 'RetryOnEmptyMiddleware - mandatory \"provider\" option is missing.',\n );\n }\n\n if (!blockTracker) {\n throw Error(\n 'RetryOnEmptyMiddleware - mandatory \"blockTracker\" option is missing.',\n );\n }\n\n return createAsyncMiddleware(async (req, res, next) => {\n const blockRefIndex: number | undefined = blockTagParamIndex(req.method);\n // skip if method does not include blockRef\n if (blockRefIndex === undefined) {\n return next();\n }\n // skip if not exact block references\n let blockRef: string | undefined =\n Array.isArray(req.params) && req.params[blockRefIndex]\n ? (req.params[blockRefIndex] as string)\n : undefined;\n // omitted blockRef implies \"latest\"\n if (blockRef === undefined) {\n blockRef = 'latest';\n }\n\n // skip if non-number block reference\n if (['latest', 'pending'].includes(blockRef)) {\n return next();\n }\n // skip if block refernce is not a valid number\n const blockRefNumber: number = Number.parseInt(blockRef.slice(2), 16);\n if (Number.isNaN(blockRefNumber)) {\n return next();\n }\n // lookup latest block\n const latestBlockNumberHex: string = await blockTracker.getLatestBlock();\n const latestBlockNumber: number = Number.parseInt(\n latestBlockNumberHex.slice(2),\n 16,\n );\n // skip if request block number is higher than current\n if (blockRefNumber > latestBlockNumber) {\n log(\n 'Requested block number %o is higher than latest block number %o, falling through to original request',\n blockRefNumber,\n latestBlockNumber,\n );\n return next();\n }\n\n log(\n 'Requested block number %o is not higher than latest block number %o, trying request until non-empty response is received',\n blockRefNumber,\n latestBlockNumber,\n );\n\n // create child request with specific block-ref\n const childRequest = klona(req);\n // attempt child request until non-empty response is received\n const childResult = await retry(10, async () => {\n log('Performing request %o', childRequest);\n const attemptResult = await provider.request<JsonRpcParams, Block>(\n childRequest,\n );\n log('Result is %o', attemptResult);\n // verify result\n const allEmptyValues: unknown[] = emptyValues;\n if (allEmptyValues.includes(attemptResult)) {\n throw new Error(\n `RetryOnEmptyMiddleware - empty result \"${JSON.stringify(\n attemptResult,\n )}\" for request \"${JSON.stringify(childRequest)}\"`,\n );\n }\n return attemptResult;\n });\n log('Copying result %o', childResult);\n // copy child result onto original response\n res.result = childResult;\n return undefined;\n });\n}\n\nasync function retry<Result>(\n maxRetries: number,\n asyncFn: () => Promise<Result>,\n): Promise<Result> {\n for (let index = 0; index < maxRetries; index++) {\n try {\n return await asyncFn();\n } catch (err: unknown) {\n if (isExecutionRevertedError(err)) {\n throw err as unknown;\n }\n log('(call %i) Request failed, waiting 1s to retry again...', index + 1);\n await timeout(1000);\n }\n }\n log('Retries exhausted');\n throw new Error('RetryOnEmptyMiddleware - retries exhausted');\n}\n"]}
package/dist/types.cjs ADDED
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.cjs","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { JsonRpcMiddleware } from '@metamask/json-rpc-engine';\nimport type {\n Json,\n JsonRpcParams,\n JsonRpcRequest,\n JsonRpcResponse,\n} from '@metamask/utils';\n\nexport interface JsonRpcRequestToCache<Params extends JsonRpcParams>\n extends JsonRpcRequest<Params> {\n skipCache?: boolean;\n}\n\nexport type JsonRpcCacheMiddleware<\n Params extends JsonRpcParams,\n Result extends Json,\n> =\n JsonRpcMiddleware<Params, Result> extends (\n req: JsonRpcRequest<Params>,\n ...args: infer X\n ) => infer Y\n ? (req: JsonRpcRequestToCache<Params>, ...args: X) => Y\n : never;\n\nexport type BlockData = string | string[];\n\nexport type Block = Record<string, BlockData>;\n\nexport type BlockCache = Record<string, Block>;\n\nexport type Cache = Record<number, BlockCache>;\n\n/**\n * A copy of the `AbstractRpcService` type in metamask/network-controller`, but\n * keeping only the `request` method.\n *\n * We cannot get `AbstractRpcService` directly from\n * `@metamask/network-controller` because relying on this package would create a\n * circular dependency.\n *\n * This type should be accurate as of `@metamask/network-controller` 24.x and\n * `@metamask/utils` 11.x.\n */\nexport type AbstractRpcServiceLike = {\n request: <Params extends JsonRpcParams, Result extends Json>(\n jsonRpcRequest: JsonRpcRequest<Params>,\n fetchOptions?: RequestInit,\n ) => Promise<JsonRpcResponse<Result | null>>;\n};\n"]}
@@ -0,0 +1,25 @@
1
+ import type { JsonRpcMiddleware } from "@metamask/json-rpc-engine";
2
+ import type { Json, JsonRpcParams, JsonRpcRequest, JsonRpcResponse } from "@metamask/utils";
3
+ export interface JsonRpcRequestToCache<Params extends JsonRpcParams> extends JsonRpcRequest<Params> {
4
+ skipCache?: boolean;
5
+ }
6
+ export type JsonRpcCacheMiddleware<Params extends JsonRpcParams, Result extends Json> = JsonRpcMiddleware<Params, Result> extends (req: JsonRpcRequest<Params>, ...args: infer X) => infer Y ? (req: JsonRpcRequestToCache<Params>, ...args: X) => Y : never;
7
+ export type BlockData = string | string[];
8
+ export type Block = Record<string, BlockData>;
9
+ export type BlockCache = Record<string, Block>;
10
+ export type Cache = Record<number, BlockCache>;
11
+ /**
12
+ * A copy of the `AbstractRpcService` type in metamask/network-controller`, but
13
+ * keeping only the `request` method.
14
+ *
15
+ * We cannot get `AbstractRpcService` directly from
16
+ * `@metamask/network-controller` because relying on this package would create a
17
+ * circular dependency.
18
+ *
19
+ * This type should be accurate as of `@metamask/network-controller` 24.x and
20
+ * `@metamask/utils` 11.x.
21
+ */
22
+ export type AbstractRpcServiceLike = {
23
+ request: <Params extends JsonRpcParams, Result extends Json>(jsonRpcRequest: JsonRpcRequest<Params>, fetchOptions?: RequestInit) => Promise<JsonRpcResponse<Result | null>>;
24
+ };
25
+ //# sourceMappingURL=types.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.cts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,kCAAkC;AACnE,OAAO,KAAK,EACV,IAAI,EACJ,aAAa,EACb,cAAc,EACd,eAAe,EAChB,wBAAwB;AAEzB,MAAM,WAAW,qBAAqB,CAAC,MAAM,SAAS,aAAa,CACjE,SAAQ,cAAc,CAAC,MAAM,CAAC;IAC9B,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,MAAM,sBAAsB,CAChC,MAAM,SAAS,aAAa,EAC5B,MAAM,SAAS,IAAI,IAEnB,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,SAAS,CACxC,GAAG,EAAE,cAAc,CAAC,MAAM,CAAC,EAC3B,GAAG,IAAI,EAAE,MAAM,CAAC,KACb,MAAM,CAAC,GACR,CAAC,GAAG,EAAE,qBAAqB,CAAC,MAAM,CAAC,EAAE,GAAG,IAAI,EAAE,CAAC,KAAK,CAAC,GACrD,KAAK,CAAC;AAEZ,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,EAAE,CAAC;AAE1C,MAAM,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AAE9C,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAE/C,MAAM,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AAE/C;;;;;;;;;;GAUG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,OAAO,EAAE,CAAC,MAAM,SAAS,aAAa,EAAE,MAAM,SAAS,IAAI,EACzD,cAAc,EAAE,cAAc,CAAC,MAAM,CAAC,EACtC,YAAY,CAAC,EAAE,WAAW,KACvB,OAAO,CAAC,eAAe,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;CAC9C,CAAC"}
@@ -0,0 +1,25 @@
1
+ import type { JsonRpcMiddleware } from "@metamask/json-rpc-engine";
2
+ import type { Json, JsonRpcParams, JsonRpcRequest, JsonRpcResponse } from "@metamask/utils";
3
+ export interface JsonRpcRequestToCache<Params extends JsonRpcParams> extends JsonRpcRequest<Params> {
4
+ skipCache?: boolean;
5
+ }
6
+ export type JsonRpcCacheMiddleware<Params extends JsonRpcParams, Result extends Json> = JsonRpcMiddleware<Params, Result> extends (req: JsonRpcRequest<Params>, ...args: infer X) => infer Y ? (req: JsonRpcRequestToCache<Params>, ...args: X) => Y : never;
7
+ export type BlockData = string | string[];
8
+ export type Block = Record<string, BlockData>;
9
+ export type BlockCache = Record<string, Block>;
10
+ export type Cache = Record<number, BlockCache>;
11
+ /**
12
+ * A copy of the `AbstractRpcService` type in metamask/network-controller`, but
13
+ * keeping only the `request` method.
14
+ *
15
+ * We cannot get `AbstractRpcService` directly from
16
+ * `@metamask/network-controller` because relying on this package would create a
17
+ * circular dependency.
18
+ *
19
+ * This type should be accurate as of `@metamask/network-controller` 24.x and
20
+ * `@metamask/utils` 11.x.
21
+ */
22
+ export type AbstractRpcServiceLike = {
23
+ request: <Params extends JsonRpcParams, Result extends Json>(jsonRpcRequest: JsonRpcRequest<Params>, fetchOptions?: RequestInit) => Promise<JsonRpcResponse<Result | null>>;
24
+ };
25
+ //# sourceMappingURL=types.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.mts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,kCAAkC;AACnE,OAAO,KAAK,EACV,IAAI,EACJ,aAAa,EACb,cAAc,EACd,eAAe,EAChB,wBAAwB;AAEzB,MAAM,WAAW,qBAAqB,CAAC,MAAM,SAAS,aAAa,CACjE,SAAQ,cAAc,CAAC,MAAM,CAAC;IAC9B,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,MAAM,sBAAsB,CAChC,MAAM,SAAS,aAAa,EAC5B,MAAM,SAAS,IAAI,IAEnB,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,SAAS,CACxC,GAAG,EAAE,cAAc,CAAC,MAAM,CAAC,EAC3B,GAAG,IAAI,EAAE,MAAM,CAAC,KACb,MAAM,CAAC,GACR,CAAC,GAAG,EAAE,qBAAqB,CAAC,MAAM,CAAC,EAAE,GAAG,IAAI,EAAE,CAAC,KAAK,CAAC,GACrD,KAAK,CAAC;AAEZ,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,EAAE,CAAC;AAE1C,MAAM,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AAE9C,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAE/C,MAAM,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AAE/C;;;;;;;;;;GAUG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,OAAO,EAAE,CAAC,MAAM,SAAS,aAAa,EAAE,MAAM,SAAS,IAAI,EACzD,cAAc,EAAE,cAAc,CAAC,MAAM,CAAC,EACtC,YAAY,CAAC,EAAE,WAAW,KACvB,OAAO,CAAC,eAAe,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;CAC9C,CAAC"}
package/dist/types.mjs ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.mjs","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { JsonRpcMiddleware } from '@metamask/json-rpc-engine';\nimport type {\n Json,\n JsonRpcParams,\n JsonRpcRequest,\n JsonRpcResponse,\n} from '@metamask/utils';\n\nexport interface JsonRpcRequestToCache<Params extends JsonRpcParams>\n extends JsonRpcRequest<Params> {\n skipCache?: boolean;\n}\n\nexport type JsonRpcCacheMiddleware<\n Params extends JsonRpcParams,\n Result extends Json,\n> =\n JsonRpcMiddleware<Params, Result> extends (\n req: JsonRpcRequest<Params>,\n ...args: infer X\n ) => infer Y\n ? (req: JsonRpcRequestToCache<Params>, ...args: X) => Y\n : never;\n\nexport type BlockData = string | string[];\n\nexport type Block = Record<string, BlockData>;\n\nexport type BlockCache = Record<string, Block>;\n\nexport type Cache = Record<number, BlockCache>;\n\n/**\n * A copy of the `AbstractRpcService` type in metamask/network-controller`, but\n * keeping only the `request` method.\n *\n * We cannot get `AbstractRpcService` directly from\n * `@metamask/network-controller` because relying on this package would create a\n * circular dependency.\n *\n * This type should be accurate as of `@metamask/network-controller` 24.x and\n * `@metamask/utils` 11.x.\n */\nexport type AbstractRpcServiceLike = {\n request: <Params extends JsonRpcParams, Result extends Json>(\n jsonRpcRequest: JsonRpcRequest<Params>,\n fetchOptions?: RequestInit,\n ) => Promise<JsonRpcResponse<Result | null>>;\n};\n"]}
@@ -0,0 +1,186 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.cacheTypeForMethod = exports.blockTagParamIndex = exports.blockTagForRequest = exports.canCache = exports.cacheIdentifierForRequest = exports.CacheStrategy = void 0;
4
+ const safe_stable_stringify_1 = require("safe-stable-stringify");
5
+ const stringify = (0, safe_stable_stringify_1.configure)({ bigint: false, circularValue: Error });
6
+ /**
7
+ * The cache strategy to use for a given method.
8
+ */
9
+ var CacheStrategy;
10
+ (function (CacheStrategy) {
11
+ /**
12
+ * Cache per-block.
13
+ */
14
+ CacheStrategy["Block"] = "block";
15
+ /**
16
+ * Cache until a chain reorganization occurs.
17
+ */
18
+ CacheStrategy["Fork"] = "fork";
19
+ /**
20
+ * Never cache.
21
+ */
22
+ CacheStrategy["Never"] = "never";
23
+ /**
24
+ * Permanently cache.
25
+ */
26
+ CacheStrategy["Permanent"] = "perma";
27
+ })(CacheStrategy || (exports.CacheStrategy = CacheStrategy = {}));
28
+ /**
29
+ * Return a cache identifier for the given request.
30
+ *
31
+ * This identifier should include any request details that might impact the
32
+ * response, with the exception of the block parameter if the `skipBlockRef`
33
+ * option is set,
34
+ *
35
+ * If the request cannot be cached, this will return `null`.
36
+ *
37
+ * @param request - The JSON-RPC request.
38
+ * @param skipBlockRef - Skip the block parameter when generating the cache
39
+ * identifier.
40
+ * @returns The cache identifier for this request, or `null` if it can't be
41
+ * cached.
42
+ */
43
+ function cacheIdentifierForRequest(request, skipBlockRef) {
44
+ const simpleParams = skipBlockRef
45
+ ? paramsWithoutBlockTag(request)
46
+ : (request.params ?? []);
47
+ if (canCache(request.method)) {
48
+ return `${request.method}:${stringify(simpleParams)}`;
49
+ }
50
+ return null;
51
+ }
52
+ exports.cacheIdentifierForRequest = cacheIdentifierForRequest;
53
+ /**
54
+ * Return whether a method can be cached or not.
55
+ *
56
+ * @param method - The method to check.
57
+ * @returns Whether the method can be cached.
58
+ */
59
+ function canCache(method) {
60
+ return cacheTypeForMethod(method) !== CacheStrategy.Never;
61
+ }
62
+ exports.canCache = canCache;
63
+ /**
64
+ * Return the block parameter for the given request, if it has one.
65
+ *
66
+ * @param request - The JSON-RPC request.
67
+ * @returns The block parameter in the given request, or `undefined` if none was found.
68
+ */
69
+ function blockTagForRequest(request) {
70
+ if (!request.params) {
71
+ return undefined;
72
+ }
73
+ const index = blockTagParamIndex(request.method);
74
+ // Block tag param not passed.
75
+ if (index === undefined ||
76
+ !Array.isArray(request.params) ||
77
+ index >= request.params.length) {
78
+ return undefined;
79
+ }
80
+ return request.params[index];
81
+ }
82
+ exports.blockTagForRequest = blockTagForRequest;
83
+ /**
84
+ * Return the request parameters without the block parameter.
85
+ *
86
+ * @param request - The JSON-RPC request.
87
+ * @returns The request parameters with the block parameter removed, if one was found.
88
+ */
89
+ function paramsWithoutBlockTag(request) {
90
+ if (!request.params) {
91
+ return [];
92
+ }
93
+ const index = blockTagParamIndex(request.method);
94
+ // Block tag param not passed.
95
+ if (index === undefined ||
96
+ !Array.isArray(request.params) ||
97
+ index >= request.params.length) {
98
+ return request.params;
99
+ }
100
+ // eth_getBlockByNumber has the block tag first, then the optional includeTx? param
101
+ if (request.method === 'eth_getBlockByNumber') {
102
+ return request.params.slice(1);
103
+ }
104
+ return request.params.slice(0, index);
105
+ }
106
+ /**
107
+ * Returns the index of the block parameter for the given method.
108
+ *
109
+ * @param method - A JSON-RPC method.
110
+ * @returns The index of the block parameter for that method, or `undefined` if
111
+ * there is no known block parameter.
112
+ */
113
+ function blockTagParamIndex(method) {
114
+ switch (method) {
115
+ // blockTag is at index 2
116
+ case 'eth_getStorageAt':
117
+ return 2;
118
+ // blockTag is at index 1
119
+ case 'eth_getBalance':
120
+ case 'eth_getCode':
121
+ case 'eth_getTransactionCount':
122
+ case 'eth_call':
123
+ return 1;
124
+ // blockTag is at index 0
125
+ case 'eth_getBlockByNumber':
126
+ return 0;
127
+ // there is no blockTag
128
+ default:
129
+ return undefined;
130
+ }
131
+ }
132
+ exports.blockTagParamIndex = blockTagParamIndex;
133
+ /**
134
+ * Return the cache type used for the given method.
135
+ *
136
+ * @param method - A JSON-RPC method.
137
+ * @returns The cache type to use for that method.
138
+ */
139
+ function cacheTypeForMethod(method) {
140
+ switch (method) {
141
+ // cache permanently
142
+ case 'web3_clientVersion':
143
+ case 'web3_sha3':
144
+ case 'eth_protocolVersion':
145
+ case 'eth_getBlockTransactionCountByHash':
146
+ case 'eth_getUncleCountByBlockHash':
147
+ case 'eth_getCode':
148
+ case 'eth_getBlockByHash':
149
+ case 'eth_getTransactionByHash':
150
+ case 'eth_getTransactionByBlockHashAndIndex':
151
+ case 'eth_getTransactionReceipt':
152
+ case 'eth_getUncleByBlockHashAndIndex':
153
+ case 'eth_getCompilers':
154
+ case 'eth_compileLLL':
155
+ case 'eth_compileSolidity':
156
+ case 'eth_compileSerpent':
157
+ case 'shh_version':
158
+ case 'test_permaCache':
159
+ return CacheStrategy.Permanent;
160
+ // cache until fork
161
+ case 'eth_getBlockByNumber':
162
+ case 'eth_getBlockTransactionCountByNumber':
163
+ case 'eth_getUncleCountByBlockNumber':
164
+ case 'eth_getTransactionByBlockNumberAndIndex':
165
+ case 'eth_getUncleByBlockNumberAndIndex':
166
+ case 'test_forkCache':
167
+ return CacheStrategy.Fork;
168
+ // cache for block
169
+ case 'eth_gasPrice':
170
+ case 'eth_blockNumber':
171
+ case 'eth_getBalance':
172
+ case 'eth_getStorageAt':
173
+ case 'eth_getTransactionCount':
174
+ case 'eth_call':
175
+ case 'eth_estimateGas':
176
+ case 'eth_getFilterLogs':
177
+ case 'eth_getLogs':
178
+ case 'test_blockCache':
179
+ return CacheStrategy.Block;
180
+ // never cache
181
+ default:
182
+ return CacheStrategy.Never;
183
+ }
184
+ }
185
+ exports.cacheTypeForMethod = cacheTypeForMethod;
186
+ //# sourceMappingURL=cache.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.cjs","sourceRoot":"","sources":["../../src/utils/cache.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Json, JsonRpcRequest } from '@metamask/utils';\nimport { configure } from 'safe-stable-stringify';\n\nconst stringify = configure({ bigint: false, circularValue: Error });\n\n/**\n * The cache strategy to use for a given method.\n */\nexport enum CacheStrategy {\n /**\n * Cache per-block.\n */\n Block = 'block',\n /**\n * Cache until a chain reorganization occurs.\n */\n Fork = 'fork',\n /**\n * Never cache.\n */\n Never = 'never',\n /**\n * Permanently cache.\n */\n Permanent = 'perma',\n}\n\n/**\n * Return a cache identifier for the given request.\n *\n * This identifier should include any request details that might impact the\n * response, with the exception of the block parameter if the `skipBlockRef`\n * option is set,\n *\n * If the request cannot be cached, this will return `null`.\n *\n * @param request - The JSON-RPC request.\n * @param skipBlockRef - Skip the block parameter when generating the cache\n * identifier.\n * @returns The cache identifier for this request, or `null` if it can't be\n * cached.\n */\nexport function cacheIdentifierForRequest(\n request: JsonRpcRequest,\n skipBlockRef?: boolean,\n): string | null {\n const simpleParams = skipBlockRef\n ? paramsWithoutBlockTag(request)\n : (request.params ?? []);\n if (canCache(request.method)) {\n return `${request.method}:${stringify(simpleParams)}`;\n }\n return null;\n}\n\n/**\n * Return whether a method can be cached or not.\n *\n * @param method - The method to check.\n * @returns Whether the method can be cached.\n */\nexport function canCache(method: string): boolean {\n return cacheTypeForMethod(method) !== CacheStrategy.Never;\n}\n\n/**\n * Return the block parameter for the given request, if it has one.\n *\n * @param request - The JSON-RPC request.\n * @returns The block parameter in the given request, or `undefined` if none was found.\n */\nexport function blockTagForRequest(request: JsonRpcRequest): Json | undefined {\n if (!request.params) {\n return undefined;\n }\n const index: number | undefined = blockTagParamIndex(request.method);\n\n // Block tag param not passed.\n if (\n index === undefined ||\n !Array.isArray(request.params) ||\n index >= request.params.length\n ) {\n return undefined;\n }\n\n return request.params[index];\n}\n\n/**\n * Return the request parameters without the block parameter.\n *\n * @param request - The JSON-RPC request.\n * @returns The request parameters with the block parameter removed, if one was found.\n */\nfunction paramsWithoutBlockTag(request: JsonRpcRequest): Json {\n if (!request.params) {\n return [];\n }\n const index: number | undefined = blockTagParamIndex(request.method);\n\n // Block tag param not passed.\n if (\n index === undefined ||\n !Array.isArray(request.params) ||\n index >= request.params.length\n ) {\n return request.params;\n }\n\n // eth_getBlockByNumber has the block tag first, then the optional includeTx? param\n if (request.method === 'eth_getBlockByNumber') {\n return request.params.slice(1);\n }\n return request.params.slice(0, index);\n}\n\n/**\n * Returns the index of the block parameter for the given method.\n *\n * @param method - A JSON-RPC method.\n * @returns The index of the block parameter for that method, or `undefined` if\n * there is no known block parameter.\n */\nexport function blockTagParamIndex(method: string): number | undefined {\n switch (method) {\n // blockTag is at index 2\n case 'eth_getStorageAt':\n return 2;\n // blockTag is at index 1\n case 'eth_getBalance':\n case 'eth_getCode':\n case 'eth_getTransactionCount':\n case 'eth_call':\n return 1;\n // blockTag is at index 0\n case 'eth_getBlockByNumber':\n return 0;\n // there is no blockTag\n default:\n return undefined;\n }\n}\n\n/**\n * Return the cache type used for the given method.\n *\n * @param method - A JSON-RPC method.\n * @returns The cache type to use for that method.\n */\nexport function cacheTypeForMethod(method: string): CacheStrategy {\n switch (method) {\n // cache permanently\n case 'web3_clientVersion':\n case 'web3_sha3':\n case 'eth_protocolVersion':\n case 'eth_getBlockTransactionCountByHash':\n case 'eth_getUncleCountByBlockHash':\n case 'eth_getCode':\n case 'eth_getBlockByHash':\n case 'eth_getTransactionByHash':\n case 'eth_getTransactionByBlockHashAndIndex':\n case 'eth_getTransactionReceipt':\n case 'eth_getUncleByBlockHashAndIndex':\n case 'eth_getCompilers':\n case 'eth_compileLLL':\n case 'eth_compileSolidity':\n case 'eth_compileSerpent':\n case 'shh_version':\n case 'test_permaCache':\n return CacheStrategy.Permanent;\n\n // cache until fork\n case 'eth_getBlockByNumber':\n case 'eth_getBlockTransactionCountByNumber':\n case 'eth_getUncleCountByBlockNumber':\n case 'eth_getTransactionByBlockNumberAndIndex':\n case 'eth_getUncleByBlockNumberAndIndex':\n case 'test_forkCache':\n return CacheStrategy.Fork;\n\n // cache for block\n case 'eth_gasPrice':\n case 'eth_blockNumber':\n case 'eth_getBalance':\n case 'eth_getStorageAt':\n case 'eth_getTransactionCount':\n case 'eth_call':\n case 'eth_estimateGas':\n case 'eth_getFilterLogs':\n case 'eth_getLogs':\n case 'test_blockCache':\n return CacheStrategy.Block;\n\n // never cache\n default:\n return CacheStrategy.Never;\n }\n}\n"]}