@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,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,97 @@
1
+ import { isEthereumDid } from "@arcblock/did";
2
+
3
+ //#region src/migration-chain.ts
4
+ var MigrationNode = class {
5
+ constructor(address, validFrom, validUntil = null, nextAddress = null) {
6
+ this.address = address;
7
+ this.validFrom = validFrom;
8
+ this.validUntil = validUntil;
9
+ this.nextAddress = nextAddress;
10
+ }
11
+ };
12
+ var MigrationChainManager = class {
13
+ constructor() {
14
+ this.chains = /* @__PURE__ */ new Map();
15
+ this.rootAddresses = /* @__PURE__ */ new Map();
16
+ }
17
+ validateMigration(migration) {
18
+ if (!migration?.tx?.from || !migration?.tx?.itxJson?.address || !migration?.time) throw new Error("Invalid migration format");
19
+ if (new Date(migration.time).toString() === "Invalid Date") throw new Error("Invalid timestamp");
20
+ return true;
21
+ }
22
+ /**
23
+ * Build migration chains from a list of migration transactions
24
+ * @param migrations - List of migration transactions
25
+ */
26
+ buildChains(migrations) {
27
+ if (!Array.isArray(migrations)) throw new Error("Migrations must be an array");
28
+ migrations.forEach((migration) => this.validateMigration(migration));
29
+ const sortedMigrations = [...migrations].sort((a, b) => new Date(a.time).getTime() - new Date(b.time).getTime());
30
+ for (const migration of sortedMigrations) {
31
+ const fromAddr = migration.tx.from;
32
+ const toAddr = migration.tx.itxJson.address;
33
+ const timestamp = new Date(migration.time);
34
+ this._processMigration(fromAddr, toAddr, timestamp);
35
+ }
36
+ }
37
+ /**
38
+ * Process a single migration and update the chains
39
+ * @param fromAddr - Source address
40
+ * @param toAddr - Destination address
41
+ * @param timestamp - Migration timestamp
42
+ */
43
+ _processMigration(fromAddr, toAddr, timestamp) {
44
+ const rootAddr = this.getRootAddress(fromAddr);
45
+ if (!this.chains.has(rootAddr)) this.chains.set(rootAddr, []);
46
+ const chain = this.chains.get(rootAddr);
47
+ if (chain.length === 0) {
48
+ chain.push(new MigrationNode(fromAddr, /* @__PURE__ */ new Date(0), timestamp));
49
+ chain.push(new MigrationNode(toAddr, timestamp));
50
+ } else {
51
+ const lastNode = chain[chain.length - 1];
52
+ lastNode.validUntil = timestamp;
53
+ lastNode.nextAddress = toAddr;
54
+ chain.push(new MigrationNode(toAddr, timestamp));
55
+ }
56
+ this.rootAddresses.set(this.formatAddress(toAddr), rootAddr);
57
+ }
58
+ /**
59
+ * Find the valid address at a specific timestamp
60
+ * @param address - Address to look up
61
+ * @param timestamp - Timestamp to check
62
+ * @returns Valid address at the timestamp
63
+ */
64
+ findAddressAtTime(address, timestamp) {
65
+ const chains = this.getMigrationHistory(address);
66
+ if (!chains?.length) return address;
67
+ let left = 0;
68
+ let right = chains.length - 1;
69
+ while (left <= right) {
70
+ const mid = Math.floor((left + right) / 2);
71
+ const node = chains[mid];
72
+ if (node.validFrom <= timestamp && (!node.validUntil || timestamp < node.validUntil)) return node.address;
73
+ if (timestamp < node.validFrom) right = mid - 1;
74
+ else left = mid + 1;
75
+ }
76
+ return chains[chains.length - 1].address;
77
+ }
78
+ formatAddress(address) {
79
+ return isEthereumDid(address) ? address.toLowerCase() : address;
80
+ }
81
+ getRootAddress(address) {
82
+ const formattedAddress = this.formatAddress(address);
83
+ return this.rootAddresses.get(formattedAddress) || formattedAddress;
84
+ }
85
+ /**
86
+ * Get the complete migration history for an address
87
+ * @param address - Address to look up
88
+ * @returns List of migration nodes
89
+ */
90
+ getMigrationHistory(address) {
91
+ const rootAddr = this.getRootAddress(address);
92
+ return this.chains.get(rootAddr) || [];
93
+ }
94
+ };
95
+
96
+ //#endregion
97
+ export { MigrationChainManager, MigrationNode };
@@ -0,0 +1,5 @@
1
+ //#region package.json
2
+ var name = "@ocap/resolver";
3
+
4
+ //#endregion
5
+ export { name };
@@ -0,0 +1,20 @@
1
+ import { TIndexedTokenState } from "./types.mjs";
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,26 @@
1
+ //#region src/token-cache.ts
2
+ var Cache = class {
3
+ constructor(dbAdapter) {
4
+ this.dbAdapter = dbAdapter;
5
+ this.cache = /* @__PURE__ */ new Map();
6
+ }
7
+ async get(address) {
8
+ if (this.cache.has(address)) return this.cache.get(address);
9
+ const token = await this.dbAdapter.get(address);
10
+ if (token) this.cache.set(address, token);
11
+ return token;
12
+ }
13
+ set(address, token) {
14
+ this.cache.set(address, token);
15
+ return token;
16
+ }
17
+ };
18
+ let cache = null;
19
+ function getInstance(dbAdapter) {
20
+ if (cache !== null) return cache;
21
+ cache = new Cache(dbAdapter);
22
+ return cache;
23
+ }
24
+
25
+ //#endregion
26
+ export { getInstance };
@@ -0,0 +1,166 @@
1
+ import { IChainConfig, IChunkIterator, IIndexDB, IIndexTable, IPipelineLogger, IResolverTransaction, IStateDB, IStateTable, ITokenInfo, TTokenDistribution } from "./types.mjs";
2
+ import { BN } from "@ocap/util";
3
+
4
+ //#region src/token-distribution.d.ts
5
+
6
+ /**
7
+ * Token balance in array format (used in stake state for distribution)
8
+ */
9
+ interface ITokenBalance {
10
+ address: string;
11
+ balance: string;
12
+ value?: string;
13
+ }
14
+ /**
15
+ * Stake state with array-format tokens (for distribution calculation)
16
+ */
17
+ interface IDistributionStakeState {
18
+ address: string;
19
+ sender: string;
20
+ message: string;
21
+ tokens: ITokenBalance[];
22
+ revokedTokens: ITokenBalance[];
23
+ renaissanceTime?: string;
24
+ }
25
+ /**
26
+ * State snapshot for tracking token changes
27
+ */
28
+ interface IStateSnapshot {
29
+ [address: string]: {
30
+ tokens: Record<string, string>;
31
+ revokedTokens: Record<string, string>;
32
+ };
33
+ }
34
+ /**
35
+ * Transaction context for distribution calculation
36
+ */
37
+ interface IResolverTransactionContext {
38
+ txn?: unknown;
39
+ stateSnapshot?: IStateSnapshot;
40
+ stakeState?: {
41
+ tokens: Record<string, string>;
42
+ revokedTokens: Record<string, string>;
43
+ };
44
+ [key: string]: unknown;
45
+ }
46
+ /**
47
+ * Distribution with BN values (for calculation)
48
+ */
49
+ interface IDistribution {
50
+ tokenAddress: string;
51
+ account: InstanceType<typeof BN>;
52
+ gas: InstanceType<typeof BN>;
53
+ fee: InstanceType<typeof BN>;
54
+ slashedVault: InstanceType<typeof BN>;
55
+ stake: InstanceType<typeof BN>;
56
+ revokedStake: InstanceType<typeof BN>;
57
+ gasStake: InstanceType<typeof BN>;
58
+ other: InstanceType<typeof BN>;
59
+ txTime: string;
60
+ }
61
+ /**
62
+ * Raw distribution input (string/number values)
63
+ */
64
+ interface IRawDistribution {
65
+ tokenAddress: string;
66
+ account?: string | number;
67
+ gas?: string | number;
68
+ fee?: string | number;
69
+ slashedVault?: string | number;
70
+ stake?: string | number;
71
+ revokedStake?: string | number;
72
+ gasStake?: string | number;
73
+ other?: string | number;
74
+ txTime?: string;
75
+ }
76
+ /**
77
+ * Options for handleTx method
78
+ */
79
+ interface IHandleTxOptions {
80
+ context?: IResolverTransactionContext;
81
+ isHandleStake?: boolean;
82
+ }
83
+ /**
84
+ * Resolver interface for TokenDistributionManager
85
+ */
86
+ interface IDistributionResolver {
87
+ indexdb: IIndexDB;
88
+ statedb: IStateDB & {
89
+ stake: IStateTable<IDistributionStakeState>;
90
+ };
91
+ config: IChainConfig;
92
+ logger?: IPipelineLogger;
93
+ formatTx: (tx: IResolverTransaction) => Promise<IResolverTransaction & {
94
+ tokenSymbols: ITokenInfo[];
95
+ }>;
96
+ listTransactionsChunks: (params: {
97
+ timeFilter?: {
98
+ startDateTime: string;
99
+ };
100
+ tokenFilter?: {
101
+ tokenFilter?: {
102
+ tokens: string[];
103
+ };
104
+ };
105
+ }) => Promise<IChunkIterator<IResolverTransaction>>;
106
+ listStakeChunks: () => Promise<IChunkIterator<IDistributionStakeState>>;
107
+ }
108
+ /**
109
+ * TokenDistribution IndexDB table interface
110
+ */
111
+ type ITokenDistributionTable = IIndexTable<TTokenDistribution> & {
112
+ insert: (data: TTokenDistribution) => Promise<void>;
113
+ update: (tokenAddress: string, data: TTokenDistribution) => Promise<void>;
114
+ };
115
+ declare class TokenDistributionManager {
116
+ resolver: IDistributionResolver;
117
+ indexdb: IIndexDB & {
118
+ tokenDistribution: ITokenDistributionTable;
119
+ };
120
+ isProcessing: boolean;
121
+ constructor(resolver: IDistributionResolver);
122
+ formatDistribution(distribution: IRawDistribution): IDistribution;
123
+ getDistribution(tokenAddress: string): Promise<TTokenDistribution | null>;
124
+ saveDistribution(distribution: IDistribution, isEnsureLatest?: boolean): Promise<TTokenDistribution>;
125
+ /**
126
+ * Calculate token distribution based on all transaction data.
127
+ * This method is usually used to update historical token distribution data
128
+ *
129
+ * @param tokenAddress Token address
130
+ * @param force If force is false, only calculate distributions for new transactions after txTime in indexdb. default is false
131
+ * @returns The updated token distribution or null if failed
132
+ */
133
+ updateByToken(tokenAddress: string, force?: boolean): Promise<TTokenDistribution | null | undefined>;
134
+ /**
135
+ * Split out revokedStake / gasStake from stake
136
+ *
137
+ * @param distribution The distribution object to update
138
+ * @returns The updated distribution
139
+ */
140
+ splitStake(distribution: IDistribution): Promise<IDistribution>;
141
+ /**
142
+ * Update token distribution by a single transaction.
143
+ * This method is usually used when a tx is completed.
144
+ *
145
+ * @param tx The transaction object
146
+ * @param context The transaction context
147
+ * @returns The updated token distributions
148
+ */
149
+ updateByTx(tx: IResolverTransaction, context: IResolverTransactionContext): Promise<TTokenDistribution[] | undefined>;
150
+ /**
151
+ * Parse token distribution for a single transaction
152
+ * @param tx The transaction
153
+ * @param distribution The distribution to update
154
+ * @param options Handle options
155
+ * @returns The updated token distribution
156
+ */
157
+ handleTx(tx: IResolverTransaction, distribution: IDistribution, {
158
+ context,
159
+ isHandleStake
160
+ }?: IHandleTxOptions): Promise<IDistribution>;
161
+ handleModerator(distribution: IDistribution): IDistribution;
162
+ getStakeState(address: string, ctx: IResolverTransactionContext): Promise<IDistributionStakeState>;
163
+ isGasStake(stakeState: IDistributionStakeState): boolean;
164
+ }
165
+ //#endregion
166
+ export { TokenDistributionManager };
@@ -0,0 +1,241 @@
1
+ import { BN, fromTokenToUnit, isSameDid } from "@ocap/util";
2
+ import { toTypeInfo } from "@arcblock/did";
3
+ import { types } from "@ocap/mcrypto";
4
+ import { isGasStakeAddress } from "@ocap/tx-protocols/lib/util";
5
+ import omit from "lodash/omit.js";
6
+ import { createIndexedTokenDistribution } from "@ocap/indexdb/lib/util";
7
+ import { eachReceipts } from "@ocap/state/lib/states/tx";
8
+
9
+ //#region src/token-distribution.ts
10
+ const { RoleType } = types;
11
+ const ZERO = new BN(0);
12
+ var TokenDistributionManager = class {
13
+ constructor(resolver) {
14
+ this.resolver = resolver;
15
+ this.indexdb = resolver.indexdb;
16
+ this.isProcessing = false;
17
+ }
18
+ formatDistribution(distribution) {
19
+ const { tokenAddress, account, gas, fee, slashedVault, stake, revokedStake, gasStake, other, txTime } = distribution;
20
+ return {
21
+ tokenAddress,
22
+ account: new BN(account || 0),
23
+ gas: new BN(gas || 0),
24
+ fee: new BN(fee || 0),
25
+ slashedVault: new BN(slashedVault || 0),
26
+ stake: new BN(stake || 0),
27
+ revokedStake: new BN(revokedStake || 0),
28
+ gasStake: new BN(gasStake || 0),
29
+ other: new BN(other || 0),
30
+ txTime: txTime || (/* @__PURE__ */ new Date(0)).toISOString()
31
+ };
32
+ }
33
+ async getDistribution(tokenAddress) {
34
+ const data = await this.indexdb.tokenDistribution.get(tokenAddress);
35
+ return data && createIndexedTokenDistribution(data);
36
+ }
37
+ async saveDistribution(distribution, isEnsureLatest = true) {
38
+ const data = createIndexedTokenDistribution(distribution);
39
+ const indexdbDistribution = await this.getDistribution(data.tokenAddress);
40
+ if (!indexdbDistribution) await this.indexdb.tokenDistribution.insert(data);
41
+ else {
42
+ if (isEnsureLatest) {
43
+ const latestTime = Math.max(new Date(indexdbDistribution.txTime).getTime(), new Date(data.txTime).getTime());
44
+ data.txTime = new Date(latestTime).toISOString();
45
+ }
46
+ await this.indexdb.tokenDistribution.update(data.tokenAddress, data);
47
+ }
48
+ return data;
49
+ }
50
+ /**
51
+ * Calculate token distribution based on all transaction data.
52
+ * This method is usually used to update historical token distribution data
53
+ *
54
+ * @param tokenAddress Token address
55
+ * @param force If force is false, only calculate distributions for new transactions after txTime in indexdb. default is false
56
+ * @returns The updated token distribution or null if failed
57
+ */
58
+ async updateByToken(tokenAddress, force) {
59
+ const { logger, config } = this.resolver;
60
+ if (this.isProcessing) {
61
+ logger?.debug?.("Token distribution is already in progress", {
62
+ tokenAddress,
63
+ force
64
+ });
65
+ return;
66
+ }
67
+ const distribution = force ? this.formatDistribution({ tokenAddress }) : this.formatDistribution(await this.getDistribution(tokenAddress) || { tokenAddress });
68
+ const isDefaultToken = tokenAddress === config.token.address;
69
+ logger?.info(`Update distribution by token (${tokenAddress})`, { distribution: createIndexedTokenDistribution(distribution) });
70
+ this.isProcessing = true;
71
+ try {
72
+ if (force && isDefaultToken) this.handleModerator(distribution);
73
+ const { next } = await this.resolver.listTransactionsChunks({
74
+ timeFilter: { startDateTime: distribution.txTime },
75
+ tokenFilter: isDefaultToken ? {} : { tokenFilter: { tokens: [tokenAddress] } }
76
+ });
77
+ let nextData = await next();
78
+ while (nextData.length) {
79
+ logger?.info("Updating token distribution in chunks", {
80
+ chunkSize: nextData.length,
81
+ startTime: nextData[0].time,
82
+ startHash: nextData[0].hash,
83
+ endTime: nextData[nextData.length - 1].time
84
+ });
85
+ const handlePromises = nextData.map((tx) => this.handleTx(tx, distribution));
86
+ await Promise.all(handlePromises);
87
+ await this.saveDistribution(distribution, false);
88
+ nextData = await next();
89
+ }
90
+ await this.splitStake(distribution);
91
+ await this.saveDistribution(distribution, false);
92
+ const result = createIndexedTokenDistribution(distribution);
93
+ logger?.info(`Token distribution update completed (${tokenAddress})`, { distribution: result });
94
+ return result;
95
+ } catch (e) {
96
+ logger?.error("Token distribution update failed", { error: e });
97
+ return null;
98
+ } finally {
99
+ this.isProcessing = false;
100
+ }
101
+ }
102
+ /**
103
+ * Split out revokedStake / gasStake from stake
104
+ *
105
+ * @param distribution The distribution object to update
106
+ * @returns The updated distribution
107
+ */
108
+ async splitStake(distribution) {
109
+ const { logger } = this.resolver;
110
+ const { tokenAddress } = distribution;
111
+ const { next } = await this.resolver.listStakeChunks();
112
+ let nextData = await next();
113
+ while (nextData.length) {
114
+ logger?.info("Updating stake distribution in chunks", {
115
+ chunkSize: nextData.length,
116
+ startTime: nextData[0].renaissanceTime,
117
+ startAddress: nextData[0].address,
118
+ endTime: nextData[nextData.length - 1].renaissanceTime
119
+ });
120
+ nextData.forEach((stakeState) => {
121
+ const isGasStake = this.isGasStake(stakeState);
122
+ const token = stakeState.tokens.find((x) => x.address === tokenAddress);
123
+ const revokedBalance = new BN(stakeState.revokedTokens.find((x) => x.address === tokenAddress)?.balance || 0);
124
+ const balance = new BN(token?.balance || 0);
125
+ distribution.revokedStake = distribution.revokedStake.add(revokedBalance);
126
+ distribution.stake = distribution.stake.sub(revokedBalance);
127
+ if (isGasStake) {
128
+ distribution.gasStake = distribution.gasStake.add(balance);
129
+ distribution.stake = distribution.stake.sub(balance);
130
+ }
131
+ });
132
+ nextData = await next();
133
+ }
134
+ return distribution;
135
+ }
136
+ /**
137
+ * Update token distribution by a single transaction.
138
+ * This method is usually used when a tx is completed.
139
+ *
140
+ * @param tx The transaction object
141
+ * @param context The transaction context
142
+ * @returns The updated token distributions
143
+ */
144
+ async updateByTx(tx, context) {
145
+ const { logger } = this.resolver;
146
+ const ctx = omit(context, "txn");
147
+ if (this.isProcessing) {
148
+ logger?.debug?.("Token distribution is already in progress", { tx });
149
+ return;
150
+ }
151
+ const tokens = (await this.resolver.formatTx(tx)).tokenSymbols.map(({ address }) => address);
152
+ return await Promise.all(tokens.map(async (tokenAddress) => {
153
+ const distribution = this.formatDistribution(await this.getDistribution(tokenAddress) || { tokenAddress });
154
+ await this.handleTx(tx, distribution, {
155
+ context: ctx,
156
+ isHandleStake: true
157
+ });
158
+ return await this.saveDistribution(distribution);
159
+ }));
160
+ }
161
+ /**
162
+ * Parse token distribution for a single transaction
163
+ * @param tx The transaction
164
+ * @param distribution The distribution to update
165
+ * @param options Handle options
166
+ * @returns The updated token distribution
167
+ */
168
+ async handleTx(tx, distribution, { context, isHandleStake = false } = {}) {
169
+ if (isHandleStake && !context) throw new Error("context is missing, handle revoke stake is not supported without context");
170
+ const type = tx.tx?.itxJson?._type;
171
+ const receipts = tx.receipts || [];
172
+ const { tokenAddress } = distribution;
173
+ const handlePromises = eachReceipts(receipts, async (address, change) => {
174
+ if (!isSameDid(change.target, tokenAddress)) return;
175
+ if (change.action === "migrate") return;
176
+ const roleType = toTypeInfo(address).role;
177
+ const value = new BN(change.value);
178
+ if (roleType === RoleType.ROLE_STAKE) {
179
+ if (isHandleStake) {
180
+ const stakeState = await this.getStakeState(address, context);
181
+ const isGasStakeResult = this.isGasStake(stakeState);
182
+ if (type === "ClaimStakeTx") {
183
+ distribution.revokedStake = distribution.revokedStake.add(value);
184
+ return;
185
+ }
186
+ if (type === "SlashStakeTx") {
187
+ const beforeState = context.stateSnapshot[address];
188
+ const afterState = context.stakeState;
189
+ if (!beforeState || !afterState) throw new Error("stake state is missing on slash stake tx");
190
+ const revokeTokenDiff = new BN(beforeState.revokedTokens[tokenAddress]).sub(new BN(afterState.revokedTokens[tokenAddress]));
191
+ const tokenDiff = new BN(beforeState.tokens[tokenAddress]).sub(new BN(afterState.tokens[tokenAddress]));
192
+ distribution.revokedStake = distribution.revokedStake.sub(revokeTokenDiff);
193
+ if (isGasStakeResult) distribution.gasStake = distribution.gasStake.sub(tokenDiff);
194
+ else distribution.stake = distribution.stake.sub(tokenDiff);
195
+ return;
196
+ }
197
+ if (isGasStakeResult) {
198
+ distribution.gasStake = distribution.gasStake.add(value);
199
+ return;
200
+ }
201
+ }
202
+ distribution.stake = distribution.stake.add(value);
203
+ } else if (change.action === "gas" && value.gt(ZERO)) distribution.gas = distribution.gas.add(value);
204
+ else if (change.action === "fee" && value.gt(ZERO)) distribution.fee = distribution.fee.add(value);
205
+ else if (type === "SlashStakeTx" && value.gt(ZERO) && isSameDid(address, this.resolver.config.vaults.slashedStake ?? "")) distribution.slashedVault = distribution.slashedVault.add(value);
206
+ else distribution.account = distribution.account.add(value);
207
+ });
208
+ await Promise.all(handlePromises);
209
+ if (isHandleStake && type === "RevokeStakeTx") {
210
+ const { address } = tx.tx.itxJson;
211
+ const stakeState = await this.getStakeState(address, context);
212
+ const isGasStakeResult = this.isGasStake(stakeState);
213
+ tx.tx.itxJson.outputs.forEach((x) => {
214
+ x.tokens.filter((change) => isSameDid(change.address, tokenAddress)).forEach((change) => {
215
+ const value = new BN(change.value || change.balance || 0);
216
+ distribution.revokedStake = distribution.revokedStake.add(value);
217
+ if (isGasStakeResult) distribution.gasStake = distribution.gasStake.sub(value);
218
+ else distribution.stake = distribution.stake.sub(value);
219
+ });
220
+ });
221
+ }
222
+ distribution.txTime = tx.time;
223
+ return distribution;
224
+ }
225
+ handleModerator(distribution) {
226
+ const { config } = this.resolver;
227
+ if (distribution.tokenAddress !== config.token.address) return distribution;
228
+ const value = config.accounts.filter((account) => Number(account.balance) > 0).map((account) => new BN(account.balance)).reduce((cur, balance) => cur.add(balance), ZERO);
229
+ distribution.account = distribution.account.add(fromTokenToUnit(value.toString()));
230
+ return distribution;
231
+ }
232
+ async getStakeState(address, ctx) {
233
+ return await this.resolver.statedb.stake.get(address, ctx);
234
+ }
235
+ isGasStake(stakeState) {
236
+ return isGasStakeAddress(stakeState.sender, stakeState.address) && stakeState.message === "stake-for-gas";
237
+ }
238
+ };
239
+
240
+ //#endregion
241
+ export { TokenDistributionManager };