@ocap/indexdb 1.6.5 → 1.6.10

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/lib/db.js CHANGED
@@ -1,15 +1,57 @@
1
+ /* eslint-disable no-unused-vars */
1
2
  const Ready = require('@ocap/util/lib/ready');
3
+ const { indexes } = require('@ocap/state');
2
4
 
3
5
  class BaseIndexDB extends Ready {
4
6
  constructor() {
5
7
  super();
6
8
 
7
- this.readyMarks = {
8
- account: false,
9
- asset: false,
10
- delegation: false,
11
- tx: false,
12
- };
9
+ this.readyMarks = indexes.reduce((acc, x) => ({ ...acc, [x]: false }), {});
10
+ this.readyListenersAttached = false;
11
+ }
12
+
13
+ attachReadyListeners() {
14
+ if (this.readyListenersAttached) {
15
+ return;
16
+ }
17
+
18
+ for (const index of indexes) {
19
+ if (typeof this[index] === 'undefined') {
20
+ throw new Error(`Missing index ${index} in indexdb adapter: ${this.name}`);
21
+ }
22
+
23
+ this[index].onReady(() => this.markReady(index));
24
+ }
25
+
26
+ this.readyListenersAttached = true;
27
+ }
28
+
29
+ listTransactions({ addressFilter = {}, paging = {}, timeFilter = {}, typeFilter = {} } = {}) {
30
+ throw new Error('listTransactions should be implemented in adapter');
31
+ }
32
+
33
+ listAssets({ ownerAddress, paging } = {}) {
34
+ throw new Error('listAssets should be implemented in adapter');
35
+ }
36
+
37
+ listFactories({ ownerAddress, paging } = {}) {
38
+ throw new Error('listFactories should be implemented in adapter');
39
+ }
40
+
41
+ listTopAccounts({ paging, tokenAddress } = {}) {
42
+ throw new Error('listTopAccounts should be implemented in adapter');
43
+ }
44
+
45
+ listTokens({ paging }) {
46
+ throw new Error('listTokens should be implemented in adapter');
47
+ }
48
+
49
+ listStakes({ paging = {}, addressFilter = {}, timeFilter = {}, assetFilter = {} } = {}) {
50
+ throw new Error('listStakes should be implemented in adapter');
51
+ }
52
+
53
+ listBlocks() {
54
+ return [];
13
55
  }
14
56
  }
15
57
 
package/lib/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  /* eslint-disable no-underscore-dangle */
2
2
  const Kareem = require('kareem');
3
+ const omit = require('lodash/omit');
3
4
  const Ready = require('@ocap/util/lib/ready');
4
5
 
5
6
  class BaseIndex extends Ready {
@@ -21,6 +22,11 @@ class BaseIndex extends Ready {
21
22
  hooks.execPreSync(x, args);
22
23
  const result = await this[`_${x}`](...args);
23
24
  hooks.execPostSync(x, result);
25
+
26
+ if (['insert', 'update'].includes(x)) {
27
+ this.emit(x, omit(result, '$loki'));
28
+ }
29
+
24
30
  return result;
25
31
  },
26
32
  });
