@metamask-previews/sentinel-api-service 0.0.0-preview-d34fa10c8

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 (70) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/LICENSE +6 -0
  3. package/LICENSE.APACHE2 +201 -0
  4. package/LICENSE.MIT +21 -0
  5. package/README.md +15 -0
  6. package/dist/constants.cjs +71 -0
  7. package/dist/constants.cjs.map +1 -0
  8. package/dist/constants.d.cts +63 -0
  9. package/dist/constants.d.cts.map +1 -0
  10. package/dist/constants.d.mts +63 -0
  11. package/dist/constants.d.mts.map +1 -0
  12. package/dist/constants.mjs +68 -0
  13. package/dist/constants.mjs.map +1 -0
  14. package/dist/errors.cjs +43 -0
  15. package/dist/errors.cjs.map +1 -0
  16. package/dist/errors.d.cts +27 -0
  17. package/dist/errors.d.cts.map +1 -0
  18. package/dist/errors.d.mts +27 -0
  19. package/dist/errors.d.mts.map +1 -0
  20. package/dist/errors.mjs +37 -0
  21. package/dist/errors.mjs.map +1 -0
  22. package/dist/index.cjs +17 -0
  23. package/dist/index.cjs.map +1 -0
  24. package/dist/index.d.cts +7 -0
  25. package/dist/index.d.cts.map +1 -0
  26. package/dist/index.d.mts +7 -0
  27. package/dist/index.d.mts.map +1 -0
  28. package/dist/index.mjs +5 -0
  29. package/dist/index.mjs.map +1 -0
  30. package/dist/logger.cjs +7 -0
  31. package/dist/logger.cjs.map +1 -0
  32. package/dist/logger.d.cts +5 -0
  33. package/dist/logger.d.cts.map +1 -0
  34. package/dist/logger.d.mts +5 -0
  35. package/dist/logger.d.mts.map +1 -0
  36. package/dist/logger.mjs +4 -0
  37. package/dist/logger.mjs.map +1 -0
  38. package/dist/sentinel-api-service-method-action-types.cjs +7 -0
  39. package/dist/sentinel-api-service-method-action-types.cjs.map +1 -0
  40. package/dist/sentinel-api-service-method-action-types.d.cts +62 -0
  41. package/dist/sentinel-api-service-method-action-types.d.cts.map +1 -0
  42. package/dist/sentinel-api-service-method-action-types.d.mts +62 -0
  43. package/dist/sentinel-api-service-method-action-types.d.mts.map +1 -0
  44. package/dist/sentinel-api-service-method-action-types.mjs +6 -0
  45. package/dist/sentinel-api-service-method-action-types.mjs.map +1 -0
  46. package/dist/sentinel-api-service.cjs +316 -0
  47. package/dist/sentinel-api-service.cjs.map +1 -0
  48. package/dist/sentinel-api-service.d.cts +90 -0
  49. package/dist/sentinel-api-service.d.cts.map +1 -0
  50. package/dist/sentinel-api-service.d.mts +90 -0
  51. package/dist/sentinel-api-service.d.mts.map +1 -0
  52. package/dist/sentinel-api-service.mjs +312 -0
  53. package/dist/sentinel-api-service.mjs.map +1 -0
  54. package/dist/structs.cjs +58 -0
  55. package/dist/structs.cjs.map +1 -0
  56. package/dist/structs.d.cts +63 -0
  57. package/dist/structs.d.cts.map +1 -0
  58. package/dist/structs.d.mts +63 -0
  59. package/dist/structs.d.mts.map +1 -0
  60. package/dist/structs.mjs +55 -0
  61. package/dist/structs.mjs.map +1 -0
  62. package/dist/types.cjs +36 -0
  63. package/dist/types.cjs.map +1 -0
  64. package/dist/types.d.cts +420 -0
  65. package/dist/types.d.cts.map +1 -0
  66. package/dist/types.d.mts +420 -0
  67. package/dist/types.d.mts.map +1 -0
  68. package/dist/types.mjs +33 -0
  69. package/dist/types.mjs.map +1 -0
  70. package/package.json +79 -0
