@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.
- package/esm/api.d.mts +24 -0
- package/esm/api.mjs +53 -0
- package/esm/hooks.d.mts +153 -0
- package/esm/hooks.mjs +267 -0
- package/esm/index.d.mts +201 -0
- package/esm/index.mjs +1327 -0
- package/esm/migration-chain.d.mts +52 -0
- package/esm/migration-chain.mjs +97 -0
- package/esm/package.mjs +5 -0
- package/esm/token-cache.d.mts +20 -0
- package/esm/token-cache.mjs +26 -0
- package/esm/token-distribution.d.mts +166 -0
- package/esm/token-distribution.mjs +241 -0
- package/esm/token-flow.d.mts +139 -0
- package/esm/token-flow.mjs +330 -0
- package/esm/types.d.mts +115 -0
- package/esm/types.mjs +1 -0
- package/lib/_virtual/rolldown_runtime.cjs +29 -0
- package/lib/api.cjs +54 -0
- package/lib/api.d.cts +24 -0
- package/lib/hooks.cjs +274 -0
- package/lib/hooks.d.cts +153 -0
- package/lib/index.cjs +1343 -0
- package/lib/index.d.cts +201 -0
- package/lib/migration-chain.cjs +99 -0
- package/lib/migration-chain.d.cts +52 -0
- package/lib/package.cjs +11 -0
- package/lib/token-cache.cjs +27 -0
- package/lib/token-cache.d.cts +20 -0
- package/lib/token-distribution.cjs +243 -0
- package/lib/token-distribution.d.cts +166 -0
- package/lib/token-flow.cjs +336 -0
- package/lib/token-flow.d.cts +139 -0
- package/lib/types.cjs +0 -0
- package/lib/types.d.cts +115 -0
- package/package.json +49 -21
- package/lib/api.js +0 -71
- package/lib/hooks.js +0 -339
- package/lib/index.js +0 -1486
- package/lib/migration-chain.js +0 -144
- package/lib/token-cache.js +0 -40
- package/lib/token-distribution.js +0 -358
- 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;
|