@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
package/CHANGELOG.md ADDED
@@ -0,0 +1,23 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [21.0.0]
11
+
12
+ ### Changed
13
+
14
+ - **BREAKING:** Increase minimum Node.js version from `^18.16` to `^18.18` ([#6866](https://github.com/MetaMask/core/pull/6866))
15
+ - Bump `@metamask/eth-block-tracker` from `^12.2.1` to `^14.0.0` ([#6866](https://github.com/MetaMask/core/pull/6866), [#6883](https://github.com/MetaMask/core/pull/6883))
16
+ - Bump `@metamask/network-controller` from `^24.2.2` to `^24.3.0` ([#6883](https://github.com/MetaMask/core/pull/6883))
17
+ - This package was migrated from `MetaMask/eth-json-rpc-middleware` to the
18
+ `MetaMask/core` monorepo.
19
+ - See [`MetaMask/eth-json-rpc-middleware`](https://github.com/MetaMask/eth-json-rpc-middleware/blob/main/CHANGELOG.md)
20
+ for the original changelog.
21
+
22
+ [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/eth-json-rpc-middleware@21.0.0...HEAD
23
+ [21.0.0]: https://github.com/MetaMask/core/releases/tag/@metamask/eth-json-rpc-middleware@21.0.0
package/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2020 MetaMask
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,23 @@
1
+ # `@metamask/eth-json-rpc-middleware`
2
+
3
+ Ethereum-related middleware for [`json-rpc-engine`](https://github.com/MetaMask/json-rpc-engine).
4
+
5
+ See tests for usage details.
6
+
7
+ ## Installation
8
+
9
+ `yarn add @metamask/eth-json-rpc-middleware`
10
+
11
+ or
12
+
13
+ `npm install @metamask/eth-json-rpc-middleware`
14
+
15
+ ## See also
16
+
17
+ - [`@metamask/eth-json-rpc-filters`](https://github.com/MetaMask/eth-json-rpc-filters).
18
+ - [`@metamask/eth-json-rpc-infura`](https://github.com/MetaMask/eth-json-rpc-infura).
19
+ - [`@metamask/json-rpc-engine`](https://github.com/MetaMask/core/tree/main/packages/json-rpc-engine).
20
+
21
+ ## Contributing
22
+
23
+ This package is part of a monorepo. Instructions for contributing can be found in the [monorepo README](https://github.com/MetaMask/core#readme).
@@ -0,0 +1,165 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createBlockCacheMiddleware = void 0;
4
+ const json_rpc_engine_1 = require("@metamask/json-rpc-engine");
5
+ const logging_utils_1 = require("./logging-utils.cjs");
6
+ const cache_1 = require("./utils/cache.cjs");
7
+ const log = (0, logging_utils_1.createModuleLogger)(logging_utils_1.projectLogger, 'block-cache');
8
+ // `<nil>` comes from https://github.com/ethereum/go-ethereum/issues/16925
9
+ const emptyValues = [undefined, null, '\u003cnil\u003e'];
10
+ //
11
+ // Cache Strategies
12
+ //
13
+ class BlockCacheStrategy {
14
+ constructor() {
15
+ this.cache = {};
16
+ }
17
+ getBlockCache(blockNumberHex) {
18
+ const blockNumber = Number.parseInt(blockNumberHex, 16);
19
+ let blockCache = this.cache[blockNumber];
20
+ // create new cache if necesary
21
+ if (!blockCache) {
22
+ const newCache = {};
23
+ this.cache[blockNumber] = newCache;
24
+ blockCache = newCache;
25
+ }
26
+ return blockCache;
27
+ }
28
+ async get(request, requestedBlockNumber) {
29
+ // lookup block cache
30
+ const blockCache = this.getBlockCache(requestedBlockNumber);
31
+ // lookup payload in block cache
32
+ const identifier = (0, cache_1.cacheIdentifierForRequest)(request, true);
33
+ return identifier ? blockCache[identifier] : undefined;
34
+ }
35
+ async set(request, requestedBlockNumber, result) {
36
+ // check if we can cached this result
37
+ const canCacheResult = this.canCacheResult(request, result);
38
+ if (!canCacheResult) {
39
+ return;
40
+ }
41
+ // set the value in the cache
42
+ const identifier = (0, cache_1.cacheIdentifierForRequest)(request, true);
43
+ if (!identifier) {
44
+ return;
45
+ }
46
+ const blockCache = this.getBlockCache(requestedBlockNumber);
47
+ blockCache[identifier] = result;
48
+ }
49
+ canCacheRequest(request) {
50
+ // check request method
51
+ if (!(0, cache_1.canCache)(request.method)) {
52
+ return false;
53
+ }
54
+ // check blockTag
55
+ const blockTag = (0, cache_1.blockTagForRequest)(request);
56
+ if (blockTag === 'pending') {
57
+ return false;
58
+ }
59
+ // can be cached
60
+ return true;
61
+ }
62
+ canCacheResult(request, result) {
63
+ // never cache empty values (e.g. undefined)
64
+ if (emptyValues.includes(result)) {
65
+ return false;
66
+ }
67
+ // check if transactions have block reference before caching
68
+ if (request.method &&
69
+ ['eth_getTransactionByHash', 'eth_getTransactionReceipt'].includes(request.method)) {
70
+ if (!result?.blockHash ||
71
+ result.blockHash ===
72
+ '0x0000000000000000000000000000000000000000000000000000000000000000') {
73
+ return false;
74
+ }
75
+ }
76
+ // otherwise true
77
+ return true;
78
+ }
79
+ // removes all block caches with block number lower than `oldBlockHex`
80
+ clearBefore(oldBlockHex) {
81
+ const oldBlockNumber = Number.parseInt(oldBlockHex, 16);
82
+ // clear old caches
83
+ Object.keys(this.cache)
84
+ .map(Number)
85
+ .filter((num) => num < oldBlockNumber)
86
+ .forEach((num) => delete this.cache[num]);
87
+ }
88
+ }
89
+ function createBlockCacheMiddleware({ blockTracker, } = {}) {
90
+ // validate options
91
+ if (!blockTracker) {
92
+ throw new Error('createBlockCacheMiddleware - No PollingBlockTracker specified');
93
+ }
94
+ // create caching strategies
95
+ const blockCache = new BlockCacheStrategy();
96
+ const strategies = {
97
+ [cache_1.CacheStrategy.Permanent]: blockCache,
98
+ [cache_1.CacheStrategy.Block]: blockCache,
99
+ [cache_1.CacheStrategy.Fork]: blockCache,
100
+ [cache_1.CacheStrategy.Never]: undefined,
101
+ };
102
+ return (0, json_rpc_engine_1.createAsyncMiddleware)(async (req, res, next) => {
103
+ // allow cach to be skipped if so specified
104
+ if (req.skipCache) {
105
+ return next();
106
+ }
107
+ // check type and matching strategy
108
+ const type = (0, cache_1.cacheTypeForMethod)(req.method);
109
+ const strategy = strategies[type];
110
+ // If there's no strategy in place, pass it down the chain.
111
+ if (!strategy) {
112
+ return next();
113
+ }
114
+ // If the strategy can't cache this request, ignore it.
115
+ if (!strategy.canCacheRequest(req)) {
116
+ return next();
117
+ }
118
+ // get block reference (number or keyword)
119
+ const requestBlockTag = (0, cache_1.blockTagForRequest)(req);
120
+ const blockTag = requestBlockTag && typeof requestBlockTag === 'string'
121
+ ? requestBlockTag
122
+ : 'latest';
123
+ log('blockTag = %o, req = %o', blockTag, req);
124
+ // get exact block number
125
+ let requestedBlockNumber;
126
+ if (blockTag === 'earliest') {
127
+ // this just exists for symmetry with "latest"
128
+ requestedBlockNumber = '0x00';
129
+ }
130
+ else if (blockTag === 'latest') {
131
+ // fetch latest block number
132
+ log('Fetching latest block number to determine cache key');
133
+ const latestBlockNumber = await blockTracker.getLatestBlock();
134
+ // clear all cache before latest block
135
+ log('Clearing values stored under block numbers before %o', latestBlockNumber);
136
+ blockCache.clearBefore(latestBlockNumber);
137
+ requestedBlockNumber = latestBlockNumber;
138
+ }
139
+ else {
140
+ // We have a hex number
141
+ requestedBlockNumber = blockTag;
142
+ }
143
+ // end on a hit, continue on a miss
144
+ const cacheResult = await strategy.get(req, requestedBlockNumber);
145
+ if (cacheResult === undefined) {
146
+ // cache miss
147
+ // wait for other middleware to handle request
148
+ log('No cache stored under block number %o, carrying request forward', requestedBlockNumber);
149
+ await next();
150
+ // add result to cache
151
+ // it's safe to cast res.result as Block, due to runtime type checks
152
+ // performed when strategy.set is called
153
+ log('Populating cache with', res);
154
+ await strategy.set(req, requestedBlockNumber, res.result);
155
+ }
156
+ else {
157
+ // fill in result from cache
158
+ log('Cache hit, reusing cache result stored under block number %o', requestedBlockNumber);
159
+ res.result = cacheResult;
160
+ }
161
+ return undefined;
162
+ });
163
+ }
164
+ exports.createBlockCacheMiddleware = createBlockCacheMiddleware;
165
+ //# sourceMappingURL=block-cache.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block-cache.cjs","sourceRoot":"","sources":["../src/block-cache.ts"],"names":[],"mappings":"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","sourcesContent":["import type { PollingBlockTracker } from '@metamask/eth-block-tracker';\nimport { createAsyncMiddleware } from '@metamask/json-rpc-engine';\nimport type { Json, JsonRpcParams, JsonRpcRequest } from '@metamask/utils';\n\nimport { projectLogger, createModuleLogger } from './logging-utils';\nimport type {\n Block,\n BlockCache,\n // eslint-disable-next-line @typescript-eslint/no-shadow\n Cache,\n JsonRpcCacheMiddleware,\n JsonRpcRequestToCache,\n} from './types';\nimport {\n cacheIdentifierForRequest,\n blockTagForRequest,\n cacheTypeForMethod,\n canCache,\n CacheStrategy,\n} from './utils/cache';\n\nconst log = createModuleLogger(projectLogger, 'block-cache');\n// `<nil>` comes from https://github.com/ethereum/go-ethereum/issues/16925\nconst emptyValues = [undefined, null, '\\u003cnil\\u003e'];\n\ninterface BlockCacheMiddlewareOptions {\n blockTracker?: PollingBlockTracker;\n}\n\n//\n// Cache Strategies\n//\n\nclass BlockCacheStrategy {\n private cache: Cache;\n\n constructor() {\n this.cache = {};\n }\n\n getBlockCache(blockNumberHex: string): BlockCache {\n const blockNumber: number = Number.parseInt(blockNumberHex, 16);\n let blockCache: BlockCache = this.cache[blockNumber];\n // create new cache if necesary\n if (!blockCache) {\n const newCache: BlockCache = {};\n this.cache[blockNumber] = newCache;\n blockCache = newCache;\n }\n return blockCache;\n }\n\n async get(\n request: JsonRpcRequest,\n requestedBlockNumber: string,\n ): Promise<Block | undefined> {\n // lookup block cache\n const blockCache: BlockCache = this.getBlockCache(requestedBlockNumber);\n // lookup payload in block cache\n const identifier: string | null = cacheIdentifierForRequest(request, true);\n return identifier ? blockCache[identifier] : undefined;\n }\n\n async set(\n request: JsonRpcRequest,\n requestedBlockNumber: string,\n result: Block,\n ): Promise<void> {\n // check if we can cached this result\n const canCacheResult: boolean = this.canCacheResult(request, result);\n if (!canCacheResult) {\n return;\n }\n\n // set the value in the cache\n const identifier: string | null = cacheIdentifierForRequest(request, true);\n if (!identifier) {\n return;\n }\n const blockCache: BlockCache = this.getBlockCache(requestedBlockNumber);\n blockCache[identifier] = result;\n }\n\n canCacheRequest(request: JsonRpcRequest): boolean {\n // check request method\n if (!canCache(request.method)) {\n return false;\n }\n // check blockTag\n const blockTag = blockTagForRequest(request);\n\n if (blockTag === 'pending') {\n return false;\n }\n // can be cached\n return true;\n }\n\n canCacheResult(request: JsonRpcRequest, result: Block): boolean {\n // never cache empty values (e.g. undefined)\n if (emptyValues.includes(result as any)) {\n return false;\n }\n\n // check if transactions have block reference before caching\n if (\n request.method &&\n ['eth_getTransactionByHash', 'eth_getTransactionReceipt'].includes(\n request.method,\n )\n ) {\n if (\n !result?.blockHash ||\n result.blockHash ===\n '0x0000000000000000000000000000000000000000000000000000000000000000'\n ) {\n return false;\n }\n }\n // otherwise true\n return true;\n }\n\n // removes all block caches with block number lower than `oldBlockHex`\n clearBefore(oldBlockHex: string): void {\n const oldBlockNumber: number = Number.parseInt(oldBlockHex, 16);\n // clear old caches\n Object.keys(this.cache)\n .map(Number)\n .filter((num) => num < oldBlockNumber)\n .forEach((num) => delete this.cache[num]);\n }\n}\n\nexport function createBlockCacheMiddleware({\n blockTracker,\n}: BlockCacheMiddlewareOptions = {}): JsonRpcCacheMiddleware<\n JsonRpcParams,\n Json\n> {\n // validate options\n if (!blockTracker) {\n throw new Error(\n 'createBlockCacheMiddleware - No PollingBlockTracker specified',\n );\n }\n\n // create caching strategies\n const blockCache: BlockCacheStrategy = new BlockCacheStrategy();\n const strategies: Record<CacheStrategy, BlockCacheStrategy | undefined> = {\n [CacheStrategy.Permanent]: blockCache,\n [CacheStrategy.Block]: blockCache,\n [CacheStrategy.Fork]: blockCache,\n [CacheStrategy.Never]: undefined,\n };\n\n return createAsyncMiddleware(\n async (req: JsonRpcRequestToCache<JsonRpcParams>, res, next) => {\n // allow cach to be skipped if so specified\n if (req.skipCache) {\n return next();\n }\n // check type and matching strategy\n const type = cacheTypeForMethod(req.method);\n const strategy = strategies[type];\n // If there's no strategy in place, pass it down the chain.\n if (!strategy) {\n return next();\n }\n\n // If the strategy can't cache this request, ignore it.\n if (!strategy.canCacheRequest(req)) {\n return next();\n }\n\n // get block reference (number or keyword)\n const requestBlockTag = blockTagForRequest(req);\n const blockTag =\n requestBlockTag && typeof requestBlockTag === 'string'\n ? requestBlockTag\n : 'latest';\n\n log('blockTag = %o, req = %o', blockTag, req);\n\n // get exact block number\n let requestedBlockNumber: string;\n if (blockTag === 'earliest') {\n // this just exists for symmetry with \"latest\"\n requestedBlockNumber = '0x00';\n } else if (blockTag === 'latest') {\n // fetch latest block number\n log('Fetching latest block number to determine cache key');\n const latestBlockNumber = await blockTracker.getLatestBlock();\n // clear all cache before latest block\n log(\n 'Clearing values stored under block numbers before %o',\n latestBlockNumber,\n );\n blockCache.clearBefore(latestBlockNumber);\n requestedBlockNumber = latestBlockNumber;\n } else {\n // We have a hex number\n requestedBlockNumber = blockTag;\n }\n // end on a hit, continue on a miss\n const cacheResult: Block | undefined = await strategy.get(\n req,\n requestedBlockNumber,\n );\n if (cacheResult === undefined) {\n // cache miss\n // wait for other middleware to handle request\n log(\n 'No cache stored under block number %o, carrying request forward',\n requestedBlockNumber,\n );\n await next();\n\n // add result to cache\n // it's safe to cast res.result as Block, due to runtime type checks\n // performed when strategy.set is called\n log('Populating cache with', res);\n await strategy.set(req, requestedBlockNumber, res.result as Block);\n } else {\n // fill in result from cache\n log(\n 'Cache hit, reusing cache result stored under block number %o',\n requestedBlockNumber,\n );\n res.result = cacheResult;\n }\n return undefined;\n },\n );\n}\n"]}
@@ -0,0 +1,9 @@
1
+ import type { PollingBlockTracker } from "@metamask/eth-block-tracker";
2
+ import type { Json, JsonRpcParams } from "@metamask/utils";
3
+ import type { JsonRpcCacheMiddleware } from "./types.cjs";
4
+ interface BlockCacheMiddlewareOptions {
5
+ blockTracker?: PollingBlockTracker;
6
+ }
7
+ export declare function createBlockCacheMiddleware({ blockTracker, }?: BlockCacheMiddlewareOptions): JsonRpcCacheMiddleware<JsonRpcParams, Json>;
8
+ export {};
9
+ //# sourceMappingURL=block-cache.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block-cache.d.cts","sourceRoot":"","sources":["../src/block-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,oCAAoC;AAEvE,OAAO,KAAK,EAAE,IAAI,EAAE,aAAa,EAAkB,wBAAwB;AAG3E,OAAO,KAAK,EAKV,sBAAsB,EAEvB,oBAAgB;AAajB,UAAU,2BAA2B;IACnC,YAAY,CAAC,EAAE,mBAAmB,CAAC;CACpC;AA2GD,wBAAgB,0BAA0B,CAAC,EACzC,YAAY,GACb,GAAE,2BAAgC,GAAG,sBAAsB,CAC1D,aAAa,EACb,IAAI,CACL,CA+FA"}
@@ -0,0 +1,9 @@
1
+ import type { PollingBlockTracker } from "@metamask/eth-block-tracker";
2
+ import type { Json, JsonRpcParams } from "@metamask/utils";
3
+ import type { JsonRpcCacheMiddleware } from "./types.mjs";
4
+ interface BlockCacheMiddlewareOptions {
5
+ blockTracker?: PollingBlockTracker;
6
+ }
7
+ export declare function createBlockCacheMiddleware({ blockTracker, }?: BlockCacheMiddlewareOptions): JsonRpcCacheMiddleware<JsonRpcParams, Json>;
8
+ export {};
9
+ //# sourceMappingURL=block-cache.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block-cache.d.mts","sourceRoot":"","sources":["../src/block-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,oCAAoC;AAEvE,OAAO,KAAK,EAAE,IAAI,EAAE,aAAa,EAAkB,wBAAwB;AAG3E,OAAO,KAAK,EAKV,sBAAsB,EAEvB,oBAAgB;AAajB,UAAU,2BAA2B;IACnC,YAAY,CAAC,EAAE,mBAAmB,CAAC;CACpC;AA2GD,wBAAgB,0BAA0B,CAAC,EACzC,YAAY,GACb,GAAE,2BAAgC,GAAG,sBAAsB,CAC1D,aAAa,EACb,IAAI,CACL,CA+FA"}
@@ -0,0 +1,161 @@
1
+ import { createAsyncMiddleware } from "@metamask/json-rpc-engine";
2
+ import { projectLogger, createModuleLogger } from "./logging-utils.mjs";
3
+ import { cacheIdentifierForRequest, blockTagForRequest, cacheTypeForMethod, canCache, CacheStrategy } from "./utils/cache.mjs";
4
+ const log = createModuleLogger(projectLogger, 'block-cache');
5
+ // `<nil>` comes from https://github.com/ethereum/go-ethereum/issues/16925
6
+ const emptyValues = [undefined, null, '\u003cnil\u003e'];
7
+ //
8
+ // Cache Strategies
9
+ //
10
+ class BlockCacheStrategy {
11
+ constructor() {
12
+ this.cache = {};
13
+ }
14
+ getBlockCache(blockNumberHex) {
15
+ const blockNumber = Number.parseInt(blockNumberHex, 16);
16
+ let blockCache = this.cache[blockNumber];
17
+ // create new cache if necesary
18
+ if (!blockCache) {
19
+ const newCache = {};
20
+ this.cache[blockNumber] = newCache;
21
+ blockCache = newCache;
22
+ }
23
+ return blockCache;
24
+ }
25
+ async get(request, requestedBlockNumber) {
26
+ // lookup block cache
27
+ const blockCache = this.getBlockCache(requestedBlockNumber);
28
+ // lookup payload in block cache
29
+ const identifier = cacheIdentifierForRequest(request, true);
30
+ return identifier ? blockCache[identifier] : undefined;
31
+ }
32
+ async set(request, requestedBlockNumber, result) {
33
+ // check if we can cached this result
34
+ const canCacheResult = this.canCacheResult(request, result);
35
+ if (!canCacheResult) {
36
+ return;
37
+ }
38
+ // set the value in the cache
39
+ const identifier = cacheIdentifierForRequest(request, true);
40
+ if (!identifier) {
41
+ return;
42
+ }
43
+ const blockCache = this.getBlockCache(requestedBlockNumber);
44
+ blockCache[identifier] = result;
45
+ }
46
+ canCacheRequest(request) {
47
+ // check request method
48
+ if (!canCache(request.method)) {
49
+ return false;
50
+ }
51
+ // check blockTag
52
+ const blockTag = blockTagForRequest(request);
53
+ if (blockTag === 'pending') {
54
+ return false;
55
+ }
56
+ // can be cached
57
+ return true;
58
+ }
59
+ canCacheResult(request, result) {
60
+ // never cache empty values (e.g. undefined)
61
+ if (emptyValues.includes(result)) {
62
+ return false;
63
+ }
64
+ // check if transactions have block reference before caching
65
+ if (request.method &&
66
+ ['eth_getTransactionByHash', 'eth_getTransactionReceipt'].includes(request.method)) {
67
+ if (!result?.blockHash ||
68
+ result.blockHash ===
69
+ '0x0000000000000000000000000000000000000000000000000000000000000000') {
70
+ return false;
71
+ }
72
+ }
73
+ // otherwise true
74
+ return true;
75
+ }
76
+ // removes all block caches with block number lower than `oldBlockHex`
77
+ clearBefore(oldBlockHex) {
78
+ const oldBlockNumber = Number.parseInt(oldBlockHex, 16);
79
+ // clear old caches
80
+ Object.keys(this.cache)
81
+ .map(Number)
82
+ .filter((num) => num < oldBlockNumber)
83
+ .forEach((num) => delete this.cache[num]);
84
+ }
85
+ }
86
+ export function createBlockCacheMiddleware({ blockTracker, } = {}) {
87
+ // validate options
88
+ if (!blockTracker) {
89
+ throw new Error('createBlockCacheMiddleware - No PollingBlockTracker specified');
90
+ }
91
+ // create caching strategies
92
+ const blockCache = new BlockCacheStrategy();
93
+ const strategies = {
94
+ [CacheStrategy.Permanent]: blockCache,
95
+ [CacheStrategy.Block]: blockCache,
96
+ [CacheStrategy.Fork]: blockCache,
97
+ [CacheStrategy.Never]: undefined,
98
+ };
99
+ return createAsyncMiddleware(async (req, res, next) => {
100
+ // allow cach to be skipped if so specified
101
+ if (req.skipCache) {
102
+ return next();
103
+ }
104
+ // check type and matching strategy
105
+ const type = cacheTypeForMethod(req.method);
106
+ const strategy = strategies[type];
107
+ // If there's no strategy in place, pass it down the chain.
108
+ if (!strategy) {
109
+ return next();
110
+ }
111
+ // If the strategy can't cache this request, ignore it.
112
+ if (!strategy.canCacheRequest(req)) {
113
+ return next();
114
+ }
115
+ // get block reference (number or keyword)
116
+ const requestBlockTag = blockTagForRequest(req);
117
+ const blockTag = requestBlockTag && typeof requestBlockTag === 'string'
118
+ ? requestBlockTag
119
+ : 'latest';
120
+ log('blockTag = %o, req = %o', blockTag, req);
121
+ // get exact block number
122
+ let requestedBlockNumber;
123
+ if (blockTag === 'earliest') {
124
+ // this just exists for symmetry with "latest"
125
+ requestedBlockNumber = '0x00';
126
+ }
127
+ else if (blockTag === 'latest') {
128
+ // fetch latest block number
129
+ log('Fetching latest block number to determine cache key');
130
+ const latestBlockNumber = await blockTracker.getLatestBlock();
131
+ // clear all cache before latest block
132
+ log('Clearing values stored under block numbers before %o', latestBlockNumber);
133
+ blockCache.clearBefore(latestBlockNumber);
134
+ requestedBlockNumber = latestBlockNumber;
135
+ }
136
+ else {
137
+ // We have a hex number
138
+ requestedBlockNumber = blockTag;
139
+ }
140
+ // end on a hit, continue on a miss
141
+ const cacheResult = await strategy.get(req, requestedBlockNumber);
142
+ if (cacheResult === undefined) {
143
+ // cache miss
144
+ // wait for other middleware to handle request
145
+ log('No cache stored under block number %o, carrying request forward', requestedBlockNumber);
146
+ await next();
147
+ // add result to cache
148
+ // it's safe to cast res.result as Block, due to runtime type checks
149
+ // performed when strategy.set is called
150
+ log('Populating cache with', res);
151
+ await strategy.set(req, requestedBlockNumber, res.result);
152
+ }
153
+ else {
154
+ // fill in result from cache
155
+ log('Cache hit, reusing cache result stored under block number %o', requestedBlockNumber);
156
+ res.result = cacheResult;
157
+ }
158
+ return undefined;
159
+ });
160
+ }
161
+ //# sourceMappingURL=block-cache.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block-cache.mjs","sourceRoot":"","sources":["../src/block-cache.ts"],"names":[],"mappings":"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","sourcesContent":["import type { PollingBlockTracker } from '@metamask/eth-block-tracker';\nimport { createAsyncMiddleware } from '@metamask/json-rpc-engine';\nimport type { Json, JsonRpcParams, JsonRpcRequest } from '@metamask/utils';\n\nimport { projectLogger, createModuleLogger } from './logging-utils';\nimport type {\n Block,\n BlockCache,\n // eslint-disable-next-line @typescript-eslint/no-shadow\n Cache,\n JsonRpcCacheMiddleware,\n JsonRpcRequestToCache,\n} from './types';\nimport {\n cacheIdentifierForRequest,\n blockTagForRequest,\n cacheTypeForMethod,\n canCache,\n CacheStrategy,\n} from './utils/cache';\n\nconst log = createModuleLogger(projectLogger, 'block-cache');\n// `<nil>` comes from https://github.com/ethereum/go-ethereum/issues/16925\nconst emptyValues = [undefined, null, '\\u003cnil\\u003e'];\n\ninterface BlockCacheMiddlewareOptions {\n blockTracker?: PollingBlockTracker;\n}\n\n//\n// Cache Strategies\n//\n\nclass BlockCacheStrategy {\n private cache: Cache;\n\n constructor() {\n this.cache = {};\n }\n\n getBlockCache(blockNumberHex: string): BlockCache {\n const blockNumber: number = Number.parseInt(blockNumberHex, 16);\n let blockCache: BlockCache = this.cache[blockNumber];\n // create new cache if necesary\n if (!blockCache) {\n const newCache: BlockCache = {};\n this.cache[blockNumber] = newCache;\n blockCache = newCache;\n }\n return blockCache;\n }\n\n async get(\n request: JsonRpcRequest,\n requestedBlockNumber: string,\n ): Promise<Block | undefined> {\n // lookup block cache\n const blockCache: BlockCache = this.getBlockCache(requestedBlockNumber);\n // lookup payload in block cache\n const identifier: string | null = cacheIdentifierForRequest(request, true);\n return identifier ? blockCache[identifier] : undefined;\n }\n\n async set(\n request: JsonRpcRequest,\n requestedBlockNumber: string,\n result: Block,\n ): Promise<void> {\n // check if we can cached this result\n const canCacheResult: boolean = this.canCacheResult(request, result);\n if (!canCacheResult) {\n return;\n }\n\n // set the value in the cache\n const identifier: string | null = cacheIdentifierForRequest(request, true);\n if (!identifier) {\n return;\n }\n const blockCache: BlockCache = this.getBlockCache(requestedBlockNumber);\n blockCache[identifier] = result;\n }\n\n canCacheRequest(request: JsonRpcRequest): boolean {\n // check request method\n if (!canCache(request.method)) {\n return false;\n }\n // check blockTag\n const blockTag = blockTagForRequest(request);\n\n if (blockTag === 'pending') {\n return false;\n }\n // can be cached\n return true;\n }\n\n canCacheResult(request: JsonRpcRequest, result: Block): boolean {\n // never cache empty values (e.g. undefined)\n if (emptyValues.includes(result as any)) {\n return false;\n }\n\n // check if transactions have block reference before caching\n if (\n request.method &&\n ['eth_getTransactionByHash', 'eth_getTransactionReceipt'].includes(\n request.method,\n )\n ) {\n if (\n !result?.blockHash ||\n result.blockHash ===\n '0x0000000000000000000000000000000000000000000000000000000000000000'\n ) {\n return false;\n }\n }\n // otherwise true\n return true;\n }\n\n // removes all block caches with block number lower than `oldBlockHex`\n clearBefore(oldBlockHex: string): void {\n const oldBlockNumber: number = Number.parseInt(oldBlockHex, 16);\n // clear old caches\n Object.keys(this.cache)\n .map(Number)\n .filter((num) => num < oldBlockNumber)\n .forEach((num) => delete this.cache[num]);\n }\n}\n\nexport function createBlockCacheMiddleware({\n blockTracker,\n}: BlockCacheMiddlewareOptions = {}): JsonRpcCacheMiddleware<\n JsonRpcParams,\n Json\n> {\n // validate options\n if (!blockTracker) {\n throw new Error(\n 'createBlockCacheMiddleware - No PollingBlockTracker specified',\n );\n }\n\n // create caching strategies\n const blockCache: BlockCacheStrategy = new BlockCacheStrategy();\n const strategies: Record<CacheStrategy, BlockCacheStrategy | undefined> = {\n [CacheStrategy.Permanent]: blockCache,\n [CacheStrategy.Block]: blockCache,\n [CacheStrategy.Fork]: blockCache,\n [CacheStrategy.Never]: undefined,\n };\n\n return createAsyncMiddleware(\n async (req: JsonRpcRequestToCache<JsonRpcParams>, res, next) => {\n // allow cach to be skipped if so specified\n if (req.skipCache) {\n return next();\n }\n // check type and matching strategy\n const type = cacheTypeForMethod(req.method);\n const strategy = strategies[type];\n // If there's no strategy in place, pass it down the chain.\n if (!strategy) {\n return next();\n }\n\n // If the strategy can't cache this request, ignore it.\n if (!strategy.canCacheRequest(req)) {\n return next();\n }\n\n // get block reference (number or keyword)\n const requestBlockTag = blockTagForRequest(req);\n const blockTag =\n requestBlockTag && typeof requestBlockTag === 'string'\n ? requestBlockTag\n : 'latest';\n\n log('blockTag = %o, req = %o', blockTag, req);\n\n // get exact block number\n let requestedBlockNumber: string;\n if (blockTag === 'earliest') {\n // this just exists for symmetry with \"latest\"\n requestedBlockNumber = '0x00';\n } else if (blockTag === 'latest') {\n // fetch latest block number\n log('Fetching latest block number to determine cache key');\n const latestBlockNumber = await blockTracker.getLatestBlock();\n // clear all cache before latest block\n log(\n 'Clearing values stored under block numbers before %o',\n latestBlockNumber,\n );\n blockCache.clearBefore(latestBlockNumber);\n requestedBlockNumber = latestBlockNumber;\n } else {\n // We have a hex number\n requestedBlockNumber = blockTag;\n }\n // end on a hit, continue on a miss\n const cacheResult: Block | undefined = await strategy.get(\n req,\n requestedBlockNumber,\n );\n if (cacheResult === undefined) {\n // cache miss\n // wait for other middleware to handle request\n log(\n 'No cache stored under block number %o, carrying request forward',\n requestedBlockNumber,\n );\n await next();\n\n // add result to cache\n // it's safe to cast res.result as Block, due to runtime type checks\n // performed when strategy.set is called\n log('Populating cache with', res);\n await strategy.set(req, requestedBlockNumber, res.result as Block);\n } else {\n // fill in result from cache\n log(\n 'Cache hit, reusing cache result stored under block number %o',\n requestedBlockNumber,\n );\n res.result = cacheResult;\n }\n return undefined;\n },\n );\n}\n"]}
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createBlockRefRewriteMiddleware = void 0;
4
+ const json_rpc_engine_1 = require("@metamask/json-rpc-engine");
5
+ const cache_1 = require("./utils/cache.cjs");
6
+ function createBlockRefRewriteMiddleware({ blockTracker, } = {}) {
7
+ if (!blockTracker) {
8
+ throw Error('BlockRefRewriteMiddleware - mandatory "blockTracker" option is missing.');
9
+ }
10
+ return (0, json_rpc_engine_1.createAsyncMiddleware)(async (req, _res, next) => {
11
+ const blockRefIndex = (0, cache_1.blockTagParamIndex)(req.method);
12
+ // skip if method does not include blockRef
13
+ if (blockRefIndex === undefined) {
14
+ return next();
15
+ }
16
+ // skip if not "latest"
17
+ const blockRef = Array.isArray(req.params) && req.params[blockRefIndex]
18
+ ? req.params[blockRefIndex]
19
+ : // omitted blockRef implies "latest"
20
+ 'latest';
21
+ if (blockRef !== 'latest') {
22
+ return next();
23
+ }
24
+ // rewrite blockRef to block-tracker's block number
25
+ const latestBlockNumber = await blockTracker.getLatestBlock();
26
+ if (Array.isArray(req.params)) {
27
+ req.params[blockRefIndex] = latestBlockNumber;
28
+ }
29
+ return next();
30
+ });
31
+ }
32
+ exports.createBlockRefRewriteMiddleware = createBlockRefRewriteMiddleware;
33
+ //# sourceMappingURL=block-ref-rewrite.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block-ref-rewrite.cjs","sourceRoot":"","sources":["../src/block-ref-rewrite.ts"],"names":[],"mappings":";;;AAEA,+DAAkE;AAGlE,6CAAmD;AAMnD,SAAgB,+BAA+B,CAAC,EAC9C,YAAY,MACwB,EAAE;IAItC,IAAI,CAAC,YAAY,EAAE;QACjB,MAAM,KAAK,CACT,yEAAyE,CAC1E,CAAC;KACH;IAED,OAAO,IAAA,uCAAqB,EAAC,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;QACrD,MAAM,aAAa,GAAuB,IAAA,0BAAkB,EAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACzE,2CAA2C;QAC3C,IAAI,aAAa,KAAK,SAAS,EAAE;YAC/B,OAAO,IAAI,EAAE,CAAC;SACf;QACD,uBAAuB;QACvB,MAAM,QAAQ,GACZ,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC;YACpD,CAAC,CAAE,GAAG,CAAC,MAAM,CAAC,aAAa,CAAY;YACvC,CAAC,CAAC,oCAAoC;gBACpC,QAAQ,CAAC;QAEf,IAAI,QAAQ,KAAK,QAAQ,EAAE;YACzB,OAAO,IAAI,EAAE,CAAC;SACf;QACD,mDAAmD;QACnD,MAAM,iBAAiB,GAAG,MAAM,YAAY,CAAC,cAAc,EAAE,CAAC;QAC9D,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE;YAC7B,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,iBAAiB,CAAC;SAC/C;QACD,OAAO,IAAI,EAAE,CAAC;IAChB,CAAC,CAAC,CAAC;AACL,CAAC;AAnCD,0EAmCC","sourcesContent":["import type { PollingBlockTracker } from '@metamask/eth-block-tracker';\nimport type { JsonRpcMiddleware } from '@metamask/json-rpc-engine';\nimport { createAsyncMiddleware } from '@metamask/json-rpc-engine';\nimport type { Json, JsonRpcParams } from '@metamask/utils';\n\nimport { blockTagParamIndex } from './utils/cache';\n\ninterface BlockRefRewriteMiddlewareOptions {\n blockTracker?: PollingBlockTracker;\n}\n\nexport function createBlockRefRewriteMiddleware({\n blockTracker,\n}: BlockRefRewriteMiddlewareOptions = {}): JsonRpcMiddleware<\n JsonRpcParams,\n Json\n> {\n if (!blockTracker) {\n throw Error(\n 'BlockRefRewriteMiddleware - 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 \"latest\"\n const blockRef: string | undefined =\n Array.isArray(req.params) && req.params[blockRefIndex]\n ? (req.params[blockRefIndex] as string)\n : // omitted blockRef implies \"latest\"\n 'latest';\n\n if (blockRef !== 'latest') {\n return next();\n }\n // rewrite blockRef to block-tracker's block number\n const latestBlockNumber = await blockTracker.getLatestBlock();\n if (Array.isArray(req.params)) {\n req.params[blockRefIndex] = latestBlockNumber;\n }\n return next();\n });\n}\n"]}
@@ -0,0 +1,9 @@
1
+ import type { PollingBlockTracker } from "@metamask/eth-block-tracker";
2
+ import type { JsonRpcMiddleware } from "@metamask/json-rpc-engine";
3
+ import type { Json, JsonRpcParams } from "@metamask/utils";
4
+ interface BlockRefRewriteMiddlewareOptions {
5
+ blockTracker?: PollingBlockTracker;
6
+ }
7
+ export declare function createBlockRefRewriteMiddleware({ blockTracker, }?: BlockRefRewriteMiddlewareOptions): JsonRpcMiddleware<JsonRpcParams, Json>;
8
+ export {};
9
+ //# sourceMappingURL=block-ref-rewrite.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block-ref-rewrite.d.cts","sourceRoot":"","sources":["../src/block-ref-rewrite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,oCAAoC;AACvE,OAAO,KAAK,EAAE,iBAAiB,EAAE,kCAAkC;AAEnE,OAAO,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,wBAAwB;AAI3D,UAAU,gCAAgC;IACxC,YAAY,CAAC,EAAE,mBAAmB,CAAC;CACpC;AAED,wBAAgB,+BAA+B,CAAC,EAC9C,YAAY,GACb,GAAE,gCAAqC,GAAG,iBAAiB,CAC1D,aAAa,EACb,IAAI,CACL,CA8BA"}
@@ -0,0 +1,9 @@
1
+ import type { PollingBlockTracker } from "@metamask/eth-block-tracker";
2
+ import type { JsonRpcMiddleware } from "@metamask/json-rpc-engine";
3
+ import type { Json, JsonRpcParams } from "@metamask/utils";
4
+ interface BlockRefRewriteMiddlewareOptions {
5
+ blockTracker?: PollingBlockTracker;
6
+ }
7
+ export declare function createBlockRefRewriteMiddleware({ blockTracker, }?: BlockRefRewriteMiddlewareOptions): JsonRpcMiddleware<JsonRpcParams, Json>;
8
+ export {};
9
+ //# sourceMappingURL=block-ref-rewrite.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block-ref-rewrite.d.mts","sourceRoot":"","sources":["../src/block-ref-rewrite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,oCAAoC;AACvE,OAAO,KAAK,EAAE,iBAAiB,EAAE,kCAAkC;AAEnE,OAAO,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,wBAAwB;AAI3D,UAAU,gCAAgC;IACxC,YAAY,CAAC,EAAE,mBAAmB,CAAC;CACpC;AAED,wBAAgB,+BAA+B,CAAC,EAC9C,YAAY,GACb,GAAE,gCAAqC,GAAG,iBAAiB,CAC1D,aAAa,EACb,IAAI,CACL,CA8BA"}
@@ -0,0 +1,29 @@
1
+ import { createAsyncMiddleware } from "@metamask/json-rpc-engine";
2
+ import { blockTagParamIndex } from "./utils/cache.mjs";
3
+ export function createBlockRefRewriteMiddleware({ blockTracker, } = {}) {
4
+ if (!blockTracker) {
5
+ throw Error('BlockRefRewriteMiddleware - mandatory "blockTracker" option is missing.');
6
+ }
7
+ return createAsyncMiddleware(async (req, _res, next) => {
8
+ const blockRefIndex = blockTagParamIndex(req.method);
9
+ // skip if method does not include blockRef
10
+ if (blockRefIndex === undefined) {
11
+ return next();
12
+ }
13
+ // skip if not "latest"
14
+ const blockRef = Array.isArray(req.params) && req.params[blockRefIndex]
15
+ ? req.params[blockRefIndex]
16
+ : // omitted blockRef implies "latest"
17
+ 'latest';
18
+ if (blockRef !== 'latest') {
19
+ return next();
20
+ }
21
+ // rewrite blockRef to block-tracker's block number
22
+ const latestBlockNumber = await blockTracker.getLatestBlock();
23
+ if (Array.isArray(req.params)) {
24
+ req.params[blockRefIndex] = latestBlockNumber;
25
+ }
26
+ return next();
27
+ });
28
+ }
29
+ //# sourceMappingURL=block-ref-rewrite.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block-ref-rewrite.mjs","sourceRoot":"","sources":["../src/block-ref-rewrite.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAE,kCAAkC;AAGlE,OAAO,EAAE,kBAAkB,EAAE,0BAAsB;AAMnD,MAAM,UAAU,+BAA+B,CAAC,EAC9C,YAAY,MACwB,EAAE;IAItC,IAAI,CAAC,YAAY,EAAE;QACjB,MAAM,KAAK,CACT,yEAAyE,CAC1E,CAAC;KACH;IAED,OAAO,qBAAqB,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;QACrD,MAAM,aAAa,GAAuB,kBAAkB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACzE,2CAA2C;QAC3C,IAAI,aAAa,KAAK,SAAS,EAAE;YAC/B,OAAO,IAAI,EAAE,CAAC;SACf;QACD,uBAAuB;QACvB,MAAM,QAAQ,GACZ,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC;YACpD,CAAC,CAAE,GAAG,CAAC,MAAM,CAAC,aAAa,CAAY;YACvC,CAAC,CAAC,oCAAoC;gBACpC,QAAQ,CAAC;QAEf,IAAI,QAAQ,KAAK,QAAQ,EAAE;YACzB,OAAO,IAAI,EAAE,CAAC;SACf;QACD,mDAAmD;QACnD,MAAM,iBAAiB,GAAG,MAAM,YAAY,CAAC,cAAc,EAAE,CAAC;QAC9D,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE;YAC7B,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,iBAAiB,CAAC;SAC/C;QACD,OAAO,IAAI,EAAE,CAAC;IAChB,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import type { PollingBlockTracker } from '@metamask/eth-block-tracker';\nimport type { JsonRpcMiddleware } from '@metamask/json-rpc-engine';\nimport { createAsyncMiddleware } from '@metamask/json-rpc-engine';\nimport type { Json, JsonRpcParams } from '@metamask/utils';\n\nimport { blockTagParamIndex } from './utils/cache';\n\ninterface BlockRefRewriteMiddlewareOptions {\n blockTracker?: PollingBlockTracker;\n}\n\nexport function createBlockRefRewriteMiddleware({\n blockTracker,\n}: BlockRefRewriteMiddlewareOptions = {}): JsonRpcMiddleware<\n JsonRpcParams,\n Json\n> {\n if (!blockTracker) {\n throw Error(\n 'BlockRefRewriteMiddleware - 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 \"latest\"\n const blockRef: string | undefined =\n Array.isArray(req.params) && req.params[blockRefIndex]\n ? (req.params[blockRefIndex] as string)\n : // omitted blockRef implies \"latest\"\n 'latest';\n\n if (blockRef !== 'latest') {\n return next();\n }\n // rewrite blockRef to block-tracker's block number\n const latestBlockNumber = await blockTracker.getLatestBlock();\n if (Array.isArray(req.params)) {\n req.params[blockRefIndex] = latestBlockNumber;\n }\n return next();\n });\n}\n"]}
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createBlockRefMiddleware = 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 log = (0, logging_utils_1.createModuleLogger)(logging_utils_1.projectLogger, 'block-ref');
9
+ function createBlockRefMiddleware({ provider, blockTracker, } = {}) {
10
+ if (!provider) {
11
+ throw Error('BlockRefMiddleware - mandatory "provider" option is missing.');
12
+ }
13
+ if (!blockTracker) {
14
+ throw Error('BlockRefMiddleware - mandatory "blockTracker" option is missing.');
15
+ }
16
+ return (0, json_rpc_engine_1.createAsyncMiddleware)(async (req, res, next) => {
17
+ const blockRefIndex = (0, cache_1.blockTagParamIndex)(req.method);
18
+ // skip if method does not include blockRef
19
+ if (blockRefIndex === undefined) {
20
+ return next();
21
+ }
22
+ const blockRef = Array.isArray(req.params)
23
+ ? (req.params[blockRefIndex] ?? 'latest')
24
+ : 'latest';
25
+ // skip if not "latest"
26
+ if (blockRef !== 'latest') {
27
+ log('blockRef is not "latest", carrying request forward');
28
+ return next();
29
+ }
30
+ // lookup latest block
31
+ const latestBlockNumber = await blockTracker.getLatestBlock();
32
+ log(`blockRef is "latest", setting param ${blockRefIndex} to latest block ${latestBlockNumber}`);
33
+ // create child request with specific block-ref
34
+ const childRequest = (0, full_1.klona)(req);
35
+ if (Array.isArray(childRequest.params)) {
36
+ childRequest.params[blockRefIndex] = latestBlockNumber;
37
+ }
38
+ // perform child request
39
+ log('Performing another request %o', childRequest);
40
+ // copy child result onto original response
41
+ res.result = await provider.request(childRequest);
42
+ return undefined;
43
+ });
44
+ }
45
+ exports.createBlockRefMiddleware = createBlockRefMiddleware;
46
+ //# sourceMappingURL=block-ref.cjs.map