@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
@@ -0,0 +1,201 @@
1
+ import { IAddressArgs, IAssetState, IChainConfig, IChunkIterator, IDataObject, IDelegateState, IEvidenceState, IFilter, IGetAccountStateArgs, IGetAccountTokensArgs, IGetStateParams, IIndexDB, IListChunksParams, IPaginatedResult, IPipelineLogger, IResolver, IResolverAccountState, IResolverChainInfo, IResolverContext, IResolverFactoryState, IResolverForgeStats, IResolverListAccountsResult, IResolverListAssetsResult, IResolverListDelegationsResult, IResolverListFactoriesResult, IResolverListRollupBlocksResult, IResolverListRollupValidatorsResult, IResolverListRollupsResult, IResolverListStakesResult, IResolverListTokenFactoriesResult, IResolverListTokensResult, IResolverListTransactionsResult, IResolverNetInfo, IResolverNodeInfo, IResolverPaging, IResolverParams, IResolverRollupState, IResolverStakeState, IResolverTransaction, IResolverValidatorsInfo, IRollupBlock, IStateDB, ITokenCache, ITokenFactoryState, ITokenFlowsResult, ITokenInfo, ITokenState, TIndexedTokenState, TRequestEstimateGas, TRequestGetEvidenceState, TRequestGetRollupBlock, TRequestGetTokenDistribution, TRequestGetTx, TRequestListAssetTransactions, TRequestListAssets, TRequestListDelegations, TRequestListFactories, TRequestListRollupBlocks, TRequestListRollupValidators, TRequestListRollups, TRequestListStakes, TRequestListTokenFactories, TRequestListTokenFlows, TRequestListTokens, TRequestListTopAccounts, TRequestListTransactions, TRequestSearch, TRequestVerifyAccountRisk, TTokenDistribution, TVerifyAccountRiskResult } from "./types.cjs";
2
+ import { MigrationChainManager } from "./migration-chain.cjs";
3
+ import { TokenDistributionManager } from "./token-distribution.cjs";
4
+ import { createExecutor } from "@ocap/tx-protocols";
5
+ import Queue from "queue";
6
+
7
+ //#region src/index.d.ts
8
+ declare const formatData: (data: IDataObject | null | undefined) => IDataObject | null | undefined;
9
+ declare const formatDelegationOps: (state: IDelegateState) => any;
10
+ declare class OCAPResolver implements IResolver {
11
+ statedb: IStateDB;
12
+ indexdb: IIndexDB;
13
+ filter?: IFilter;
14
+ config: IChainConfig;
15
+ chainAddr: string;
16
+ tokenItx: TIndexedTokenState | null;
17
+ consensus: string;
18
+ queue: Queue;
19
+ tokenCache: ITokenCache;
20
+ tokenDistribution: TokenDistributionManager;
21
+ executor: ReturnType<typeof createExecutor>;
22
+ logger: IPipelineLogger;
23
+ migrationChain?: MigrationChainManager;
24
+ formatTx: (tx: IResolverTransaction | null, ctx?: IResolverContext) => Promise<IResolverTransaction | null>;
25
+ /**
26
+ * Creates an instance of OCAPResolver.
27
+ * @param params Parameters to bootstrap the resolver
28
+ * @param params.statedb @ocap/statedb adapter
29
+ * @param params.indexdb @ocap/indexdb adapter
30
+ * @param params.config @ocap/config object
31
+ * @param params.filter bloom filter to do anti-replay check
32
+ * @param params.validateTokenConfig should we validate token supply and token-holder balance, should be disabled when starting an existing chain
33
+ */
34
+ constructor({
35
+ statedb,
36
+ indexdb,
37
+ config,
38
+ filter,
39
+ validateTokenConfig,
40
+ logger
41
+ }: IResolverParams);
42
+ sendTx({
43
+ tx: txBase64,
44
+ extra
45
+ }: {
46
+ tx: string;
47
+ extra?: Record<string, unknown>;
48
+ }, ctx?: IResolverContext): Promise<string>;
49
+ getTx({
50
+ hash
51
+ }: TRequestGetTx, ctx?: IResolverContext): Promise<IResolverTransaction | null>;
52
+ getBlock(): null;
53
+ getBlocks(): never[];
54
+ getUnconfirmedTxs(): never[];
55
+ getChainInfo(): Promise<IResolverChainInfo>;
56
+ getNodeInfo(): Promise<IResolverNodeInfo>;
57
+ getNetInfo(): Promise<IResolverNetInfo>;
58
+ getValidatorsInfo(): Promise<IResolverValidatorsInfo>;
59
+ getConfig(): string;
60
+ getAccountState({
61
+ address,
62
+ traceMigration,
63
+ expandContext
64
+ }: IGetAccountStateArgs & {
65
+ traceMigration?: boolean;
66
+ }, ctx?: IResolverContext): Promise<IResolverAccountState | null>;
67
+ getStakeState({
68
+ address
69
+ }: IAddressArgs, ctx?: IResolverContext): Promise<IResolverStakeState | null>;
70
+ getRollupState({
71
+ address
72
+ }: IAddressArgs, ctx?: IResolverContext): Promise<IResolverRollupState | null>;
73
+ getRollupBlock({
74
+ hash,
75
+ height,
76
+ rollupAddress
77
+ }: Partial<TRequestGetRollupBlock>, ctx?: IResolverContext): Promise<IRollupBlock | null>;
78
+ getAssetState({
79
+ address
80
+ }: IAddressArgs, ctx?: IResolverContext): Promise<IAssetState | null>;
81
+ getEvidenceState({
82
+ hash
83
+ }: TRequestGetEvidenceState, ctx?: IResolverContext): Promise<IEvidenceState | null>;
84
+ getFactoryState({
85
+ address
86
+ }: IAddressArgs, ctx?: IResolverContext): Promise<IResolverFactoryState | null>;
87
+ getTokenState({
88
+ address
89
+ }: IAddressArgs, ctx?: IResolverContext): Promise<ITokenState | null>;
90
+ getTokenFactoryState(args: IAddressArgs, ctx?: IResolverContext): Promise<ITokenFactoryState | null>;
91
+ getDelegateState({
92
+ address
93
+ }: IAddressArgs, ctx?: IResolverContext): Promise<IDelegateState | null>;
94
+ getAccountTokens({
95
+ address,
96
+ token
97
+ }: IGetAccountTokensArgs, ctx?: IResolverContext): Promise<ITokenInfo[]>;
98
+ getForgeState(): Promise<Record<string, unknown>>;
99
+ getForgeStats(): Promise<IResolverForgeStats>;
100
+ listTransactions(args?: Partial<TRequestListTransactions>, ctx?: IResolverContext): Promise<IResolverListTransactionsResult>;
101
+ listAssets(args?: Partial<TRequestListAssets>, ctx?: IResolverContext): Promise<IResolverListAssetsResult>;
102
+ listAssetTransactions(args?: Partial<TRequestListAssetTransactions>, ctx?: IResolverContext): Promise<IResolverListTransactionsResult>;
103
+ listFactories(args?: Partial<TRequestListFactories>, ctx?: IResolverContext): Promise<IResolverListFactoriesResult>;
104
+ listTopAccounts(args?: Partial<TRequestListTopAccounts>): Promise<IResolverListAccountsResult>;
105
+ listTokens(args?: Partial<TRequestListTokens>, ctx?: IResolverContext): Promise<IResolverListTokensResult>;
106
+ listTokenFactories(args?: Partial<TRequestListTokenFactories>, ctx?: IResolverContext): Promise<IResolverListTokenFactoriesResult>;
107
+ listStakes(args?: Partial<TRequestListStakes>, ctx?: IResolverContext): Promise<IResolverListStakesResult>;
108
+ listRollups(args?: Partial<TRequestListRollups>, ctx?: IResolverContext): Promise<IResolverListRollupsResult>;
109
+ listRollupBlocks(args?: Partial<TRequestListRollupBlocks>, ctx?: IResolverContext): Promise<IResolverListRollupBlocksResult>;
110
+ listRollupValidators(args?: Partial<TRequestListRollupValidators>, ctx?: IResolverContext): Promise<IResolverListRollupValidatorsResult>;
111
+ listDelegations(args?: Partial<TRequestListDelegations>, ctx?: IResolverContext): Promise<IResolverListDelegationsResult>;
112
+ verifyAccountRisk(args?: Partial<TRequestVerifyAccountRisk>, ctx?: IResolverContext): Promise<TVerifyAccountRiskResult | null> | null;
113
+ listTokenFlows(args?: Partial<TRequestListTokenFlows>, ctx?: IResolverContext): Promise<ITokenFlowsResult> | never[];
114
+ search(args: Partial<TRequestSearch>): Promise<{
115
+ results: Array<{
116
+ type: string;
117
+ id: string;
118
+ }>;
119
+ }>;
120
+ estimateGas(args?: Partial<TRequestEstimateGas>): {
121
+ estimate: {
122
+ max: string;
123
+ };
124
+ };
125
+ listBlocks(): {
126
+ blocks: never[];
127
+ };
128
+ validateTokenConfig(): void;
129
+ runAsLambda<T>(fn: (txn?: unknown) => T | Promise<T>, args?: Record<string, unknown>): Promise<T>;
130
+ getChain(): Promise<Record<string, unknown> | null>;
131
+ getToken(id: string): Promise<ITokenState | null>;
132
+ initializeStateDB(): Promise<void>;
133
+ connectIndexDB(): void;
134
+ _getState<T = Record<string, unknown>>({
135
+ table,
136
+ id,
137
+ dataKey,
138
+ extraParams,
139
+ onRead,
140
+ expandContext,
141
+ ctx
142
+ }: IGetStateParams): Promise<T | null>;
143
+ _doPaginatedSearch<TArgs extends Record<string, unknown>>(fn: string, args: TArgs, listKey: string, dataKey?: string | string[] | null, ctx?: IResolverContext): Promise<Record<string, unknown>>;
144
+ _formatTx(tx: IResolverTransaction | null, ctx?: IResolverContext): Promise<IResolverTransaction | null>;
145
+ _getAllResults<T>(dataKey: string, fn: (paging: IResolverPaging) => Promise<any>, limit?: number): Promise<T[]>;
146
+ formatTokenArray(tokens: ITokenInfo[]): Promise<ITokenInfo[]>;
147
+ formatTokenMap(tokens: Record<string, string>): Promise<ITokenInfo[]>;
148
+ getTxTokenSymbols(tx: IResolverTransaction): Promise<ITokenInfo[]>;
149
+ fixReceiptTokens(tx: IResolverTransaction): IResolverTransaction;
150
+ fixTokenSymbols(tx: IResolverTransaction): IResolverTransaction;
151
+ enrichIndexContext(ctx: Record<string, unknown>): Record<string, unknown>;
152
+ formatDelegateState(state: IDelegateState): Promise<IDelegateState>;
153
+ buildMigrationChain(): Promise<void>;
154
+ getMigrationChain(): Promise<MigrationChainManager>;
155
+ /** fix accounts field of legacy approve_withdraw transactions in indexdb */
156
+ fixApproveWithdrawAccounts(): Promise<IResolverTransaction[]>;
157
+ updateTokenDistribution({
158
+ tokenAddress,
159
+ force
160
+ }?: {
161
+ tokenAddress?: string | undefined;
162
+ force?: boolean | undefined;
163
+ }): Promise<unknown>;
164
+ getTokenDistribution(args?: Partial<TRequestGetTokenDistribution>): Promise<TTokenDistribution | null>;
165
+ /**
166
+ * Fetch data in chunks to handle large datasets
167
+ * @param fn Function to fetch data
168
+ * @param params Chunk parameters
169
+ * @returns Iterator with next function
170
+ */
171
+ listChunks<T extends Record<string, unknown>>(fn: (params: {
172
+ size: number;
173
+ cursor: number;
174
+ time?: string;
175
+ }) => Promise<{
176
+ [key: string]: T[];
177
+ }>, {
178
+ total,
179
+ concurrency,
180
+ chunkSize,
181
+ pageSize,
182
+ timeKey,
183
+ dataKey
184
+ }: IListChunksParams): IChunkIterator<T>;
185
+ listTransactionsChunks(args?: Partial<TRequestListTransactions>, {
186
+ chunkSize,
187
+ pageSize
188
+ }?: {
189
+ chunkSize?: number | undefined;
190
+ pageSize?: number | undefined;
191
+ }): Promise<IChunkIterator<IResolverTransaction>>;
192
+ listStakeChunks(args?: Partial<TRequestListStakes>, {
193
+ chunkSize,
194
+ pageSize
195
+ }?: {
196
+ chunkSize?: number | undefined;
197
+ pageSize?: number | undefined;
198
+ }): Promise<IChunkIterator<IResolverStakeState>>;
199
+ }
200
+ //#endregion
201
+ export { type IChainConfig, type IChunkIterator, type IDataObject, type IPaginatedResult, type IResolverPaging, type IResolverParams, type IResolverTransaction, type ITokenInfo, OCAPResolver as default, formatData, formatDelegationOps };
@@ -0,0 +1,99 @@
1
+ const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
2
+ let _arcblock_did = require("@arcblock/did");
3
+
4
+ //#region src/migration-chain.ts
5
+ var MigrationNode = class {
6
+ constructor(address, validFrom, validUntil = null, nextAddress = null) {
7
+ this.address = address;
8
+ this.validFrom = validFrom;
9
+ this.validUntil = validUntil;
10
+ this.nextAddress = nextAddress;
11
+ }
12
+ };
13
+ var MigrationChainManager = class {
14
+ constructor() {
15
+ this.chains = /* @__PURE__ */ new Map();
16
+ this.rootAddresses = /* @__PURE__ */ new Map();
17
+ }
18
+ validateMigration(migration) {
19
+ if (!migration?.tx?.from || !migration?.tx?.itxJson?.address || !migration?.time) throw new Error("Invalid migration format");
20
+ if (new Date(migration.time).toString() === "Invalid Date") throw new Error("Invalid timestamp");
21
+ return true;
22
+ }
23
+ /**
24
+ * Build migration chains from a list of migration transactions
25
+ * @param migrations - List of migration transactions
26
+ */
27
+ buildChains(migrations) {
28
+ if (!Array.isArray(migrations)) throw new Error("Migrations must be an array");
29
+ migrations.forEach((migration) => this.validateMigration(migration));
30
+ const sortedMigrations = [...migrations].sort((a, b) => new Date(a.time).getTime() - new Date(b.time).getTime());
31
+ for (const migration of sortedMigrations) {
32
+ const fromAddr = migration.tx.from;
33
+ const toAddr = migration.tx.itxJson.address;
34
+ const timestamp = new Date(migration.time);
35
+ this._processMigration(fromAddr, toAddr, timestamp);
36
+ }
37
+ }
38
+ /**
39
+ * Process a single migration and update the chains
40
+ * @param fromAddr - Source address
41
+ * @param toAddr - Destination address
42
+ * @param timestamp - Migration timestamp
43
+ */
44
+ _processMigration(fromAddr, toAddr, timestamp) {
45
+ const rootAddr = this.getRootAddress(fromAddr);
46
+ if (!this.chains.has(rootAddr)) this.chains.set(rootAddr, []);
47
+ const chain = this.chains.get(rootAddr);
48
+ if (chain.length === 0) {
49
+ chain.push(new MigrationNode(fromAddr, /* @__PURE__ */ new Date(0), timestamp));
50
+ chain.push(new MigrationNode(toAddr, timestamp));
51
+ } else {
52
+ const lastNode = chain[chain.length - 1];
53
+ lastNode.validUntil = timestamp;
54
+ lastNode.nextAddress = toAddr;
55
+ chain.push(new MigrationNode(toAddr, timestamp));
56
+ }
57
+ this.rootAddresses.set(this.formatAddress(toAddr), rootAddr);
58
+ }
59
+ /**
60
+ * Find the valid address at a specific timestamp
61
+ * @param address - Address to look up
62
+ * @param timestamp - Timestamp to check
63
+ * @returns Valid address at the timestamp
64
+ */
65
+ findAddressAtTime(address, timestamp) {
66
+ const chains = this.getMigrationHistory(address);
67
+ if (!chains?.length) return address;
68
+ let left = 0;
69
+ let right = chains.length - 1;
70
+ while (left <= right) {
71
+ const mid = Math.floor((left + right) / 2);
72
+ const node = chains[mid];
73
+ if (node.validFrom <= timestamp && (!node.validUntil || timestamp < node.validUntil)) return node.address;
74
+ if (timestamp < node.validFrom) right = mid - 1;
75
+ else left = mid + 1;
76
+ }
77
+ return chains[chains.length - 1].address;
78
+ }
79
+ formatAddress(address) {
80
+ return (0, _arcblock_did.isEthereumDid)(address) ? address.toLowerCase() : address;
81
+ }
82
+ getRootAddress(address) {
83
+ const formattedAddress = this.formatAddress(address);
84
+ return this.rootAddresses.get(formattedAddress) || formattedAddress;
85
+ }
86
+ /**
87
+ * Get the complete migration history for an address
88
+ * @param address - Address to look up
89
+ * @returns List of migration nodes
90
+ */
91
+ getMigrationHistory(address) {
92
+ const rootAddr = this.getRootAddress(address);
93
+ return this.chains.get(rootAddr) || [];
94
+ }
95
+ };
96
+
97
+ //#endregion
98
+ exports.MigrationChainManager = MigrationChainManager;
99
+ exports.MigrationNode = MigrationNode;
@@ -0,0 +1,52 @@
1
+ //#region src/migration-chain.d.ts
2
+ interface Migration {
3
+ time: string;
4
+ tx: {
5
+ from: string;
6
+ itxJson: {
7
+ address: string;
8
+ };
9
+ };
10
+ }
11
+ declare class MigrationNode {
12
+ address: string;
13
+ validFrom: Date;
14
+ validUntil: Date | null;
15
+ nextAddress: string | null;
16
+ constructor(address: string, validFrom: Date, validUntil?: Date | null, nextAddress?: string | null);
17
+ }
18
+ declare class MigrationChainManager {
19
+ chains: Map<string, MigrationNode[]>;
20
+ rootAddresses: Map<string, string>;
21
+ constructor();
22
+ validateMigration(migration: Migration): boolean;
23
+ /**
24
+ * Build migration chains from a list of migration transactions
25
+ * @param migrations - List of migration transactions
26
+ */
27
+ buildChains(migrations: Migration[]): void;
28
+ /**
29
+ * Process a single migration and update the chains
30
+ * @param fromAddr - Source address
31
+ * @param toAddr - Destination address
32
+ * @param timestamp - Migration timestamp
33
+ */
34
+ private _processMigration;
35
+ /**
36
+ * Find the valid address at a specific timestamp
37
+ * @param address - Address to look up
38
+ * @param timestamp - Timestamp to check
39
+ * @returns Valid address at the timestamp
40
+ */
41
+ findAddressAtTime(address: string, timestamp: Date): string;
42
+ formatAddress(address: string): string;
43
+ getRootAddress(address: string): string;
44
+ /**
45
+ * Get the complete migration history for an address
46
+ * @param address - Address to look up
47
+ * @returns List of migration nodes
48
+ */
49
+ getMigrationHistory(address: string): MigrationNode[];
50
+ }
51
+ //#endregion
52
+ export { MigrationChainManager, MigrationNode };
@@ -0,0 +1,11 @@
1
+
2
+ //#region package.json
3
+ var name = "@ocap/resolver";
4
+
5
+ //#endregion
6
+ Object.defineProperty(exports, 'name', {
7
+ enumerable: true,
8
+ get: function () {
9
+ return name;
10
+ }
11
+ });
@@ -0,0 +1,27 @@
1
+
2
+ //#region src/token-cache.ts
3
+ var Cache = class {
4
+ constructor(dbAdapter) {
5
+ this.dbAdapter = dbAdapter;
6
+ this.cache = /* @__PURE__ */ new Map();
7
+ }
8
+ async get(address) {
9
+ if (this.cache.has(address)) return this.cache.get(address);
10
+ const token = await this.dbAdapter.get(address);
11
+ if (token) this.cache.set(address, token);
12
+ return token;
13
+ }
14
+ set(address, token) {
15
+ this.cache.set(address, token);
16
+ return token;
17
+ }
18
+ };
19
+ let cache = null;
20
+ function getInstance(dbAdapter) {
21
+ if (cache !== null) return cache;
22
+ cache = new Cache(dbAdapter);
23
+ return cache;
24
+ }
25
+
26
+ //#endregion
27
+ exports.getInstance = getInstance;
@@ -0,0 +1,20 @@
1
+ import { TIndexedTokenState } from "./types.cjs";
2
+
3
+ //#region src/token-cache.d.ts
4
+
5
+ /**
6
+ * Token DB adapter interface for cache
7
+ */
8
+ interface ITokenDBAdapter {
9
+ get(address: string): Promise<TIndexedTokenState | null>;
10
+ }
11
+ declare class Cache {
12
+ private dbAdapter;
13
+ private cache;
14
+ constructor(dbAdapter: ITokenDBAdapter);
15
+ get(address: string): Promise<TIndexedTokenState | null | undefined>;
16
+ set(address: string, token: TIndexedTokenState): TIndexedTokenState;
17
+ }
18
+ declare function getInstance(dbAdapter: ITokenDBAdapter): Cache;
19
+ //#endregion
20
+ export { getInstance };
@@ -0,0 +1,243 @@
1
+ const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
2
+ let _ocap_util = require("@ocap/util");
3
+ let _arcblock_did = require("@arcblock/did");
4
+ let _ocap_mcrypto = require("@ocap/mcrypto");
5
+ let _ocap_tx_protocols_lib_util = require("@ocap/tx-protocols/lib/util");
6
+ let lodash_omit = require("lodash/omit");
7
+ lodash_omit = require_rolldown_runtime.__toESM(lodash_omit);
8
+ let _ocap_indexdb_lib_util = require("@ocap/indexdb/lib/util");
9
+ let _ocap_state_lib_states_tx = require("@ocap/state/lib/states/tx");
10
+
11
+ //#region src/token-distribution.ts
12
+ const { RoleType } = _ocap_mcrypto.types;
13
+ const ZERO = new _ocap_util.BN(0);
14
+ var TokenDistributionManager = class {
15
+ constructor(resolver) {
16
+ this.resolver = resolver;
17
+ this.indexdb = resolver.indexdb;
18
+ this.isProcessing = false;
19
+ }
20
+ formatDistribution(distribution) {
21
+ const { tokenAddress, account, gas, fee, slashedVault, stake, revokedStake, gasStake, other, txTime } = distribution;
22
+ return {
23
+ tokenAddress,
24
+ account: new _ocap_util.BN(account || 0),
25
+ gas: new _ocap_util.BN(gas || 0),
26
+ fee: new _ocap_util.BN(fee || 0),
27
+ slashedVault: new _ocap_util.BN(slashedVault || 0),
28
+ stake: new _ocap_util.BN(stake || 0),
29
+ revokedStake: new _ocap_util.BN(revokedStake || 0),
30
+ gasStake: new _ocap_util.BN(gasStake || 0),
31
+ other: new _ocap_util.BN(other || 0),
32
+ txTime: txTime || (/* @__PURE__ */ new Date(0)).toISOString()
33
+ };
34
+ }
35
+ async getDistribution(tokenAddress) {
36
+ const data = await this.indexdb.tokenDistribution.get(tokenAddress);
37
+ return data && (0, _ocap_indexdb_lib_util.createIndexedTokenDistribution)(data);
38
+ }
39
+ async saveDistribution(distribution, isEnsureLatest = true) {
40
+ const data = (0, _ocap_indexdb_lib_util.createIndexedTokenDistribution)(distribution);
41
+ const indexdbDistribution = await this.getDistribution(data.tokenAddress);
42
+ if (!indexdbDistribution) await this.indexdb.tokenDistribution.insert(data);
43
+ else {
44
+ if (isEnsureLatest) {
45
+ const latestTime = Math.max(new Date(indexdbDistribution.txTime).getTime(), new Date(data.txTime).getTime());
46
+ data.txTime = new Date(latestTime).toISOString();
47
+ }
48
+ await this.indexdb.tokenDistribution.update(data.tokenAddress, data);
49
+ }
50
+ return data;
51
+ }
52
+ /**
53
+ * Calculate token distribution based on all transaction data.
54
+ * This method is usually used to update historical token distribution data
55
+ *
56
+ * @param tokenAddress Token address
57
+ * @param force If force is false, only calculate distributions for new transactions after txTime in indexdb. default is false
58
+ * @returns The updated token distribution or null if failed
59
+ */
60
+ async updateByToken(tokenAddress, force) {
61
+ const { logger, config } = this.resolver;
62
+ if (this.isProcessing) {
63
+ logger?.debug?.("Token distribution is already in progress", {
64
+ tokenAddress,
65
+ force
66
+ });
67
+ return;
68
+ }
69
+ const distribution = force ? this.formatDistribution({ tokenAddress }) : this.formatDistribution(await this.getDistribution(tokenAddress) || { tokenAddress });
70
+ const isDefaultToken = tokenAddress === config.token.address;
71
+ logger?.info(`Update distribution by token (${tokenAddress})`, { distribution: (0, _ocap_indexdb_lib_util.createIndexedTokenDistribution)(distribution) });
72
+ this.isProcessing = true;
73
+ try {
74
+ if (force && isDefaultToken) this.handleModerator(distribution);
75
+ const { next } = await this.resolver.listTransactionsChunks({
76
+ timeFilter: { startDateTime: distribution.txTime },
77
+ tokenFilter: isDefaultToken ? {} : { tokenFilter: { tokens: [tokenAddress] } }
78
+ });
79
+ let nextData = await next();
80
+ while (nextData.length) {
81
+ logger?.info("Updating token distribution in chunks", {
82
+ chunkSize: nextData.length,
83
+ startTime: nextData[0].time,
84
+ startHash: nextData[0].hash,
85
+ endTime: nextData[nextData.length - 1].time
86
+ });
87
+ const handlePromises = nextData.map((tx) => this.handleTx(tx, distribution));
88
+ await Promise.all(handlePromises);
89
+ await this.saveDistribution(distribution, false);
90
+ nextData = await next();
91
+ }
92
+ await this.splitStake(distribution);
93
+ await this.saveDistribution(distribution, false);
94
+ const result = (0, _ocap_indexdb_lib_util.createIndexedTokenDistribution)(distribution);
95
+ logger?.info(`Token distribution update completed (${tokenAddress})`, { distribution: result });
96
+ return result;
97
+ } catch (e) {
98
+ logger?.error("Token distribution update failed", { error: e });
99
+ return null;
100
+ } finally {
101
+ this.isProcessing = false;
102
+ }
103
+ }
104
+ /**
105
+ * Split out revokedStake / gasStake from stake
106
+ *
107
+ * @param distribution The distribution object to update
108
+ * @returns The updated distribution
109
+ */
110
+ async splitStake(distribution) {
111
+ const { logger } = this.resolver;
112
+ const { tokenAddress } = distribution;
113
+ const { next } = await this.resolver.listStakeChunks();
114
+ let nextData = await next();
115
+ while (nextData.length) {
116
+ logger?.info("Updating stake distribution in chunks", {
117
+ chunkSize: nextData.length,
118
+ startTime: nextData[0].renaissanceTime,
119
+ startAddress: nextData[0].address,
120
+ endTime: nextData[nextData.length - 1].renaissanceTime
121
+ });
122
+ nextData.forEach((stakeState) => {
123
+ const isGasStake = this.isGasStake(stakeState);
124
+ const token = stakeState.tokens.find((x) => x.address === tokenAddress);
125
+ const revokedBalance = new _ocap_util.BN(stakeState.revokedTokens.find((x) => x.address === tokenAddress)?.balance || 0);
126
+ const balance = new _ocap_util.BN(token?.balance || 0);
127
+ distribution.revokedStake = distribution.revokedStake.add(revokedBalance);
128
+ distribution.stake = distribution.stake.sub(revokedBalance);
129
+ if (isGasStake) {
130
+ distribution.gasStake = distribution.gasStake.add(balance);
131
+ distribution.stake = distribution.stake.sub(balance);
132
+ }
133
+ });
134
+ nextData = await next();
135
+ }
136
+ return distribution;
137
+ }
138
+ /**
139
+ * Update token distribution by a single transaction.
140
+ * This method is usually used when a tx is completed.
141
+ *
142
+ * @param tx The transaction object
143
+ * @param context The transaction context
144
+ * @returns The updated token distributions
145
+ */
146
+ async updateByTx(tx, context) {
147
+ const { logger } = this.resolver;
148
+ const ctx = (0, lodash_omit.default)(context, "txn");
149
+ if (this.isProcessing) {
150
+ logger?.debug?.("Token distribution is already in progress", { tx });
151
+ return;
152
+ }
153
+ const tokens = (await this.resolver.formatTx(tx)).tokenSymbols.map(({ address }) => address);
154
+ return await Promise.all(tokens.map(async (tokenAddress) => {
155
+ const distribution = this.formatDistribution(await this.getDistribution(tokenAddress) || { tokenAddress });
156
+ await this.handleTx(tx, distribution, {
157
+ context: ctx,
158
+ isHandleStake: true
159
+ });
160
+ return await this.saveDistribution(distribution);
161
+ }));
162
+ }
163
+ /**
164
+ * Parse token distribution for a single transaction
165
+ * @param tx The transaction
166
+ * @param distribution The distribution to update
167
+ * @param options Handle options
168
+ * @returns The updated token distribution
169
+ */
170
+ async handleTx(tx, distribution, { context, isHandleStake = false } = {}) {
171
+ if (isHandleStake && !context) throw new Error("context is missing, handle revoke stake is not supported without context");
172
+ const type = tx.tx?.itxJson?._type;
173
+ const receipts = tx.receipts || [];
174
+ const { tokenAddress } = distribution;
175
+ const handlePromises = (0, _ocap_state_lib_states_tx.eachReceipts)(receipts, async (address, change) => {
176
+ if (!(0, _ocap_util.isSameDid)(change.target, tokenAddress)) return;
177
+ if (change.action === "migrate") return;
178
+ const roleType = (0, _arcblock_did.toTypeInfo)(address).role;
179
+ const value = new _ocap_util.BN(change.value);
180
+ if (roleType === RoleType.ROLE_STAKE) {
181
+ if (isHandleStake) {
182
+ const stakeState = await this.getStakeState(address, context);
183
+ const isGasStakeResult = this.isGasStake(stakeState);
184
+ if (type === "ClaimStakeTx") {
185
+ distribution.revokedStake = distribution.revokedStake.add(value);
186
+ return;
187
+ }
188
+ if (type === "SlashStakeTx") {
189
+ const beforeState = context.stateSnapshot[address];
190
+ const afterState = context.stakeState;
191
+ if (!beforeState || !afterState) throw new Error("stake state is missing on slash stake tx");
192
+ const revokeTokenDiff = new _ocap_util.BN(beforeState.revokedTokens[tokenAddress]).sub(new _ocap_util.BN(afterState.revokedTokens[tokenAddress]));
193
+ const tokenDiff = new _ocap_util.BN(beforeState.tokens[tokenAddress]).sub(new _ocap_util.BN(afterState.tokens[tokenAddress]));
194
+ distribution.revokedStake = distribution.revokedStake.sub(revokeTokenDiff);
195
+ if (isGasStakeResult) distribution.gasStake = distribution.gasStake.sub(tokenDiff);
196
+ else distribution.stake = distribution.stake.sub(tokenDiff);
197
+ return;
198
+ }
199
+ if (isGasStakeResult) {
200
+ distribution.gasStake = distribution.gasStake.add(value);
201
+ return;
202
+ }
203
+ }
204
+ distribution.stake = distribution.stake.add(value);
205
+ } else if (change.action === "gas" && value.gt(ZERO)) distribution.gas = distribution.gas.add(value);
206
+ else if (change.action === "fee" && value.gt(ZERO)) distribution.fee = distribution.fee.add(value);
207
+ else if (type === "SlashStakeTx" && value.gt(ZERO) && (0, _ocap_util.isSameDid)(address, this.resolver.config.vaults.slashedStake ?? "")) distribution.slashedVault = distribution.slashedVault.add(value);
208
+ else distribution.account = distribution.account.add(value);
209
+ });
210
+ await Promise.all(handlePromises);
211
+ if (isHandleStake && type === "RevokeStakeTx") {
212
+ const { address } = tx.tx.itxJson;
213
+ const stakeState = await this.getStakeState(address, context);
214
+ const isGasStakeResult = this.isGasStake(stakeState);
215
+ tx.tx.itxJson.outputs.forEach((x) => {
216
+ x.tokens.filter((change) => (0, _ocap_util.isSameDid)(change.address, tokenAddress)).forEach((change) => {
217
+ const value = new _ocap_util.BN(change.value || change.balance || 0);
218
+ distribution.revokedStake = distribution.revokedStake.add(value);
219
+ if (isGasStakeResult) distribution.gasStake = distribution.gasStake.sub(value);
220
+ else distribution.stake = distribution.stake.sub(value);
221
+ });
222
+ });
223
+ }
224
+ distribution.txTime = tx.time;
225
+ return distribution;
226
+ }
227
+ handleModerator(distribution) {
228
+ const { config } = this.resolver;
229
+ if (distribution.tokenAddress !== config.token.address) return distribution;
230
+ const value = config.accounts.filter((account) => Number(account.balance) > 0).map((account) => new _ocap_util.BN(account.balance)).reduce((cur, balance) => cur.add(balance), ZERO);
231
+ distribution.account = distribution.account.add((0, _ocap_util.fromTokenToUnit)(value.toString()));
232
+ return distribution;
233
+ }
234
+ async getStakeState(address, ctx) {
235
+ return await this.resolver.statedb.stake.get(address, ctx);
236
+ }
237
+ isGasStake(stakeState) {
238
+ return (0, _ocap_tx_protocols_lib_util.isGasStakeAddress)(stakeState.sender, stakeState.address) && stakeState.message === "stake-for-gas";
239
+ }
240
+ };
241
+
242
+ //#endregion
243
+ exports.TokenDistributionManager = TokenDistributionManager;