@gearbox-protocol/sdk 16.6.0-next.4 → 16.6.0-next.6
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/dist/cjs/dev/index.js +5 -2
- package/dist/cjs/dev/kycUtils.js +56 -30
- package/dist/cjs/dev/midasUtils.js +41 -14
- package/dist/cjs/dev/securitizeUtils.js +74 -38
- package/dist/esm/dev/index.js +3 -3
- package/dist/esm/dev/kycUtils.js +57 -33
- package/dist/esm/dev/midasUtils.js +39 -13
- package/dist/esm/dev/securitizeUtils.js +76 -40
- package/dist/esm/sdk/prepare/PrepareApi.js +6 -6
- package/dist/types/dev/index.d.ts +3 -3
- package/dist/types/dev/kycUtils.d.ts +15 -20
- package/dist/types/dev/midasUtils.d.ts +21 -5
- package/dist/types/dev/securitizeUtils.d.ts +3 -9
- package/package.json +1 -1
package/dist/cjs/dev/index.js
CHANGED
|
@@ -29,6 +29,7 @@ const require_dev_withdrawalUtils = require("./withdrawalUtils.js");
|
|
|
29
29
|
exports.AccountOpener = require_dev_AccountOpener.AccountOpener;
|
|
30
30
|
exports.Create2Deployer = require_dev_create2.Create2Deployer;
|
|
31
31
|
exports.DEFAULT_CREATE2_SALT = require_dev_create2.DEFAULT_CREATE2_SALT;
|
|
32
|
+
exports.DEFAULT_MIDAS_ADMIN = require_dev_kycUtils.DEFAULT_MIDAS_ADMIN;
|
|
32
33
|
exports.EthCallSpy = require_dev_EthCallSpy.EthCallSpy;
|
|
33
34
|
exports.NoAvailableTransportsError = require_dev_RevolverTransport.NoAvailableTransportsError;
|
|
34
35
|
exports.ONCHAIN_EXECUTION_ID_ADDRESS = require_dev_verifyTestnet.ONCHAIN_EXECUTION_ID_ADDRESS;
|
|
@@ -42,12 +43,13 @@ exports.calcLiquidatableLTs = require_dev_calcLiquidatableLTs.calcLiquidatableLT
|
|
|
42
43
|
exports.claimDSToken = require_dev_securitizeUtils.claimDSToken;
|
|
43
44
|
exports.claimDSTokens = require_dev_securitizeUtils.claimDSTokens;
|
|
44
45
|
exports.claimFromFaucet = require_dev_claimFromFaucet.claimFromFaucet;
|
|
45
|
-
exports.collectMidasGateways = require_dev_midasUtils.collectMidasGateways;
|
|
46
|
-
exports.collectMidasMTokens = require_dev_midasUtils.collectMidasMTokens;
|
|
47
46
|
exports.createAnvilClient = require_dev_createAnvilClient.createAnvilClient;
|
|
48
47
|
exports.createMinter = require_dev_mint_factory.createMinter;
|
|
49
48
|
exports.deployUsingPublicCreate2 = require_dev_create2.deployUsingPublicCreate2;
|
|
50
49
|
exports.detectChain = require_dev_detectChain.detectChain;
|
|
50
|
+
exports.discoverMidasCreditSuites = require_dev_midasUtils.discoverMidasCreditSuites;
|
|
51
|
+
exports.discoverMidasGateways = require_dev_midasUtils.discoverMidasGateways;
|
|
52
|
+
exports.discoverMidasMTokens = require_dev_midasUtils.discoverMidasMTokens;
|
|
51
53
|
exports.enableDSTokenBackDating = require_dev_securitizeUtils.enableDSTokenBackDating;
|
|
52
54
|
exports.evmMineDetailed = require_dev_createAnvilClient.evmMineDetailed;
|
|
53
55
|
exports.extendAnvilClient = require_dev_createAnvilClient.extendAnvilClient;
|
|
@@ -57,6 +59,7 @@ exports.getDrpcUrl = require_dev_providers.getDrpcUrl;
|
|
|
57
59
|
exports.getErpcKey = require_dev_providers.getErpcKey;
|
|
58
60
|
exports.getPublicCreate2Address = require_dev_create2.getPublicCreate2Address;
|
|
59
61
|
exports.getRpcProviderUrl = require_dev_providers.getRpcProviderUrl;
|
|
62
|
+
exports.getSecuritizeAdmin = require_dev_kycUtils.getSecuritizeAdmin;
|
|
60
63
|
exports.getThirdWebUrl = require_dev_providers.getThirdWebUrl;
|
|
61
64
|
exports.greenlistMidasGateway = require_dev_kycUtils.greenlistMidasGateway;
|
|
62
65
|
exports.httpTransportOptionsSchema = require_dev_transports.httpTransportOptionsSchema;
|
package/dist/cjs/dev/kycUtils.js
CHANGED
|
@@ -8,19 +8,49 @@ const require_abi_rwa_iDSToken = require("../abi/rwa/iDSToken.js");
|
|
|
8
8
|
const require_dev_midasUtils = require("./midasUtils.js");
|
|
9
9
|
const require_dev_withdrawalAbi = require("./withdrawalAbi.js");
|
|
10
10
|
let viem = require("viem");
|
|
11
|
-
let viem_accounts = require("viem/accounts");
|
|
12
11
|
//#region src/dev/kycUtils.ts
|
|
12
|
+
const iMockDSTokenAdminAbi = (0, viem.parseAbi)(["function admin() view returns (address)"]);
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
14
|
+
* DS trust-service master of `token`: Ownable owner at storage slot 0 of the
|
|
15
|
+
* trust service, same as periphery-v3 SecuritizeAttachHelper. MockDSToken
|
|
16
|
+
* returns itself as the trust service, so `admin()` is used instead.
|
|
17
|
+
*/
|
|
18
|
+
async function getSecuritizeAdmin(anvil, token, logger) {
|
|
19
|
+
const trustServiceId = await anvil.readContract({
|
|
20
|
+
address: token,
|
|
21
|
+
abi: require_abi_rwa_iDSToken.iDSTokenAbi,
|
|
22
|
+
functionName: "TRUST_SERVICE"
|
|
23
|
+
});
|
|
24
|
+
const trustService = await anvil.readContract({
|
|
25
|
+
address: token,
|
|
26
|
+
abi: require_abi_rwa_iDSToken.iDSTokenAbi,
|
|
27
|
+
functionName: "getDSService",
|
|
28
|
+
args: [trustServiceId]
|
|
29
|
+
});
|
|
30
|
+
if ((0, viem.isAddressEqual)(trustService, token)) {
|
|
31
|
+
const admin = await anvil.readContract({
|
|
32
|
+
address: token,
|
|
33
|
+
abi: iMockDSTokenAdminAbi,
|
|
34
|
+
functionName: "admin"
|
|
35
|
+
});
|
|
36
|
+
logger?.debug(`securitize: found admin ${admin} for ${token} (token.admin)`);
|
|
37
|
+
return admin;
|
|
38
|
+
}
|
|
39
|
+
const word = await anvil.getStorageAt({
|
|
40
|
+
address: trustService,
|
|
41
|
+
slot: (0, viem.toHex)(0, { size: 32 })
|
|
42
|
+
});
|
|
43
|
+
if (!word) throw new Error(`securitize: empty trust-service owner slot on ${trustService}`);
|
|
44
|
+
const admin = (0, viem.getAddress)((0, viem.slice)(word, 12));
|
|
45
|
+
logger?.debug(`securitize: found admin ${admin} for ${token} (trust service ${trustService})`);
|
|
46
|
+
return admin;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Impersonates the DS trust-service admin of `token` on the fork
|
|
16
50
|
*/
|
|
17
51
|
async function useSecuritizeAdmin(props) {
|
|
18
|
-
const { anvil,
|
|
19
|
-
|
|
20
|
-
account: (0, viem_accounts.privateKeyToAccount)(adminPrivateKey),
|
|
21
|
-
release: async () => {}
|
|
22
|
-
};
|
|
23
|
-
if (!admin) throw new Error("securitize: either adminPrivateKey or admin address is required");
|
|
52
|
+
const { anvil, token, logger } = props;
|
|
53
|
+
const admin = await getSecuritizeAdmin(anvil, token, logger);
|
|
24
54
|
await anvil.impersonateAccount({ address: admin });
|
|
25
55
|
await anvil.setBalance({
|
|
26
56
|
address: admin,
|
|
@@ -260,8 +290,7 @@ async function greenlistMidasGateway(props) {
|
|
|
260
290
|
}
|
|
261
291
|
}
|
|
262
292
|
/**
|
|
263
|
-
* Finds the Midas gateway that issues `token`
|
|
264
|
-
* adapters of all loaded credit managers and the gateways of Midas degen NFTs
|
|
293
|
+
* Finds the Midas gateway that issues `token` among the loaded credit managers.
|
|
265
294
|
*/
|
|
266
295
|
async function findMidasGateway(props) {
|
|
267
296
|
const { anvil, token, marketConfigurators, logger } = props;
|
|
@@ -273,21 +302,19 @@ async function findMidasGateway(props) {
|
|
|
273
302
|
});
|
|
274
303
|
await sdk.attach({ marketConfigurators });
|
|
275
304
|
}
|
|
276
|
-
const
|
|
277
|
-
if (
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
abi: require_dev_withdrawalAbi.midasGatewayAbi,
|
|
283
|
-
functionName: "mToken"
|
|
284
|
-
}))
|
|
285
|
-
})).findIndex((mToken) => (0, viem.isAddressEqual)(mToken, token));
|
|
286
|
-
if (index === -1) throw new Error(`no midas gateway found for token ${token}`);
|
|
287
|
-
logger?.debug(`midas: gateway for ${token} is ${candidates[index]}`);
|
|
288
|
-
return candidates[index];
|
|
305
|
+
const suites = await require_dev_midasUtils.discoverMidasCreditSuites(sdk);
|
|
306
|
+
if (suites.length === 0) throw new Error("no midas gateway adapters or midas degen NFTs found in loaded markets");
|
|
307
|
+
const suite = suites.find(({ mToken }) => (0, viem.isAddressEqual)(mToken, token));
|
|
308
|
+
if (!suite) throw new Error(`no midas gateway found for token ${token}`);
|
|
309
|
+
logger?.debug(`midas: gateway for ${token} is ${suite.gateway}`);
|
|
310
|
+
return suite.gateway;
|
|
289
311
|
}
|
|
290
312
|
/**
|
|
313
|
+
* Midas access control admin on mainnet. Holds the admin role of the greenlist
|
|
314
|
+
* operator role.
|
|
315
|
+
*/
|
|
316
|
+
const DEFAULT_MIDAS_ADMIN = "0xd4195CF4df289a4748C1A7B6dDBE770e27bA1227";
|
|
317
|
+
/**
|
|
291
318
|
* Registers a wallet as an investor (fake KYC) in every Securitize DSToken and
|
|
292
319
|
* every Midas gateway of the markets loaded by `sdk`.
|
|
293
320
|
*
|
|
@@ -295,17 +322,15 @@ async function findMidasGateway(props) {
|
|
|
295
322
|
* result instead of aborting the whole run.
|
|
296
323
|
*/
|
|
297
324
|
async function registerRWAInvestor(props) {
|
|
298
|
-
const { anvil, sdk, investor,
|
|
325
|
+
const { anvil, sdk, investor, midasAdmin = DEFAULT_MIDAS_ADMIN, logger } = props;
|
|
299
326
|
const securitizeTokens = [];
|
|
300
327
|
const midasGateways = [];
|
|
301
328
|
const failed = [];
|
|
302
329
|
const dsTokens = new require_onchain_utils_AddressSet.AddressSet(sdk.rwa.factories.flatMap((factory) => factory.getTokens()));
|
|
303
|
-
|
|
330
|
+
for (const token of dsTokens) try {
|
|
304
331
|
await registerSecuritizeInvestor({
|
|
305
332
|
anvil,
|
|
306
333
|
investor,
|
|
307
|
-
adminPrivateKey,
|
|
308
|
-
admin: securitizeAdmin,
|
|
309
334
|
token,
|
|
310
335
|
logger
|
|
311
336
|
});
|
|
@@ -318,8 +343,7 @@ async function registerRWAInvestor(props) {
|
|
|
318
343
|
error: e
|
|
319
344
|
});
|
|
320
345
|
}
|
|
321
|
-
|
|
322
|
-
const gateways = await require_dev_midasUtils.collectMidasGateways(sdk);
|
|
346
|
+
const gateways = await require_dev_midasUtils.discoverMidasGateways(sdk);
|
|
323
347
|
for (const gateway of gateways) try {
|
|
324
348
|
await greenlistMidasGateway({
|
|
325
349
|
anvil,
|
|
@@ -345,6 +369,8 @@ async function registerRWAInvestor(props) {
|
|
|
345
369
|
};
|
|
346
370
|
}
|
|
347
371
|
//#endregion
|
|
372
|
+
exports.DEFAULT_MIDAS_ADMIN = DEFAULT_MIDAS_ADMIN;
|
|
373
|
+
exports.getSecuritizeAdmin = getSecuritizeAdmin;
|
|
348
374
|
exports.greenlistMidasGateway = greenlistMidasGateway;
|
|
349
375
|
exports.registerMidasInvestor = registerMidasInvestor;
|
|
350
376
|
exports.registerRWAInvestor = registerRWAInvestor;
|
|
@@ -147,29 +147,56 @@ async function unpauseMidasIssuanceVault(props) {
|
|
|
147
147
|
}
|
|
148
148
|
};
|
|
149
149
|
}
|
|
150
|
-
|
|
151
|
-
|
|
150
|
+
/**
|
|
151
|
+
* Midas credit suites of the loaded credit managers.
|
|
152
|
+
*
|
|
153
|
+
* A manager with a gateway adapter is a suite on its own, so its degen NFT
|
|
154
|
+
* is not consulted. Suites that only have a Midas degen NFT read `mToken`
|
|
155
|
+
* from the gateway.
|
|
156
|
+
*/
|
|
157
|
+
async function discoverMidasCreditSuites(sdk) {
|
|
158
|
+
const suites = [];
|
|
159
|
+
for (const cm of sdk.marketRegister.creditManagers) {
|
|
160
|
+
const creditManager = cm.creditManager.address;
|
|
161
|
+
let hasAdapter = false;
|
|
162
|
+
for (const adapter of cm.creditManager.adapters.values()) if (adapter instanceof require_onchain_market_adapters_contracts_MidasGatewayAdapterContract.MidasGatewayAdapterContract) {
|
|
163
|
+
hasAdapter = true;
|
|
164
|
+
suites.push({
|
|
165
|
+
creditManager,
|
|
166
|
+
gateway: adapter.gateway,
|
|
167
|
+
mToken: adapter.mToken
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
if (hasAdapter) continue;
|
|
171
|
+
const nft = await cm.degenNFT();
|
|
172
|
+
if (nft instanceof require_onchain_market_rwa_midas_MidasDegenNFT.MidasDegenNFT) {
|
|
173
|
+
const [mToken] = await nft.getTokens();
|
|
174
|
+
suites.push({
|
|
175
|
+
creditManager,
|
|
176
|
+
gateway: nft.gateway,
|
|
177
|
+
mToken
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return suites;
|
|
152
182
|
}
|
|
153
183
|
/**
|
|
154
|
-
*
|
|
155
|
-
* plus gateways of Midas degen NFTs (permissioned-mode suites with no gateway adapter).
|
|
184
|
+
* Gateways of the Midas credit suites of the loaded credit managers.
|
|
156
185
|
*/
|
|
157
|
-
async function
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
const nfts = await Promise.all(sdk.marketRegister.creditManagers.map((cm) => cm.degenNFT()));
|
|
161
|
-
for (const nft of nfts) if (nft instanceof require_onchain_market_rwa_midas_MidasDegenNFT.MidasDegenNFT) gateways.add(nft.gateway);
|
|
162
|
-
return gateways.asArray();
|
|
186
|
+
async function discoverMidasGateways(sdk) {
|
|
187
|
+
const suites = await discoverMidasCreditSuites(sdk);
|
|
188
|
+
return new require_onchain_utils_AddressSet.AddressSet(suites.map((suite) => suite.gateway)).asArray();
|
|
163
189
|
}
|
|
164
190
|
/**
|
|
165
191
|
* mTokens of all Midas gateway adapters of the loaded credit managers
|
|
166
192
|
*/
|
|
167
|
-
function
|
|
193
|
+
function discoverMidasMTokens(sdk) {
|
|
168
194
|
const mTokens = new require_onchain_utils_AddressSet.AddressSet();
|
|
169
|
-
for (const adapter of
|
|
195
|
+
for (const cm of sdk.marketRegister.creditManagers) for (const adapter of cm.creditManager.adapters.values()) if (adapter instanceof require_onchain_market_adapters_contracts_MidasGatewayAdapterContract.MidasGatewayAdapterContract) mTokens.add(adapter.mToken);
|
|
170
196
|
return mTokens.asArray();
|
|
171
197
|
}
|
|
172
198
|
//#endregion
|
|
173
|
-
exports.
|
|
174
|
-
exports.
|
|
199
|
+
exports.discoverMidasCreditSuites = discoverMidasCreditSuites;
|
|
200
|
+
exports.discoverMidasGateways = discoverMidasGateways;
|
|
201
|
+
exports.discoverMidasMTokens = discoverMidasMTokens;
|
|
175
202
|
exports.unpauseMidasIssuanceVault = unpauseMidasIssuanceVault;
|
|
@@ -5,7 +5,6 @@ require("../onchain/index.js");
|
|
|
5
5
|
const require_abi_rwa_iDSToken = require("../abi/rwa/iDSToken.js");
|
|
6
6
|
const require_dev_kycUtils = require("./kycUtils.js");
|
|
7
7
|
let viem = require("viem");
|
|
8
|
-
let viem_accounts = require("viem/accounts");
|
|
9
8
|
//#region src/dev/securitizeUtils.ts
|
|
10
9
|
/**
|
|
11
10
|
* `COMPLIANCE_CONFIGURATION_SERVICE` id in the DS protocol service registry
|
|
@@ -111,7 +110,7 @@ async function issueDSTokens(props) {
|
|
|
111
110
|
* Tokens without one (e.g. MockDSToken) are skipped
|
|
112
111
|
*/
|
|
113
112
|
async function getComplianceConfigurationServices({ anvil, tokens, logger }) {
|
|
114
|
-
const services = new
|
|
113
|
+
const services = /* @__PURE__ */ new Map();
|
|
115
114
|
for (const token of new require_onchain_utils_AddressSet.AddressSet(tokens)) try {
|
|
116
115
|
const service = await anvil.readContract({
|
|
117
116
|
address: token,
|
|
@@ -123,11 +122,16 @@ async function getComplianceConfigurationServices({ anvil, tokens, logger }) {
|
|
|
123
122
|
logger?.debug(`${token} has no compliance configuration service`);
|
|
124
123
|
continue;
|
|
125
124
|
}
|
|
126
|
-
|
|
125
|
+
const checksummed = (0, viem.getAddress)(service);
|
|
126
|
+
if (services.has(checksummed)) continue;
|
|
127
|
+
services.set(checksummed, await require_dev_kycUtils.getSecuritizeAdmin(anvil, token, logger));
|
|
127
128
|
} catch (e) {
|
|
128
129
|
logger?.debug(`Failed to get compliance configuration service of ${token}: ${e}`);
|
|
129
130
|
}
|
|
130
|
-
return [...services]
|
|
131
|
+
return [...services].map(([service, admin]) => ({
|
|
132
|
+
service,
|
|
133
|
+
admin
|
|
134
|
+
}));
|
|
131
135
|
}
|
|
132
136
|
/**
|
|
133
137
|
* Sets `disallowBackDating` to false on compliance configuration services of
|
|
@@ -135,18 +139,17 @@ async function getComplianceConfigurationServices({ anvil, tokens, logger }) {
|
|
|
135
139
|
* honoured: DS protocol silently replaces it with `block.timestamp` otherwise,
|
|
136
140
|
* and freshly minted tokens stay under lock-up.
|
|
137
141
|
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
142
|
+
* Impersonates the DS trust-service admin of each token. Ownable `owner()` of
|
|
143
|
+
* the compliance configuration service alone is not enough.
|
|
140
144
|
*
|
|
141
145
|
* The flag is only read while tokens are issued, so restoring it does not
|
|
142
146
|
* re-lock tokens minted in the meantime.
|
|
143
147
|
*/
|
|
144
148
|
async function enableDSTokenBackDating(props) {
|
|
145
|
-
const { anvil,
|
|
146
|
-
const account = (0, viem_accounts.privateKeyToAccount)(adminPrivateKey);
|
|
149
|
+
const { anvil, logger } = props;
|
|
147
150
|
const services = await getComplianceConfigurationServices(props);
|
|
148
151
|
let toRestore = [];
|
|
149
|
-
for (const service of services) {
|
|
152
|
+
for (const { service, admin } of services) {
|
|
150
153
|
let disallowBackDating;
|
|
151
154
|
try {
|
|
152
155
|
disallowBackDating = await anvil.readContract({
|
|
@@ -163,32 +166,53 @@ async function enableDSTokenBackDating(props) {
|
|
|
163
166
|
continue;
|
|
164
167
|
}
|
|
165
168
|
logger?.info(`Allowing back-dating on ${service}`);
|
|
166
|
-
await anvil.
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
169
|
+
await anvil.impersonateAccount({ address: admin });
|
|
170
|
+
try {
|
|
171
|
+
await anvil.setBalance({
|
|
172
|
+
address: admin,
|
|
173
|
+
value: (0, viem.parseEther)("100")
|
|
174
|
+
});
|
|
175
|
+
await anvil.writeContractSync({
|
|
176
|
+
account: admin,
|
|
177
|
+
chain: anvil.chain,
|
|
178
|
+
address: service,
|
|
179
|
+
abi: iDSComplianceConfigurationServiceAbi,
|
|
180
|
+
functionName: "setDisallowBackDating",
|
|
181
|
+
args: [false],
|
|
182
|
+
throwOnReceiptRevert: true
|
|
183
|
+
});
|
|
184
|
+
} finally {
|
|
185
|
+
await anvil.stopImpersonatingAccount({ address: admin });
|
|
186
|
+
}
|
|
187
|
+
toRestore.push({
|
|
188
|
+
service,
|
|
189
|
+
admin
|
|
174
190
|
});
|
|
175
|
-
toRestore.push(service);
|
|
176
191
|
}
|
|
177
192
|
return async () => {
|
|
178
193
|
const services = toRestore;
|
|
179
194
|
toRestore = [];
|
|
180
|
-
for (const service of services) {
|
|
195
|
+
for (const { service, admin } of services) {
|
|
181
196
|
logger?.info(`Disallowing back-dating on ${service}`);
|
|
182
197
|
try {
|
|
183
|
-
await anvil.
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
198
|
+
await anvil.impersonateAccount({ address: admin });
|
|
199
|
+
try {
|
|
200
|
+
await anvil.setBalance({
|
|
201
|
+
address: admin,
|
|
202
|
+
value: (0, viem.parseEther)("100")
|
|
203
|
+
});
|
|
204
|
+
await anvil.writeContractSync({
|
|
205
|
+
account: admin,
|
|
206
|
+
chain: anvil.chain,
|
|
207
|
+
address: service,
|
|
208
|
+
abi: iDSComplianceConfigurationServiceAbi,
|
|
209
|
+
functionName: "setDisallowBackDating",
|
|
210
|
+
args: [true],
|
|
211
|
+
throwOnReceiptRevert: true
|
|
212
|
+
});
|
|
213
|
+
} finally {
|
|
214
|
+
await anvil.stopImpersonatingAccount({ address: admin });
|
|
215
|
+
}
|
|
192
216
|
} catch (e) {
|
|
193
217
|
logger?.warn(`Failed to restore disallowBackDating on ${service}: ${e}`);
|
|
194
218
|
}
|
|
@@ -196,8 +220,7 @@ async function enableDSTokenBackDating(props) {
|
|
|
196
220
|
};
|
|
197
221
|
}
|
|
198
222
|
async function claimDSToken(props) {
|
|
199
|
-
const { anvil, investor,
|
|
200
|
-
const account = (0, viem_accounts.privateKeyToAccount)(adminPrivateKey);
|
|
223
|
+
const { anvil, investor, token, marketConfigurators, rwaFactories, usdAmount: usdAmountProp = "100000" } = props;
|
|
201
224
|
const symbol = await anvil.readContract({
|
|
202
225
|
address: token,
|
|
203
226
|
abi: viem.erc20Abi,
|
|
@@ -221,18 +244,31 @@ async function claimDSToken(props) {
|
|
|
221
244
|
if (amount === 0n) throw new Error(`No market found for token ${token}`);
|
|
222
245
|
logger?.debug(`${usdAmountProp} USD === ${amount} ${symbol}`);
|
|
223
246
|
await require_dev_kycUtils.registerSecuritizeInvestor({
|
|
224
|
-
...props,
|
|
225
|
-
logger
|
|
226
|
-
});
|
|
227
|
-
logger?.debug(`Issuing ${amount} tokens to ${investor}...`);
|
|
228
|
-
const mintHash = await issueDSTokens({
|
|
229
247
|
anvil,
|
|
230
|
-
account,
|
|
231
|
-
token,
|
|
232
248
|
investor,
|
|
233
|
-
|
|
249
|
+
token,
|
|
234
250
|
logger
|
|
235
251
|
});
|
|
252
|
+
const admin = await require_dev_kycUtils.getSecuritizeAdmin(anvil, token, logger);
|
|
253
|
+
logger?.debug(`Issuing ${amount} tokens to ${investor}...`);
|
|
254
|
+
await anvil.impersonateAccount({ address: admin });
|
|
255
|
+
let mintHash;
|
|
256
|
+
try {
|
|
257
|
+
await anvil.setBalance({
|
|
258
|
+
address: admin,
|
|
259
|
+
value: (0, viem.parseEther)("100")
|
|
260
|
+
});
|
|
261
|
+
mintHash = await issueDSTokens({
|
|
262
|
+
anvil,
|
|
263
|
+
account: admin,
|
|
264
|
+
token,
|
|
265
|
+
investor,
|
|
266
|
+
amount,
|
|
267
|
+
logger
|
|
268
|
+
});
|
|
269
|
+
} finally {
|
|
270
|
+
await anvil.stopImpersonatingAccount({ address: admin });
|
|
271
|
+
}
|
|
236
272
|
logger?.debug(`Done! tx: ${mintHash}`);
|
|
237
273
|
const balance = await anvil.readContract({
|
|
238
274
|
address: token,
|
package/dist/esm/dev/index.js
CHANGED
|
@@ -13,8 +13,8 @@ import { detectChain } from "./detectChain.js";
|
|
|
13
13
|
import { isOutOfSyncError } from "./isOutOfSyncError.js";
|
|
14
14
|
import { isRateLimitError } from "./isRateLimitError.js";
|
|
15
15
|
import { isTransientError } from "./isTransientError.js";
|
|
16
|
-
import {
|
|
17
|
-
import { greenlistMidasGateway, registerMidasInvestor, registerRWAInvestor, registerSecuritizeInvestor } from "./kycUtils.js";
|
|
16
|
+
import { discoverMidasCreditSuites, discoverMidasGateways, discoverMidasMTokens, unpauseMidasIssuanceVault } from "./midasUtils.js";
|
|
17
|
+
import { DEFAULT_MIDAS_ADMIN, getSecuritizeAdmin, greenlistMidasGateway, registerMidasInvestor, registerRWAInvestor, registerSecuritizeInvestor } from "./kycUtils.js";
|
|
18
18
|
import { isRangeError, logSplitterTransport } from "./logSplitterTransport.js";
|
|
19
19
|
import { setLTZero, setLTs } from "./ltUtils.js";
|
|
20
20
|
import { migrateFaucet } from "./migrateFaucet.js";
|
|
@@ -25,4 +25,4 @@ import { claimDSToken, claimDSTokens, enableDSTokenBackDating } from "./securiti
|
|
|
25
25
|
import "./types.js";
|
|
26
26
|
import { ONCHAIN_EXECUTION_ID_ADDRESS, verifyTestnet } from "./verifyTestnet.js";
|
|
27
27
|
import { makePendingWithdrawalsClaimable } from "./withdrawalUtils.js";
|
|
28
|
-
export { AccountOpener, Create2Deployer, DEFAULT_CREATE2_SALT, EthCallSpy, NoAvailableTransportsError, ONCHAIN_EXECUTION_ID_ADDRESS, OpenTxRevertedError, PUBLIC_CREATE2_FACTORY, RevolverTransport, SUPPORTED_RPC_PROVIDERS, SelectionStrategy, anvilNodeInfo, calcLiquidatableLTs, claimDSToken, claimDSTokens, claimFromFaucet,
|
|
28
|
+
export { AccountOpener, Create2Deployer, DEFAULT_CREATE2_SALT, DEFAULT_MIDAS_ADMIN, EthCallSpy, NoAvailableTransportsError, ONCHAIN_EXECUTION_ID_ADDRESS, OpenTxRevertedError, PUBLIC_CREATE2_FACTORY, RevolverTransport, SUPPORTED_RPC_PROVIDERS, SelectionStrategy, anvilNodeInfo, calcLiquidatableLTs, claimDSToken, claimDSTokens, claimFromFaucet, createAnvilClient, createMinter, deployUsingPublicCreate2, detectChain, discoverMidasCreditSuites, discoverMidasGateways, discoverMidasMTokens, enableDSTokenBackDating, evmMineDetailed, extendAnvilClient, getAlchemyUrl, getAnkrUrl, getDrpcUrl, getErpcKey, getPublicCreate2Address, getRpcProviderUrl, getSecuritizeAdmin, getThirdWebUrl, greenlistMidasGateway, httpTransportOptionsSchema, iDegenNftv2Abi, iFaucetAbi, iOnchainExecutionIdAbi, iOwnableAbi, iaclTraitAbi, isAnvil, isDeployedUsingPublicCreate2, isOutOfSyncError, isRangeError, isRateLimitError, isTransientError, logSplitterTransport, makePendingWithdrawalsClaimable, migrateFaucet, providerConfigSchema, registerMidasInvestor, registerRWAInvestor, registerSecuritizeInvestor, replaceStorage, resilientTransport, resilientTransportOptionsSchema, revolverTransportConfigBaseSchema, revolverTransportConfigSchema, rpcProvidersSchema, setLTZero, setLTs, unpauseMidasIssuanceVault, verifyTestnet, writeContractSync };
|
package/dist/esm/dev/kycUtils.js
CHANGED
|
@@ -4,22 +4,52 @@ import { OnchainSDK } from "../onchain/OnchainSDK.js";
|
|
|
4
4
|
import "../onchain/index.js";
|
|
5
5
|
import { iDSRegistryServiceAbi } from "../abi/rwa/iDSRegistryService.js";
|
|
6
6
|
import { iDSTokenAbi } from "../abi/rwa/iDSToken.js";
|
|
7
|
-
import {
|
|
7
|
+
import { discoverMidasCreditSuites, discoverMidasGateways } from "./midasUtils.js";
|
|
8
8
|
import { midasGatewayAbi } from "./withdrawalAbi.js";
|
|
9
|
-
import { isAddressEqual, parseAbi, parseEther, zeroAddress } from "viem";
|
|
10
|
-
import { privateKeyToAccount } from "viem/accounts";
|
|
9
|
+
import { getAddress, isAddressEqual, parseAbi, parseEther, slice, toHex, zeroAddress } from "viem";
|
|
11
10
|
//#region src/dev/kycUtils.ts
|
|
11
|
+
const iMockDSTokenAdminAbi = parseAbi(["function admin() view returns (address)"]);
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
13
|
+
* DS trust-service master of `token`: Ownable owner at storage slot 0 of the
|
|
14
|
+
* trust service, same as periphery-v3 SecuritizeAttachHelper. MockDSToken
|
|
15
|
+
* returns itself as the trust service, so `admin()` is used instead.
|
|
16
|
+
*/
|
|
17
|
+
async function getSecuritizeAdmin(anvil, token, logger) {
|
|
18
|
+
const trustServiceId = await anvil.readContract({
|
|
19
|
+
address: token,
|
|
20
|
+
abi: iDSTokenAbi,
|
|
21
|
+
functionName: "TRUST_SERVICE"
|
|
22
|
+
});
|
|
23
|
+
const trustService = await anvil.readContract({
|
|
24
|
+
address: token,
|
|
25
|
+
abi: iDSTokenAbi,
|
|
26
|
+
functionName: "getDSService",
|
|
27
|
+
args: [trustServiceId]
|
|
28
|
+
});
|
|
29
|
+
if (isAddressEqual(trustService, token)) {
|
|
30
|
+
const admin = await anvil.readContract({
|
|
31
|
+
address: token,
|
|
32
|
+
abi: iMockDSTokenAdminAbi,
|
|
33
|
+
functionName: "admin"
|
|
34
|
+
});
|
|
35
|
+
logger?.debug(`securitize: found admin ${admin} for ${token} (token.admin)`);
|
|
36
|
+
return admin;
|
|
37
|
+
}
|
|
38
|
+
const word = await anvil.getStorageAt({
|
|
39
|
+
address: trustService,
|
|
40
|
+
slot: toHex(0, { size: 32 })
|
|
41
|
+
});
|
|
42
|
+
if (!word) throw new Error(`securitize: empty trust-service owner slot on ${trustService}`);
|
|
43
|
+
const admin = getAddress(slice(word, 12));
|
|
44
|
+
logger?.debug(`securitize: found admin ${admin} for ${token} (trust service ${trustService})`);
|
|
45
|
+
return admin;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Impersonates the DS trust-service admin of `token` on the fork
|
|
15
49
|
*/
|
|
16
50
|
async function useSecuritizeAdmin(props) {
|
|
17
|
-
const { anvil,
|
|
18
|
-
|
|
19
|
-
account: privateKeyToAccount(adminPrivateKey),
|
|
20
|
-
release: async () => {}
|
|
21
|
-
};
|
|
22
|
-
if (!admin) throw new Error("securitize: either adminPrivateKey or admin address is required");
|
|
51
|
+
const { anvil, token, logger } = props;
|
|
52
|
+
const admin = await getSecuritizeAdmin(anvil, token, logger);
|
|
23
53
|
await anvil.impersonateAccount({ address: admin });
|
|
24
54
|
await anvil.setBalance({
|
|
25
55
|
address: admin,
|
|
@@ -259,8 +289,7 @@ async function greenlistMidasGateway(props) {
|
|
|
259
289
|
}
|
|
260
290
|
}
|
|
261
291
|
/**
|
|
262
|
-
* Finds the Midas gateway that issues `token`
|
|
263
|
-
* adapters of all loaded credit managers and the gateways of Midas degen NFTs
|
|
292
|
+
* Finds the Midas gateway that issues `token` among the loaded credit managers.
|
|
264
293
|
*/
|
|
265
294
|
async function findMidasGateway(props) {
|
|
266
295
|
const { anvil, token, marketConfigurators, logger } = props;
|
|
@@ -272,21 +301,19 @@ async function findMidasGateway(props) {
|
|
|
272
301
|
});
|
|
273
302
|
await sdk.attach({ marketConfigurators });
|
|
274
303
|
}
|
|
275
|
-
const
|
|
276
|
-
if (
|
|
277
|
-
const
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
abi: midasGatewayAbi,
|
|
282
|
-
functionName: "mToken"
|
|
283
|
-
}))
|
|
284
|
-
})).findIndex((mToken) => isAddressEqual(mToken, token));
|
|
285
|
-
if (index === -1) throw new Error(`no midas gateway found for token ${token}`);
|
|
286
|
-
logger?.debug(`midas: gateway for ${token} is ${candidates[index]}`);
|
|
287
|
-
return candidates[index];
|
|
304
|
+
const suites = await discoverMidasCreditSuites(sdk);
|
|
305
|
+
if (suites.length === 0) throw new Error("no midas gateway adapters or midas degen NFTs found in loaded markets");
|
|
306
|
+
const suite = suites.find(({ mToken }) => isAddressEqual(mToken, token));
|
|
307
|
+
if (!suite) throw new Error(`no midas gateway found for token ${token}`);
|
|
308
|
+
logger?.debug(`midas: gateway for ${token} is ${suite.gateway}`);
|
|
309
|
+
return suite.gateway;
|
|
288
310
|
}
|
|
289
311
|
/**
|
|
312
|
+
* Midas access control admin on mainnet. Holds the admin role of the greenlist
|
|
313
|
+
* operator role.
|
|
314
|
+
*/
|
|
315
|
+
const DEFAULT_MIDAS_ADMIN = "0xd4195CF4df289a4748C1A7B6dDBE770e27bA1227";
|
|
316
|
+
/**
|
|
290
317
|
* Registers a wallet as an investor (fake KYC) in every Securitize DSToken and
|
|
291
318
|
* every Midas gateway of the markets loaded by `sdk`.
|
|
292
319
|
*
|
|
@@ -294,17 +321,15 @@ async function findMidasGateway(props) {
|
|
|
294
321
|
* result instead of aborting the whole run.
|
|
295
322
|
*/
|
|
296
323
|
async function registerRWAInvestor(props) {
|
|
297
|
-
const { anvil, sdk, investor,
|
|
324
|
+
const { anvil, sdk, investor, midasAdmin = DEFAULT_MIDAS_ADMIN, logger } = props;
|
|
298
325
|
const securitizeTokens = [];
|
|
299
326
|
const midasGateways = [];
|
|
300
327
|
const failed = [];
|
|
301
328
|
const dsTokens = new AddressSet(sdk.rwa.factories.flatMap((factory) => factory.getTokens()));
|
|
302
|
-
|
|
329
|
+
for (const token of dsTokens) try {
|
|
303
330
|
await registerSecuritizeInvestor({
|
|
304
331
|
anvil,
|
|
305
332
|
investor,
|
|
306
|
-
adminPrivateKey,
|
|
307
|
-
admin: securitizeAdmin,
|
|
308
333
|
token,
|
|
309
334
|
logger
|
|
310
335
|
});
|
|
@@ -317,8 +342,7 @@ async function registerRWAInvestor(props) {
|
|
|
317
342
|
error: e
|
|
318
343
|
});
|
|
319
344
|
}
|
|
320
|
-
|
|
321
|
-
const gateways = await collectMidasGateways(sdk);
|
|
345
|
+
const gateways = await discoverMidasGateways(sdk);
|
|
322
346
|
for (const gateway of gateways) try {
|
|
323
347
|
await greenlistMidasGateway({
|
|
324
348
|
anvil,
|
|
@@ -344,4 +368,4 @@ async function registerRWAInvestor(props) {
|
|
|
344
368
|
};
|
|
345
369
|
}
|
|
346
370
|
//#endregion
|
|
347
|
-
export { greenlistMidasGateway, registerMidasInvestor, registerRWAInvestor, registerSecuritizeInvestor };
|
|
371
|
+
export { DEFAULT_MIDAS_ADMIN, getSecuritizeAdmin, greenlistMidasGateway, registerMidasInvestor, registerRWAInvestor, registerSecuritizeInvestor };
|
|
@@ -146,27 +146,53 @@ async function unpauseMidasIssuanceVault(props) {
|
|
|
146
146
|
}
|
|
147
147
|
};
|
|
148
148
|
}
|
|
149
|
-
|
|
150
|
-
|
|
149
|
+
/**
|
|
150
|
+
* Midas credit suites of the loaded credit managers.
|
|
151
|
+
*
|
|
152
|
+
* A manager with a gateway adapter is a suite on its own, so its degen NFT
|
|
153
|
+
* is not consulted. Suites that only have a Midas degen NFT read `mToken`
|
|
154
|
+
* from the gateway.
|
|
155
|
+
*/
|
|
156
|
+
async function discoverMidasCreditSuites(sdk) {
|
|
157
|
+
const suites = [];
|
|
158
|
+
for (const cm of sdk.marketRegister.creditManagers) {
|
|
159
|
+
const creditManager = cm.creditManager.address;
|
|
160
|
+
let hasAdapter = false;
|
|
161
|
+
for (const adapter of cm.creditManager.adapters.values()) if (adapter instanceof MidasGatewayAdapterContract) {
|
|
162
|
+
hasAdapter = true;
|
|
163
|
+
suites.push({
|
|
164
|
+
creditManager,
|
|
165
|
+
gateway: adapter.gateway,
|
|
166
|
+
mToken: adapter.mToken
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
if (hasAdapter) continue;
|
|
170
|
+
const nft = await cm.degenNFT();
|
|
171
|
+
if (nft instanceof MidasDegenNFT) {
|
|
172
|
+
const [mToken] = await nft.getTokens();
|
|
173
|
+
suites.push({
|
|
174
|
+
creditManager,
|
|
175
|
+
gateway: nft.gateway,
|
|
176
|
+
mToken
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return suites;
|
|
151
181
|
}
|
|
152
182
|
/**
|
|
153
|
-
*
|
|
154
|
-
* plus gateways of Midas degen NFTs (permissioned-mode suites with no gateway adapter).
|
|
183
|
+
* Gateways of the Midas credit suites of the loaded credit managers.
|
|
155
184
|
*/
|
|
156
|
-
async function
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
const nfts = await Promise.all(sdk.marketRegister.creditManagers.map((cm) => cm.degenNFT()));
|
|
160
|
-
for (const nft of nfts) if (nft instanceof MidasDegenNFT) gateways.add(nft.gateway);
|
|
161
|
-
return gateways.asArray();
|
|
185
|
+
async function discoverMidasGateways(sdk) {
|
|
186
|
+
const suites = await discoverMidasCreditSuites(sdk);
|
|
187
|
+
return new AddressSet(suites.map((suite) => suite.gateway)).asArray();
|
|
162
188
|
}
|
|
163
189
|
/**
|
|
164
190
|
* mTokens of all Midas gateway adapters of the loaded credit managers
|
|
165
191
|
*/
|
|
166
|
-
function
|
|
192
|
+
function discoverMidasMTokens(sdk) {
|
|
167
193
|
const mTokens = new AddressSet();
|
|
168
|
-
for (const adapter of
|
|
194
|
+
for (const cm of sdk.marketRegister.creditManagers) for (const adapter of cm.creditManager.adapters.values()) if (adapter instanceof MidasGatewayAdapterContract) mTokens.add(adapter.mToken);
|
|
169
195
|
return mTokens.asArray();
|
|
170
196
|
}
|
|
171
197
|
//#endregion
|
|
172
|
-
export {
|
|
198
|
+
export { discoverMidasCreditSuites, discoverMidasGateways, discoverMidasMTokens, unpauseMidasIssuanceVault };
|
|
@@ -2,9 +2,8 @@ import { AddressSet } from "../onchain/utils/AddressSet.js";
|
|
|
2
2
|
import { OnchainSDK } from "../onchain/OnchainSDK.js";
|
|
3
3
|
import "../onchain/index.js";
|
|
4
4
|
import { iDSTokenAbi } from "../abi/rwa/iDSToken.js";
|
|
5
|
-
import { registerSecuritizeInvestor } from "./kycUtils.js";
|
|
6
|
-
import { erc20Abi, isAddressEqual, parseAbi, zeroAddress } from "viem";
|
|
7
|
-
import { privateKeyToAccount } from "viem/accounts";
|
|
5
|
+
import { getSecuritizeAdmin, registerSecuritizeInvestor } from "./kycUtils.js";
|
|
6
|
+
import { erc20Abi, getAddress, isAddressEqual, parseAbi, parseEther, zeroAddress } from "viem";
|
|
8
7
|
//#region src/dev/securitizeUtils.ts
|
|
9
8
|
/**
|
|
10
9
|
* `COMPLIANCE_CONFIGURATION_SERVICE` id in the DS protocol service registry
|
|
@@ -110,7 +109,7 @@ async function issueDSTokens(props) {
|
|
|
110
109
|
* Tokens without one (e.g. MockDSToken) are skipped
|
|
111
110
|
*/
|
|
112
111
|
async function getComplianceConfigurationServices({ anvil, tokens, logger }) {
|
|
113
|
-
const services = new
|
|
112
|
+
const services = /* @__PURE__ */ new Map();
|
|
114
113
|
for (const token of new AddressSet(tokens)) try {
|
|
115
114
|
const service = await anvil.readContract({
|
|
116
115
|
address: token,
|
|
@@ -122,11 +121,16 @@ async function getComplianceConfigurationServices({ anvil, tokens, logger }) {
|
|
|
122
121
|
logger?.debug(`${token} has no compliance configuration service`);
|
|
123
122
|
continue;
|
|
124
123
|
}
|
|
125
|
-
|
|
124
|
+
const checksummed = getAddress(service);
|
|
125
|
+
if (services.has(checksummed)) continue;
|
|
126
|
+
services.set(checksummed, await getSecuritizeAdmin(anvil, token, logger));
|
|
126
127
|
} catch (e) {
|
|
127
128
|
logger?.debug(`Failed to get compliance configuration service of ${token}: ${e}`);
|
|
128
129
|
}
|
|
129
|
-
return [...services]
|
|
130
|
+
return [...services].map(([service, admin]) => ({
|
|
131
|
+
service,
|
|
132
|
+
admin
|
|
133
|
+
}));
|
|
130
134
|
}
|
|
131
135
|
/**
|
|
132
136
|
* Sets `disallowBackDating` to false on compliance configuration services of
|
|
@@ -134,18 +138,17 @@ async function getComplianceConfigurationServices({ anvil, tokens, logger }) {
|
|
|
134
138
|
* honoured: DS protocol silently replaces it with `block.timestamp` otherwise,
|
|
135
139
|
* and freshly minted tokens stay under lock-up.
|
|
136
140
|
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
141
|
+
* Impersonates the DS trust-service admin of each token. Ownable `owner()` of
|
|
142
|
+
* the compliance configuration service alone is not enough.
|
|
139
143
|
*
|
|
140
144
|
* The flag is only read while tokens are issued, so restoring it does not
|
|
141
145
|
* re-lock tokens minted in the meantime.
|
|
142
146
|
*/
|
|
143
147
|
async function enableDSTokenBackDating(props) {
|
|
144
|
-
const { anvil,
|
|
145
|
-
const account = privateKeyToAccount(adminPrivateKey);
|
|
148
|
+
const { anvil, logger } = props;
|
|
146
149
|
const services = await getComplianceConfigurationServices(props);
|
|
147
150
|
let toRestore = [];
|
|
148
|
-
for (const service of services) {
|
|
151
|
+
for (const { service, admin } of services) {
|
|
149
152
|
let disallowBackDating;
|
|
150
153
|
try {
|
|
151
154
|
disallowBackDating = await anvil.readContract({
|
|
@@ -162,32 +165,53 @@ async function enableDSTokenBackDating(props) {
|
|
|
162
165
|
continue;
|
|
163
166
|
}
|
|
164
167
|
logger?.info(`Allowing back-dating on ${service}`);
|
|
165
|
-
await anvil.
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
168
|
+
await anvil.impersonateAccount({ address: admin });
|
|
169
|
+
try {
|
|
170
|
+
await anvil.setBalance({
|
|
171
|
+
address: admin,
|
|
172
|
+
value: parseEther("100")
|
|
173
|
+
});
|
|
174
|
+
await anvil.writeContractSync({
|
|
175
|
+
account: admin,
|
|
176
|
+
chain: anvil.chain,
|
|
177
|
+
address: service,
|
|
178
|
+
abi: iDSComplianceConfigurationServiceAbi,
|
|
179
|
+
functionName: "setDisallowBackDating",
|
|
180
|
+
args: [false],
|
|
181
|
+
throwOnReceiptRevert: true
|
|
182
|
+
});
|
|
183
|
+
} finally {
|
|
184
|
+
await anvil.stopImpersonatingAccount({ address: admin });
|
|
185
|
+
}
|
|
186
|
+
toRestore.push({
|
|
187
|
+
service,
|
|
188
|
+
admin
|
|
173
189
|
});
|
|
174
|
-
toRestore.push(service);
|
|
175
190
|
}
|
|
176
191
|
return async () => {
|
|
177
192
|
const services = toRestore;
|
|
178
193
|
toRestore = [];
|
|
179
|
-
for (const service of services) {
|
|
194
|
+
for (const { service, admin } of services) {
|
|
180
195
|
logger?.info(`Disallowing back-dating on ${service}`);
|
|
181
196
|
try {
|
|
182
|
-
await anvil.
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
197
|
+
await anvil.impersonateAccount({ address: admin });
|
|
198
|
+
try {
|
|
199
|
+
await anvil.setBalance({
|
|
200
|
+
address: admin,
|
|
201
|
+
value: parseEther("100")
|
|
202
|
+
});
|
|
203
|
+
await anvil.writeContractSync({
|
|
204
|
+
account: admin,
|
|
205
|
+
chain: anvil.chain,
|
|
206
|
+
address: service,
|
|
207
|
+
abi: iDSComplianceConfigurationServiceAbi,
|
|
208
|
+
functionName: "setDisallowBackDating",
|
|
209
|
+
args: [true],
|
|
210
|
+
throwOnReceiptRevert: true
|
|
211
|
+
});
|
|
212
|
+
} finally {
|
|
213
|
+
await anvil.stopImpersonatingAccount({ address: admin });
|
|
214
|
+
}
|
|
191
215
|
} catch (e) {
|
|
192
216
|
logger?.warn(`Failed to restore disallowBackDating on ${service}: ${e}`);
|
|
193
217
|
}
|
|
@@ -195,8 +219,7 @@ async function enableDSTokenBackDating(props) {
|
|
|
195
219
|
};
|
|
196
220
|
}
|
|
197
221
|
async function claimDSToken(props) {
|
|
198
|
-
const { anvil, investor,
|
|
199
|
-
const account = privateKeyToAccount(adminPrivateKey);
|
|
222
|
+
const { anvil, investor, token, marketConfigurators, rwaFactories, usdAmount: usdAmountProp = "100000" } = props;
|
|
200
223
|
const symbol = await anvil.readContract({
|
|
201
224
|
address: token,
|
|
202
225
|
abi: erc20Abi,
|
|
@@ -220,18 +243,31 @@ async function claimDSToken(props) {
|
|
|
220
243
|
if (amount === 0n) throw new Error(`No market found for token ${token}`);
|
|
221
244
|
logger?.debug(`${usdAmountProp} USD === ${amount} ${symbol}`);
|
|
222
245
|
await registerSecuritizeInvestor({
|
|
223
|
-
...props,
|
|
224
|
-
logger
|
|
225
|
-
});
|
|
226
|
-
logger?.debug(`Issuing ${amount} tokens to ${investor}...`);
|
|
227
|
-
const mintHash = await issueDSTokens({
|
|
228
246
|
anvil,
|
|
229
|
-
account,
|
|
230
|
-
token,
|
|
231
247
|
investor,
|
|
232
|
-
|
|
248
|
+
token,
|
|
233
249
|
logger
|
|
234
250
|
});
|
|
251
|
+
const admin = await getSecuritizeAdmin(anvil, token, logger);
|
|
252
|
+
logger?.debug(`Issuing ${amount} tokens to ${investor}...`);
|
|
253
|
+
await anvil.impersonateAccount({ address: admin });
|
|
254
|
+
let mintHash;
|
|
255
|
+
try {
|
|
256
|
+
await anvil.setBalance({
|
|
257
|
+
address: admin,
|
|
258
|
+
value: parseEther("100")
|
|
259
|
+
});
|
|
260
|
+
mintHash = await issueDSTokens({
|
|
261
|
+
anvil,
|
|
262
|
+
account: admin,
|
|
263
|
+
token,
|
|
264
|
+
investor,
|
|
265
|
+
amount,
|
|
266
|
+
logger
|
|
267
|
+
});
|
|
268
|
+
} finally {
|
|
269
|
+
await anvil.stopImpersonatingAccount({ address: admin });
|
|
270
|
+
}
|
|
235
271
|
logger?.debug(`Done! tx: ${mintHash}`);
|
|
236
272
|
const balance = await anvil.readContract({
|
|
237
273
|
address: token,
|
|
@@ -55,7 +55,7 @@ var PrepareApi = class extends MultichainConstruct {
|
|
|
55
55
|
const at = stateBlock(sdk);
|
|
56
56
|
const intent = resumable(params.intent ?? params.claimable.intent);
|
|
57
57
|
if (!intent) return sdkErr(noRecordedIntent());
|
|
58
|
-
const creditAccount = await slice(sdk, position.creditAccount);
|
|
58
|
+
const creditAccount = await slice$1(sdk, position.creditAccount);
|
|
59
59
|
if (!creditAccount) return sdkErr(creditAccountNotFound(position.creditAccount));
|
|
60
60
|
return finalized(await service(sdk).finishIntent({
|
|
61
61
|
intent,
|
|
@@ -404,7 +404,7 @@ var PrepareApi = class extends MultichainConstruct {
|
|
|
404
404
|
* described, see {@link IOpportunitiesPrepare.maxWithdraw}.
|
|
405
405
|
**/
|
|
406
406
|
async #account(sdk, position) {
|
|
407
|
-
const creditAccount = await slice(sdk, position.creditAccount);
|
|
407
|
+
const creditAccount = await slice$1(sdk, position.creditAccount);
|
|
408
408
|
if (!creditAccount) throw new Error(creditAccountNotFound(position.creditAccount).message);
|
|
409
409
|
return creditAccount;
|
|
410
410
|
}
|
|
@@ -421,7 +421,7 @@ var PrepareApi = class extends MultichainConstruct {
|
|
|
421
421
|
try {
|
|
422
422
|
const sdk = await this.#chain(position.chainId);
|
|
423
423
|
const at = stateBlock(sdk);
|
|
424
|
-
const creditAccount = await slice(sdk, position.creditAccount);
|
|
424
|
+
const creditAccount = await slice$1(sdk, position.creditAccount);
|
|
425
425
|
if (!creditAccount) return neitherRoute(creditAccountNotFound(position.creditAccount));
|
|
426
426
|
return routed(await service(sdk).intentRoutes({
|
|
427
427
|
intent,
|
|
@@ -447,7 +447,7 @@ var PrepareApi = class extends MultichainConstruct {
|
|
|
447
447
|
try {
|
|
448
448
|
const sdk = await this.#chain(position.chainId);
|
|
449
449
|
const at = stateBlock(sdk);
|
|
450
|
-
const creditAccount = await slice(sdk, position.creditAccount);
|
|
450
|
+
const creditAccount = await slice$1(sdk, position.creditAccount);
|
|
451
451
|
if (!creditAccount) return sdkErr(creditAccountNotFound(position.creditAccount));
|
|
452
452
|
return planned(await service(sdk).startIntent({
|
|
453
453
|
intent,
|
|
@@ -479,7 +479,7 @@ function stateBlock(sdk) {
|
|
|
479
479
|
* connected to hold no such account — closed since it was listed, or named on
|
|
480
480
|
* the wrong chain. Read rather than thrown, so the caller gets a code for it.
|
|
481
481
|
**/
|
|
482
|
-
async function slice(sdk, creditAccount) {
|
|
482
|
+
async function slice$1(sdk, creditAccount) {
|
|
483
483
|
const data = await sdk.accounts.getCreditAccountData(creditAccount);
|
|
484
484
|
return data && toCreditAccountSlice(data);
|
|
485
485
|
}
|
|
@@ -497,7 +497,7 @@ async function slice(sdk, creditAccount) {
|
|
|
497
497
|
**/
|
|
498
498
|
async function reusable(sdk, strategy, creditAccount) {
|
|
499
499
|
if (!creditAccount) return;
|
|
500
|
-
const account = await slice(sdk, creditAccount);
|
|
500
|
+
const account = await slice$1(sdk, creditAccount);
|
|
501
501
|
if (!account || !isAddressEqual(account.creditManager, strategy.creditManager)) return sdkErr(creditAccountNotFound(creditAccount));
|
|
502
502
|
if (account.totalDebt > 0n || account.tokens.some((t) => t.quota > 0n)) return sdkErr(creditAccountNotEmpty(creditAccount));
|
|
503
503
|
return { account };
|
|
@@ -11,10 +11,10 @@ import { detectChain } from "./detectChain.js";
|
|
|
11
11
|
import { isOutOfSyncError } from "./isOutOfSyncError.js";
|
|
12
12
|
import { isRateLimitError } from "./isRateLimitError.js";
|
|
13
13
|
import { isTransientError } from "./isTransientError.js";
|
|
14
|
-
import { GreenlistMidasGatewayProps, RWAKycFailure, RegisterMidasInvestorProps, RegisterRWAInvestorProps, RegisterRWAInvestorResult, RegisterSecuritizeInvestorProps, greenlistMidasGateway, registerMidasInvestor, registerRWAInvestor, registerSecuritizeInvestor } from "./kycUtils.js";
|
|
14
|
+
import { DEFAULT_MIDAS_ADMIN, GreenlistMidasGatewayProps, RWAKycFailure, RegisterMidasInvestorProps, RegisterRWAInvestorProps, RegisterRWAInvestorResult, RegisterSecuritizeInvestorProps, getSecuritizeAdmin, greenlistMidasGateway, registerMidasInvestor, registerRWAInvestor, registerSecuritizeInvestor } from "./kycUtils.js";
|
|
15
15
|
import { LogSplitterTransportOptions, isRangeError, logSplitterTransport } from "./logSplitterTransport.js";
|
|
16
16
|
import { setLTZero, setLTs } from "./ltUtils.js";
|
|
17
|
-
import { RestoreMidasIssuanceVaultPause, UnpauseMidasIssuanceVaultProps,
|
|
17
|
+
import { MidasCreditSuite, RestoreMidasIssuanceVaultPause, UnpauseMidasIssuanceVaultProps, discoverMidasCreditSuites, discoverMidasGateways, discoverMidasMTokens, unpauseMidasIssuanceVault } from "./midasUtils.js";
|
|
18
18
|
import { migrateFaucet } from "./migrateFaucet.js";
|
|
19
19
|
import { IMinter } from "./mint/types.js";
|
|
20
20
|
import { createMinter } from "./mint/factory.js";
|
|
@@ -26,4 +26,4 @@ import { EnableDSTokenBackDatingProps, RestoreDSTokenBackDating, claimDSToken, c
|
|
|
26
26
|
import { httpTransportOptionsSchema } from "./transports.js";
|
|
27
27
|
import { ONCHAIN_EXECUTION_ID_ADDRESS, VerifyTestnetParams, verifyTestnet } from "./verifyTestnet.js";
|
|
28
28
|
import { MakePendingWithdrawalsClaimableOptions, makePendingWithdrawalsClaimable } from "./withdrawalUtils.js";
|
|
29
|
-
export { AccountOpener, AccountOpenerOptions, AnvilActions, AnvilClient, AnvilClientConfig, AnvilDealParameters, AnvilNodeInfo, AnvilRPCSchema, CheckMulticallFn, Create2Deployer, Create2Parameters, DEFAULT_CREATE2_SALT, DetectedCall, EnableDSTokenBackDatingProps, EnsureExistsUsingPublicCreate2ReturnType, EthCallMethod, EthCallRequest, EthCallSpy, GetCreate2AddressParameters, GreenlistMidasGatewayProps, IMinter, type LogSplitterTransportOptions, MakePendingWithdrawalsClaimableOptions, NoAvailableTransportsError, ONCHAIN_EXECUTION_ID_ADDRESS, OpenAccountHumanizedPreview, OpenAccountResult, OpenAccountsResult, OpenTxRevertedError, PUBLIC_CREATE2_FACTORY, PoolDepositResult, ProviderConfig, ProviderStatus, RWAKycFailure, RegisterMidasInvestorProps, RegisterRWAInvestorProps, RegisterRWAInvestorResult, RegisterSecuritizeInvestorProps, ReplaceStorageParams, ResilientTransportOptions, RestoreDSTokenBackDating, RestoreMidasIssuanceVaultPause, RevolverTransport, RevolverTransportConfig, RevolverTransportValue, RpcErrorResult, RpcProvider, RpcResponse, RpcSuccessResult, SUPPORTED_RPC_PROVIDERS, SelectionStrategy, TargetAccount, UnpauseMidasIssuanceVaultProps, VerifyTestnetParams, anvilNodeInfo, calcLiquidatableLTs, claimDSToken, claimDSTokens, claimFromFaucet,
|
|
29
|
+
export { AccountOpener, AccountOpenerOptions, AnvilActions, AnvilClient, AnvilClientConfig, AnvilDealParameters, AnvilNodeInfo, AnvilRPCSchema, CheckMulticallFn, Create2Deployer, Create2Parameters, DEFAULT_CREATE2_SALT, DEFAULT_MIDAS_ADMIN, DetectedCall, EnableDSTokenBackDatingProps, EnsureExistsUsingPublicCreate2ReturnType, EthCallMethod, EthCallRequest, EthCallSpy, GetCreate2AddressParameters, GreenlistMidasGatewayProps, IMinter, type LogSplitterTransportOptions, MakePendingWithdrawalsClaimableOptions, MidasCreditSuite, NoAvailableTransportsError, ONCHAIN_EXECUTION_ID_ADDRESS, OpenAccountHumanizedPreview, OpenAccountResult, OpenAccountsResult, OpenTxRevertedError, PUBLIC_CREATE2_FACTORY, PoolDepositResult, ProviderConfig, ProviderStatus, RWAKycFailure, RegisterMidasInvestorProps, RegisterRWAInvestorProps, RegisterRWAInvestorResult, RegisterSecuritizeInvestorProps, ReplaceStorageParams, ResilientTransportOptions, RestoreDSTokenBackDating, RestoreMidasIssuanceVaultPause, RevolverTransport, RevolverTransportConfig, RevolverTransportValue, RpcErrorResult, RpcProvider, RpcResponse, RpcSuccessResult, SUPPORTED_RPC_PROVIDERS, SelectionStrategy, TargetAccount, UnpauseMidasIssuanceVaultProps, VerifyTestnetParams, anvilNodeInfo, calcLiquidatableLTs, claimDSToken, claimDSTokens, claimFromFaucet, createAnvilClient, createMinter, deployUsingPublicCreate2, detectChain, discoverMidasCreditSuites, discoverMidasGateways, discoverMidasMTokens, enableDSTokenBackDating, evmMineDetailed, extendAnvilClient, getAlchemyUrl, getAnkrUrl, getDrpcUrl, getErpcKey, getPublicCreate2Address, getRpcProviderUrl, getSecuritizeAdmin, getThirdWebUrl, greenlistMidasGateway, httpTransportOptionsSchema, iDegenNftv2Abi, iFaucetAbi, iOnchainExecutionIdAbi, iOwnableAbi, iaclTraitAbi, isAnvil, isDeployedUsingPublicCreate2, isOutOfSyncError, isRangeError, isRateLimitError, isTransientError, logSplitterTransport, makePendingWithdrawalsClaimable, migrateFaucet, providerConfigSchema, registerMidasInvestor, registerRWAInvestor, registerSecuritizeInvestor, replaceStorage, resilientTransport, resilientTransportOptionsSchema, revolverTransportConfigBaseSchema, revolverTransportConfigSchema, rpcProvidersSchema, setLTZero, setLTs, unpauseMidasIssuanceVault, verifyTestnet, writeContractSync };
|
|
@@ -2,7 +2,7 @@ import { ILogger } from "../onchain/types/logger.js";
|
|
|
2
2
|
import { OnchainSDK } from "../onchain/OnchainSDK.js";
|
|
3
3
|
import "../onchain/index.js";
|
|
4
4
|
import { AnvilClient } from "./createAnvilClient.js";
|
|
5
|
-
import { Address
|
|
5
|
+
import { Address } from "viem";
|
|
6
6
|
//#region src/dev/kycUtils.d.ts
|
|
7
7
|
interface RegisterSecuritizeInvestorProps {
|
|
8
8
|
anvil: AnvilClient;
|
|
@@ -10,20 +10,18 @@ interface RegisterSecuritizeInvestorProps {
|
|
|
10
10
|
* Wallet to register in the Securitize DS registry
|
|
11
11
|
*/
|
|
12
12
|
investor: Address;
|
|
13
|
-
/**
|
|
14
|
-
* Private key of the DS registry admin, signs the registry writes
|
|
15
|
-
*/
|
|
16
|
-
adminPrivateKey?: Hex;
|
|
17
|
-
/**
|
|
18
|
-
* DS registry admin, impersonated on the fork when no private key is given
|
|
19
|
-
*/
|
|
20
|
-
admin?: Address;
|
|
21
13
|
/**
|
|
22
14
|
* DSToken address
|
|
23
15
|
*/
|
|
24
16
|
token: Address;
|
|
25
17
|
logger?: ILogger;
|
|
26
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* DS trust-service master of `token`: Ownable owner at storage slot 0 of the
|
|
21
|
+
* trust service, same as periphery-v3 SecuritizeAttachHelper. MockDSToken
|
|
22
|
+
* returns itself as the trust service, so `admin()` is used instead.
|
|
23
|
+
*/
|
|
24
|
+
declare function getSecuritizeAdmin(anvil: AnvilClient, token: Address, logger?: ILogger): Promise<Address>;
|
|
27
25
|
/**
|
|
28
26
|
* Registers a wallet as a Securitize investor (fake KYC) in the DS registry
|
|
29
27
|
* service of a DSToken.
|
|
@@ -82,6 +80,11 @@ interface GreenlistMidasGatewayProps {
|
|
|
82
80
|
* Greenlists an investor in the access control of a single Midas gateway
|
|
83
81
|
*/
|
|
84
82
|
declare function greenlistMidasGateway(props: GreenlistMidasGatewayProps): Promise<void>;
|
|
83
|
+
/**
|
|
84
|
+
* Midas access control admin on mainnet. Holds the admin role of the greenlist
|
|
85
|
+
* operator role.
|
|
86
|
+
*/
|
|
87
|
+
declare const DEFAULT_MIDAS_ADMIN: Address;
|
|
85
88
|
interface RegisterRWAInvestorProps {
|
|
86
89
|
anvil: AnvilClient;
|
|
87
90
|
/**
|
|
@@ -93,16 +96,8 @@ interface RegisterRWAInvestorProps {
|
|
|
93
96
|
*/
|
|
94
97
|
investor: Address;
|
|
95
98
|
/**
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*/
|
|
99
|
-
adminPrivateKey?: Hex;
|
|
100
|
-
/**
|
|
101
|
-
* Securitize registry admin, impersonated on the fork
|
|
102
|
-
*/
|
|
103
|
-
securitizeAdmin?: Address;
|
|
104
|
-
/**
|
|
105
|
-
* Override midas access control admin address
|
|
99
|
+
* Midas access control admin, impersonated on the fork.
|
|
100
|
+
* Defaults to {@link DEFAULT_MIDAS_ADMIN}.
|
|
106
101
|
*/
|
|
107
102
|
midasAdmin?: Address;
|
|
108
103
|
logger?: ILogger;
|
|
@@ -134,4 +129,4 @@ interface RegisterRWAInvestorResult {
|
|
|
134
129
|
*/
|
|
135
130
|
declare function registerRWAInvestor(props: RegisterRWAInvestorProps): Promise<RegisterRWAInvestorResult>;
|
|
136
131
|
//#endregion
|
|
137
|
-
export { GreenlistMidasGatewayProps, RWAKycFailure, RegisterMidasInvestorProps, RegisterRWAInvestorProps, RegisterRWAInvestorResult, RegisterSecuritizeInvestorProps, greenlistMidasGateway, registerMidasInvestor, registerRWAInvestor, registerSecuritizeInvestor };
|
|
132
|
+
export { DEFAULT_MIDAS_ADMIN, GreenlistMidasGatewayProps, RWAKycFailure, RegisterMidasInvestorProps, RegisterRWAInvestorProps, RegisterRWAInvestorResult, RegisterSecuritizeInvestorProps, getSecuritizeAdmin, greenlistMidasGateway, registerMidasInvestor, registerRWAInvestor, registerSecuritizeInvestor };
|
|
@@ -34,13 +34,29 @@ type RestoreMidasIssuanceVaultPause = () => Promise<void>;
|
|
|
34
34
|
*/
|
|
35
35
|
declare function unpauseMidasIssuanceVault(props: UnpauseMidasIssuanceVaultProps): Promise<RestoreMidasIssuanceVaultPause>;
|
|
36
36
|
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
37
|
+
* One Midas credit suite: a credit manager with a Midas gateway adapter,
|
|
38
|
+
* or with a Midas degen NFT when it has no such adapter.
|
|
39
39
|
*/
|
|
40
|
-
|
|
40
|
+
interface MidasCreditSuite {
|
|
41
|
+
creditManager: Address;
|
|
42
|
+
gateway: Address;
|
|
43
|
+
mToken: Address;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Midas credit suites of the loaded credit managers.
|
|
47
|
+
*
|
|
48
|
+
* A manager with a gateway adapter is a suite on its own, so its degen NFT
|
|
49
|
+
* is not consulted. Suites that only have a Midas degen NFT read `mToken`
|
|
50
|
+
* from the gateway.
|
|
51
|
+
*/
|
|
52
|
+
declare function discoverMidasCreditSuites(sdk: OnchainSDK): Promise<MidasCreditSuite[]>;
|
|
53
|
+
/**
|
|
54
|
+
* Gateways of the Midas credit suites of the loaded credit managers.
|
|
55
|
+
*/
|
|
56
|
+
declare function discoverMidasGateways(sdk: OnchainSDK): Promise<Address[]>;
|
|
41
57
|
/**
|
|
42
58
|
* mTokens of all Midas gateway adapters of the loaded credit managers
|
|
43
59
|
*/
|
|
44
|
-
declare function
|
|
60
|
+
declare function discoverMidasMTokens(sdk: OnchainSDK): Address[];
|
|
45
61
|
//#endregion
|
|
46
|
-
export { RestoreMidasIssuanceVaultPause, UnpauseMidasIssuanceVaultProps,
|
|
62
|
+
export { MidasCreditSuite, RestoreMidasIssuanceVaultPause, UnpauseMidasIssuanceVaultProps, discoverMidasCreditSuites, discoverMidasGateways, discoverMidasMTokens, unpauseMidasIssuanceVault };
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { ILogger } from "../onchain/types/logger.js";
|
|
2
2
|
import "../onchain/index.js";
|
|
3
3
|
import { AnvilClient } from "./createAnvilClient.js";
|
|
4
|
-
import { Address
|
|
4
|
+
import { Address } from "viem";
|
|
5
5
|
//#region src/dev/securitizeUtils.d.ts
|
|
6
6
|
interface ClaimDSTokenProps {
|
|
7
7
|
anvil: AnvilClient;
|
|
8
8
|
investor: Address;
|
|
9
|
-
adminPrivateKey: Hex;
|
|
10
9
|
/**
|
|
11
10
|
* DSToken addresses
|
|
12
11
|
*/
|
|
@@ -24,11 +23,6 @@ type ClaimDSTokensProps = Omit<ClaimDSTokenProps, "token"> & {
|
|
|
24
23
|
};
|
|
25
24
|
interface EnableDSTokenBackDatingProps {
|
|
26
25
|
anvil: AnvilClient;
|
|
27
|
-
/**
|
|
28
|
-
* Securitize DS admin with sufficient trust to call compliance setters
|
|
29
|
-
* (same key used for issueTokens / registerInvestor)
|
|
30
|
-
*/
|
|
31
|
-
adminPrivateKey: Hex;
|
|
32
26
|
/**
|
|
33
27
|
* DSToken addresses whose compliance configuration should allow back-dating
|
|
34
28
|
*/
|
|
@@ -46,8 +40,8 @@ type RestoreDSTokenBackDating = () => Promise<void>;
|
|
|
46
40
|
* honoured: DS protocol silently replaces it with `block.timestamp` otherwise,
|
|
47
41
|
* and freshly minted tokens stay under lock-up.
|
|
48
42
|
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
43
|
+
* Impersonates the DS trust-service admin of each token. Ownable `owner()` of
|
|
44
|
+
* the compliance configuration service alone is not enough.
|
|
51
45
|
*
|
|
52
46
|
* The flag is only read while tokens are issued, so restoring it does not
|
|
53
47
|
* re-lock tokens minted in the meantime.
|