@@ -0,0 +1,312 @@
1
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
2
+ if (kind === "m") throw new TypeError("Private method is not writable");
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
4
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
5
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
6
+ };
7
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
9
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
+ };
12
+ var _SentinelApiService_instances, _SentinelApiService_fetch, _SentinelApiService_clientId, _SentinelApiService_clientVersion, _SentinelApiService_environmentDomain, _SentinelApiService_jsonRpc, _SentinelApiService_getHeaders, _SentinelApiService_resolveUrl, _SentinelApiService_getNetwork, _SentinelApiService_buildUrl;
13
+ import { BaseDataService } from "@metamask/base-data-service";
14
+ import { handleWhen, HttpError } from "@metamask/controller-utils";
15
+ import { validate } from "@metamask/superstruct";
16
+ import { BASE_URL_TEMPLATE, DEFAULT_ENVIRONMENT, ENDPOINT_NETWORKS, ENDPOINT_SMART_TRANSACTIONS, ENVIRONMENT_DOMAIN, NETWORKS_STALE_TIME_MS, NETWORKS_SUBDOMAIN, RPC_METHOD_SEND_RELAY, RPC_METHOD_SIMULATE, serviceName } from "./constants.mjs";
17
+ import { SentinelApiResponseValidationError, SentinelChainNotSupportedError, SentinelJsonRpcError } from "./errors.mjs";
18
+ import { projectLogger, createModuleLogger } from "./logger.mjs";
19
+ import { SentinelSmartTransactionResponseStruct, SentinelNetworkRegistryStruct, SentinelRelaySubmitResponseStruct, SentinelSimulationResponseStruct } from "./structs.mjs";
20
+ const log = createModuleLogger(projectLogger, serviceName);
21
+ const MESSENGER_EXPOSED_METHODS = [
22
+ 'getNetworks',
23
+ 'simulateTransactions',
24
+ 'submitRelayTransaction',
25
+ 'getSmartTransaction',
26
+ ];
27
+ /**
28
+ * Data service that centralises all interactions with the MetaMask Sentinel
29
+ * API (`tx-sentinel-<network>.api.cx.metamask.io`).
30
+ *
31
+ * It exposes one method per Sentinel endpoint:
32
+ * - {@link SentinelApiService.getNetworks} — the supported-network registry
33
+ * (`/networks`), cached since it is stable and identical across subdomains.
34
+ * - {@link SentinelApiService.simulateTransactions} — transaction simulation
35
+ * (`infura_simulateTransactions`), used by `@metamask/transaction-controller`
36
+ * and `@metamask/transaction-pay-controller`.
37
+ * - {@link SentinelApiService.submitRelayTransaction} — gas station relay
38
+ * submission (`eth_sendRelayTransaction`), used by the extension and mobile.
39
+ * - {@link SentinelApiService.getSmartTransaction} — smart-transaction status
40
+ * lookup (`/smart-transactions/{uuid}`).
41
+ *
42
+ * Consumers derive higher-level concerns (whether a chain supports simulation
43
+ * or relay, polling loops, etc.) from the raw endpoint responses.
44
+ */
45
+ export class SentinelApiService extends BaseDataService {
46
+ /**
47
+ * Constructs a new SentinelApiService.
48
+ *
49
+ * @param options - The constructor options. See
50
+ * {@link SentinelApiServiceOptions}.
51
+ * @param options.messenger - The messenger suited for this service.
52
+ * @param options.fetch - The `fetch` function to use for requests. Defaults
53
+ * to the global `fetch`.
54
+ * @param options.environment - The Sentinel API environment to target
55
+ * (`dev`, `uat`, or `prod`). Defaults to `prod`.
56
+ * @param options.clientId - Identifier for the calling client (for example
57
+ * `extension` or `mobile`), sent as the `X-Client-Id` header.
58
+ * @param options.clientVersion - Version of the calling client, sent as the
59
+ * `X-Client-Version` header when provided.
60
+ * @param options.queryClientConfig - Configuration for the underlying
61
+ * TanStack Query client.
62
+ * @param options.policyOptions - Options to pass to `createServicePolicy`.
63
+ * Retries are disabled by default (`maxRetries: 0`) to preserve the
64
+ * single-attempt behaviour of the clients this service replaces; pass
65
+ * `maxRetries` here to opt in.
66
+ */
67
+ constructor({ messenger, fetch: fetchFunction = globalThis.fetch, environment = DEFAULT_ENVIRONMENT, clientId, clientVersion, queryClientConfig = {}, policyOptions = {}, }) {
68
+ super({
69
+ name: serviceName,
70
+ messenger,
71
+ queryClientConfig,
72
+ policyOptions: {
73
+ // Disable retries by default so the service is behaviourally
74
+ // backwards-compatible with the single-request clients it replaces.
75
+ // Callers can override via `policyOptions.maxRetries`.
76
+ maxRetries: 0,
77
+ retryFilterPolicy: handleWhen((error) => !(error instanceof SentinelApiResponseValidationError) &&
78
+ !(error instanceof SentinelChainNotSupportedError) &&
79
+ !(error instanceof SentinelJsonRpcError)),
80
+ ...policyOptions,
81
+ },
82
+ });
83
+ _SentinelApiService_instances.add(this);
84
+ _SentinelApiService_fetch.set(this, void 0);
85
+ _SentinelApiService_clientId.set(this, void 0);
86
+ _SentinelApiService_clientVersion.set(this, void 0);
87
+ _SentinelApiService_environmentDomain.set(this, void 0);
88
+ __classPrivateFieldSet(this, _SentinelApiService_fetch, fetchFunction, "f");
89
+ __classPrivateFieldSet(this, _SentinelApiService_environmentDomain, ENVIRONMENT_DOMAIN[environment], "f");
90
+ __classPrivateFieldSet(this, _SentinelApiService_clientId, clientId, "f");
91
+ __classPrivateFieldSet(this, _SentinelApiService_clientVersion, clientVersion, "f");
92
+ this.messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
93
+ log('Initialized');
94
+ }
95
+ /**
96
+ * Fetches the Sentinel supported-network registry. The result is cached, as
97
+ * the registry is stable and identical across network subdomains.
98
+ *
99
+ * @returns The network registry, keyed by decimal chain ID.
100
+ */
101
+ async getNetworks() {
102
+ const url = `${__classPrivateFieldGet(this, _SentinelApiService_instances, "m", _SentinelApiService_buildUrl).call(this, NETWORKS_SUBDOMAIN)}${ENDPOINT_NETWORKS}`;
103
+ const result = await this.fetchQuery({
104
+ queryKey: [`${this.name}:getNetworks`],
105
+ staleTime: NETWORKS_STALE_TIME_MS,
106
+ queryFn: async () => {
107
+ const headers = await __classPrivateFieldGet(this, _SentinelApiService_instances, "m", _SentinelApiService_getHeaders).call(this);
108
+ log('getNetworks', 'Request', url);
109
+ const response = await __classPrivateFieldGet(this, _SentinelApiService_fetch, "f").call(this, url, { headers });
110
+ if (!response.ok) {
111
+ throw new HttpError(response.status, `Sentinel API: Networks request failed with status '${response.status}'`);
112
+ }
113
+ const json = await response.json();
114
+ const [error] = validate(json, SentinelNetworkRegistryStruct);
115
+ if (error) {
116
+ throw new SentinelApiResponseValidationError(`Sentinel API: Malformed response from networks endpoint: ${error.message}`);
117
+ }
118
+ log('getNetworks', 'Response', json);
119
+ return json;
120
+ },
121
+ });
122
+ return result;
123
+ }
124
+ /**
125
+ * Simulates transactions against the Sentinel API via
126
+ * `infura_simulateTransactions`. Not cached, since each request body is
127
+ * unique and stale simulations must not be reused.
128
+ *
129
+ * @param chainId - The chain ID to simulate on.
130
+ * @param request - The simulation request.
131
+ * @param options - Additional options.
132
+ * @param options.getUrl - Optional callback that receives the default
133
+ * Sentinel URL resolved for the chain and returns the URL to use instead.
134
+ * Lets consumers rewrite the request URL (for example to route through the
135
+ * MetaMask Shield proxy) without the service knowing about those concerns.
136
+ * @returns The simulation response.
137
+ */
138
+ async simulateTransactions(chainId, request, options = {}) {
139
+ const defaultUrl = await __classPrivateFieldGet(this, _SentinelApiService_instances, "m", _SentinelApiService_resolveUrl).call(this, chainId, 'confirmations');
140
+ const url = options.getUrl ? await options.getUrl(defaultUrl) : defaultUrl;
141
+ const result = await this.fetchQuery({
142
+ queryKey: [`${this.name}:simulateTransactions`, chainId, request, url],
143
+ staleTime: 0,
144
+ queryFn: async () => {
145
+ log('simulateTransactions', 'Request', url, request);
146
+ const rpcResult = await __classPrivateFieldGet(this, _SentinelApiService_instances, "m", _SentinelApiService_jsonRpc).call(this, url, RPC_METHOD_SIMULATE, [
147
+ request,
148
+ ]);
149
+ const [error] = validate(rpcResult, SentinelSimulationResponseStruct);
150
+ if (error) {
151
+ throw new SentinelApiResponseValidationError(`Sentinel API: Malformed response from simulation endpoint: ${error.message}`);
152
+ }
153
+ log('simulateTransactions', 'Response', rpcResult);
154
+ return rpcResult;
155
+ },
156
+ });
157
+ return result;
158
+ }
159
+ /**
160
+ * Submits a signed relay (gas station) transaction to the Sentinel API via
161
+ * `eth_sendRelayTransaction`. Not cached.
162
+ *
163
+ * @param request - The relay submit request.
164
+ * @returns The relay submit response containing the tracking UUID.
165
+ */
166
+ async submitRelayTransaction(request) {
167
+ const url = await __classPrivateFieldGet(this, _SentinelApiService_instances, "m", _SentinelApiService_resolveUrl).call(this, request.chainId, 'relayTransactions');
168
+ const result = await this.fetchQuery({
169
+ queryKey: [`${this.name}:submitRelayTransaction`, request],
170
+ staleTime: 0,
171
+ queryFn: async () => {
172
+ log('submitRelayTransaction', 'Request', url, request);
173
+ const rpcResult = await __classPrivateFieldGet(this, _SentinelApiService_instances, "m", _SentinelApiService_jsonRpc).call(this, url, RPC_METHOD_SEND_RELAY, [
174
+ request,
175
+ ]);
176
+ const [error] = validate(rpcResult, SentinelRelaySubmitResponseStruct);
177
+ if (error) {
178
+ throw new SentinelApiResponseValidationError(`Sentinel API: Malformed response from relay submit endpoint: ${error.message}`);
179
+ }
180
+ log('submitRelayTransaction', 'Response', rpcResult);
181
+ return rpcResult;
182
+ },
183
+ });
184
+ return result;
185
+ }
186
+ /**
187
+ * Looks up the state of a submitted smart transaction by UUID against the
188
+ * `/smart-transactions/{uuid}` endpoint. Performs a single request; callers
189
+ * own any polling loop. Not cached.
190
+ *
191
+ * @param request - The smart-transaction lookup request.
192
+ * @returns The response envelope containing the smart transaction(s)
193
+ * associated with the requested UUID.
194
+ */
195
+ async getSmartTransaction(request) {
196
+ const { chainId, uuid } = request;
197
+ const baseUrl = await __classPrivateFieldGet(this, _SentinelApiService_instances, "m", _SentinelApiService_resolveUrl).call(this, chainId, 'relayTransactions');
198
+ const url = `${baseUrl}${ENDPOINT_SMART_TRANSACTIONS}/${uuid}`;
199
+ const result = await this.fetchQuery({
200
+ queryKey: [`${this.name}:getSmartTransaction`, chainId, uuid],
201
+ staleTime: 0,
202
+ queryFn: async () => {
203
+ const headers = await __classPrivateFieldGet(this, _SentinelApiService_instances, "m", _SentinelApiService_getHeaders).call(this);
204
+ log('getSmartTransaction', 'Request', url);
205
+ const response = await __classPrivateFieldGet(this, _SentinelApiService_fetch, "f").call(this, url, { headers });
206
+ if (!response.ok) {
207
+ throw new HttpError(response.status, `Sentinel API: Smart-transaction request failed with status '${response.status}'`);
208
+ }
209
+ const json = await response.json();
210
+ const [error] = validate(json, SentinelSmartTransactionResponseStruct);
211
+ if (error) {
212
+ throw new SentinelApiResponseValidationError(`Sentinel API: Malformed response from smart-transactions endpoint: ${error.message}`);
213
+ }
214
+ log('getSmartTransaction', 'Response', json);
215
+ return json;
216
+ },
217
+ });
218
+ return result;
219
+ }
220
+ }
221
+ _SentinelApiService_fetch = new WeakMap(), _SentinelApiService_clientId = new WeakMap(), _SentinelApiService_clientVersion = new WeakMap(), _SentinelApiService_environmentDomain = new WeakMap(), _SentinelApiService_instances = new WeakSet(), _SentinelApiService_jsonRpc =
222
+ /**
223
+ * Performs a JSON-RPC POST to the Sentinel API and returns the `result`.
224
+ *
225
+ * @param url - The URL to post to.
226
+ * @param method - The JSON-RPC method name.
227
+ * @param params - The JSON-RPC params.
228
+ * @returns The `result` field of the JSON-RPC response.
229
+ */
230
+ async function _SentinelApiService_jsonRpc(url, method, params) {
231
+ const headers = await __classPrivateFieldGet(this, _SentinelApiService_instances, "m", _SentinelApiService_getHeaders).call(this);
232
+ const response = await __classPrivateFieldGet(this, _SentinelApiService_fetch, "f").call(this, url, {
233
+ method: 'POST',
234
+ headers: {
235
+ ...headers,
236
+ 'Content-Type': 'application/json',
237
+ },
238
+ body: JSON.stringify({
239
+ id: '1',
240
+ jsonrpc: '2.0',
241
+ method,
242
+ params,
243
+ }),
244
+ });
245
+ if (!response.ok) {
246
+ throw new HttpError(response.status, `Sentinel API: JSON-RPC request '${method}' failed with status '${response.status}'`);
247
+ }
248
+ const responseJson = (await response.json());
249
+ if (responseJson.error) {
250
+ const { code, message } = responseJson.error;
251
+ throw new SentinelJsonRpcError(`Sentinel API: JSON-RPC error: ${message}`, code);
252
+ }
253
+ if (responseJson.result === undefined) {
254
+ throw new SentinelJsonRpcError('Sentinel API: JSON-RPC response missing result', -32603);
255
+ }
256
+ return responseJson.result;
257
+ }, _SentinelApiService_getHeaders =
258
+ /**
259
+ * Builds the outbound headers for a Sentinel request: the client identity
260
+ * headers plus a best-effort `Authorization` bearer token. Token retrieval
261
+ * failures are swallowed so unauthenticated requests still proceed.
262
+ *
263
+ * @returns The headers to attach to the request.
264
+ */
265
+ async function _SentinelApiService_getHeaders() {
266
+ const headers = {};
267
+ if (__classPrivateFieldGet(this, _SentinelApiService_clientId, "f") !== undefined) {
268
+ headers['X-Client-Id'] = __classPrivateFieldGet(this, _SentinelApiService_clientId, "f");
269
+ }
270
+ if (__classPrivateFieldGet(this, _SentinelApiService_clientVersion, "f") !== undefined) {
271
+ headers['X-Client-Version'] = __classPrivateFieldGet(this, _SentinelApiService_clientVersion, "f");
272
+ }
273
+ try {
274
+ const token = await this.messenger.call('AuthenticationController:getBearerToken');
275
+ if (token) {
276
+ headers.Authorization = `Bearer ${token}`;
277
+ }
278
+ }
279
+ catch (error) {
280
+ // Proceed without auth if token retrieval fails.
281
+ log('getHeaders', 'Auth token unavailable, proceeding unauthenticated', error);
282
+ }
283
+ return headers;
284
+ }, _SentinelApiService_resolveUrl =
285
+ /**
286
+ * Resolves the Sentinel URL for a chain, asserting the required capability.
287
+ *
288
+ * @param chainId - The chain ID to resolve a URL for.
289
+ * @param capability - The capability flag that must be enabled.
290
+ * @returns The resolved base URL.
291
+ */
292
+ async function _SentinelApiService_resolveUrl(chainId, capability) {
293
+ const network = await __classPrivateFieldGet(this, _SentinelApiService_instances, "m", _SentinelApiService_getNetwork).call(this, chainId);
294
+ if (!network?.[capability]) {
295
+ throw new SentinelChainNotSupportedError(chainId, capability);
296
+ }
297
+ return __classPrivateFieldGet(this, _SentinelApiService_instances, "m", _SentinelApiService_buildUrl).call(this, network.network);
298
+ }, _SentinelApiService_getNetwork =
299
+ /**
300
+ * Looks up a single network entry from the registry by chain ID.
301
+ *
302
+ * @param chainId - The chain ID to look up.
303
+ * @returns The network entry, or undefined if not present.
304
+ */
305
+ async function _SentinelApiService_getNetwork(chainId) {
306
+ const registry = await this.getNetworks();
307
+ const chainIdDecimal = BigInt(chainId).toString(10);
308
+ return registry[chainIdDecimal];
309
+ }, _SentinelApiService_buildUrl = function _SentinelApiService_buildUrl(subdomain) {
310
+ return BASE_URL_TEMPLATE.replace('{0}', subdomain).replace('{1}', __classPrivateFieldGet(this, _SentinelApiService_environmentDomain, "f"));
311
+ };
312
+ //# sourceMappingURL=sentinel-api-service.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sentinel-api-service.mjs","sourceRoot":"","sources":["../src/sentinel-api-service.ts"],"names":[],"mappings":"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","sourcesContent":["import { BaseDataService } from '@metamask/base-data-service';\nimport { handleWhen, HttpError } from '@metamask/controller-utils';\nimport { validate } from '@metamask/superstruct';\nimport type { Hex, Json } from '@metamask/utils';\n\nimport {\n BASE_URL_TEMPLATE,\n DEFAULT_ENVIRONMENT,\n ENDPOINT_NETWORKS,\n ENDPOINT_SMART_TRANSACTIONS,\n ENVIRONMENT_DOMAIN,\n NETWORKS_STALE_TIME_MS,\n NETWORKS_SUBDOMAIN,\n RPC_METHOD_SEND_RELAY,\n RPC_METHOD_SIMULATE,\n serviceName,\n} from './constants';\nimport {\n SentinelApiResponseValidationError,\n SentinelChainNotSupportedError,\n SentinelJsonRpcError,\n} from './errors';\nimport { projectLogger, createModuleLogger } from './logger';\nimport {\n SentinelSmartTransactionResponseStruct,\n SentinelNetworkRegistryStruct,\n SentinelRelaySubmitResponseStruct,\n SentinelSimulationResponseStruct,\n} from './structs';\nimport type {\n SentinelApiServiceMessenger,\n SentinelApiServiceOptions,\n SentinelNetwork,\n SentinelNetworkRegistry,\n SentinelRelaySubmitRequest,\n SentinelRelaySubmitResponse,\n SentinelSimulationRequest,\n SentinelSimulationResponse,\n SentinelSmartTransactionRequest,\n SentinelSmartTransactionResponse,\n} from './types';\n\nconst log = createModuleLogger(projectLogger, serviceName);\n\nconst MESSENGER_EXPOSED_METHODS = [\n 'getNetworks',\n 'simulateTransactions',\n 'submitRelayTransaction',\n 'getSmartTransaction',\n] as const;\n\n/**\n * Data service that centralises all interactions with the MetaMask Sentinel\n * API (`tx-sentinel-<network>.api.cx.metamask.io`).\n *\n * It exposes one method per Sentinel endpoint:\n * - {@link SentinelApiService.getNetworks} — the supported-network registry\n * (`/networks`), cached since it is stable and identical across subdomains.\n * - {@link SentinelApiService.simulateTransactions} — transaction simulation\n * (`infura_simulateTransactions`), used by `@metamask/transaction-controller`\n * and `@metamask/transaction-pay-controller`.\n * - {@link SentinelApiService.submitRelayTransaction} — gas station relay\n * submission (`eth_sendRelayTransaction`), used by the extension and mobile.\n * - {@link SentinelApiService.getSmartTransaction} — smart-transaction status\n * lookup (`/smart-transactions/{uuid}`).\n *\n * Consumers derive higher-level concerns (whether a chain supports simulation\n * or relay, polling loops, etc.) from the raw endpoint responses.\n */\nexport class SentinelApiService extends BaseDataService<\n typeof serviceName,\n SentinelApiServiceMessenger\n> {\n readonly #fetch: typeof fetch;\n\n readonly #clientId?: string;\n\n readonly #clientVersion?: string;\n\n readonly #environmentDomain: string;\n\n /**\n * Constructs a new SentinelApiService.\n *\n * @param options - The constructor options. See\n * {@link SentinelApiServiceOptions}.\n * @param options.messenger - The messenger suited for this service.\n * @param options.fetch - The `fetch` function to use for requests. Defaults\n * to the global `fetch`.\n * @param options.environment - The Sentinel API environment to target\n * (`dev`, `uat`, or `prod`). Defaults to `prod`.\n * @param options.clientId - Identifier for the calling client (for example\n * `extension` or `mobile`), sent as the `X-Client-Id` header.\n * @param options.clientVersion - Version of the calling client, sent as the\n * `X-Client-Version` header when provided.\n * @param options.queryClientConfig - Configuration for the underlying\n * TanStack Query client.\n * @param options.policyOptions - Options to pass to `createServicePolicy`.\n * Retries are disabled by default (`maxRetries: 0`) to preserve the\n * single-attempt behaviour of the clients this service replaces; pass\n * `maxRetries` here to opt in.\n */\n constructor({\n messenger,\n fetch: fetchFunction = globalThis.fetch,\n environment = DEFAULT_ENVIRONMENT,\n clientId,\n clientVersion,\n queryClientConfig = {},\n policyOptions = {},\n }: SentinelApiServiceOptions) {\n super({\n name: serviceName,\n messenger,\n queryClientConfig,\n policyOptions: {\n // Disable retries by default so the service is behaviourally\n // backwards-compatible with the single-request clients it replaces.\n // Callers can override via `policyOptions.maxRetries`.\n maxRetries: 0,\n retryFilterPolicy: handleWhen(\n (error) =>\n !(error instanceof SentinelApiResponseValidationError) &&\n !(error instanceof SentinelChainNotSupportedError) &&\n !(error instanceof SentinelJsonRpcError),\n ),\n ...policyOptions,\n },\n });\n\n this.#fetch = fetchFunction;\n this.#environmentDomain = ENVIRONMENT_DOMAIN[environment];\n this.#clientId = clientId;\n this.#clientVersion = clientVersion;\n\n this.messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n\n log('Initialized');\n }\n\n /**\n * Fetches the Sentinel supported-network registry. The result is cached, as\n * the registry is stable and identical across network subdomains.\n *\n * @returns The network registry, keyed by decimal chain ID.\n */\n async getNetworks(): Promise<SentinelNetworkRegistry> {\n const url = `${this.#buildUrl(NETWORKS_SUBDOMAIN)}${ENDPOINT_NETWORKS}`;\n\n const result = await this.fetchQuery({\n queryKey: [`${this.name}:getNetworks`],\n staleTime: NETWORKS_STALE_TIME_MS,\n queryFn: async (): Promise<Json> => {\n const headers = await this.#getHeaders();\n\n log('getNetworks', 'Request', url);\n\n const response = await this.#fetch(url, { headers });\n\n if (!response.ok) {\n throw new HttpError(\n response.status,\n `Sentinel API: Networks request failed with status '${response.status}'`,\n );\n }\n\n const json: Json = await response.json();\n\n const [error] = validate(json, SentinelNetworkRegistryStruct);\n if (error) {\n throw new SentinelApiResponseValidationError(\n `Sentinel API: Malformed response from networks endpoint: ${error.message}`,\n );\n }\n\n log('getNetworks', 'Response', json);\n\n return json;\n },\n });\n\n return result as unknown as SentinelNetworkRegistry;\n }\n\n /**\n * Simulates transactions against the Sentinel API via\n * `infura_simulateTransactions`. Not cached, since each request body is\n * unique and stale simulations must not be reused.\n *\n * @param chainId - The chain ID to simulate on.\n * @param request - The simulation request.\n * @param options - Additional options.\n * @param options.getUrl - Optional callback that receives the default\n * Sentinel URL resolved for the chain and returns the URL to use instead.\n * Lets consumers rewrite the request URL (for example to route through the\n * MetaMask Shield proxy) without the service knowing about those concerns.\n * @returns The simulation response.\n */\n async simulateTransactions(\n chainId: Hex,\n request: SentinelSimulationRequest,\n options: { getUrl?: (defaultUrl: string) => string | Promise<string> } = {},\n ): Promise<SentinelSimulationResponse> {\n const defaultUrl = await this.#resolveUrl(chainId, 'confirmations');\n const url = options.getUrl ? await options.getUrl(defaultUrl) : defaultUrl;\n\n const result = await this.fetchQuery({\n queryKey: [`${this.name}:simulateTransactions`, chainId, request, url],\n staleTime: 0,\n queryFn: async (): Promise<Json> => {\n log('simulateTransactions', 'Request', url, request);\n\n const rpcResult = await this.#jsonRpc(url, RPC_METHOD_SIMULATE, [\n request,\n ]);\n\n const [error] = validate(rpcResult, SentinelSimulationResponseStruct);\n if (error) {\n throw new SentinelApiResponseValidationError(\n `Sentinel API: Malformed response from simulation endpoint: ${error.message}`,\n );\n }\n\n log('simulateTransactions', 'Response', rpcResult);\n\n return rpcResult;\n },\n });\n\n return result as unknown as SentinelSimulationResponse;\n }\n\n /**\n * Submits a signed relay (gas station) transaction to the Sentinel API via\n * `eth_sendRelayTransaction`. Not cached.\n *\n * @param request - The relay submit request.\n * @returns The relay submit response containing the tracking UUID.\n */\n async submitRelayTransaction(\n request: SentinelRelaySubmitRequest,\n ): Promise<SentinelRelaySubmitResponse> {\n const url = await this.#resolveUrl(request.chainId, 'relayTransactions');\n\n const result = await this.fetchQuery({\n queryKey: [`${this.name}:submitRelayTransaction`, request],\n staleTime: 0,\n queryFn: async (): Promise<Json> => {\n log('submitRelayTransaction', 'Request', url, request);\n\n const rpcResult = await this.#jsonRpc(url, RPC_METHOD_SEND_RELAY, [\n request,\n ]);\n\n const [error] = validate(rpcResult, SentinelRelaySubmitResponseStruct);\n if (error) {\n throw new SentinelApiResponseValidationError(\n `Sentinel API: Malformed response from relay submit endpoint: ${error.message}`,\n );\n }\n\n log('submitRelayTransaction', 'Response', rpcResult);\n\n return rpcResult;\n },\n });\n\n return result as unknown as SentinelRelaySubmitResponse;\n }\n\n /**\n * Looks up the state of a submitted smart transaction by UUID against the\n * `/smart-transactions/{uuid}` endpoint. Performs a single request; callers\n * own any polling loop. Not cached.\n *\n * @param request - The smart-transaction lookup request.\n * @returns The response envelope containing the smart transaction(s)\n * associated with the requested UUID.\n */\n async getSmartTransaction(\n request: SentinelSmartTransactionRequest,\n ): Promise<SentinelSmartTransactionResponse> {\n const { chainId, uuid } = request;\n const baseUrl = await this.#resolveUrl(chainId, 'relayTransactions');\n const url = `${baseUrl}${ENDPOINT_SMART_TRANSACTIONS}/${uuid}`;\n\n const result = await this.fetchQuery({\n queryKey: [`${this.name}:getSmartTransaction`, chainId, uuid],\n staleTime: 0,\n queryFn: async (): Promise<Json> => {\n const headers = await this.#getHeaders();\n\n log('getSmartTransaction', 'Request', url);\n\n const response = await this.#fetch(url, { headers });\n\n if (!response.ok) {\n throw new HttpError(\n response.status,\n `Sentinel API: Smart-transaction request failed with status '${response.status}'`,\n );\n }\n\n const json: Json = await response.json();\n\n const [error] = validate(json, SentinelSmartTransactionResponseStruct);\n if (error) {\n throw new SentinelApiResponseValidationError(\n `Sentinel API: Malformed response from smart-transactions endpoint: ${error.message}`,\n );\n }\n\n log('getSmartTransaction', 'Response', json);\n\n return json;\n },\n });\n\n return result as unknown as SentinelSmartTransactionResponse;\n }\n\n /**\n * Performs a JSON-RPC POST to the Sentinel API and returns the `result`.\n *\n * @param url - The URL to post to.\n * @param method - The JSON-RPC method name.\n * @param params - The JSON-RPC params.\n * @returns The `result` field of the JSON-RPC response.\n */\n async #jsonRpc(url: string, method: string, params: Json[]): Promise<Json> {\n const headers = await this.#getHeaders();\n\n const response = await this.#fetch(url, {\n method: 'POST',\n headers: {\n ...headers,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n id: '1',\n jsonrpc: '2.0',\n method,\n params,\n }),\n });\n\n if (!response.ok) {\n throw new HttpError(\n response.status,\n `Sentinel API: JSON-RPC request '${method}' failed with status '${response.status}'`,\n );\n }\n\n const responseJson = (await response.json()) as {\n error?: { code?: number; message?: string };\n result?: Json;\n };\n\n if (responseJson.error) {\n const { code, message } = responseJson.error;\n throw new SentinelJsonRpcError(\n `Sentinel API: JSON-RPC error: ${message}`,\n code,\n );\n }\n\n if (responseJson.result === undefined) {\n throw new SentinelJsonRpcError(\n 'Sentinel API: JSON-RPC response missing result',\n -32603,\n );\n }\n\n return responseJson.result;\n }\n\n /**\n * Builds the outbound headers for a Sentinel request: the client identity\n * headers plus a best-effort `Authorization` bearer token. Token retrieval\n * failures are swallowed so unauthenticated requests still proceed.\n *\n * @returns The headers to attach to the request.\n */\n async #getHeaders(): Promise<Record<string, string>> {\n const headers: Record<string, string> = {};\n\n if (this.#clientId !== undefined) {\n headers['X-Client-Id'] = this.#clientId;\n }\n\n if (this.#clientVersion !== undefined) {\n headers['X-Client-Version'] = this.#clientVersion;\n }\n\n try {\n const token = await this.messenger.call(\n 'AuthenticationController:getBearerToken',\n );\n if (token) {\n headers.Authorization = `Bearer ${token}`;\n }\n } catch (error: unknown) {\n // Proceed without auth if token retrieval fails.\n log(\n 'getHeaders',\n 'Auth token unavailable, proceeding unauthenticated',\n error,\n );\n }\n\n return headers;\n }\n\n /**\n * Resolves the Sentinel URL for a chain, asserting the required capability.\n *\n * @param chainId - The chain ID to resolve a URL for.\n * @param capability - The capability flag that must be enabled.\n * @returns The resolved base URL.\n */\n async #resolveUrl(\n chainId: Hex,\n capability: 'confirmations' | 'relayTransactions',\n ): Promise<string> {\n const network = await this.#getNetwork(chainId);\n\n if (!network?.[capability]) {\n throw new SentinelChainNotSupportedError(chainId, capability);\n }\n\n return this.#buildUrl(network.network);\n }\n\n /**\n * Looks up a single network entry from the registry by chain ID.\n *\n * @param chainId - The chain ID to look up.\n * @returns The network entry, or undefined if not present.\n */\n async #getNetwork(chainId: Hex): Promise<SentinelNetwork | undefined> {\n const registry = await this.getNetworks();\n const chainIdDecimal = BigInt(chainId).toString(10);\n return registry[chainIdDecimal];\n }\n\n /**\n * Builds a Sentinel base URL for the given network subdomain, targeting the\n * environment this service was constructed with.\n *\n * @param subdomain - The network subdomain (for example `ethereum-mainnet`).\n * @returns The full base URL.\n */\n #buildUrl(subdomain: string): string {\n return BASE_URL_TEMPLATE.replace('{0}', subdomain).replace(\n '{1}',\n this.#environmentDomain,\n );\n }\n}\n"]}
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SentinelSmartTransactionResponseStruct = exports.SentinelRelaySubmitResponseStruct = exports.SentinelSimulationResponseStruct = exports.SentinelNetworkRegistryStruct = void 0;
4
+ const superstruct_1 = require("@metamask/superstruct");
5
+ /**
6
+ * Validates a single network entry from the `/networks` registry. Uses `type`
7
+ * (loose) validation so that additional fields returned by the API do not
8
+ * cause rejection; only the fields we depend on are asserted.
9
+ */
10
+ const SentinelNetworkStruct = (0, superstruct_1.type)({
11
+ network: (0, superstruct_1.string)(),
12
+ confirmations: (0, superstruct_1.optional)((0, superstruct_1.boolean)()),
13
+ chainID: (0, superstruct_1.optional)((0, superstruct_1.number)()),
14
+ relayTransactions: (0, superstruct_1.optional)((0, superstruct_1.boolean)()),
15
+ smartTransactions: (0, superstruct_1.optional)((0, superstruct_1.boolean)()),
16
+ sendBundle: (0, superstruct_1.optional)((0, superstruct_1.boolean)()),
17
+ });
18
+ /**
19
+ * Validates the `/networks` registry response (a map keyed by decimal chain
20
+ * ID).
21
+ */
22
+ exports.SentinelNetworkRegistryStruct = (0, superstruct_1.record)((0, superstruct_1.string)(), SentinelNetworkStruct);
23
+ /**
24
+ * Validates a simulated-transaction result. Loose to tolerate the many
25
+ * optional fields the API may return.
26
+ */
27
+ const SentinelSimulationResponseTransactionStruct = (0, superstruct_1.type)({
28
+ error: (0, superstruct_1.optional)((0, superstruct_1.string)()),
29
+ });
30
+ /**
31
+ * Validates the top-level simulation response. Only the `transactions` array
32
+ * is required by consumers.
33
+ */
34
+ exports.SentinelSimulationResponseStruct = (0, superstruct_1.type)({
35
+ transactions: (0, superstruct_1.array)(SentinelSimulationResponseTransactionStruct),
36
+ });
37
+ /**
38
+ * Validates the response from submitting a relay transaction.
39
+ */
40
+ exports.SentinelRelaySubmitResponseStruct = (0, superstruct_1.type)({
41
+ uuid: (0, superstruct_1.string)(),
42
+ });
43
+ /**
44
+ * Validates a single entry in the smart-transactions endpoint response.
45
+ */
46
+ const SentinelSmartTransactionStruct = (0, superstruct_1.type)({
47
+ hash: (0, superstruct_1.optional)((0, superstruct_1.string)()),
48
+ status: (0, superstruct_1.string)(),
49
+ errorReason: (0, superstruct_1.optional)((0, superstruct_1.nullable)((0, superstruct_1.string)())),
50
+ });
51
+ /**
52
+ * Validates the smart-transactions endpoint response body (a list of
53
+ * transactions).
54
+ */
55
+ exports.SentinelSmartTransactionResponseStruct = (0, superstruct_1.type)({
56
+ transactions: (0, superstruct_1.array)(SentinelSmartTransactionStruct),
57
+ });
58
+ //# sourceMappingURL=structs.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"structs.cjs","sourceRoot":"","sources":["../src/structs.ts"],"names":[],"mappings":";;;AAAA,uDAS+B;AAE/B;;;;GAIG;AACH,MAAM,qBAAqB,GAAG,IAAA,kBAAI,EAAC;IACjC,OAAO,EAAE,IAAA,oBAAM,GAAE;IACjB,aAAa,EAAE,IAAA,sBAAQ,EAAC,IAAA,qBAAO,GAAE,CAAC;IAClC,OAAO,EAAE,IAAA,sBAAQ,EAAC,IAAA,oBAAM,GAAE,CAAC;IAC3B,iBAAiB,EAAE,IAAA,sBAAQ,EAAC,IAAA,qBAAO,GAAE,CAAC;IACtC,iBAAiB,EAAE,IAAA,sBAAQ,EAAC,IAAA,qBAAO,GAAE,CAAC;IACtC,UAAU,EAAE,IAAA,sBAAQ,EAAC,IAAA,qBAAO,GAAE,CAAC;CAChC,CAAC,CAAC;AAEH;;;GAGG;AACU,QAAA,6BAA6B,GAAG,IAAA,oBAAM,EACjD,IAAA,oBAAM,GAAE,EACR,qBAAqB,CACtB,CAAC;AAEF;;;GAGG;AACH,MAAM,2CAA2C,GAAG,IAAA,kBAAI,EAAC;IACvD,KAAK,EAAE,IAAA,sBAAQ,EAAC,IAAA,oBAAM,GAAE,CAAC;CAC1B,CAAC,CAAC;AAEH;;;GAGG;AACU,QAAA,gCAAgC,GAAG,IAAA,kBAAI,EAAC;IACnD,YAAY,EAAE,IAAA,mBAAK,EAAC,2CAA2C,CAAC;CACjE,CAAC,CAAC;AAEH;;GAEG;AACU,QAAA,iCAAiC,GAAG,IAAA,kBAAI,EAAC;IACpD,IAAI,EAAE,IAAA,oBAAM,GAAE;CACf,CAAC,CAAC;AAEH;;GAEG;AACH,MAAM,8BAA8B,GAAG,IAAA,kBAAI,EAAC;IAC1C,IAAI,EAAE,IAAA,sBAAQ,EAAC,IAAA,oBAAM,GAAE,CAAC;IACxB,MAAM,EAAE,IAAA,oBAAM,GAAE;IAChB,WAAW,EAAE,IAAA,sBAAQ,EAAC,IAAA,sBAAQ,EAAC,IAAA,oBAAM,GAAE,CAAC,CAAC;CAC1C,CAAC,CAAC;AAEH;;;GAGG;AACU,QAAA,sCAAsC,GAAG,IAAA,kBAAI,EAAC;IACzD,YAAY,EAAE,IAAA,mBAAK,EAAC,8BAA8B,CAAC;CACpD,CAAC,CAAC","sourcesContent":["import {\n array,\n boolean,\n nullable,\n number,\n optional,\n record,\n string,\n type,\n} from '@metamask/superstruct';\n\n/**\n * Validates a single network entry from the `/networks` registry. Uses `type`\n * (loose) validation so that additional fields returned by the API do not\n * cause rejection; only the fields we depend on are asserted.\n */\nconst SentinelNetworkStruct = type({\n network: string(),\n confirmations: optional(boolean()),\n chainID: optional(number()),\n relayTransactions: optional(boolean()),\n smartTransactions: optional(boolean()),\n sendBundle: optional(boolean()),\n});\n\n/**\n * Validates the `/networks` registry response (a map keyed by decimal chain\n * ID).\n */\nexport const SentinelNetworkRegistryStruct = record(\n string(),\n SentinelNetworkStruct,\n);\n\n/**\n * Validates a simulated-transaction result. Loose to tolerate the many\n * optional fields the API may return.\n */\nconst SentinelSimulationResponseTransactionStruct = type({\n error: optional(string()),\n});\n\n/**\n * Validates the top-level simulation response. Only the `transactions` array\n * is required by consumers.\n */\nexport const SentinelSimulationResponseStruct = type({\n transactions: array(SentinelSimulationResponseTransactionStruct),\n});\n\n/**\n * Validates the response from submitting a relay transaction.\n */\nexport const SentinelRelaySubmitResponseStruct = type({\n uuid: string(),\n});\n\n/**\n * Validates a single entry in the smart-transactions endpoint response.\n */\nconst SentinelSmartTransactionStruct = type({\n hash: optional(string()),\n status: string(),\n errorReason: optional(nullable(string())),\n});\n\n/**\n * Validates the smart-transactions endpoint response body (a list of\n * transactions).\n */\nexport const SentinelSmartTransactionResponseStruct = type({\n transactions: array(SentinelSmartTransactionStruct),\n});\n"]}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Validates the `/networks` registry response (a map keyed by decimal chain
3
+ * ID).
4
+ */
5
+ export declare const SentinelNetworkRegistryStruct: import("@metamask/superstruct").Struct<Record<string, {
6
+ network: string;
7
+ confirmations?: boolean | undefined;
8
+ chainID?: number | undefined;
9
+ relayTransactions?: boolean | undefined;
10
+ smartTransactions?: boolean | undefined;
11
+ sendBundle?: boolean | undefined;
12
+ }>, null>;
13
+ /**
14
+ * Validates the top-level simulation response. Only the `transactions` array
15
+ * is required by consumers.
16
+ */
17
+ export declare const SentinelSimulationResponseStruct: import("@metamask/superstruct").Struct<{
18
+ transactions: {
19
+ error?: string | undefined;
20
+ }[];
21
+ }, {
22
+ transactions: import("@metamask/superstruct").Struct<{
23
+ error?: string | undefined;
24
+ }[], import("@metamask/superstruct").Struct<{
25
+ error?: string | undefined;
26
+ }, {
27
+ error: import("@metamask/superstruct").Struct<string | undefined, null>;
28
+ }>>;
29
+ }>;
30
+ /**
31
+ * Validates the response from submitting a relay transaction.
32
+ */
33
+ export declare const SentinelRelaySubmitResponseStruct: import("@metamask/superstruct").Struct<{
34
+ uuid: string;
35
+ }, {
36
+ uuid: import("@metamask/superstruct").Struct<string, null>;
37
+ }>;
38
+ /**
39
+ * Validates the smart-transactions endpoint response body (a list of
40
+ * transactions).
41
+ */
42
+ export declare const SentinelSmartTransactionResponseStruct: import("@metamask/superstruct").Struct<{
43
+ transactions: {
44
+ status: string;
45
+ hash?: string | undefined;
46
+ errorReason?: string | null | undefined;
47
+ }[];
48
+ }, {
49
+ transactions: import("@metamask/superstruct").Struct<{
50
+ status: string;
51
+ hash?: string | undefined;
52
+ errorReason?: string | null | undefined;
53
+ }[], import("@metamask/superstruct").Struct<{
54
+ status: string;
55
+ hash?: string | undefined;
56
+ errorReason?: string | null | undefined;
57
+ }, {
58
+ hash: import("@metamask/superstruct").Struct<string | undefined, null>;
59
+ status: import("@metamask/superstruct").Struct<string, null>;
60
+ errorReason: import("@metamask/superstruct").Struct<string | null | undefined, null>;
61
+ }>>;
62
+ }>;
63
+ //# sourceMappingURL=structs.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"structs.d.cts","sourceRoot":"","sources":["../src/structs.ts"],"names":[],"mappings":"AAyBA;;;GAGG;AACH,eAAO,MAAM,6BAA6B;;;;;;;SAGzC,CAAC;AAUF;;;GAGG;AACH,eAAO,MAAM,gCAAgC;;;;;;;;;;;;EAE3C,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,iCAAiC;;;;EAE5C,CAAC;AAWH;;;GAGG;AACH,eAAO,MAAM,sCAAsC;;;;;;;;;;;;;;;;;;;;EAEjD,CAAC"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Validates the `/networks` registry response (a map keyed by decimal chain
3
+ * ID).
4
+ */
5
+ export declare const SentinelNetworkRegistryStruct: import("@metamask/superstruct").Struct<Record<string, {
6
+ network: string;
7
+ confirmations?: boolean | undefined;
8
+ chainID?: number | undefined;
9
+ relayTransactions?: boolean | undefined;
10
+ smartTransactions?: boolean | undefined;
11
+ sendBundle?: boolean | undefined;
12
+ }>, null>;
13
+ /**
14
+ * Validates the top-level simulation response. Only the `transactions` array
15
+ * is required by consumers.
16
+ */
17
+ export declare const SentinelSimulationResponseStruct: import("@metamask/superstruct").Struct<{
18
+ transactions: {
19
+ error?: string | undefined;
20
+ }[];
21
+ }, {
22
+ transactions: import("@metamask/superstruct").Struct<{
23
+ error?: string | undefined;
24
+ }[], import("@metamask/superstruct").Struct<{
25
+ error?: string | undefined;
26
+ }, {
27
+ error: import("@metamask/superstruct").Struct<string | undefined, null>;
28
+ }>>;
29
+ }>;
30
+ /**
31
+ * Validates the response from submitting a relay transaction.
32
+ */
33
+ export declare const SentinelRelaySubmitResponseStruct: import("@metamask/superstruct").Struct<{
34
+ uuid: string;
35
+ }, {
36
+ uuid: import("@metamask/superstruct").Struct<string, null>;
37
+ }>;
38
+ /**
39
+ * Validates the smart-transactions endpoint response body (a list of
40
+ * transactions).
41
+ */
42
+ export declare const SentinelSmartTransactionResponseStruct: import("@metamask/superstruct").Struct<{
43
+ transactions: {
44
+ status: string;
45
+ hash?: string | undefined;
46
+ errorReason?: string | null | undefined;
47
+ }[];
48
+ }, {
49
+ transactions: import("@metamask/superstruct").Struct<{
50
+ status: string;
51
+ hash?: string | undefined;
52
+ errorReason?: string | null | undefined;
53
+ }[], import("@metamask/superstruct").Struct<{
54
+ status: string;
55
+ hash?: string | undefined;
56
+ errorReason?: string | null | undefined;
57
+ }, {
58
+ hash: import("@metamask/superstruct").Struct<string | undefined, null>;
59
+ status: import("@metamask/superstruct").Struct<string, null>;
60
+ errorReason: import("@metamask/superstruct").Struct<string | null | undefined, null>;
61
+ }>>;
62
+ }>;
63
+ //# sourceMappingURL=structs.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"structs.d.mts","sourceRoot":"","sources":["../src/structs.ts"],"names":[],"mappings":"AAyBA;;;GAGG;AACH,eAAO,MAAM,6BAA6B;;;;;;;SAGzC,CAAC;AAUF;;;GAGG;AACH,eAAO,MAAM,gCAAgC;;;;;;;;;;;;EAE3C,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,iCAAiC;;;;EAE5C,CAAC;AAWH;;;GAGG;AACH,eAAO,MAAM,sCAAsC;;;;;;;;;;;;;;;;;;;;EAEjD,CAAC"}
@@ -0,0 +1,55 @@
1
+ import { array, boolean, nullable, number, optional, record, string, type } from "@metamask/superstruct";
2
+ /**
3
+ * Validates a single network entry from the `/networks` registry. Uses `type`
4
+ * (loose) validation so that additional fields returned by the API do not
5
+ * cause rejection; only the fields we depend on are asserted.
6
+ */
7
+ const SentinelNetworkStruct = type({
8
+ network: string(),
9
+ confirmations: optional(boolean()),
10
+ chainID: optional(number()),
11
+ relayTransactions: optional(boolean()),
12
+ smartTransactions: optional(boolean()),
13
+ sendBundle: optional(boolean()),
14
+ });
15
+ /**
16
+ * Validates the `/networks` registry response (a map keyed by decimal chain
17
+ * ID).
18
+ */
19
+ export const SentinelNetworkRegistryStruct = record(string(), SentinelNetworkStruct);
20
+ /**
21
+ * Validates a simulated-transaction result. Loose to tolerate the many
22
+ * optional fields the API may return.
23
+ */
24
+ const SentinelSimulationResponseTransactionStruct = type({
25
+ error: optional(string()),
26
+ });
27
+ /**
28
+ * Validates the top-level simulation response. Only the `transactions` array
29
+ * is required by consumers.
30
+ */
31
+ export const SentinelSimulationResponseStruct = type({
32
+ transactions: array(SentinelSimulationResponseTransactionStruct),
33
+ });
34
+ /**
35
+ * Validates the response from submitting a relay transaction.
36
+ */
37
+ export const SentinelRelaySubmitResponseStruct = type({
38
+ uuid: string(),
39
+ });
40
+ /**
41
+ * Validates a single entry in the smart-transactions endpoint response.
42
+ */
43
+ const SentinelSmartTransactionStruct = type({
44
+ hash: optional(string()),
45
+ status: string(),
46
+ errorReason: optional(nullable(string())),
47
+ });
48
+ /**
49
+ * Validates the smart-transactions endpoint response body (a list of
50
+ * transactions).
51
+ */
52
+ export const SentinelSmartTransactionResponseStruct = type({
53
+ transactions: array(SentinelSmartTransactionStruct),
54
+ });
55
+ //# sourceMappingURL=structs.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"structs.mjs","sourceRoot":"","sources":["../src/structs.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,EACL,OAAO,EACP,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,MAAM,EACN,MAAM,EACN,IAAI,EACL,8BAA8B;AAE/B;;;;GAIG;AACH,MAAM,qBAAqB,GAAG,IAAI,CAAC;IACjC,OAAO,EAAE,MAAM,EAAE;IACjB,aAAa,EAAE,QAAQ,CAAC,OAAO,EAAE,CAAC;IAClC,OAAO,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;IAC3B,iBAAiB,EAAE,QAAQ,CAAC,OAAO,EAAE,CAAC;IACtC,iBAAiB,EAAE,QAAQ,CAAC,OAAO,EAAE,CAAC;IACtC,UAAU,EAAE,QAAQ,CAAC,OAAO,EAAE,CAAC;CAChC,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,MAAM,CACjD,MAAM,EAAE,EACR,qBAAqB,CACtB,CAAC;AAEF;;;GAGG;AACH,MAAM,2CAA2C,GAAG,IAAI,CAAC;IACvD,KAAK,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;CAC1B,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAG,IAAI,CAAC;IACnD,YAAY,EAAE,KAAK,CAAC,2CAA2C,CAAC;CACjE,CAAC,CAAC;AAEH;;GAEG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAAG,IAAI,CAAC;IACpD,IAAI,EAAE,MAAM,EAAE;CACf,CAAC,CAAC;AAEH;;GAEG;AACH,MAAM,8BAA8B,GAAG,IAAI,CAAC;IAC1C,IAAI,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;IACxB,MAAM,EAAE,MAAM,EAAE;IAChB,WAAW,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;CAC1C,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,sCAAsC,GAAG,IAAI,CAAC;IACzD,YAAY,EAAE,KAAK,CAAC,8BAA8B,CAAC;CACpD,CAAC,CAAC","sourcesContent":["import {\n array,\n boolean,\n nullable,\n number,\n optional,\n record,\n string,\n type,\n} from '@metamask/superstruct';\n\n/**\n * Validates a single network entry from the `/networks` registry. Uses `type`\n * (loose) validation so that additional fields returned by the API do not\n * cause rejection; only the fields we depend on are asserted.\n */\nconst SentinelNetworkStruct = type({\n network: string(),\n confirmations: optional(boolean()),\n chainID: optional(number()),\n relayTransactions: optional(boolean()),\n smartTransactions: optional(boolean()),\n sendBundle: optional(boolean()),\n});\n\n/**\n * Validates the `/networks` registry response (a map keyed by decimal chain\n * ID).\n */\nexport const SentinelNetworkRegistryStruct = record(\n string(),\n SentinelNetworkStruct,\n);\n\n/**\n * Validates a simulated-transaction result. Loose to tolerate the many\n * optional fields the API may return.\n */\nconst SentinelSimulationResponseTransactionStruct = type({\n error: optional(string()),\n});\n\n/**\n * Validates the top-level simulation response. Only the `transactions` array\n * is required by consumers.\n */\nexport const SentinelSimulationResponseStruct = type({\n transactions: array(SentinelSimulationResponseTransactionStruct),\n});\n\n/**\n * Validates the response from submitting a relay transaction.\n */\nexport const SentinelRelaySubmitResponseStruct = type({\n uuid: string(),\n});\n\n/**\n * Validates a single entry in the smart-transactions endpoint response.\n */\nconst SentinelSmartTransactionStruct = type({\n hash: optional(string()),\n status: string(),\n errorReason: optional(nullable(string())),\n});\n\n/**\n * Validates the smart-transactions endpoint response body (a list of\n * transactions).\n */\nexport const SentinelSmartTransactionResponseStruct = type({\n transactions: array(SentinelSmartTransactionStruct),\n});\n"]}