@ocap/resolver 1.28.8 → 1.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/esm/api.d.mts +24 -0
  2. package/esm/api.mjs +53 -0
  3. package/esm/hooks.d.mts +153 -0
  4. package/esm/hooks.mjs +267 -0
  5. package/esm/index.d.mts +201 -0
  6. package/esm/index.mjs +1327 -0
  7. package/esm/migration-chain.d.mts +52 -0
  8. package/esm/migration-chain.mjs +97 -0
  9. package/esm/package.mjs +5 -0
  10. package/esm/token-cache.d.mts +20 -0
  11. package/esm/token-cache.mjs +26 -0
  12. package/esm/token-distribution.d.mts +166 -0
  13. package/esm/token-distribution.mjs +241 -0
  14. package/esm/token-flow.d.mts +139 -0
  15. package/esm/token-flow.mjs +330 -0
  16. package/esm/types.d.mts +115 -0
  17. package/esm/types.mjs +1 -0
  18. package/lib/_virtual/rolldown_runtime.cjs +29 -0
  19. package/lib/api.cjs +54 -0
  20. package/lib/api.d.cts +24 -0
  21. package/lib/hooks.cjs +274 -0
  22. package/lib/hooks.d.cts +153 -0
  23. package/lib/index.cjs +1343 -0
  24. package/lib/index.d.cts +201 -0
  25. package/lib/migration-chain.cjs +99 -0
  26. package/lib/migration-chain.d.cts +52 -0
  27. package/lib/package.cjs +11 -0
  28. package/lib/token-cache.cjs +27 -0
  29. package/lib/token-cache.d.cts +20 -0
  30. package/lib/token-distribution.cjs +243 -0
  31. package/lib/token-distribution.d.cts +166 -0
  32. package/lib/token-flow.cjs +336 -0
  33. package/lib/token-flow.d.cts +139 -0
  34. package/lib/types.cjs +0 -0
  35. package/lib/types.d.cts +115 -0
  36. package/package.json +49 -21
  37. package/lib/api.js +0 -71
  38. package/lib/hooks.js +0 -339
  39. package/lib/index.js +0 -1486
  40. package/lib/migration-chain.js +0 -144
  41. package/lib/token-cache.js +0 -40
  42. package/lib/token-distribution.js +0 -358
  43. package/lib/token-flow.js +0 -445
