@metamask-previews/compliance-controller 3.0.0-preview-9fd631f7d → 3.0.0-preview-6fbab7311
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.
- package/CHANGELOG.md +8 -0
- package/dist/ComplianceService.d.ts +17 -36
- package/dist/ComplianceService.d.ts.map +1 -1
- package/dist/ComplianceService.js +35 -91
- package/dist/ComplianceService.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -9,15 +9,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
9
9
|
|
|
10
10
|
### Added
|
|
11
11
|
|
|
12
|
+
- Add `ComplianceServiceInvalidateQueriesAction`, `ComplianceServiceCacheUpdatedEvent` and `ComplianceServiceGranularCacheUpdatedEvent`, also included in `ComplianceServiceActions` and `ComplianceServiceEvents` ([#10880](https://github.com/MetaMask/core/pull/10880))
|
|
12
13
|
- Add support for Node.js 26 ([#10981](https://github.com/MetaMask/core/pull/10981))
|
|
13
14
|
|
|
14
15
|
### Changed
|
|
15
16
|
|
|
17
|
+
- **BREAKING:** `ComplianceService` now extends `BaseDataService` from `@metamask/base-data-service` ([#10880](https://github.com/MetaMask/core/pull/10880))
|
|
18
|
+
- `ComplianceServiceMessenger` now includes the `ComplianceService:invalidateQueries` action and `ComplianceService:cacheUpdated` events. Messengers built from `ComplianceServiceActions` / `ComplianceServiceEvents` pick these up automatically.
|
|
19
|
+
- Concurrent `checkWalletCompliance` / `checkWalletsCompliance` calls with identical arguments now share one request. Sequential calls still always hit the API ([#10880](https://github.com/MetaMask/core/pull/10880))
|
|
20
|
+
- Responses that do not match the expected shape now throw `Query function for "ComplianceService:<method>" returned an unexpected response: …`, instead of `Malformed response received from <api name>` ([#10880](https://github.com/MetaMask/core/pull/10880))
|
|
21
|
+
- Response bodies that are not valid JSON are now retried by the service policy and count toward opening the circuit breaker, instead of failing immediately. Valid JSON with an invalid shape still fails immediately ([#10880](https://github.com/MetaMask/core/pull/10880))
|
|
22
|
+
- Add dependency `@metamask/base-data-service` ([#10880](https://github.com/MetaMask/core/pull/10880))
|
|
16
23
|
- Bump `reselect` from `^5.1.1` to `^5.3.0` ([#10532](https://github.com/MetaMask/core/pull/10532))
|
|
17
24
|
- Bump `@metamask/base-controller` from `^10.0.0` to `^10.1.0` ([#10864](https://github.com/MetaMask/core/pull/10864))
|
|
18
25
|
|
|
19
26
|
### Removed
|
|
20
27
|
|
|
28
|
+
- **BREAKING:** Remove `onRetry`, `onBreak` and `onDegraded` from `ComplianceService` ([#10880](https://github.com/MetaMask/core/pull/10880))
|
|
21
29
|
- Remove the unused `@metamask/utils` dependency ([#10185](https://github.com/MetaMask/core/pull/10185))
|
|
22
30
|
|
|
23
31
|
## [3.0.0]
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { BaseDataService } from '@metamask/base-data-service';
|
|
2
|
+
import type { CreateServicePolicyOptions, DataServiceCacheUpdatedEvent, DataServiceGranularCacheUpdatedEvent, DataServiceInvalidateQueriesAction } from '@metamask/base-data-service';
|
|
2
3
|
import type { Messenger } from '@metamask/messenger';
|
|
3
4
|
import type { Infer } from '@metamask/superstruct';
|
|
4
|
-
import type { IDisposable } from 'cockatiel';
|
|
5
5
|
import type { ComplianceServiceMethodActions } from './ComplianceService-method-action-types.js';
|
|
6
6
|
/**
|
|
7
7
|
* The name of the {@link ComplianceService}, used to namespace the service's
|
|
@@ -27,18 +27,30 @@ export type ComplianceServiceOptions = {
|
|
|
27
27
|
env?: ComplianceServiceEnvironment;
|
|
28
28
|
policyOptions?: CreateServicePolicyOptions;
|
|
29
29
|
};
|
|
30
|
+
/**
|
|
31
|
+
* Invalidates cached queries for {@link ComplianceService}.
|
|
32
|
+
*/
|
|
33
|
+
export type ComplianceServiceInvalidateQueriesAction = DataServiceInvalidateQueriesAction<typeof serviceName>;
|
|
30
34
|
/**
|
|
31
35
|
* Actions that {@link ComplianceService} exposes to other consumers.
|
|
32
36
|
*/
|
|
33
|
-
export type ComplianceServiceActions = ComplianceServiceMethodActions;
|
|
37
|
+
export type ComplianceServiceActions = ComplianceServiceMethodActions | ComplianceServiceInvalidateQueriesAction;
|
|
34
38
|
/**
|
|
35
39
|
* Actions from other messengers that {@link ComplianceService} calls.
|
|
36
40
|
*/
|
|
37
41
|
type AllowedActions = never;
|
|
42
|
+
/**
|
|
43
|
+
* Published when {@link ComplianceService}'s cache is updated.
|
|
44
|
+
*/
|
|
45
|
+
export type ComplianceServiceCacheUpdatedEvent = DataServiceCacheUpdatedEvent<typeof serviceName>;
|
|
46
|
+
/**
|
|
47
|
+
* Published when a key within {@link ComplianceService}'s cache is updated.
|
|
48
|
+
*/
|
|
49
|
+
export type ComplianceServiceGranularCacheUpdatedEvent = DataServiceGranularCacheUpdatedEvent<typeof serviceName>;
|
|
38
50
|
/**
|
|
39
51
|
* Events that {@link ComplianceService} exposes to other consumers.
|
|
40
52
|
*/
|
|
41
|
-
export type ComplianceServiceEvents =
|
|
53
|
+
export type ComplianceServiceEvents = ComplianceServiceCacheUpdatedEvent | ComplianceServiceGranularCacheUpdatedEvent;
|
|
42
54
|
/**
|
|
43
55
|
* Events from other messengers that {@link ComplianceService} subscribes to.
|
|
44
56
|
*/
|
|
@@ -119,12 +131,8 @@ type BatchWalletCheckResponseItem = Infer<typeof BatchWalletCheckResponseItemStr
|
|
|
119
131
|
* // => { address: '0x1234...', blocked: false }
|
|
120
132
|
* ```
|
|
121
133
|
*/
|
|
122
|
-
export declare class ComplianceService {
|
|
134
|
+
export declare class ComplianceService extends BaseDataService<typeof serviceName, ComplianceServiceMessenger> {
|
|
123
135
|
#private;
|
|
124
|
-
/**
|
|
125
|
-
* The name of the service.
|
|
126
|
-
*/
|
|
127
|
-
readonly name: typeof serviceName;
|
|
128
136
|
/**
|
|
129
137
|
* Constructs a new ComplianceService object.
|
|
130
138
|
*
|
|
@@ -138,33 +146,6 @@ export declare class ComplianceService {
|
|
|
138
146
|
* is used to wrap each request. See {@link CreateServicePolicyOptions}.
|
|
139
147
|
*/
|
|
140
148
|
constructor({ messenger, fetch: fetchFunction, apiUrl, env, policyOptions, }: ComplianceServiceOptions);
|
|
141
|
-
/**
|
|
142
|
-
* Registers a handler that will be called after a request returns a non-500
|
|
143
|
-
* response, causing a retry.
|
|
144
|
-
*
|
|
145
|
-
* @param listener - The handler to be called.
|
|
146
|
-
* @returns An object that can be used to unregister the handler.
|
|
147
|
-
* @see {@link createServicePolicy}
|
|
148
|
-
*/
|
|
149
|
-
onRetry(listener: Parameters<ServicePolicy['onRetry']>[0]): IDisposable;
|
|
150
|
-
/**
|
|
151
|
-
* Registers a handler that will be called after a set number of retry rounds
|
|
152
|
-
* prove that requests to the API endpoint consistently return a 5xx response.
|
|
153
|
-
*
|
|
154
|
-
* @param listener - The handler to be called.
|
|
155
|
-
* @returns An object that can be used to unregister the handler.
|
|
156
|
-
* @see {@link createServicePolicy}
|
|
157
|
-
*/
|
|
158
|
-
onBreak(listener: Parameters<ServicePolicy['onBreak']>[0]): IDisposable;
|
|
159
|
-
/**
|
|
160
|
-
* Registers a handler that will be called when the service is degraded due
|
|
161
|
-
* to slow responses or repeated failures.
|
|
162
|
-
*
|
|
163
|
-
* @param listener - The handler to be called.
|
|
164
|
-
* @returns An object that can be used to unregister the handler.
|
|
165
|
-
* @see {@link createServicePolicy}
|
|
166
|
-
*/
|
|
167
|
-
onDegraded(listener: Parameters<ServicePolicy['onDegraded']>[0]): IDisposable;
|
|
168
149
|
/**
|
|
169
150
|
* Checks compliance status for a single wallet address.
|
|
170
151
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ComplianceService.d.ts","sourceRoot":"","sources":["../src/ComplianceService.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"ComplianceService.d.ts","sourceRoot":"","sources":["../src/ComplianceService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,EACV,0BAA0B,EAC1B,4BAA4B,EAC5B,oCAAoC,EACpC,kCAAkC,EACnC,MAAM,6BAA6B,CAAC;AAErC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAGnD,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,4CAA4C,CAAC;AAIjG;;;GAGG;AACH,eAAO,MAAM,WAAW,sBAAsB,CAAC;AAE/C;;GAEG;AACH,MAAM,MAAM,4BAA4B,GAAG,YAAY,GAAG,aAAa,CAAC;AAOxE,MAAM,MAAM,wBAAwB,GAAG;IACrC,SAAS,EAAE,0BAA0B,CAAC;IACtC,KAAK,EAAE,OAAO,KAAK,CAAC;IACpB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,GAAG,CAAC,EAAE,4BAA4B,CAAC;IACnC,aAAa,CAAC,EAAE,0BAA0B,CAAC;CAC5C,CAAC;AASF;;GAEG;AACH,MAAM,MAAM,wCAAwC,GAClD,kCAAkC,CAAC,OAAO,WAAW,CAAC,CAAC;AAEzD;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAChC,8BAA8B,GAC9B,wCAAwC,CAAC;AAE7C;;GAEG;AACH,KAAK,cAAc,GAAG,KAAK,CAAC;AAE5B;;GAEG;AACH,MAAM,MAAM,kCAAkC,GAAG,4BAA4B,CAC3E,OAAO,WAAW,CACnB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,0CAA0C,GACpD,oCAAoC,CAAC,OAAO,WAAW,CAAC,CAAC;AAE3D;;GAEG;AACH,MAAM,MAAM,uBAAuB,GAC/B,kCAAkC,GAClC,0CAA0C,CAAC;AAE/C;;GAEG;AACH,KAAK,aAAa,GAAG,KAAK,CAAC;AAE3B;;;GAGG;AACH,MAAM,MAAM,0BAA0B,GAAG,SAAS,CAChD,OAAO,WAAW,EAClB,wBAAwB,GAAG,cAAc,EACzC,uBAAuB,GAAG,aAAa,CACxC,CAAC;AAIF;;GAEG;AACH,QAAA,MAAM,yBAAyB;;;;;;EAG7B,CAAC;AAEH;;GAEG;AACH,KAAK,mBAAmB,GAAG,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAEnE;;;GAGG;AACH,QAAA,MAAM,kCAAkC;;;;;;EAA4B,CAAC;AAErE;;GAEG;AACH,KAAK,4BAA4B,GAAG,KAAK,CACvC,OAAO,kCAAkC,CAC1C,CAAC;AAIF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,qBAAa,iBAAkB,SAAQ,eAAe,CACpD,OAAO,WAAW,EAClB,0BAA0B,CAC3B;;IAWC;;;;;;;;;;;OAWG;IACH,YAAY,EACV,SAAS,EACT,KAAK,EAAE,aAAa,EACpB,MAAM,EACN,GAAkB,EAClB,aAAkB,GACnB,EAAE,wBAAwB,EAS1B;IAED;;;;;OAKG;IACG,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAqBzE;IAED;;;;;OAKG;IACG,sBAAsB,CAC1B,SAAS,EAAE,MAAM,EAAE,GAClB,OAAO,CAAC,4BAA4B,EAAE,CAAC,CAsBzC;CACF"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BaseDataService } from '@metamask/base-data-service';
|
|
2
|
+
import { HttpError } from '@metamask/controller-utils';
|
|
2
3
|
import { array, boolean, object, string } from '@metamask/superstruct';
|
|
3
4
|
// === GENERAL ===
|
|
4
5
|
/**
|
|
@@ -71,15 +72,7 @@ const BatchWalletCheckResponseItemStruct = WalletCheckResponseStruct;
|
|
|
71
72
|
* // => { address: '0x1234...', blocked: false }
|
|
72
73
|
* ```
|
|
73
74
|
*/
|
|
74
|
-
export class ComplianceService {
|
|
75
|
-
/**
|
|
76
|
-
* The name of the service.
|
|
77
|
-
*/
|
|
78
|
-
name;
|
|
79
|
-
/**
|
|
80
|
-
* The messenger suited for this service.
|
|
81
|
-
*/
|
|
82
|
-
#messenger;
|
|
75
|
+
export class ComplianceService extends BaseDataService {
|
|
83
76
|
/**
|
|
84
77
|
* A function that can be used to make an HTTP request.
|
|
85
78
|
*/
|
|
@@ -88,12 +81,6 @@ export class ComplianceService {
|
|
|
88
81
|
* The resolved base URL for the Compliance API.
|
|
89
82
|
*/
|
|
90
83
|
#complianceApiUrl;
|
|
91
|
-
/**
|
|
92
|
-
* The policy that wraps each request.
|
|
93
|
-
*
|
|
94
|
-
* @see {@link createServicePolicy}
|
|
95
|
-
*/
|
|
96
|
-
#policy;
|
|
97
84
|
/**
|
|
98
85
|
* Constructs a new ComplianceService object.
|
|
99
86
|
*
|
|
@@ -107,45 +94,10 @@ export class ComplianceService {
|
|
|
107
94
|
* is used to wrap each request. See {@link CreateServicePolicyOptions}.
|
|
108
95
|
*/
|
|
109
96
|
constructor({ messenger, fetch: fetchFunction, apiUrl, env = 'production', policyOptions = {}, }) {
|
|
110
|
-
|
|
111
|
-
this.#messenger = messenger;
|
|
97
|
+
super({ name: serviceName, messenger, policyOptions });
|
|
112
98
|
this.#fetch = fetchFunction;
|
|
113
99
|
this.#complianceApiUrl = getComplianceApiUrl({ apiUrl, env });
|
|
114
|
-
this
|
|
115
|
-
this.#messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Registers a handler that will be called after a request returns a non-500
|
|
119
|
-
* response, causing a retry.
|
|
120
|
-
*
|
|
121
|
-
* @param listener - The handler to be called.
|
|
122
|
-
* @returns An object that can be used to unregister the handler.
|
|
123
|
-
* @see {@link createServicePolicy}
|
|
124
|
-
*/
|
|
125
|
-
onRetry(listener) {
|
|
126
|
-
return this.#policy.onRetry(listener);
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* Registers a handler that will be called after a set number of retry rounds
|
|
130
|
-
* prove that requests to the API endpoint consistently return a 5xx response.
|
|
131
|
-
*
|
|
132
|
-
* @param listener - The handler to be called.
|
|
133
|
-
* @returns An object that can be used to unregister the handler.
|
|
134
|
-
* @see {@link createServicePolicy}
|
|
135
|
-
*/
|
|
136
|
-
onBreak(listener) {
|
|
137
|
-
return this.#policy.onBreak(listener);
|
|
138
|
-
}
|
|
139
|
-
/**
|
|
140
|
-
* Registers a handler that will be called when the service is degraded due
|
|
141
|
-
* to slow responses or repeated failures.
|
|
142
|
-
*
|
|
143
|
-
* @param listener - The handler to be called.
|
|
144
|
-
* @returns An object that can be used to unregister the handler.
|
|
145
|
-
* @see {@link createServicePolicy}
|
|
146
|
-
*/
|
|
147
|
-
onDegraded(listener) {
|
|
148
|
-
return this.#policy.onDegraded(listener);
|
|
100
|
+
this.messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
|
|
149
101
|
}
|
|
150
102
|
/**
|
|
151
103
|
* Checks compliance status for a single wallet address.
|
|
@@ -154,16 +106,20 @@ export class ComplianceService {
|
|
|
154
106
|
* @returns The compliance status of the wallet.
|
|
155
107
|
*/
|
|
156
108
|
async checkWalletCompliance(address) {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
109
|
+
return this.fetchQuery({
|
|
110
|
+
queryKey: [`${this.name}:checkWalletCompliance`, address],
|
|
111
|
+
staleTime: 0,
|
|
112
|
+
gcTime: 0,
|
|
113
|
+
responseStruct: WalletCheckResponseStruct,
|
|
114
|
+
queryFn: async () => {
|
|
115
|
+
const url = new URL(`v1/wallet/${encodeURIComponent(address)}`, this.#complianceApiUrl);
|
|
116
|
+
const response = await this.#fetch(url);
|
|
117
|
+
if (!response.ok) {
|
|
118
|
+
throw new HttpError(response.status, `Fetching '${url.toString()}' failed with status '${response.status}'`);
|
|
119
|
+
}
|
|
120
|
+
return (await response.json());
|
|
121
|
+
},
|
|
164
122
|
});
|
|
165
|
-
const jsonResponse = await response.json();
|
|
166
|
-
return validateResponse(jsonResponse, WalletCheckResponseStruct, 'compliance wallet check API');
|
|
167
123
|
}
|
|
168
124
|
/**
|
|
169
125
|
* Checks compliance status for multiple wallet addresses in a single request.
|
|
@@ -172,20 +128,24 @@ export class ComplianceService {
|
|
|
172
128
|
* @returns The compliance statuses of the wallets.
|
|
173
129
|
*/
|
|
174
130
|
async checkWalletsCompliance(addresses) {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
131
|
+
return this.fetchQuery({
|
|
132
|
+
queryKey: [`${this.name}:checkWalletsCompliance`, addresses],
|
|
133
|
+
staleTime: 0,
|
|
134
|
+
gcTime: 0,
|
|
135
|
+
responseStruct: array(BatchWalletCheckResponseItemStruct),
|
|
136
|
+
queryFn: async () => {
|
|
137
|
+
const url = new URL('v1/wallet/batch', this.#complianceApiUrl);
|
|
138
|
+
const response = await this.#fetch(url, {
|
|
139
|
+
method: 'POST',
|
|
140
|
+
headers: { 'Content-Type': 'application/json' },
|
|
141
|
+
body: JSON.stringify(addresses),
|
|
142
|
+
});
|
|
143
|
+
if (!response.ok) {
|
|
144
|
+
throw new HttpError(response.status, `Fetching '${url.toString()}' failed with status '${response.status}'`);
|
|
145
|
+
}
|
|
146
|
+
return (await response.json());
|
|
147
|
+
},
|
|
186
148
|
});
|
|
187
|
-
const jsonResponse = await response.json();
|
|
188
|
-
return validateResponse(jsonResponse, array(BatchWalletCheckResponseItemStruct), 'compliance batch check API');
|
|
189
149
|
}
|
|
190
150
|
}
|
|
191
151
|
function getComplianceApiUrl({ apiUrl, env, }) {
|
|
@@ -207,20 +167,4 @@ function getComplianceApiUrl({ apiUrl, env, }) {
|
|
|
207
167
|
}
|
|
208
168
|
return url.href;
|
|
209
169
|
}
|
|
210
|
-
/**
|
|
211
|
-
* Validates an API response against a superstruct schema.
|
|
212
|
-
*
|
|
213
|
-
* @param data - The raw response data to validate.
|
|
214
|
-
* @param struct - The superstruct schema to validate against.
|
|
215
|
-
* @param struct.is - The type guard function from the schema.
|
|
216
|
-
* @param apiName - A human-readable name for the API, used in error messages.
|
|
217
|
-
* @returns The validated data.
|
|
218
|
-
* @throws If the data does not match the schema.
|
|
219
|
-
*/
|
|
220
|
-
function validateResponse(data, struct, apiName) {
|
|
221
|
-
if (struct.is(data)) {
|
|
222
|
-
return data;
|
|
223
|
-
}
|
|
224
|
-
throw new Error(`Malformed response received from ${apiName}`);
|
|
225
|
-
}
|
|
226
170
|
//# sourceMappingURL=ComplianceService.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ComplianceService.js","sourceRoot":"","sources":["../src/ComplianceService.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n CreateServicePolicyOptions,\n ServicePolicy,\n} from '@metamask/controller-utils';\nimport { createServicePolicy, HttpError } from '@metamask/controller-utils';\nimport type { Messenger } from '@metamask/messenger';\nimport type { Infer } from '@metamask/superstruct';\nimport { array, boolean, object, string } from '@metamask/superstruct';\nimport type { IDisposable } from 'cockatiel';\n\nimport type { ComplianceServiceMethodActions } from './ComplianceService-method-action-types.js';\n\n// === GENERAL ===\n\n/**\n * The name of the {@link ComplianceService}, used to namespace the service's\n * actions and events.\n */\nexport const serviceName = 'ComplianceService';\n\n/**\n * The supported environments for the Compliance API.\n */\nexport type ComplianceServiceEnvironment = 'production' | 'development';\n\nconst COMPLIANCE_API_URLS: Record<ComplianceServiceEnvironment, string> = {\n production: 'https://compliance.api.cx.metamask.io',\n development: 'https://compliance.dev-api.cx.metamask.io',\n};\n\nexport type ComplianceServiceOptions = {\n messenger: ComplianceServiceMessenger;\n fetch: typeof fetch;\n /**\n * Explicit Compliance API URL. Prefer this for application builds so API\n * endpoints can be managed by build configuration. Path components are\n * preserved as a base path for Compliance API routes.\n */\n apiUrl?: string;\n /**\n * Fallback environment used when `apiUrl` is not provided.\n */\n env?: ComplianceServiceEnvironment;\n policyOptions?: CreateServicePolicyOptions;\n};\n\n// === MESSENGER ===\n\nconst MESSENGER_EXPOSED_METHODS = [\n 'checkWalletCompliance',\n 'checkWalletsCompliance',\n] as const;\n\n/**\n * Actions that {@link ComplianceService} exposes to other consumers.\n */\nexport type ComplianceServiceActions = ComplianceServiceMethodActions;\n\n/**\n * Actions from other messengers that {@link ComplianceService} calls.\n */\ntype AllowedActions = never;\n\n/**\n * Events that {@link ComplianceService} exposes to other consumers.\n */\nexport type ComplianceServiceEvents = never;\n\n/**\n * Events from other messengers that {@link ComplianceService} subscribes to.\n */\ntype AllowedEvents = never;\n\n/**\n * The messenger restricted to actions and events accessed by\n * {@link ComplianceService}.\n */\nexport type ComplianceServiceMessenger = Messenger<\n typeof serviceName,\n ComplianceServiceActions | AllowedActions,\n ComplianceServiceEvents | AllowedEvents\n>;\n\n// === API RESPONSE SCHEMAS ===\n\n/**\n * Schema for the response from `GET /v1/wallet/:address`.\n */\nconst WalletCheckResponseStruct = object({\n address: string(),\n blocked: boolean(),\n});\n\n/**\n * The validated shape of a single wallet compliance check response.\n */\ntype WalletCheckResponse = Infer<typeof WalletCheckResponseStruct>;\n\n/**\n * Schema for each item in the response from `POST /v1/wallet/batch`.\n * Reuses the same shape as a single wallet check.\n */\nconst BatchWalletCheckResponseItemStruct = WalletCheckResponseStruct;\n\n/**\n * The validated shape of a single item in a batch compliance check response.\n */\ntype BatchWalletCheckResponseItem = Infer<\n typeof BatchWalletCheckResponseItemStruct\n>;\n\n// === SERVICE DEFINITION ===\n\n/**\n * `ComplianceService` communicates with the Compliance API to check whether\n * wallet addresses are sanctioned under OFAC regulations.\n *\n * @example\n *\n * ``` ts\n * import { Messenger } from '@metamask/messenger';\n * import type {\n * ComplianceServiceActions,\n * ComplianceServiceEvents,\n * } from '@metamask/compliance-controller';\n * import { ComplianceService } from '@metamask/compliance-controller';\n *\n * const rootMessenger = new Messenger<\n * 'Root',\n * ComplianceServiceActions,\n * ComplianceServiceEvents,\n * >({ namespace: 'Root' });\n * const serviceMessenger = new Messenger<\n * 'ComplianceService',\n * ComplianceServiceActions,\n * ComplianceServiceEvents,\n * typeof rootMessenger,\n * >({\n * namespace: 'ComplianceService',\n * parent: rootMessenger,\n * });\n * new ComplianceService({\n * messenger: serviceMessenger,\n * fetch,\n * apiUrl: 'https://compliance.api.cx.metamask.io',\n * });\n *\n * // Check a single wallet\n * const result = await rootMessenger.call(\n * 'ComplianceService:checkWalletCompliance',\n * '0x1234...',\n * );\n * // => { address: '0x1234...', blocked: false }\n * ```\n */\nexport class ComplianceService {\n /**\n * The name of the service.\n */\n readonly name: typeof serviceName;\n\n /**\n * The messenger suited for this service.\n */\n readonly #messenger: ConstructorParameters<\n typeof ComplianceService\n >[0]['messenger'];\n\n /**\n * A function that can be used to make an HTTP request.\n */\n readonly #fetch: ConstructorParameters<typeof ComplianceService>[0]['fetch'];\n\n /**\n * The resolved base URL for the Compliance API.\n */\n readonly #complianceApiUrl: string;\n\n /**\n * The policy that wraps each request.\n *\n * @see {@link createServicePolicy}\n */\n readonly #policy: ServicePolicy;\n\n /**\n * Constructs a new ComplianceService object.\n *\n * @param args - The constructor arguments.\n * @param args.messenger - The messenger suited for this service.\n * @param args.fetch - A function that can be used to make an HTTP request.\n * @param args.apiUrl - The explicit Compliance API URL.\n * @param args.env - The fallback environment to use for the Compliance API\n * when `apiUrl` is not provided.\n * @param args.policyOptions - Options to pass to `createServicePolicy`, which\n * is used to wrap each request. See {@link CreateServicePolicyOptions}.\n */\n constructor({\n messenger,\n fetch: fetchFunction,\n apiUrl,\n env = 'production',\n policyOptions = {},\n }: ComplianceServiceOptions) {\n this.name = serviceName;\n this.#messenger = messenger;\n this.#fetch = fetchFunction;\n this.#complianceApiUrl = getComplianceApiUrl({ apiUrl, env });\n this.#policy = createServicePolicy(policyOptions);\n\n this.#messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n }\n\n /**\n * Registers a handler that will be called after a request returns a non-500\n * response, causing a retry.\n *\n * @param listener - The handler to be called.\n * @returns An object that can be used to unregister the handler.\n * @see {@link createServicePolicy}\n */\n onRetry(listener: Parameters<ServicePolicy['onRetry']>[0]): IDisposable {\n return this.#policy.onRetry(listener);\n }\n\n /**\n * Registers a handler that will be called after a set number of retry rounds\n * prove that requests to the API endpoint consistently return a 5xx response.\n *\n * @param listener - The handler to be called.\n * @returns An object that can be used to unregister the handler.\n * @see {@link createServicePolicy}\n */\n onBreak(listener: Parameters<ServicePolicy['onBreak']>[0]): IDisposable {\n return this.#policy.onBreak(listener);\n }\n\n /**\n * Registers a handler that will be called when the service is degraded due\n * to slow responses or repeated failures.\n *\n * @param listener - The handler to be called.\n * @returns An object that can be used to unregister the handler.\n * @see {@link createServicePolicy}\n */\n onDegraded(\n listener: Parameters<ServicePolicy['onDegraded']>[0],\n ): IDisposable {\n return this.#policy.onDegraded(listener);\n }\n\n /**\n * Checks compliance status for a single wallet address.\n *\n * @param address - The wallet address to check.\n * @returns The compliance status of the wallet.\n */\n async checkWalletCompliance(address: string): Promise<WalletCheckResponse> {\n const response = await this.#policy.execute(async () => {\n const url = new URL(\n `v1/wallet/${encodeURIComponent(address)}`,\n this.#complianceApiUrl,\n );\n const localResponse = await this.#fetch(url);\n if (!localResponse.ok) {\n throw new HttpError(\n localResponse.status,\n `Fetching '${url.toString()}' failed with status '${localResponse.status}'`,\n );\n }\n return localResponse;\n });\n const jsonResponse: unknown = await response.json();\n\n return validateResponse(\n jsonResponse,\n WalletCheckResponseStruct,\n 'compliance wallet check API',\n );\n }\n\n /**\n * Checks compliance status for multiple wallet addresses in a single request.\n *\n * @param addresses - The wallet addresses to check.\n * @returns The compliance statuses of the wallets.\n */\n async checkWalletsCompliance(\n addresses: string[],\n ): Promise<BatchWalletCheckResponseItem[]> {\n const response = await this.#policy.execute(async () => {\n const url = new URL('v1/wallet/batch', this.#complianceApiUrl);\n const localResponse = await this.#fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(addresses),\n });\n if (!localResponse.ok) {\n throw new HttpError(\n localResponse.status,\n `Fetching '${url.toString()}' failed with status '${localResponse.status}'`,\n );\n }\n return localResponse;\n });\n const jsonResponse: unknown = await response.json();\n\n return validateResponse(\n jsonResponse,\n array(BatchWalletCheckResponseItemStruct),\n 'compliance batch check API',\n );\n }\n}\n\nfunction getComplianceApiUrl({\n apiUrl,\n env,\n}: {\n apiUrl?: string;\n env: ComplianceServiceEnvironment;\n}): string {\n if (apiUrl === undefined) {\n return COMPLIANCE_API_URLS[env];\n }\n\n let url: URL;\n try {\n url = new URL(apiUrl);\n } catch {\n throw new Error(`Invalid Compliance API URL: ${apiUrl}`);\n }\n\n if (url.search || url.hash) {\n throw new Error(\n `Invalid Compliance API URL: ${apiUrl}. Query strings and fragments are not supported.`,\n );\n }\n if (!url.pathname.endsWith('/')) {\n url.pathname = `${url.pathname}/`;\n }\n return url.href;\n}\n\n/**\n * Validates an API response against a superstruct schema.\n *\n * @param data - The raw response data to validate.\n * @param struct - The superstruct schema to validate against.\n * @param struct.is - The type guard function from the schema.\n * @param apiName - A human-readable name for the API, used in error messages.\n * @returns The validated data.\n * @throws If the data does not match the schema.\n */\nfunction validateResponse<Response>(\n data: unknown,\n struct: { is: (value: unknown) => value is Response },\n apiName: string,\n): Response {\n if (struct.is(data)) {\n return data;\n }\n throw new Error(`Malformed response received from ${apiName}`);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ComplianceService.js","sourceRoot":"","sources":["../src/ComplianceService.ts"],"names":[],"mappings":"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","sourcesContent":["import { BaseDataService } from '@metamask/base-data-service';\nimport type {\n CreateServicePolicyOptions,\n DataServiceCacheUpdatedEvent,\n DataServiceGranularCacheUpdatedEvent,\n DataServiceInvalidateQueriesAction,\n} from '@metamask/base-data-service';\nimport { HttpError } from '@metamask/controller-utils';\nimport type { Messenger } from '@metamask/messenger';\nimport type { Infer } from '@metamask/superstruct';\nimport { array, boolean, object, string } from '@metamask/superstruct';\n\nimport type { ComplianceServiceMethodActions } from './ComplianceService-method-action-types.js';\n\n// === GENERAL ===\n\n/**\n * The name of the {@link ComplianceService}, used to namespace the service's\n * actions and events.\n */\nexport const serviceName = 'ComplianceService';\n\n/**\n * The supported environments for the Compliance API.\n */\nexport type ComplianceServiceEnvironment = 'production' | 'development';\n\nconst COMPLIANCE_API_URLS: Record<ComplianceServiceEnvironment, string> = {\n production: 'https://compliance.api.cx.metamask.io',\n development: 'https://compliance.dev-api.cx.metamask.io',\n};\n\nexport type ComplianceServiceOptions = {\n messenger: ComplianceServiceMessenger;\n fetch: typeof fetch;\n /**\n * Explicit Compliance API URL. Prefer this for application builds so API\n * endpoints can be managed by build configuration. Path components are\n * preserved as a base path for Compliance API routes.\n */\n apiUrl?: string;\n /**\n * Fallback environment used when `apiUrl` is not provided.\n */\n env?: ComplianceServiceEnvironment;\n policyOptions?: CreateServicePolicyOptions;\n};\n\n// === MESSENGER ===\n\nconst MESSENGER_EXPOSED_METHODS = [\n 'checkWalletCompliance',\n 'checkWalletsCompliance',\n] as const;\n\n/**\n * Invalidates cached queries for {@link ComplianceService}.\n */\nexport type ComplianceServiceInvalidateQueriesAction =\n DataServiceInvalidateQueriesAction<typeof serviceName>;\n\n/**\n * Actions that {@link ComplianceService} exposes to other consumers.\n */\nexport type ComplianceServiceActions =\n | ComplianceServiceMethodActions\n | ComplianceServiceInvalidateQueriesAction;\n\n/**\n * Actions from other messengers that {@link ComplianceService} calls.\n */\ntype AllowedActions = never;\n\n/**\n * Published when {@link ComplianceService}'s cache is updated.\n */\nexport type ComplianceServiceCacheUpdatedEvent = DataServiceCacheUpdatedEvent<\n typeof serviceName\n>;\n\n/**\n * Published when a key within {@link ComplianceService}'s cache is updated.\n */\nexport type ComplianceServiceGranularCacheUpdatedEvent =\n DataServiceGranularCacheUpdatedEvent<typeof serviceName>;\n\n/**\n * Events that {@link ComplianceService} exposes to other consumers.\n */\nexport type ComplianceServiceEvents =\n | ComplianceServiceCacheUpdatedEvent\n | ComplianceServiceGranularCacheUpdatedEvent;\n\n/**\n * Events from other messengers that {@link ComplianceService} subscribes to.\n */\ntype AllowedEvents = never;\n\n/**\n * The messenger restricted to actions and events accessed by\n * {@link ComplianceService}.\n */\nexport type ComplianceServiceMessenger = Messenger<\n typeof serviceName,\n ComplianceServiceActions | AllowedActions,\n ComplianceServiceEvents | AllowedEvents\n>;\n\n// === API RESPONSE SCHEMAS ===\n\n/**\n * Schema for the response from `GET /v1/wallet/:address`.\n */\nconst WalletCheckResponseStruct = object({\n address: string(),\n blocked: boolean(),\n});\n\n/**\n * The validated shape of a single wallet compliance check response.\n */\ntype WalletCheckResponse = Infer<typeof WalletCheckResponseStruct>;\n\n/**\n * Schema for each item in the response from `POST /v1/wallet/batch`.\n * Reuses the same shape as a single wallet check.\n */\nconst BatchWalletCheckResponseItemStruct = WalletCheckResponseStruct;\n\n/**\n * The validated shape of a single item in a batch compliance check response.\n */\ntype BatchWalletCheckResponseItem = Infer<\n typeof BatchWalletCheckResponseItemStruct\n>;\n\n// === SERVICE DEFINITION ===\n\n/**\n * `ComplianceService` communicates with the Compliance API to check whether\n * wallet addresses are sanctioned under OFAC regulations.\n *\n * @example\n *\n * ``` ts\n * import { Messenger } from '@metamask/messenger';\n * import type {\n * ComplianceServiceActions,\n * ComplianceServiceEvents,\n * } from '@metamask/compliance-controller';\n * import { ComplianceService } from '@metamask/compliance-controller';\n *\n * const rootMessenger = new Messenger<\n * 'Root',\n * ComplianceServiceActions,\n * ComplianceServiceEvents,\n * >({ namespace: 'Root' });\n * const serviceMessenger = new Messenger<\n * 'ComplianceService',\n * ComplianceServiceActions,\n * ComplianceServiceEvents,\n * typeof rootMessenger,\n * >({\n * namespace: 'ComplianceService',\n * parent: rootMessenger,\n * });\n * new ComplianceService({\n * messenger: serviceMessenger,\n * fetch,\n * apiUrl: 'https://compliance.api.cx.metamask.io',\n * });\n *\n * // Check a single wallet\n * const result = await rootMessenger.call(\n * 'ComplianceService:checkWalletCompliance',\n * '0x1234...',\n * );\n * // => { address: '0x1234...', blocked: false }\n * ```\n */\nexport class ComplianceService extends BaseDataService<\n typeof serviceName,\n ComplianceServiceMessenger\n> {\n /**\n * A function that can be used to make an HTTP request.\n */\n readonly #fetch: typeof fetch;\n\n /**\n * The resolved base URL for the Compliance API.\n */\n readonly #complianceApiUrl: string;\n\n /**\n * Constructs a new ComplianceService object.\n *\n * @param args - The constructor arguments.\n * @param args.messenger - The messenger suited for this service.\n * @param args.fetch - A function that can be used to make an HTTP request.\n * @param args.apiUrl - The explicit Compliance API URL.\n * @param args.env - The fallback environment to use for the Compliance API\n * when `apiUrl` is not provided.\n * @param args.policyOptions - Options to pass to `createServicePolicy`, which\n * is used to wrap each request. See {@link CreateServicePolicyOptions}.\n */\n constructor({\n messenger,\n fetch: fetchFunction,\n apiUrl,\n env = 'production',\n policyOptions = {},\n }: ComplianceServiceOptions) {\n super({ name: serviceName, messenger, policyOptions });\n this.#fetch = fetchFunction;\n this.#complianceApiUrl = getComplianceApiUrl({ apiUrl, env });\n\n this.messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n }\n\n /**\n * Checks compliance status for a single wallet address.\n *\n * @param address - The wallet address to check.\n * @returns The compliance status of the wallet.\n */\n async checkWalletCompliance(address: string): Promise<WalletCheckResponse> {\n return this.fetchQuery({\n queryKey: [`${this.name}:checkWalletCompliance`, address],\n staleTime: 0,\n gcTime: 0,\n responseStruct: WalletCheckResponseStruct,\n queryFn: async () => {\n const url = new URL(\n `v1/wallet/${encodeURIComponent(address)}`,\n this.#complianceApiUrl,\n );\n const response = await this.#fetch(url);\n if (!response.ok) {\n throw new HttpError(\n response.status,\n `Fetching '${url.toString()}' failed with status '${response.status}'`,\n );\n }\n return (await response.json()) as WalletCheckResponse;\n },\n });\n }\n\n /**\n * Checks compliance status for multiple wallet addresses in a single request.\n *\n * @param addresses - The wallet addresses to check.\n * @returns The compliance statuses of the wallets.\n */\n async checkWalletsCompliance(\n addresses: string[],\n ): Promise<BatchWalletCheckResponseItem[]> {\n return this.fetchQuery({\n queryKey: [`${this.name}:checkWalletsCompliance`, addresses],\n staleTime: 0,\n gcTime: 0,\n responseStruct: array(BatchWalletCheckResponseItemStruct),\n queryFn: async () => {\n const url = new URL('v1/wallet/batch', this.#complianceApiUrl);\n const response = await this.#fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(addresses),\n });\n if (!response.ok) {\n throw new HttpError(\n response.status,\n `Fetching '${url.toString()}' failed with status '${response.status}'`,\n );\n }\n return (await response.json()) as BatchWalletCheckResponseItem[];\n },\n });\n }\n}\n\nfunction getComplianceApiUrl({\n apiUrl,\n env,\n}: {\n apiUrl?: string;\n env: ComplianceServiceEnvironment;\n}): string {\n if (apiUrl === undefined) {\n return COMPLIANCE_API_URLS[env];\n }\n\n let url: URL;\n try {\n url = new URL(apiUrl);\n } catch {\n throw new Error(`Invalid Compliance API URL: ${apiUrl}`);\n }\n\n if (url.search || url.hash) {\n throw new Error(\n `Invalid Compliance API URL: ${apiUrl}. Query strings and fragments are not supported.`,\n );\n }\n if (!url.pathname.endsWith('/')) {\n url.pathname = `${url.pathname}/`;\n }\n return url.href;\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { ComplianceServiceActions, ComplianceServiceEnvironment, ComplianceServiceEvents, ComplianceServiceMessenger, ComplianceServiceOptions, } from './ComplianceService.js';
|
|
1
|
+
export type { ComplianceServiceActions, ComplianceServiceCacheUpdatedEvent, ComplianceServiceEnvironment, ComplianceServiceEvents, ComplianceServiceGranularCacheUpdatedEvent, ComplianceServiceInvalidateQueriesAction, ComplianceServiceMessenger, ComplianceServiceOptions, } from './ComplianceService.js';
|
|
2
2
|
export type { ComplianceServiceCheckWalletComplianceAction, ComplianceServiceCheckWalletsComplianceAction, } from './ComplianceService-method-action-types.js';
|
|
3
3
|
export { ComplianceService } from './ComplianceService.js';
|
|
4
4
|
export type { ComplianceControllerActions, ComplianceControllerEvents, ComplianceControllerGetStateAction, ComplianceControllerMessenger, ComplianceControllerState, ComplianceControllerStateChangeEvent, } from './ComplianceController.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,wBAAwB,EACxB,4BAA4B,EAC5B,uBAAuB,EACvB,0BAA0B,EAC1B,wBAAwB,GACzB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EACV,4CAA4C,EAC5C,6CAA6C,GAC9C,MAAM,4CAA4C,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,YAAY,EACV,2BAA2B,EAC3B,0BAA0B,EAC1B,kCAAkC,EAClC,6BAA6B,EAC7B,yBAAyB,EACzB,oCAAoC,GACrC,MAAM,2BAA2B,CAAC;AACnC,YAAY,EACV,+CAA+C,EAC/C,gDAAgD,EAChD,8CAA8C,GAC/C,MAAM,+CAA+C,CAAC;AACvD,OAAO,EACL,oBAAoB,EACpB,mCAAmC,GACpC,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,0BAA0B,EAC1B,qBAAqB,GACtB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,wBAAwB,EACxB,kCAAkC,EAClC,4BAA4B,EAC5B,uBAAuB,EACvB,0CAA0C,EAC1C,wCAAwC,EACxC,0BAA0B,EAC1B,wBAAwB,GACzB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EACV,4CAA4C,EAC5C,6CAA6C,GAC9C,MAAM,4CAA4C,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,YAAY,EACV,2BAA2B,EAC3B,0BAA0B,EAC1B,kCAAkC,EAClC,6BAA6B,EAC7B,yBAAyB,EACzB,oCAAoC,GACrC,MAAM,2BAA2B,CAAC;AACnC,YAAY,EACV,+CAA+C,EAC/C,gDAAgD,EAChD,8CAA8C,GAC/C,MAAM,+CAA+C,CAAC;AACvD,OAAO,EACL,oBAAoB,EACpB,mCAAmC,GACpC,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,0BAA0B,EAC1B,qBAAqB,GACtB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAc3D,OAAO,EACL,oBAAoB,EACpB,mCAAmC,GACpC,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,0BAA0B,EAC1B,qBAAqB,GACtB,MAAM,gBAAgB,CAAC","sourcesContent":["export type {\n ComplianceServiceActions,\n ComplianceServiceCacheUpdatedEvent,\n ComplianceServiceEnvironment,\n ComplianceServiceEvents,\n ComplianceServiceGranularCacheUpdatedEvent,\n ComplianceServiceInvalidateQueriesAction,\n ComplianceServiceMessenger,\n ComplianceServiceOptions,\n} from './ComplianceService.js';\nexport type {\n ComplianceServiceCheckWalletComplianceAction,\n ComplianceServiceCheckWalletsComplianceAction,\n} from './ComplianceService-method-action-types.js';\nexport { ComplianceService } from './ComplianceService.js';\nexport type {\n ComplianceControllerActions,\n ComplianceControllerEvents,\n ComplianceControllerGetStateAction,\n ComplianceControllerMessenger,\n ComplianceControllerState,\n ComplianceControllerStateChangeEvent,\n} from './ComplianceController.js';\nexport type {\n ComplianceControllerCheckWalletComplianceAction,\n ComplianceControllerCheckWalletsComplianceAction,\n ComplianceControllerClearComplianceStateAction,\n} from './ComplianceController-method-action-types.js';\nexport {\n ComplianceController,\n getDefaultComplianceControllerState,\n} from './ComplianceController.js';\nexport {\n selectAreAnyWalletsBlocked,\n selectIsWalletBlocked,\n} from './selectors.js';\nexport type { WalletComplianceStatus } from './types.js';\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metamask-previews/compliance-controller",
|
|
3
|
-
"version": "3.0.0-preview-
|
|
3
|
+
"version": "3.0.0-preview-6fbab7311",
|
|
4
4
|
"description": "Manages OFAC compliance checks for wallet addresses",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Ethereum",
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@metamask/base-controller": "^10.1.0",
|
|
54
|
+
"@metamask/base-data-service": "^2.1.0",
|
|
54
55
|
"@metamask/controller-utils": "^13.0.0",
|
|
55
56
|
"@metamask/messenger": "^3.0.0",
|
|
56
57
|
"@metamask/superstruct": "^3.4.1",
|
|
@@ -59,7 +60,7 @@
|
|
|
59
60
|
"devDependencies": {
|
|
60
61
|
"@metamask/auto-changelog": "^6.2.1",
|
|
61
62
|
"@typescript/native": "npm:typescript@^7.0.2",
|
|
62
|
-
"nock": "^
|
|
63
|
+
"nock": "^15.0.5",
|
|
63
64
|
"rimraf": "^6.1.3",
|
|
64
65
|
"typedoc": "^0.28.20",
|
|
65
66
|
"typedoc-plugin-missing-exports": "^4.1.4",
|