package/lib/util.js ADDED
@@ -0,0 +1,514 @@
1
+ /* eslint-disable no-restricted-globals */
2
+ const get = require('lodash/get');
3
+ const uniq = require('lodash/uniq');
4
+ const pick = require('lodash/pick');
5
+ const createSortedList = require('@ocap/util/lib/create-sorted-list');
6
+ const { BN } = require('@ocap/util');
7
+
8
+ const formatTokenMeta = (token, tokenStates) => {
9
+ if (!tokenStates) {
10
+ return token;
11
+ }
12
+
13
+ const tokenState = tokenStates.find((x) => x.address === token.address);
14
+ token.decimal = tokenState.decimal;
15
+ token.unit = tokenState.unit;
16
+ token.symbol = tokenState.symbol;
17
+
18
+ return token;
19
+ };
20
+
21
+ const createIndexedAccount = async (x, getTokenFn) => {
22
+ const tasks = Object.keys(x.tokens || {}).map((address) => getTokenFn(address));
23
+ const tokenStates = await Promise.all(tasks);
24
+
25
+ return {
26
+ address: x.address,
27
+ balance: x.balance || '0',
28
+ moniker: x.moniker,
29
+ nonce: x.nonce,
30
+ numAssets: x.numAssets || 0,
31
+ numTxs: x.numTxs || 0,
32
+ recentNumTxs: [],
33
+ renaissanceTime: x.context.renaissanceTime,
34
+ tokens: Object.keys(x.tokens || {}).map((address) =>
35
+ formatTokenMeta({ address, balance: x.tokens[address] }, tokenStates)
36
+ ), // eslint-disable-line function-paren-newline
37
+ genesisTime: x.context.genesisTime,
38
+ migratedTo: x.migratedTo[0] || '',
39
+ migratedFrom: x.migratedFrom[0] || '',
40
+ };
41
+ };
42
+
43
+ const createIndexedAsset = (x) => ({
44
+ address: x.address,
45
+ issuer: x.issuer,
46
+ moniker: x.moniker,
47
+ owner: x.owner,
48
+ parent: x.parent,
49
+ readonly: x.readonly,
50
+ transferrable: x.transferrable,
51
+ ttl: x.ttl,
52
+ consumedTime: x.consumedTime,
53
+ genesisTime: x.context.genesisTime,
54
+ renaissanceTime: x.context.renaissanceTime,
55
+ data: x.data,
56
+ display: x.display,
57
+ endpoint: x.endpoint,
58
+ tags: x.tags,
59
+ });
60
+
61
+ const createIndexedDelegation = (x, ctx) => ({
62
+ address: x.address,
63
+ data: x.data,
64
+ genesisTime: x.context.genesisTime,
65
+ renaissanceTime: x.context.renaissanceTime,
66
+ ops: x.ops,
67
+ from: get(ctx, 'senderState.address', ''),
68
+ to: get(ctx, 'receiverState.address', ''),
69
+ });
70
+
71
+ const createIndexedToken = (x) => ({
72
+ address: x.address,
73
+ issuer: x.issuer,
74
+ name: x.name,
75
+ description: x.description,
76
+ symbol: x.symbol,
77
+ unit: x.unit,
78
+ decimal: x.decimal,
79
+ icon: x.icon,
80
+ totalSupply: x.totalSupply,
81
+ initialSupply: x.initialSupply,
82
+ foreignToken: x.foreignToken,
83
+ data: x.data,
84
+ genesisTime: x.context.genesisTime,
85
+ renaissanceTime: x.context.renaissanceTime,
86
+ });
87
+
88
+ const createIndexedFactory = async (factory, context) => {
89
+ if (context.factoryTokens && context.factoryTokens.length > 0) {
90
+ factory.input.tokens = factory.input.tokens.map((token) => formatTokenMeta(token, context.tokenStates));
91
+ }
92
+ return {
93
+ ...pick(factory, [
94
+ 'address',
95
+ 'owner',
96
+ 'name',
97
+ 'description',
98
+ 'settlement',
99
+ 'limit',
100
+ 'trustedIssuers',
101
+ 'input',
102
+ 'output',
103
+ 'hooks',
104
+ 'data',
105
+ 'numMinted',
106
+ 'lastSettlement',
107
+ 'balance',
108
+ 'display',
109
+ ]),
110
+ tokens: Object.keys(factory.tokens || {}).map((address) =>
111
+ formatTokenMeta({ address, balance: factory.tokens[address] }, context.tokenStates)
112
+ ), // eslint-disable-line function-paren-newline
113
+ genesisTime: factory.context.genesisTime,
114
+ renaissanceTime: factory.context.renaissanceTime,
115
+ };
116
+ };
117
+
118
+ // These fields are used internally to filter transactions by some entity
119
+ const createIndexedTransaction = (tx, ctx) => {
120
+ tx.accounts = [tx.tx.from, tx.sender, tx.receiver];
121
+ tx.assets = [];
122
+ tx.tokens = [];
123
+ tx.factories = [];
124
+ tx.stakes = [];
125
+ tx.rollups = [];
126
+
127
+ tx.valid = tx.code === 'OK';
128
+
129
+ // accounts
130
+ if (ctx.senderState) {
131
+ tx.accounts.push(ctx.senderState.address);
132
+ }
133
+ if (ctx.receiverState) {
134
+ tx.accounts.push(ctx.receiverState.address);
135
+ }
136
+ if (ctx.vaultState) {
137
+ tx.accounts.push(ctx.vaultState.address);
138
+ }
139
+ if (ctx.updatedAccounts && Array.isArray(ctx.updatedAccounts)) {
140
+ tx.accounts.push(...ctx.updatedAccounts.map((x) => x.address));
141
+ }
142
+ if (ctx.delegatorState) {
143
+ tx.accounts.push(ctx.delegatorState.address);
144
+ }
145
+ if (Array.isArray(ctx.delegatorStates)) {
146
+ tx.accounts.push(...ctx.delegatorStates.map((x) => x.address));
147
+ }
148
+ if (Array.isArray(ctx.signerStates)) {
149
+ tx.accounts.push(...ctx.signerStates.map((x) => x.address));
150
+ }
151
+ if (Array.isArray(ctx.receiverStates)) {
152
+ tx.accounts.push(...ctx.receiverStates.map((x) => x.address));
153
+ }
154
+
155
+ // assets
156
+ if (ctx.assetState) {
157
+ tx.assets.push(ctx.assetState.address);
158
+ }
159
+ if (ctx.assetStates && Array.isArray(ctx.assetStates)) {
160
+ tx.assets.push(...ctx.assetStates.map((x) => x.address));
161
+ }
162
+
163
+ // tokens
164
+ if (ctx.tokenState) {
165
+ tx.tokens.push(ctx.tokenState.address);
166
+ }
167
+ if (ctx.tokenStates && Array.isArray(ctx.tokenStates)) {
168
+ tx.tokens.push(...ctx.tokenStates.map((x) => x.address));
169
+ }
170
+
171
+ // factories
172
+ if (ctx.factoryState) {
173
+ tx.factories.push(ctx.factoryState.address);
174
+ }
175
+
176
+ // stakes
177
+ if (ctx.stakeState) {
178
+ tx.stakes.push(ctx.stakeState.address);
179
+ }
180
+ if (Array.isArray(ctx.stakeStates)) {
181
+ tx.stakes.push(...ctx.stakeStates.map((x) => x.address));
182
+ }
183
+
184
+ // rollups
185
+ if (ctx.rollupState) {
186
+ tx.rollups.push(ctx.rollupState.address);
187
+ }
188
+
189
+ const pickedFields = ['address', 'symbol', 'decimal', 'unit'];
190
+ if (Array.isArray(ctx.tokenStates)) {
191
+ tx.tokenSymbols = ctx.tokenStates.map((t) => pick(t, pickedFields));
192
+ } else if (ctx.tokenState) {
193
+ tx.tokenSymbols = [pick(ctx.tokenState, pickedFields)];
194
+ } else {
195
+ tx.tokenSymbols = [];
196
+ }
197
+
198
+ tx.accounts = [...tx.accounts, ...tx.stakes];
199
+
200
+ return tx;
201
+ };
202
+
203
+ const createIndexedStake = async (x, ctx) => {
204
+ return {
205
+ ...pick(x, [
206
+ 'address',
207
+ 'sender',
208
+ 'receiver',
209
+ 'assets',
210
+ 'revocable',
211
+ 'data',
212
+ 'message',
213
+ 'revokeWaitingPeriod',
214
+ 'revokedAssets',
215
+ ]),
216
+ tokens: Object.keys(x.tokens || {}).map((address) =>
217
+ formatTokenMeta({ address, balance: x.tokens[address] }, ctx.tokenStates)
218
+ ),
219
+ revokedTokens: Object.keys(x.revokedTokens || {}).map((address) =>
220
+ formatTokenMeta({ address, balance: x.revokedTokens[address] }, ctx.tokenStates)
221
+ ),
222
+ genesisTime: x.context.genesisTime,
223
+ renaissanceTime: x.context.renaissanceTime,
224
+ };
225
+ };
226
+
227
+ const createIndexedRollup = async (x, ctx) => {
228
+ const rollup = {
229
+ ...pick(x, [
230
+ 'address',
231
+ 'paused',
232
+ 'tokenAddress',
233
+ 'contractAddress',
234
+ 'seedValidators',
235
+ 'validators',
236
+ 'minStakeAmount',
237
+ 'maxStakeAmount',
238
+ 'minSignerCount',
239
+ 'maxSignerCount',
240
+ 'minBlockSize',
241
+ 'maxBlockSize',
242
+ 'minBlockInterval',
243
+ 'minBlockConfirmation',
244
+ 'minDepositAmount',
245
+ 'maxDepositAmount',
246
+ 'minWithdrawAmount',
247
+ 'maxWithdrawAmount',
248
+ 'depositFeeRate',
249
+ 'withdrawFeeRate',
250
+ 'proposerFeeShare',
251
+ 'publisherFeeShare',
252
+ 'minDepositFee',
253
+ 'maxDepositFee',
254
+ 'minWithdrawFee',
255
+ 'maxWithdrawFee',
256
+ 'issuer',
257
+ 'blockHeight',
258
+ 'blockHash',
259
+ 'leaveWaitingPeriod',
260
+ 'publishWaitingPeriod',
261
+ 'publishSlashRate',
262
+ 'migrateHistory',
263
+ 'data',
264
+ ]),
265
+ genesisTime: x.context.genesisTime,
266
+ renaissanceTime: x.context.renaissanceTime,
267
+ };
268
+
269
+ if (ctx.tokenStates) {
270
+ const tokenState = ctx.tokenStates.find((t) => t.address === x.tokenAddress);
271
+ rollup.tokenInfo = formatTokenMeta({ address: x.tokenAddress, value: '0' }, ctx.tokenStates);
272
+ rollup.foreignToken = tokenState.foreignToken;
273
+ }
274
+
275
+ return rollup;
276
+ };
277
+
278
+ const createIndexedRollupBlock = async (x, ctx) => {
279
+ return {
280
+ ...pick(x, [
281
+ 'hash',
282
+ 'height',
283
+ 'merkleRoot',
284
+ 'previousHash',
285
+ 'txsHash',
286
+ 'txs',
287
+ 'proposer',
288
+ 'signatures',
289
+ 'rollup',
290
+ 'mintedAmount',
291
+ 'burnedAmount',
292
+ 'rewardAmount',
293
+ 'minReward',
294
+ 'data',
295
+ ]),
296
+ genesisTime: x.context.genesisTime,
297
+ renaissanceTime: x.context.renaissanceTime,
298
+ tokenInfo: formatTokenMeta({ address: ctx.rollupState.tokenAddress, value: '0' }, ctx.tokenStates),
299
+ validators: (x.signaturesList || x.signatures || []).map((v) => v.signer),
300
+ };
301
+ };
302
+
303
+ const formatTxBeforeInsert = (row) => {
304
+ const tx = { ...row };
305
+ tx.sender = tx.sender || '';
306
+ tx.receiver = tx.receiver || '';
307
+
308
+ // index assets
309
+ tx.assets = tx.assets || [];
310
+ if (['transfer', 'transfer_v2'].includes(tx.type) && tx.tx.itxJson.assets) {
311
+ tx.assets.push(...tx.tx.itxJson.assets);
312
+ } else if (['transfer_v3', 'stake'].includes(tx.type)) {
313
+ tx.assets.push(...createSortedList(tx.tx.itxJson.inputs.map((x) => x.assets)));
314
+ } else if (['revoke_stake'].includes(tx.type)) {
315
+ tx.assets.push(...createSortedList(tx.tx.itxJson.outputs.map((x) => x.assets)));
316
+ } else if (['exchange', 'exchange_v2'].includes(tx.type)) {
317
+ tx.assets.push(...(tx.tx.itxJson.sender.assets || []), ...(tx.tx.itxJson.receiver.assets || []));
318
+ } else if (['create_asset', 'update_asset', 'acquire_asset_v2', 'mint_asset'].includes(tx.type)) {
319
+ tx.assets.push(tx.tx.itxJson.address);
320
+ }
321
+
322
+ // index factories
323
+ tx.factories = tx.factories || [];
324
+ if (['acquire_asset_v2', 'acquire_asset_v3', 'mint_asset', 'create_factory'].includes(tx.type)) {
325
+ tx.factories.push(tx.tx.itxJson.factory);
326
+ }
327
+
328
+ // index tokens
329
+ tx.tokens = tx.tokens || [];
330
+ if (tx.type === 'transfer_v2' && tx.tx.itxJson.tokens) {
331
+ tx.tokens.push(...tx.tx.itxJson.tokens.map((x) => x.address));
332
+ } else if (['transfer_v3', 'stake'].includes(tx.type)) {
333
+ tx.tokens.push(...createSortedList(tx.tx.itxJson.inputs.map((x) => x.tokens.map((t) => t.address))));
334
+ } else if (['revoke_stake'].includes(tx.type)) {
335
+ tx.tokens.push(...createSortedList(tx.tx.itxJson.outputs.map((x) => x.tokens.map((t) => t.address))));
336
+ } else if (tx.type === 'exchange_v2') {
337
+ tx.tokens.push(
338
+ ...(tx.tx.itxJson.sender.tokens || []).map((x) => x.address),
339
+ ...(tx.tx.itxJson.receiver.tokens || []).map((x) => x.address)
340
+ );
341
+ }
342
+
343
+ tx.accounts = tx.accounts || [];
344
+ tx.stakes = tx.stakes || [];
345
+
346
+ tx.assets = uniq(tx.assets).filter(Boolean);
347
+ tx.factories = uniq(tx.factories).filter(Boolean);
348
+ tx.tokens = uniq(tx.tokens).filter(Boolean);
349
+ tx.accounts = uniq(tx.accounts).filter(Boolean);
350
+ tx.stakes = uniq(tx.stakes).filter(Boolean);
351
+ tx.rollups = uniq(tx.rollups).filter(Boolean);
352
+
353
+ return tx;
354
+ };
355
+
356
+ const formatTxAfterRead = (tx) => {
357
+ delete tx.assets;
358
+ delete tx.factories;
359
+ delete tx.tokens;
360
+ delete tx.accounts;
361
+ delete tx.stakes;
362
+ delete tx.rollups;
363
+
364
+ return tx;
365
+ };
366
+
367
+ const parseDateTime = (str) => {
368
+ if (!str) {
369
+ return '';
370
+ }
371
+
372
+ const parsed = Date.parse(str);
373
+ if (isNaN(parsed)) {
374
+ return '';
375
+ }
376
+
377
+ return new Date(parsed).toISOString();
378
+ };
379
+
380
+ const PAGE_SIZE_DEFAULT = 20;
381
+ const PAGE_SIZE_MAX = 100;
382
+
383
+ const formatPagination = ({ paging, defaultSortField, supportedSortFields = [] }) => {
384
+ if (!defaultSortField) {
385
+ throw new Error('argument defaultSortField is required');
386
+ }
387
+
388
+ const pagination = paging || { size: PAGE_SIZE_DEFAULT };
389
+ if (Array.isArray(pagination.order)) {
390
+ [pagination.order] = pagination.order;
391
+ }
392
+
393
+ if (!pagination.order) {
394
+ pagination.order = { field: defaultSortField, type: 'desc' };
395
+ }
396
+
397
+ if (!pagination.order.field) {
398
+ pagination.order.field = defaultSortField;
399
+ }
400
+
401
+ if (supportedSortFields.length > 0 && !supportedSortFields.includes(pagination.order.field)) {
402
+ pagination.order.field = defaultSortField;
403
+ }
404
+
405
+ if (!pagination.order.type) {
406
+ pagination.order.type = 'desc';
407
+ }
408
+
409
+ if (pagination.size === null || typeof pagination.size === 'undefined') {
410
+ pagination.size = PAGE_SIZE_DEFAULT;
411
+ }
412
+
413
+ // Since Elasticsearch does not support cursor based pagination, we just use offset as cursor
414
+ if (pagination.cursor) {
415
+ pagination.cursor = Number(pagination.cursor);
416
+ } else {
417
+ pagination.cursor = 0;
418
+ }
419
+
420
+ pagination.size = Math.min(pagination.size, PAGE_SIZE_MAX);
421
+
422
+ return pagination;
423
+ };
424
+
425
+ const formatNextPagination = (total, pagination) => {
426
+ const nextCursor = pagination.cursor + pagination.size;
427
+ if (total > nextCursor) {
428
+ return { cursor: nextCursor, next: true, total };
429
+ }
430
+
431
+ return { cursor: '0', next: false, total };
432
+ };
433
+
434
+ const isDefaultTokenChanged = (tx, token) => {
435
+ const itx = tx.tx.itxJson;
436
+ if (
437
+ [
438
+ 'fg:t:transfer',
439
+ 'fg:t:transfer_v2',
440
+ 'fg:t:transfer_v3',
441
+ 'fg:t:acquire_asset_v2',
442
+ 'fg:t:acquire_asset_v3',
443
+ 'fg:t:exchange',
444
+ 'fg:t:exchange_v2',
445
+ 'fg:t:deposit_token',
446
+ 'fg:t:withdraw_token',
447
+ 'fg:t:faucet',
448
+ 'fg:t:poke',
449
+ 'fg:t:setup_swap',
450
+ 'fg:t:retrieve_swap',
451
+ 'fg:t:stake',
452
+ 'fg:t:revoke_stake',
453
+ ].includes(itx.type_url) === false
454
+ ) {
455
+ return false;
456
+ }
457
+
458
+ const ZERO = new BN(0);
459
+ const isDefaultToken = (id) => [token.address, ''].includes(id);
460
+ const isPositive = (v) => new BN(v || 0).gt(ZERO);
461
+ const isChangedByInput = (input) =>
462
+ isPositive(input.value) || input.tokens.some((x) => isDefaultToken(x.address) && isPositive(x.value));
463
+
464
+ switch (itx.type_url) {
465
+ case 'fg:t:transfer':
466
+ return isPositive(itx.value);
467
+ case 'fg:t:transfer_v2':
468
+ return isChangedByInput(itx);
469
+ case 'fg:t:transfer_v3':
470
+ case 'fg:t:acquire_asset_v3':
471
+ return itx.inputs.some(isChangedByInput);
472
+ case 'fg:t:acquire_asset_v2':
473
+ return tx.receipts.some((r) => r.changes.some((c) => isDefaultToken(c.target) && isPositive(c.value)));
474
+ case 'fg:t:exchange':
475
+ return isPositive(itx.sender.value) || isPositive(itx.receiver.value);
476
+ case 'fg:t:exchange_v2':
477
+ return isChangedByInput(itx.sender) || isChangedByInput(itx.receiver);
478
+ case 'fg:t:deposit_token':
479
+ case 'fg:t:withdraw_token':
480
+ return isPositive(itx.value);
481
+ case 'fg:t:setup_swap':
482
+ case 'fg:t:retrieve_swap':
483
+ return isPositive(itx.value);
484
+ case 'fg:t:poke':
485
+ return true;
486
+ case 'fg:t:faucet':
487
+ return !itx.token;
488
+ case 'fg:t:stake':
489
+ return itx.inputs.some(isChangedByInput);
490
+ case 'fg:t:revoke_stake':
491
+ return itx.outputs.some(isChangedByInput);
492
+ default:
493
+ return false;
494
+ }
495
+ };
496
+
497
+ module.exports = {
498
+ createIndexedAccount,
499
+ createIndexedAsset,
500
+ createIndexedFactory,
501
+ createIndexedDelegation,
502
+ createIndexedToken,
503
+ createIndexedTransaction,
504
+ createIndexedStake,
505
+ createIndexedRollup,
506
+ createIndexedRollupBlock,
507
+ formatPagination,
508
+ formatNextPagination,
509
+ formatTxAfterRead,
510
+ formatTxBeforeInsert,
511
+ parseDateTime,
512
+ formatTokenMeta,
513
+ isDefaultTokenChanged,
514
+ };
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.6.5",
6
+ "version": "1.6.10",
7
7
  "description": "Defines the basic interface for OCAP IndexDB",