package/lib/index.cjs ADDED
@@ -0,0 +1,1343 @@
1
+ Object.defineProperty(exports, '__esModule', { value: true });
2
+ const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
3
+ const require_package = require('./package.cjs');
4
+ const require_hooks = require('./hooks.cjs');
5
+ const require_migration_chain = require('./migration-chain.cjs');
6
+ const require_token_cache = require('./token-cache.cjs');
7
+ const require_token_distribution = require('./token-distribution.cjs');
8
+ const require_token_flow = require('./token-flow.cjs');
9
+ let _arcblock_did_util = require("@arcblock/did-util");
10
+ let _ocap_util = require("@ocap/util");
11
+ let debug = require("debug");
12
+ debug = require_rolldown_runtime.__toESM(debug);
13
+ let lodash_pick = require("lodash/pick");
14
+ lodash_pick = require_rolldown_runtime.__toESM(lodash_pick);
15
+ let _arcblock_did = require("@arcblock/did");
16
+ let _arcblock_validator = require("@arcblock/validator");
17
+ let _ocap_config = require("@ocap/config");
18
+ _ocap_config = require_rolldown_runtime.__toESM(_ocap_config);
19
+ let _ocap_mcrypto = require("@ocap/mcrypto");
20
+ let _ocap_message = require("@ocap/message");
21
+ let _ocap_state = require("@ocap/state");
22
+ _ocap_state = require_rolldown_runtime.__toESM(_ocap_state);
23
+ let _ocap_tx_protocols = require("@ocap/tx-protocols");
24
+ let _ocap_tx_protocols_lib_util = require("@ocap/tx-protocols/lib/util");
25
+ let _ocap_util_lib_constant = require("@ocap/util/lib/constant");
26
+ let _ocap_util_lib_error = require("@ocap/util/lib/error");
27
+ let _ocap_util_lib_md5 = require("@ocap/util/lib/md5");
28
+ let lodash_get = require("lodash/get");
29
+ lodash_get = require_rolldown_runtime.__toESM(lodash_get);
30
+ let lodash_isEmpty = require("lodash/isEmpty");
31
+ lodash_isEmpty = require_rolldown_runtime.__toESM(lodash_isEmpty);
32
+ let lodash_isEqual = require("lodash/isEqual");
33
+ lodash_isEqual = require_rolldown_runtime.__toESM(lodash_isEqual);
34
+ let lodash_omit = require("lodash/omit");
35
+ lodash_omit = require_rolldown_runtime.__toESM(lodash_omit);
36
+ let lodash_set = require("lodash/set");
37
+ lodash_set = require_rolldown_runtime.__toESM(lodash_set);
38
+ let lodash_unionBy = require("lodash/unionBy");
39
+ lodash_unionBy = require_rolldown_runtime.__toESM(lodash_unionBy);
40
+ let lodash_uniq = require("lodash/uniq");
41
+ lodash_uniq = require_rolldown_runtime.__toESM(lodash_uniq);
42
+ let queue = require("queue");
43
+ queue = require_rolldown_runtime.__toESM(queue);
44
+ let _ocap_indexdb_lib_util = require("@ocap/indexdb/lib/util");
45
+ let _ocap_state_lib_states_tx = require("@ocap/state/lib/states/tx");
46
+
47
+ //#region src/index.ts
48
+ const debug$1 = (0, debug.default)(require_package.name);
49
+ const noop = (x) => x;
50
+ const CHAIN_ADDR = (0, _ocap_util_lib_md5.md5)("OCAP_CHAIN_ADDR");
51
+ const maxGasOps = {
52
+ "fg:t:account_migrate": {
53
+ create: 2,
54
+ update: 1
55
+ },
56
+ "fg:t:delegate": {
57
+ create: 3,
58
+ update: 1
59
+ },
60
+ "fg:t:revoke_delegate": {
61
+ create: 1,
62
+ update: 2
63
+ },
64
+ "fg:t:create_asset": {
65
+ create: 2,
66
+ update: 2
67
+ },
68
+ "fg:t:update_asset": {
69
+ create: 1,
70
+ update: 2
71
+ },
72
+ "fg:t:consume_asset": {
73
+ create: 1,
74
+ update: 2
75
+ },
76
+ "fg:t:create_factory": {
77
+ create: 2,
78
+ update: 2
79
+ },
80
+ "fg:t:acquire_asset_v2": {
81
+ create: 2,
82
+ update: 12
83
+ },
84
+ "fg:t:acquire_asset_v3": {
85
+ create: 3,
86
+ update: 12
87
+ },
88
+ "fg:t:mint_asset": {
89
+ create: 3,
90
+ update: 10
91
+ },
92
+ "fg:t:create_token": {
93
+ create: 2,
94
+ update: 3
95
+ },
96
+ "fg:t:deposit_token_v2": {
97
+ create: 3,
98
+ update: 3
99
+ },
100
+ "fg:t:withdraw_token_v2": {
101
+ create: 2,
102
+ update: 3
103
+ },
104
+ "fg:t:stake": {
105
+ create: 2,
106
+ update: 18
107
+ },
108
+ "fg:t:revoke_stake": {
109
+ create: 1,
110
+ update: 2
111
+ },
112
+ "fg:t:claim_stake": {
113
+ create: 2,
114
+ update: 18
115
+ },
116
+ "fg:t:slash_stake": { update: 18 },
117
+ "fg:t:return_stake": { update: 8 },
118
+ "fg:t:transfer": {
119
+ create: 2,
120
+ update: 20
121
+ },
122
+ "fg:t:transfer_v2": {
123
+ create: 2,
124
+ update: 20
125
+ },
126
+ "fg:t:transfer_v3": {
127
+ create: 9,
128
+ update: 16
129
+ },
130
+ "fg:t:exchange_v2": {
131
+ create: 1,
132
+ update: 18
133
+ },
134
+ "fg:t:create_token_factory": {
135
+ create: 2,
136
+ update: 3
137
+ },
138
+ "fg:t:update_token_factory": {
139
+ create: 0,
140
+ update: 3
141
+ },
142
+ "fg:t:mint_token": {
143
+ create: 2,
144
+ update: 13
145
+ },
146
+ "fg:t:burn_token": {
147
+ create: 2,
148
+ update: 13
149
+ },
150
+ "fg:t:create_rollup": {
151
+ create: 2,
152
+ update: 2
153
+ },
154
+ "fg:t:update_rollup": {
155
+ create: 1,
156
+ update: 2
157
+ },
158
+ "fg:t:pause_rollup": {
159
+ create: 1,
160
+ update: 2
161
+ },
162
+ "fg:t:close_rollup": {
163
+ create: 1,
164
+ update: 26
165
+ },
166
+ "fg:t:resume_rollup": {
167
+ create: 1,
168
+ update: 2
169
+ },
170
+ "fg:t:join_rollup": {
171
+ create: 2,
172
+ update: 3
173
+ },
174
+ "fg:t:leave_rollup": {
175
+ create: 2,
176
+ update: 3
177
+ },
178
+ "fg:t:migrate_rollup": {
179
+ create: 1,
180
+ update: 2
181
+ },
182
+ "fg:t:create_rollup_block": {
183
+ create: 2,
184
+ update: 38
185
+ },
186
+ "fg:t:claim_block_reward": {
187
+ create: 2,
188
+ update: 38
189
+ }
190
+ };
191
+ const txRequestConfig = {
192
+ maxRequest: Number(process.env.TX_MAX_REQUEST) || 200,
193
+ concurrent: Number(process.env.TX_CONCURRENCY) || 10,
194
+ timeout: Number(process.env.TX_TIMEOUT) || 1e3 * 30
195
+ };
196
+ const formatData = (data) => {
197
+ if (!data) return data;
198
+ if (data.type_url) return data;
199
+ if (data.typeUrl && ["json", "vc"].includes(data.typeUrl)) try {
200
+ JSON.parse(data.value);
201
+ data.type_url = data.typeUrl;
202
+ return data;
203
+ } catch (_err) {
204
+ try {
205
+ JSON.parse(data.value.replace(/\\"/g, "\""));
206
+ data.type_url = data.typeUrl;
207
+ data.value = data.value.replace(/\\"/g, "\"");
208
+ return data;
209
+ } catch (_e) {}
210
+ }
211
+ const decoded = data.typeUrl ? (0, _ocap_tx_protocols_lib_util.decodeAnySafe)(data) : data;
212
+ if (!decoded) return {
213
+ typeUrl: "",
214
+ type_url: "",
215
+ value: ""
216
+ };
217
+ if ([
218
+ "json",
219
+ "vc",
220
+ "AssetFactory"
221
+ ].includes(decoded.type)) return {
222
+ typeUrl: decoded.type,
223
+ type_url: decoded.type,
224
+ value: JSON.stringify(decoded.value)
225
+ };
226
+ return {
227
+ typeUrl: decoded.type,
228
+ type_url: decoded.type,
229
+ value: decoded.value
230
+ };
231
+ };
232
+ const formatDelegationOps = (state) => {
233
+ if (state?.ops && typeof state.ops === "object" && !Array.isArray(state.ops)) {
234
+ const opsRecord = state.ops;
235
+ state.ops = Object.keys(opsRecord).map((x) => ({
236
+ key: x,
237
+ value: opsRecord[x]
238
+ }));
239
+ }
240
+ return state;
241
+ };
242
+ const extractTokenMeta = (address, tokenStates) => {
243
+ const tokenState = tokenStates.filter(Boolean).find((t) => t.address === address);
244
+ if (!tokenState) return {};
245
+ return {
246
+ address,
247
+ decimal: typeof tokenState.decimal === "undefined" ? _ocap_util_lib_constant.DEFAULT_TOKEN_DECIMAL : tokenState.decimal,
248
+ unit: tokenState.unit,
249
+ symbol: tokenState.symbol,
250
+ foreignToken: tokenState.foreignToken
251
+ };
252
+ };
253
+ var OCAPResolver = class {
254
+ /**
255
+ * Creates an instance of OCAPResolver.
256
+ * @param params Parameters to bootstrap the resolver
257
+ * @param params.statedb @ocap/statedb adapter
258
+ * @param params.indexdb @ocap/indexdb adapter
259
+ * @param params.config @ocap/config object
260
+ * @param params.filter bloom filter to do anti-replay check
261
+ * @param params.validateTokenConfig should we validate token supply and token-holder balance, should be disabled when starting an existing chain
262
+ */
263
+ constructor({ statedb, indexdb, config, filter, validateTokenConfig = true, logger }) {
264
+ if (!statedb) throw new Error("OCAP Resolver requires a valid statedb implementation to work");
265
+ if (!indexdb) throw new Error("OCAP Resolver requires a valid indexdb implementation to work");
266
+ this.logger = logger || console;
267
+ this.statedb = statedb;
268
+ this.indexdb = indexdb;
269
+ this.filter = filter;
270
+ this.config = Object.freeze(_ocap_config.validate(config));
271
+ this.chainAddr = (0, _ocap_util_lib_md5.md5)(config.chainId);
272
+ this.tokenItx = _ocap_config.genTokenItx(this.config);
273
+ this.consensus = `${statedb.name} v${statedb.version}`;
274
+ this.queue = new queue.default({
275
+ autostart: true,
276
+ concurrency: txRequestConfig.concurrent,
277
+ timeout: txRequestConfig.timeout
278
+ });
279
+ if (indexdb) {
280
+ this.tokenCache = require_token_cache.getInstance(indexdb.token);
281
+ this.tokenDistribution = new require_token_distribution.TokenDistributionManager(this);
282
+ }
283
+ if (this.tokenItx) {
284
+ this.config.token.address = this.tokenItx.address;
285
+ if (this.tokenCache) this.tokenCache.set(this.tokenItx.address, this.tokenItx);
286
+ }
287
+ this.executor = (0, _ocap_tx_protocols.createExecutor)({
288
+ filter,
289
+ runAsLambda: typeof statedb.runAsLambda === "function" ? statedb.runAsLambda.bind(statedb) : void 0
290
+ });
291
+ this.formatTx = this._formatTx.bind(this);
292
+ if (validateTokenConfig) this.validateTokenConfig();
293
+ this.connectIndexDB();
294
+ this.initializeStateDB().catch((err) => {
295
+ this.logger.error("Failed to initialize statedb:", err.message);
296
+ process.exit(1);
297
+ });
298
+ }
299
+ sendTx({ tx: txBase64, extra }, ctx = {}) {
300
+ debug$1("sendTx", {
301
+ txBase64,
302
+ request: ctx.request
303
+ });
304
+ if (process.env.CHAIN_MODE === "readonly") throw new _ocap_util_lib_error.CustomError("FORBIDDEN", "This chain node is running in readonly mode");
305
+ if (this.queue.length >= txRequestConfig.maxRequest) throw new _ocap_util_lib_error.CustomError("FORBIDDEN", "Chain is busy");
306
+ const context = {
307
+ txBase64,
308
+ statedb: this.statedb,
309
+ indexdb: this.indexdb,
310
+ config: this.config,
311
+ filter: this.filter,
312
+ logger: this.logger,
313
+ extra: {
314
+ ...ctx,
315
+ txExtra: extra
316
+ }
317
+ };
318
+ return new Promise((resolve, reject) => {
319
+ const task = async () => {
320
+ try {
321
+ resolve((await this.executor.execute(context)).txHash);
322
+ } catch (err) {
323
+ reject(err);
324
+ }
325
+ };
326
+ this.queue.push(task);
327
+ });
328
+ }
329
+ getTx({ hash }, ctx) {
330
+ if (_arcblock_validator.patterns.txHash.test(hash)) return this._getState({
331
+ table: "tx",
332
+ id: hash.toUpperCase(),
333
+ dataKey: "tx.itxJson.data",
334
+ onRead: (tx) => this.formatTx(tx, ctx),
335
+ expandContext: false,
336
+ ctx
337
+ });
338
+ return Promise.resolve(null);
339
+ }
340
+ getBlock() {
341
+ return null;
342
+ }
343
+ getBlocks() {
344
+ return [];
345
+ }
346
+ getUnconfirmedTxs() {
347
+ return [];
348
+ }
349
+ async getChainInfo() {
350
+ return {
351
+ id: this.chainAddr,
352
+ totalTxs: await this.indexdb.tx.count(),
353
+ blockHash: (0, _ocap_util_lib_md5.md5)(""),
354
+ blockHeight: 0,
355
+ blockTime: "",
356
+ version: this.config.version,
357
+ address: this.chainAddr,
358
+ appHash: (0, _ocap_util_lib_md5.md5)((/* @__PURE__ */ new Date()).toISOString()),
359
+ consensusVersion: this.consensus,
360
+ forgeAppsVersion: [],
361
+ moniker: this.config.chainId,
362
+ network: this.config.chainId,
363
+ supportedTxs: this.config.transaction.supportedTxs,
364
+ synced: true,
365
+ votingPower: "10"
366
+ };
367
+ }
368
+ getNodeInfo() {
369
+ return Promise.resolve({
370
+ address: this.chainAddr,
371
+ appHash: (0, _ocap_util_lib_md5.md5)((/* @__PURE__ */ new Date()).toISOString()),
372
+ consensusVersion: this.consensus,
373
+ forgeAppsVersion: [],
374
+ geoInfo: {
375
+ city: "Unknown",
376
+ country: "Unknown",
377
+ latitude: 0,
378
+ longitude: 0
379
+ },
380
+ id: this.chainAddr,
381
+ ip: "",
382
+ moniker: this.config.chainId,
383
+ network: this.config.chainId,
384
+ p2pAddress: "",
385
+ supportedTxs: this.config.transaction.supportedTxs,
386
+ synced: true,
387
+ votingPower: "10"
388
+ });
389
+ }
390
+ getNetInfo() {
391
+ return Promise.resolve({
392
+ listeners: [],
393
+ listening: true,
394
+ nPeers: 0,
395
+ peers: []
396
+ });
397
+ }
398
+ getValidatorsInfo() {
399
+ return Promise.resolve({
400
+ blockHeight: 0,
401
+ validators: [{
402
+ address: (0, _arcblock_did.fromPublicKey)(this.chainAddr),
403
+ name: "",
404
+ proposerPriority: "0",
405
+ votingPower: "10"
406
+ }]
407
+ });
408
+ }
409
+ getConfig() {
410
+ return JSON.stringify(this.config);
411
+ }
412
+ getAccountState({ address, traceMigration = true, expandContext = true }, ctx) {
413
+ return this._getState({
414
+ table: "account",
415
+ id: address,
416
+ dataKey: "data",
417
+ extraParams: { traceMigration },
418
+ expandContext,
419
+ onRead: async (state) => {
420
+ if (state) {
421
+ if (state.tokens) state.tokens = await this.formatTokenMap(state.tokens);
422
+ }
423
+ return state;
424
+ },
425
+ ctx
426
+ });
427
+ }
428
+ getStakeState({ address }, ctx) {
429
+ return this._getState({
430
+ table: "stake",
431
+ id: address,
432
+ dataKey: "data",
433
+ onRead: async (state) => {
434
+ if (state) {
435
+ if (state.tokens) state.tokens = await this.formatTokenMap(state.tokens);
436
+ if (state.revokedTokens) state.revokedTokens = await this.formatTokenMap(state.revokedTokens);
437
+ }
438
+ return state;
439
+ },
440
+ ctx
441
+ });
442
+ }
443
+ async getRollupState({ address }, ctx) {
444
+ const rollup = await this._getState({
445
+ table: "rollup",
446
+ id: address,
447
+ dataKey: "data",
448
+ onRead: async (state) => {
449
+ if (state) {
450
+ state.tokenInfo = (await this.formatTokenMap({ [state.tokenAddress]: "0" })).find((x) => x.address === state.tokenAddress);
451
+ state.foreignToken = state.tokenInfo?.foreignToken;
452
+ }
453
+ return state;
454
+ },
455
+ ctx
456
+ });
457
+ if (!rollup) return null;
458
+ const indexed = await this.indexdb.rollup.get(address);
459
+ if (indexed) {
460
+ rollup.totalDepositAmount = indexed.totalDepositAmount || "0";
461
+ rollup.totalWithdrawAmount = indexed.totalWithdrawAmount || "0";
462
+ }
463
+ return rollup;
464
+ }
465
+ async getRollupBlock({ hash, height, rollupAddress }, ctx) {
466
+ if (hash) return this._getState({
467
+ table: "rollupBlock",
468
+ id: hash,
469
+ dataKey: "data",
470
+ ctx
471
+ });
472
+ const blockHeight = Number(height || 0);
473
+ if (blockHeight > 0 && rollupAddress) {
474
+ const { blocks } = await this._doPaginatedSearch("listRollupBlocks", {
475
+ height: blockHeight,
476
+ rollupAddress
477
+ }, "blocks", "data", ctx);
478
+ if (blocks.length) return this._getState({
479
+ table: "rollupBlock",
480
+ id: blocks[0].hash,
481
+ dataKey: "data",
482
+ ctx
483
+ });
484
+ return null;
485
+ }
486
+ throw new Error("Can not get rollup block without hash or height + rollup");
487
+ }
488
+ getAssetState({ address }, ctx) {
489
+ return this._getState({
490
+ table: "asset",
491
+ id: address,
492
+ dataKey: "data",
493
+ onRead: (state) => {
494
+ if (state && Array.isArray(state.tagsList)) state.tags = state.tagsList;
495
+ return state;
496
+ },
497
+ ctx
498
+ });
499
+ }
500
+ getEvidenceState({ hash }, ctx) {
501
+ return this._getState({
502
+ table: "evidence",
503
+ id: hash,
504
+ dataKey: null,
505
+ ctx
506
+ });
507
+ }
508
+ getFactoryState({ address }, ctx) {
509
+ return this._getState({
510
+ table: "factory",
511
+ id: address,
512
+ dataKey: "data",
513
+ onRead: async (state) => {
514
+ if (state) {
515
+ state.tokens = await this.formatTokenMap(state.tokens || {});
516
+ const input = state.input;
517
+ if (Array.isArray(input?.tokens) && input.tokens.length > 0) input.tokens = await this.formatTokenArray(input.tokens || []);
518
+ const output = state.output;
519
+ if (output) output.data = formatData(output.data);
520
+ }
521
+ return state;
522
+ },
523
+ ctx
524
+ });
525
+ }
526
+ async getTokenState({ address }, ctx) {
527
+ const state = await this._getState({
528
+ table: "token",
529
+ id: address,
530
+ dataKey: "data",
531
+ ctx
532
+ });
533
+ if (state) {
534
+ if (typeof state.decimal === "undefined") state.decimal = _ocap_util_lib_constant.DEFAULT_TOKEN_DECIMAL;
535
+ if (state.metadata) state.metadata = formatData(state.metadata);
536
+ }
537
+ return state;
538
+ }
539
+ getTokenFactoryState(args, ctx) {
540
+ return this._getState({
541
+ table: "tokenFactory",
542
+ id: args.address,
543
+ dataKey: "data",
544
+ ctx,
545
+ onRead: async (state) => {
546
+ if (state) {
547
+ const [token, reserveToken] = await Promise.all([this.getTokenState({ address: state.tokenAddress }), this.getTokenState({ address: state.reserveAddress })]);
548
+ state.token = {
549
+ ...token,
550
+ metadata: formatData(token?.metadata)
551
+ };
552
+ state.reserveToken = {
553
+ ...reserveToken,
554
+ metadata: formatData(reserveToken?.metadata)
555
+ };
556
+ }
557
+ return state;
558
+ }
559
+ });
560
+ }
561
+ getDelegateState({ address }, ctx) {
562
+ return this._getState({
563
+ table: "delegation",
564
+ id: address,
565
+ dataKey: "data",
566
+ onRead: async (state) => {
567
+ if (state) {
568
+ formatDelegationOps(state);
569
+ await this.formatDelegateState(state);
570
+ }
571
+ return state;
572
+ },
573
+ ctx
574
+ });
575
+ }
576
+ async getAccountTokens({ address, token }, ctx) {
577
+ if (!address) return [];
578
+ let state = null;
579
+ if ((0, _arcblock_did.toTypeInfo)(address).role === _ocap_mcrypto.types.RoleType.ROLE_ASSET) state = await this._getState({
580
+ table: "factory",
581
+ id: address,
582
+ dataKey: "data",
583
+ expandContext: false,
584
+ ctx
585
+ });
586
+ else state = await this._getState({
587
+ table: "account",
588
+ id: address,
589
+ dataKey: "data",
590
+ expandContext: false,
591
+ ctx
592
+ });
593
+ if (!state || !state.tokens) return [];
594
+ const allTokens = Object.keys(state.tokens);
595
+ const wantedTokens = token ? allTokens.filter((x) => x === token) : allTokens;
596
+ const tokensInfo = (await Promise.all(wantedTokens.map((x) => this._getState({
597
+ table: "token",
598
+ id: x,
599
+ dataKey: "data",
600
+ expandContext: false,
601
+ ctx
602
+ })))).reduce((acc, x) => {
603
+ acc[x.address] = {
604
+ symbol: x.symbol,
605
+ decimal: x.decimal || _ocap_util_lib_constant.DEFAULT_TOKEN_DECIMAL,
606
+ unit: x.unit
607
+ };
608
+ return acc;
609
+ }, {});
610
+ return wantedTokens.map((x) => ({
611
+ address: x,
612
+ ...tokensInfo[x],
613
+ balance: state.tokens[x]
614
+ }));
615
+ }
616
+ getForgeState() {
617
+ const { accounts, token, transaction, vaults } = this.config;
618
+ return Promise.resolve({
619
+ accountConfig: accounts,
620
+ address: "forge_state",
621
+ consensus: {
622
+ maxBytes: "150000",
623
+ maxCandidates: 256,
624
+ maxGas: "-1",
625
+ maxValidators: 64,
626
+ paramChanged: false,
627
+ validatorChanged: false
628
+ },
629
+ data: null,
630
+ tasks: [],
631
+ token,
632
+ vaults,
633
+ txConfig: {
634
+ ...transaction,
635
+ txGas: {
636
+ ...transaction.txGas,
637
+ minStake: (0, _ocap_util.fromTokenToUnit)(transaction.txGas.minStake, token.decimal).toString(10),
638
+ maxStake: (0, _ocap_util.fromTokenToUnit)(transaction.txGas.maxStake, token.decimal).toString(10)
639
+ },
640
+ txStake: {
641
+ ...transaction.txStake,
642
+ createToken: (0, _ocap_util.fromTokenToUnit)(transaction.txStake.createToken, token.decimal).toString(10),
643
+ createCreditToken: (0, _ocap_util.fromTokenToUnit)(transaction.txStake.createCreditToken, token.decimal).toString(10)
644
+ },
645
+ txFee: Object.keys(transaction.txFee).filter((x) => x !== "default").map((x) => ({
646
+ typeUrl: x,
647
+ fee: (0, _ocap_util.fromTokenToUnit)(transaction.txFee[x], token.decimal).toString(10)
648
+ }))
649
+ },
650
+ upgradeInfo: null,
651
+ reservedSymbols: this.config.reservedSymbols
652
+ });
653
+ }
654
+ async getForgeStats() {
655
+ const [accountCount, assetCount, txCount] = await Promise.all([
656
+ this.indexdb.account.count(),
657
+ this.indexdb.asset.count(),
658
+ this.indexdb.tx.count()
659
+ ]);
660
+ return {
661
+ avgBlockTime: 1,
662
+ avgTps: 0,
663
+ maxTps: 0,
664
+ numAccountMigrateTxs: ["0"],
665
+ numBlocks: [txCount],
666
+ numConsensusUpgradeTxs: [0],
667
+ numConsumeAssetTxs: ["0"],
668
+ numCreateAssetTxs: [assetCount],
669
+ numDeclareFileTxs: ["0"],
670
+ numDeclareTxs: [accountCount],
671
+ numExchangeTxs: ["0"],
672
+ numPokeTxs: ["0"],
673
+ numStakeTxs: ["0"],
674
+ numStakes: ["0"],
675
+ numSysUpgradeTxs: [0],
676
+ numTransferTxs: ["0"],
677
+ numTxs: [txCount],
678
+ numUpdateAssetTxs: ["0"],
679
+ numValidators: [1],
680
+ tps: [0]
681
+ };
682
+ }
683
+ async listTransactions(args = {}, ctx) {
684
+ return await this._doPaginatedSearch("listTransactions", args, "transactions", "tx.itxJson.data", ctx);
685
+ }
686
+ listAssets(args = {}, ctx) {
687
+ return this._doPaginatedSearch("listAssets", args, "assets", "data", ctx);
688
+ }
689
+ listAssetTransactions(args = {}, ctx = {}) {
690
+ if (!args.address) return Promise.resolve({ transactions: [] });
691
+ return this._doPaginatedSearch("listTransactions", {
692
+ ...args,
693
+ assetFilter: { assets: [args.address] }
694
+ }, "transactions", "tx.itxJson.data", ctx);
695
+ }
696
+ async listFactories(args = {}, ctx) {
697
+ const result = await this._doPaginatedSearch("listFactories", args, "factories", "data", ctx);
698
+ result.factories = result.factories.map((x) => {
699
+ const output = x.output;
700
+ if (output) output.data = formatData(output.data);
701
+ return x;
702
+ });
703
+ return result;
704
+ }
705
+ listTopAccounts(args = {}) {
706
+ if (!args.tokenAddress) args.tokenAddress = this.tokenItx.address;
707
+ return this._doPaginatedSearch("listTopAccounts", args, "accounts");
708
+ }
709
+ listTokens(args = {}, ctx) {
710
+ return this._doPaginatedSearch("listTokens", args, "tokens", ["data", "metadata"], ctx);
711
+ }
712
+ listTokenFactories(args = {}, ctx) {
713
+ return this._doPaginatedSearch("listTokenFactories", args, "tokenFactories", "data", ctx);
714
+ }
715
+ listStakes(args = {}, ctx) {
716
+ return this._doPaginatedSearch("listStakes", args, "stakes", "data", ctx);
717
+ }
718
+ listRollups(args = {}, ctx) {
719
+ return this._doPaginatedSearch("listRollups", args, "rollups", "data", ctx);
720
+ }
721
+ listRollupBlocks(args = {}, ctx) {
722
+ return this._doPaginatedSearch("listRollupBlocks", args, "blocks", "data", ctx);
723
+ }
724
+ async listRollupValidators(args = {}, ctx) {
725
+ if (!args.rollupAddress) return { validators: [] };
726
+ const rollup = await this.statedb.rollup.get(args.rollupAddress);
727
+ if (!rollup) return { validators: [] };
728
+ const result = await this._doPaginatedSearch("listRollupValidators", args, "validators", null, ctx);
729
+ const stakes = (await Promise.all(result.validators.map((x) => {
730
+ const stakeAddress = (0, _arcblock_did_util.toStakeAddress)(x.address, args.rollupAddress);
731
+ return this.statedb.stake.get(stakeAddress);
732
+ }))).filter(Boolean);
733
+ result.validators.forEach((x) => {
734
+ const stakeAddress = (0, _arcblock_did_util.toStakeAddress)(x.address, args.rollupAddress);
735
+ const stake = stakes.find((s) => s.address === stakeAddress);
736
+ x.availableStake = stake ? stake.tokens[rollup.tokenAddress] : "0";
737
+ });
738
+ return result;
739
+ }
740
+ listDelegations(args = {}, ctx) {
741
+ return this._doPaginatedSearch("listDelegations", args, "delegations", "data", ctx);
742
+ }
743
+ verifyAccountRisk(args = {}, ctx) {
744
+ if (process.env.TOKEN_FLOW_ENABLED === "true") return require_token_flow.verifyAccountRisk({
745
+ ...args,
746
+ tokenAddress: (0, _ocap_util.toAddress)(args.tokenAddress || this.config.token.address),
747
+ accountAddress: (0, _ocap_util.toAddress)(args.accountAddress || ""),
748
+ accountLimit: Number(process.env.VERIFY_RISK_ACCOUNT_LIMIT) || void 0,
749
+ txLimit: Number(process.env.VERIFY_RISK_TX_LIMIT) || void 0,
750
+ tolerance: process.env.VERIFY_RISK_TOLERANCE || void 0
751
+ }, this, ctx);
752
+ return null;
753
+ }
754
+ listTokenFlows(args = {}, ctx) {
755
+ if (process.env.TOKEN_FLOW_ENABLED === "true") return require_token_flow.listTokenFlows(args, this, ctx);
756
+ return [];
757
+ }
758
+ async search(args) {
759
+ if (!args.keyword) return { results: [] };
760
+ const doSearch = async (type, keyword) => {
761
+ return await this.indexdb[type].get(keyword) ? {
762
+ type,
763
+ id: keyword
764
+ } : null;
765
+ };
766
+ if (_arcblock_validator.patterns.txHash.test(args.keyword)) return { results: (await Promise.all([doSearch("tx", args.keyword.toUpperCase()), doSearch("rollupBlock", args.keyword)])).filter(Boolean) };
767
+ const entitiesByDid = [
768
+ "account",
769
+ "asset",
770
+ "delegation",
771
+ "factory",
772
+ "token",
773
+ "stake",
774
+ "rollup",
775
+ "tokenFactory"
776
+ ];
777
+ if ((0, _arcblock_did.isValid)(args.keyword)) {
778
+ const keyword = (0, _ocap_util.toAddress)(args.keyword);
779
+ return { results: (await Promise.all(entitiesByDid.map((type) => doSearch(type, keyword)))).filter(Boolean) };
780
+ }
781
+ return { results: [] };
782
+ }
783
+ estimateGas(args = {}) {
784
+ const { typeUrl = "" } = args;
785
+ if (!typeUrl || !maxGasOps[typeUrl]) throw new _ocap_util_lib_error.CustomError("INVALID_REQUEST", "Unknown tx typeUrl");
786
+ const { txGas, maxTxSize } = this.config.transaction;
787
+ const estimate = maxGasOps[args.typeUrl];
788
+ let totalGas = new _ocap_util.BN(txGas.price).mul(new _ocap_util.BN(txGas.dataStorage)).mul(new _ocap_util.BN(maxTxSize[args.typeUrl]));
789
+ totalGas = totalGas.add(new _ocap_util.BN(txGas.price).mul(new _ocap_util.BN(txGas.createState)).mul(new _ocap_util.BN((estimate.create || 0) + 1)));
790
+ totalGas = totalGas.add(new _ocap_util.BN(txGas.price).mul(new _ocap_util.BN(txGas.updateState)).mul(new _ocap_util.BN(estimate.update)));
791
+ return { estimate: { max: totalGas.toString(10) } };
792
+ }
793
+ listBlocks() {
794
+ return { blocks: [] };
795
+ }
796
+ validateTokenConfig() {
797
+ const { accounts, token } = this.config;
798
+ const sum = accounts.reduce((acc, x) => acc.add((0, _ocap_util.toBN)(x.balance ?? 0)), (0, _ocap_util.toBN)(0));
799
+ const initialSupply = (0, _ocap_util.toBN)(token.initialSupply);
800
+ if (!sum.eq(initialSupply)) throw new Error("Invalid config, account balance sum does not equal to token supply");
801
+ }
802
+ runAsLambda(fn, args) {
803
+ if (typeof this.statedb.runAsLambda === "function") return this.statedb.runAsLambda((txn) => fn(txn), args);
804
+ return Promise.resolve(fn());
805
+ }
806
+ async getChain() {
807
+ return this.statedb.chain.get(CHAIN_ADDR);
808
+ }
809
+ async getToken(id) {
810
+ return this.statedb.token.get(id);
811
+ }
812
+ async initializeStateDB() {
813
+ const { accounts, token } = this.config;
814
+ const { account: accountDB, chain: chainDB, token: tokenDB } = this.statedb;
815
+ const { account: accountState, chain: chainState, token: tokenState } = _ocap_state;
816
+ const context = {
817
+ txTime: (/* @__PURE__ */ new Date()).toISOString(),
818
+ txHash: ""
819
+ };
820
+ await this.runAsLambda(async (txn) => {
821
+ const info = await this.statedb.chain.get(CHAIN_ADDR, { txn });
822
+ const config = (0, lodash_omit.default)(this.config, ["reservedSymbols"]);
823
+ if (!info) {
824
+ const state = chainState.create({
825
+ ...config,
826
+ address: CHAIN_ADDR,
827
+ context
828
+ });
829
+ const result = await chainDB.create(CHAIN_ADDR, state, { txn });
830
+ this.logger.debug("create chain state", { result });
831
+ } else if ((0, lodash_isEqual.default)((0, lodash_pick.default)(info, Object.keys(config)), config) === false) {
832
+ const state = chainState.update(info, {
833
+ ...config,
834
+ context
835
+ });
836
+ const result = await chainDB.update(CHAIN_ADDR, state, { txn });
837
+ this.logger.debug("update chain state", { result });
838
+ }
839
+ }, { commitMessage: "initialize chain" });
840
+ if (this.tokenItx) {
841
+ await this.runAsLambda(async (txn) => {
842
+ const existToken = await this.statedb.token.get(this.tokenItx.address);
843
+ if (!existToken) {
844
+ const state = tokenState.create(this.tokenItx, context);
845
+ const result = await tokenDB.create(this.tokenItx.address, state, { txn });
846
+ tokenDB.emit("create", result, { txn });
847
+ this.logger.debug("create token state", { address: result?.address });
848
+ } else {
849
+ const changedKeys = [
850
+ "icon",
851
+ "website",
852
+ "metadata"
853
+ ];
854
+ const pickStateToken = (0, lodash_pick.default)(existToken, changedKeys);
855
+ const pickConfigToken = (0, lodash_pick.default)(this.tokenItx, changedKeys);
856
+ if (!(0, lodash_isEqual.default)(pickStateToken, pickConfigToken)) {
857
+ const state = tokenState.update(existToken, pickConfigToken);
858
+ const result = await tokenDB.update(this.tokenItx.address, state, { txn });
859
+ tokenDB.emit("update", result, { txn });
860
+ this.logger.info("update token state", { result });
861
+ }
862
+ }
863
+ }, { commitMessage: "initialize token" });
864
+ await this.runAsLambda(async (txn) => {
865
+ for (let i = 0; i < accounts.length; i++) {
866
+ const { address, balance, moniker, pk } = accounts[i];
867
+ try {
868
+ if (!await accountDB.get(address, { txn })) {
869
+ const balanceStr = (0, _ocap_util.fromTokenToUnit)(balance ?? 0, token.decimal ?? 18).toString(10);
870
+ const state = accountState.create({
871
+ address,
872
+ tokens: { [this.tokenItx.address]: balanceStr },
873
+ moniker: moniker || "token-holder",
874
+ pk
875
+ }, context);
876
+ const result = await accountDB.create(address, state, { txn });
877
+ accountDB.emit("create", result, { txn });
878
+ this.logger.info("create account done", { address });
879
+ }
880
+ } catch (err) {
881
+ this.logger.error("Failed to initialize initial token holders", err);
882
+ }
883
+ }
884
+ }, { commitMessage: "initialize accounts" });
885
+ }
886
+ }
887
+ connectIndexDB() {
888
+ const getToken = this.getToken.bind(this);
889
+ this.statedb.tx.on("create", async (x, ctx) => {
890
+ if (!ctx) return;
891
+ try {
892
+ const tx = await (0, _ocap_indexdb_lib_util.createIndexedTransaction)(x, ctx, this.indexdb);
893
+ await this.indexdb.tx.insert(tx);
894
+ if (typeof require_hooks.onCreateTx === "function") await require_hooks.onCreateTx(tx, ctx, this);
895
+ } catch (error) {
896
+ this.logger.error("create tx index failed", {
897
+ account: x,
898
+ error
899
+ });
900
+ }
901
+ });
902
+ this.statedb.account.on("create", async (x) => {
903
+ try {
904
+ await this.indexdb.account.insert(await (0, _ocap_indexdb_lib_util.createIndexedAccount)(x, getToken));
905
+ } catch (error) {
906
+ this.logger.error("create account index failed", {
907
+ account: x,
908
+ error
909
+ });
910
+ }
911
+ });
912
+ this.statedb.account.on("update", async (x) => {
913
+ try {
914
+ await this.indexdb.account.update(x.address, await (0, _ocap_indexdb_lib_util.createIndexedAccount)(x, getToken));
915
+ } catch (error) {
916
+ this.logger.error("update account index failed", {
917
+ account: x,
918
+ error
919
+ });
920
+ }
921
+ });
922
+ const mapping = {
923
+ asset: [
924
+ _ocap_indexdb_lib_util.createIndexedAsset,
925
+ "address",
926
+ void 0,
927
+ void 0
928
+ ],
929
+ delegation: [
930
+ _ocap_indexdb_lib_util.createIndexedDelegation,
931
+ "address",
932
+ void 0,
933
+ void 0
934
+ ],
935
+ token: [
936
+ _ocap_indexdb_lib_util.createIndexedToken,
937
+ "address",
938
+ null,
939
+ require_hooks.onUpdateToken
940
+ ],
941
+ factory: [
942
+ _ocap_indexdb_lib_util.createIndexedFactory,
943
+ "address",
944
+ void 0,
945
+ void 0
946
+ ],
947
+ stake: [
948
+ _ocap_indexdb_lib_util.createIndexedStake,
949
+ "address",
950
+ void 0,
951
+ void 0
952
+ ],
953
+ rollup: [
954
+ _ocap_indexdb_lib_util.createIndexedRollup,
955
+ "address",
956
+ require_hooks.onCreateRollup,
957
+ void 0
958
+ ],
959
+ rollupBlock: [
960
+ _ocap_indexdb_lib_util.createIndexedRollupBlock,
961
+ "hash",
962
+ require_hooks.onCreateRollupBlock,
963
+ void 0
964
+ ],
965
+ tokenFactory: [
966
+ _ocap_indexdb_lib_util.createIndexedTokenFactory,
967
+ "address",
968
+ void 0,
969
+ void 0
970
+ ]
971
+ };
972
+ Object.keys(mapping).forEach((table) => {
973
+ const [fn, key, onCreate, onUpdate] = mapping[table];
974
+ this.statedb[table].on("create", async (x, _ctx) => {
975
+ try {
976
+ const ctx = this.enrichIndexContext(_ctx);
977
+ const doc = await fn(x, ctx, this.indexdb);
978
+ await this.indexdb[table].insert(doc);
979
+ if (typeof onCreate === "function") await onCreate(doc, ctx, this.indexdb, this);
980
+ } catch (error) {
981
+ this.logger.error(`create ${table} index failed`, {
982
+ [table]: x,
983
+ error
984
+ });
985
+ }
986
+ });
987
+ this.statedb[table].on("update", async (x, _ctx) => {
988
+ try {
989
+ const ctx = this.enrichIndexContext(_ctx);
990
+ const doc = await fn(x, ctx, this.indexdb);
991
+ await this.indexdb[table].update(doc[key], doc);
992
+ if (typeof onUpdate === "function") onUpdate(doc, ctx, this.indexdb, this);
993
+ } catch (error) {
994
+ this.logger.error(`update ${table} index failed`, {
995
+ [table]: x,
996
+ error
997
+ });
998
+ }
999
+ });
1000
+ });
1001
+ }
1002
+ async _getState({ table, id, dataKey, extraParams = {}, onRead = noop, expandContext = true, ctx }) {
1003
+ if (!id) throw new _ocap_util_lib_error.CustomError("INVALID_REQUEST", `Missing required parameter to read ${table} state`);
1004
+ const unified = (0, _arcblock_did.isValid)(id) ? (0, _ocap_util.toAddress)(id) : id;
1005
+ const state = await this.statedb[table].get(unified, { ...extraParams });
1006
+ if (state) {
1007
+ if (dataKey) (0, lodash_set.default)(state, dataKey, formatData((0, lodash_get.default)(state, dataKey)));
1008
+ if (expandContext && state.context) {
1009
+ const { genesisTx, renaissanceTx } = state.context;
1010
+ const txs = (await Promise.all((0, lodash_uniq.default)([genesisTx, renaissanceTx]).filter((x) => Boolean(x)).map((x) => this.getTx({ hash: x }, ctx)))).filter(Boolean);
1011
+ state.context.genesisTx = txs.find((x) => x.hash === genesisTx);
1012
+ state.context.renaissanceTx = txs.find((x) => x.hash === renaissanceTx);
1013
+ if (!state.context.genesisTx) state.context.genesisTx = { hash: genesisTx };
1014
+ if (!state.context.renaissanceTx) state.context.renaissanceTx = { hash: renaissanceTx };
1015
+ }
1016
+ }
1017
+ return onRead(state);
1018
+ }
1019
+ async _doPaginatedSearch(fn, args, listKey, dataKey, ctx) {
1020
+ const { [listKey]: items = [], ...rest } = await this.indexdb[fn](args);
1021
+ let data = items;
1022
+ if (dataKey) data = await Promise.all(items.map(async (x) => {
1023
+ let tx = x;
1024
+ [].concat(dataKey).forEach((key) => {
1025
+ tx = (0, lodash_set.default)(tx, key, formatData((0, lodash_get.default)(tx, key)));
1026
+ });
1027
+ if (listKey === "transactions") tx = await this.formatTx(tx, ctx);
1028
+ if (listKey === "delegations") tx = await this.formatDelegateState(tx);
1029
+ return tx;
1030
+ }));
1031
+ const final = {
1032
+ [listKey]: data,
1033
+ ...rest
1034
+ };
1035
+ if (!final.paging) final.paging = {
1036
+ cursor: "0",
1037
+ next: false,
1038
+ total: 0
1039
+ };
1040
+ return final;
1041
+ }
1042
+ async _formatTx(tx, ctx) {
1043
+ if (!tx) return tx;
1044
+ if (!tx.sender && !tx.receiver) {
1045
+ const typeUrl$1 = (0, _ocap_message.fromTypeUrl)(tx.tx.itx.typeUrl);
1046
+ tx.sender = (0, _ocap_state_lib_states_tx.getTxSender)({
1047
+ tx: tx.tx,
1048
+ itx: tx.tx.itxJson,
1049
+ typeUrl: typeUrl$1
1050
+ });
1051
+ tx.receiver = (0, _ocap_state_lib_states_tx.getTxReceiver)({
1052
+ tx: tx.tx,
1053
+ itx: tx.tx.itxJson,
1054
+ typeUrl: typeUrl$1
1055
+ });
1056
+ }
1057
+ if (!tx.receipts) tx.receipts = (0, _ocap_state_lib_states_tx.getTxReceipts)(tx, { config: this.config });
1058
+ const typeUrl = (0, lodash_get.default)(tx, "tx.itxJson.type_url");
1059
+ if (typeUrl === "fg:t:revoke_withdraw" || typeUrl === "fg:t:approve_withdraw") {
1060
+ const withdrawHash = (0, lodash_get.default)(tx, "tx.itxJson.withdraw_tx_hash");
1061
+ if (withdrawHash && withdrawHash !== tx.hash) {
1062
+ const withdrawTx = await this.getTx({ hash: withdrawHash }, ctx);
1063
+ tx.receipts = (0, _ocap_state_lib_states_tx.getTxReceipts)(tx, {
1064
+ config: this.config,
1065
+ withdrawTx
1066
+ });
1067
+ }
1068
+ }
1069
+ if (typeUrl && [
1070
+ "fg:t:deposit_token",
1071
+ "fg:t:withdraw_token",
1072
+ "fg:t:revoke_swap"
1073
+ ].includes(typeUrl)) tx.receipts = (0, _ocap_state_lib_states_tx.getTxReceipts)(tx, { config: this.config });
1074
+ if (typeUrl === "fg:t:account_migrate") {
1075
+ const fromState = await this.statedb.account.get(tx.tx.from, { traceMigration: false });
1076
+ if (fromState) tx.receipts = (0, _ocap_state_lib_states_tx.getTxReceipts)(tx, {
1077
+ config: this.config,
1078
+ fromState
1079
+ });
1080
+ }
1081
+ if (typeUrl === "fg:t:declare") tx.receipts = (0, _ocap_state_lib_states_tx.getTxReceipts)(tx, {
1082
+ config: this.config,
1083
+ time: tx.time
1084
+ });
1085
+ tx.receipts = (0, _ocap_state_lib_states_tx.mergeTxReceipts)(tx.receipts);
1086
+ tx.tokenSymbols = await this.getTxTokenSymbols(tx);
1087
+ this.fixReceiptTokens(tx);
1088
+ this.fixTokenSymbols(tx);
1089
+ return (0, _ocap_state_lib_states_tx.attachPaidTxGas)(tx);
1090
+ }
1091
+ async _getAllResults(dataKey, fn, limit) {
1092
+ const pageSize = 100;
1093
+ const { paging, [dataKey]: firstPage } = await fn({ size: pageSize });
1094
+ if (limit && paging.total > limit) throw new _ocap_util_lib_error.CustomError("EXCEED_LIMIT", `Total exceeds limit ${limit}`);
1095
+ if (paging.total <= pageSize) return firstPage;
1096
+ const totalPage = Math.ceil(paging.total / pageSize) - 1;
1097
+ const cursors = new Array(totalPage).fill(true).map((_, i) => (i + 1) * pageSize);
1098
+ const step = Number(process.env.RESOLVER_BATCH_CONCURRENCY) || 3;
1099
+ let results = firstPage;
1100
+ for (let i = 0; i < cursors.length; i += step) {
1101
+ const batchResults = await Promise.all(cursors.slice(i, i + step).map(async (cursor) => {
1102
+ const { [dataKey]: list } = await fn({
1103
+ size: pageSize,
1104
+ cursor
1105
+ });
1106
+ return list;
1107
+ }));
1108
+ results = results.concat(batchResults.flat());
1109
+ }
1110
+ return results;
1111
+ }
1112
+ async formatTokenArray(tokens) {
1113
+ const uniqTokens = (0, lodash_unionBy.default)(tokens, "address");
1114
+ const tokenStates = await Promise.all(uniqTokens.map((token) => this.tokenCache.get(token.address)));
1115
+ return uniqTokens.map((token) => ({
1116
+ ...token,
1117
+ ...extractTokenMeta(token.address, tokenStates)
1118
+ }));
1119
+ }
1120
+ async formatTokenMap(tokens) {
1121
+ if ((0, lodash_isEmpty.default)(tokens)) return [];
1122
+ const tasks = Object.keys(tokens).map((address) => this.tokenCache.get(address));
1123
+ const tokenStates = await Promise.all(tasks);
1124
+ return Object.keys(tokens).map((address) => ({
1125
+ value: tokens[address],
1126
+ ...extractTokenMeta(address, tokenStates)
1127
+ }));
1128
+ }
1129
+ async getTxTokenSymbols(tx) {
1130
+ if (!tx) return [];
1131
+ const rollupTxs = [
1132
+ "DepositTokenV2Tx",
1133
+ "WithdrawTokenV2Tx",
1134
+ "JoinRollupTx",
1135
+ "LeaveRollupTx",
1136
+ "CreateRollupBlockTx",
1137
+ "ClaimBlockRewardTx"
1138
+ ];
1139
+ const typeUrl = (0, _ocap_util.formatTxType)(tx.tx.itxJson._type || "");
1140
+ const tokens = [];
1141
+ if ((0, _ocap_indexdb_lib_util.isDefaultTokenChanged)(tx, this.config.token)) tokens.push(this.tokenItx);
1142
+ if (typeUrl === "AcquireAssetV2Tx") {
1143
+ const factoryInput = (await this.indexdb.factory.get(tx.tx.itxJson.factory))?.input;
1144
+ if (factoryInput?.tokens) tokens.push(...factoryInput.tokens);
1145
+ } else if (typeUrl === "ExchangeV2Tx") {
1146
+ tokens.push(...tx.tx.itxJson.sender?.tokens || []);
1147
+ tokens.push(...tx.tx.itxJson.receiver?.tokens || []);
1148
+ } else if (typeUrl === "TransferV2Tx") tokens.push(...tx.tx.itxJson.tokens || []);
1149
+ else if ([
1150
+ "TransferV3Tx",
1151
+ "AcquireAssetV3Tx",
1152
+ "StakeTx"
1153
+ ].includes(typeUrl)) tokens.push(...(0, lodash_unionBy.default)((tx.tx.itxJson.inputs || []).reduce((acc, x) => acc.concat(x.tokens || []), []), "address").filter((x) => x.address));
1154
+ else if ([
1155
+ "RevokeStakeTx",
1156
+ "SlashStakeTx",
1157
+ "ReturnStakeTx"
1158
+ ].includes(typeUrl)) tokens.push(...(0, lodash_unionBy.default)((tx.tx.itxJson.outputs || []).reduce((acc, x) => acc.concat(x.tokens || []), []), "address").filter((x) => x.address));
1159
+ else if (typeUrl === "ClaimStakeTx") {
1160
+ const revokeTx = await this.indexdb.tx.get(tx.tx.itxJson.evidence.hash);
1161
+ if (revokeTx) {
1162
+ const revokeTxAny = revokeTx;
1163
+ tokens.push(...(0, lodash_unionBy.default)((revokeTxAny.tx?.itxJson?.outputs || []).reduce((acc, x) => acc.concat(x.tokens || []), []), "address").filter((x) => x.address));
1164
+ } else this.logger.warn("Revoke stake tx not found", { tx });
1165
+ } else if (rollupTxs.includes(typeUrl)) {
1166
+ const rollup = await this.indexdb.rollup.get(tx.tx.itxJson.rollup);
1167
+ if (rollup) tokens.push({
1168
+ address: rollup.tokenAddress,
1169
+ value: "0"
1170
+ });
1171
+ } else if (typeUrl === "CreateTokenTx") return [extractTokenMeta(tx.tx.itxJson.address, [tx.tx.itxJson])];
1172
+ else if (typeUrl === "MintTokenTx" || typeUrl === "BurnTokenTx") {
1173
+ const token = await this.indexdb.tokenFactory.get(tx.tx.itxJson.tokenFactory);
1174
+ if (token) tokens.push({ address: token.tokenAddress });
1175
+ } else if (typeUrl === "CreateTokenFactoryTx") tokens.push({ address: tx.tx.itxJson.token.address });
1176
+ if (tokens.length > 0) return this.formatTokenArray(tokens);
1177
+ return [];
1178
+ }
1179
+ fixReceiptTokens(tx) {
1180
+ tx.receipts.forEach((receipt) => receipt.changes.forEach((x) => {
1181
+ if (x.target === "") x.target = this.tokenItx.address;
1182
+ }));
1183
+ return tx;
1184
+ }
1185
+ fixTokenSymbols(tx) {
1186
+ if (tx.tokenSymbols.findIndex((x) => x.address === this.tokenItx.address) === -1) tx.tokenSymbols.push(extractTokenMeta(this.tokenItx.address, [this.tokenItx]));
1187
+ return tx;
1188
+ }
1189
+ enrichIndexContext(ctx) {
1190
+ if (Array.isArray(ctx.tokenStates)) return {
1191
+ ...ctx,
1192
+ tokenStates: [...ctx.tokenStates, this.tokenItx]
1193
+ };
1194
+ if (ctx.tokenState) return {
1195
+ ...ctx,
1196
+ tokenStates: [ctx.tokenState]
1197
+ };
1198
+ return ctx;
1199
+ }
1200
+ async formatDelegateState(state) {
1201
+ if (Array.isArray(state.ops) && state.ops.length > 0) await Promise.all(state.ops.map(async (op) => {
1202
+ if (op.value.limit && Array.isArray(op.value.limit.tokens)) op.value.limit.tokens = await this.formatTokenArray(op.value.limit.tokens || []);
1203
+ }));
1204
+ return state;
1205
+ }
1206
+ async buildMigrationChain() {
1207
+ const migrationTxs = await this._getAllResults("transactions", (paging) => this.listTransactions({
1208
+ paging,
1209
+ typeFilter: { types: ["account_migrate"] },
1210
+ validityFilter: { validity: "VALID" }
1211
+ }));
1212
+ const migrationChain = new require_migration_chain.MigrationChainManager();
1213
+ migrationChain.buildChains(migrationTxs);
1214
+ this.migrationChain = migrationChain;
1215
+ }
1216
+ async getMigrationChain() {
1217
+ if (!this.migrationChain) await this.buildMigrationChain();
1218
+ return this.migrationChain;
1219
+ }
1220
+ /** fix accounts field of legacy approve_withdraw transactions in indexdb */
1221
+ async fixApproveWithdrawAccounts() {
1222
+ const approveWithdrawTxs = await this._getAllResults("transactions", (paging) => this.listTransactions({
1223
+ paging,
1224
+ typeFilter: { types: ["approve_withdraw"] },
1225
+ validityFilter: { validity: ["VALID"] }
1226
+ }));
1227
+ return (await Promise.all(approveWithdrawTxs.map(async (tx) => {
1228
+ const withdrawHash = tx.tx?.itxJson?.withdraw_tx_hash;
1229
+ if (!withdrawHash) return;
1230
+ const indexdbTx = await this.indexdb.tx.get(tx.hash);
1231
+ const from = (await this.getTx({ hash: withdrawHash }))?.tx?.from;
1232
+ if (!from || !indexdbTx) return;
1233
+ const accounts = (0, lodash_uniq.default)((indexdbTx.accounts || []).concat([from]).filter(Boolean));
1234
+ debug$1("fixApproveWithdrawAccounts", {
1235
+ tx,
1236
+ from,
1237
+ accounts
1238
+ });
1239
+ return await this.indexdb.tx.update(tx.hash, { accounts });
1240
+ }))).filter(Boolean);
1241
+ }
1242
+ async updateTokenDistribution({ tokenAddress = this.config.token.address, force = false } = {}) {
1243
+ const validation = _arcblock_validator.Joi.object({
1244
+ tokenAddress: _arcblock_validator.Joi.DID().prefix().role("ROLE_TOKEN").required(),
1245
+ force: _arcblock_validator.Joi.boolean().required()
1246
+ }).validate({
1247
+ tokenAddress,
1248
+ force
1249
+ });
1250
+ if (validation.error) throw new _ocap_util_lib_error.CustomError("INVALID_PARAMS", validation.error.message);
1251
+ return await this.tokenDistribution.updateByToken(tokenAddress, force);
1252
+ }
1253
+ async getTokenDistribution(args = {}) {
1254
+ const { tokenAddress = this.config.token.address } = args;
1255
+ return await this.tokenDistribution.getDistribution(tokenAddress);
1256
+ }
1257
+ /**
1258
+ * Fetch data in chunks to handle large datasets
1259
+ * @param fn Function to fetch data
1260
+ * @param params Chunk parameters
1261
+ * @returns Iterator with next function
1262
+ */
1263
+ listChunks(fn, { total, concurrency = 3, chunkSize = 2100, pageSize = 100, timeKey, dataKey }) {
1264
+ let done = false;
1265
+ let time;
1266
+ const totalPage = Math.ceil(total / pageSize);
1267
+ let curPage = 0;
1268
+ const next = async () => {
1269
+ if (done) return [];
1270
+ let results = [];
1271
+ for (; curPage < totalPage; curPage += concurrency) {
1272
+ const flatResults = (await Promise.all(new Array(Math.min(concurrency, totalPage - curPage)).fill(true).map(async (_, i) => {
1273
+ const { [dataKey]: list } = await fn({
1274
+ size: pageSize,
1275
+ cursor: i * pageSize,
1276
+ time
1277
+ });
1278
+ return list;
1279
+ }))).flat();
1280
+ if (!flatResults.length) {
1281
+ done = true;
1282
+ return results;
1283
+ }
1284
+ results = results.concat(flatResults);
1285
+ time = results.length ? results[results.length - 1][timeKey] : void 0;
1286
+ if (results.length >= chunkSize) return results;
1287
+ }
1288
+ done = true;
1289
+ return results;
1290
+ };
1291
+ return {
1292
+ next,
1293
+ done
1294
+ };
1295
+ }
1296
+ async listTransactionsChunks(args = {}, { chunkSize = 2100, pageSize = 100 } = {}) {
1297
+ return this.listChunks(({ size, time, cursor }) => this.listTransactions({
1298
+ ...args,
1299
+ paging: {
1300
+ order: {
1301
+ field: "time",
1302
+ type: "asc"
1303
+ },
1304
+ ...args.paging || {},
1305
+ size,
1306
+ cursor
1307
+ },
1308
+ timeFilter: Object.assign(args.timeFilter || {}, time ? { startDateTime: time } : {})
1309
+ }), {
1310
+ dataKey: "transactions",
1311
+ timeKey: "time",
1312
+ total: await this.indexdb.tx.count(),
1313
+ chunkSize,
1314
+ pageSize
1315
+ });
1316
+ }
1317
+ async listStakeChunks(args = {}, { chunkSize = 2100, pageSize = 100 } = {}) {
1318
+ return this.listChunks(({ size, time, cursor }) => this.listStakes({
1319
+ ...args,
1320
+ paging: {
1321
+ order: {
1322
+ field: "time",
1323
+ type: "asc"
1324
+ },
1325
+ ...args.paging || {},
1326
+ size,
1327
+ cursor
1328
+ },
1329
+ timeFilter: Object.assign(args.timeFilter || {}, time ? { startDateTime: time } : {})
1330
+ }), {
1331
+ dataKey: "stakes",
1332
+ timeKey: "renaissanceTime",
1333
+ total: await this.indexdb.stake.count(),
1334
+ chunkSize,
1335
+ pageSize
1336
+ });
1337
+ }
1338
+ };
1339
+
1340
+ //#endregion
1341
+ exports.default = OCAPResolver;
1342
+ exports.formatData = formatData;
1343
+ exports.formatDelegationOps = formatDelegationOps;