8
8
  "main": "lib/main.js",
9
9
  "files": [
@@ -12,18 +12,23 @@
12
12
  "scripts": {
13
13
  "lint": "eslint tests lib",
14
14
  "lint:fix": "eslint --fix tests lib",
15
- "test": "node tools/jest.js",
15
+ "test": "jest --forceExit --detectOpenHandles",
16
16
  "coverage": "npm run test -- --coverage"
17
17
  },
18
18
  "keywords": [],
19
19
  "author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
20
+ "contributors": [
21
+ "wangshijun <shijun@arcblock.io> (https://github.com/wangshijun)"
22
+ ],
20
23
  "license": "MIT",
21
24
  "devDependencies": {
22
- "jest": "^26.6.3"
25
+ "jest": "^27.3.1"
23
26
  },
24
27
  "dependencies": {
25
- "@ocap/util": "^1.6.5",
26
- "kareem": "^2.3.2"
28
+ "@ocap/state": "1.6.10",
29
+ "@ocap/util": "1.6.10",
30
+ "kareem": "^2.3.2",
31
+ "lodash": "^4.17.21"
27
32
  },
28
- "gitHead": "5779448f13824de38978df3c84c9da0c1e1ad989"
33
+ "gitHead": "ab272e8db3a15c6571cc7fae7cc3d3e0fdd4bdb1"
29
34
  }