@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/migration-chain.js
DELETED
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
const { isEthereumDid } = require('@arcblock/did');
|
|
2
|
-
|
|
3
|
-
class MigrationNode {
|
|
4
|
-
constructor(address, validFrom, validUntil = null, nextAddress = null) {
|
|
5
|
-
this.address = address;
|
|
6
|
-
this.validFrom = validFrom;
|
|
7
|
-
this.validUntil = validUntil;
|
|
8
|
-
this.nextAddress = nextAddress;
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
class MigrationChainManager {
|
|
13
|
-
constructor() {
|
|
14
|
-
// Main storage for migration chains
|
|
15
|
-
this.chains = new Map();
|
|
16
|
-
// Index for quick root lookup
|
|
17
|
-
this.rootAddresses = new Map();
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
validateMigration(migration) {
|
|
21
|
-
if (!migration?.tx?.from || !migration?.tx?.itxJson?.address || !migration?.time) {
|
|
22
|
-
throw new Error('Invalid migration format');
|
|
23
|
-
}
|
|
24
|
-
if (new Date(migration.time).toString() === 'Invalid Date') {
|
|
25
|
-
throw new Error('Invalid timestamp');
|
|
26
|
-
}
|
|
27
|
-
return true;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Build migration chains from a list of migration transactions
|
|
32
|
-
* @param {Array} migrations - List of migration transactions
|
|
33
|
-
*/
|
|
34
|
-
buildChains(migrations) {
|
|
35
|
-
if (!Array.isArray(migrations)) {
|
|
36
|
-
throw new Error('Migrations must be an array');
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
migrations.forEach((migration) => this.validateMigration(migration));
|
|
40
|
-
|
|
41
|
-
// Sort migrations by timestamp
|
|
42
|
-
const sortedMigrations = [...migrations].sort((a, b) => new Date(a.time) - new Date(b.time));
|
|
43
|
-
|
|
44
|
-
for (const migration of sortedMigrations) {
|
|
45
|
-
const fromAddr = migration.tx.from;
|
|
46
|
-
const toAddr = migration.tx.itxJson.address;
|
|
47
|
-
const timestamp = new Date(migration.time);
|
|
48
|
-
|
|
49
|
-
this._processMigration(fromAddr, toAddr, timestamp);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Process a single migration and update the chains
|
|
55
|
-
* @param {string} fromAddr - Source address
|
|
56
|
-
* @param {string} toAddr - Destination address
|
|
57
|
-
* @param {Date} timestamp - Migration timestamp
|
|
58
|
-
* @private
|
|
59
|
-
*/
|
|
60
|
-
_processMigration(fromAddr, toAddr, timestamp) {
|
|
61
|
-
// Find or create the root address for this chain
|
|
62
|
-
const rootAddr = this.getRootAddress(fromAddr);
|
|
63
|
-
|
|
64
|
-
// Get or create the chain
|
|
65
|
-
if (!this.chains.has(rootAddr)) {
|
|
66
|
-
this.chains.set(rootAddr, []);
|
|
67
|
-
}
|
|
68
|
-
const chain = this.chains.get(rootAddr);
|
|
69
|
-
|
|
70
|
-
if (chain.length === 0) {
|
|
71
|
-
// First migration in the chain
|
|
72
|
-
chain.push(new MigrationNode(fromAddr, new Date(0), timestamp));
|
|
73
|
-
chain.push(new MigrationNode(toAddr, timestamp));
|
|
74
|
-
} else {
|
|
75
|
-
// Update the previous node's valid_until and next_address
|
|
76
|
-
const lastNode = chain[chain.length - 1];
|
|
77
|
-
lastNode.validUntil = timestamp;
|
|
78
|
-
lastNode.nextAddress = toAddr;
|
|
79
|
-
// Add the new node
|
|
80
|
-
chain.push(new MigrationNode(toAddr, timestamp));
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Update root address mapping for the new address
|
|
84
|
-
this.rootAddresses.set(this.formatAddress(toAddr), rootAddr);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Find the valid address at a specific timestamp
|
|
89
|
-
* @param {string} address - Address to look up
|
|
90
|
-
* @param {Date} timestamp - Timestamp to check
|
|
91
|
-
* @returns {string} Valid address at the timestamp
|
|
92
|
-
*/
|
|
93
|
-
findAddressAtTime(address, timestamp) {
|
|
94
|
-
const chains = this.getMigrationHistory(address);
|
|
95
|
-
|
|
96
|
-
if (!chains?.length) {
|
|
97
|
-
return address;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Binary search for the correct address
|
|
101
|
-
let left = 0;
|
|
102
|
-
let right = chains.length - 1;
|
|
103
|
-
|
|
104
|
-
while (left <= right) {
|
|
105
|
-
const mid = Math.floor((left + right) / 2);
|
|
106
|
-
const node = chains[mid];
|
|
107
|
-
|
|
108
|
-
if (node.validFrom <= timestamp && (!node.validUntil || timestamp < node.validUntil)) {
|
|
109
|
-
return node.address;
|
|
110
|
-
}
|
|
111
|
-
if (timestamp < node.validFrom) {
|
|
112
|
-
right = mid - 1;
|
|
113
|
-
} else {
|
|
114
|
-
left = mid + 1;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
return chains[chains.length - 1].address;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
formatAddress(address) {
|
|
122
|
-
return isEthereumDid(address) ? address.toLowerCase() : address;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
getRootAddress(address) {
|
|
126
|
-
const formattedAddress = this.formatAddress(address);
|
|
127
|
-
return this.rootAddresses.get(formattedAddress) || formattedAddress;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Get the complete migration history for an address
|
|
132
|
-
* @param {string} address - Address to look up
|
|
133
|
-
* @returns {Array} List of migration nodes
|
|
134
|
-
*/
|
|
135
|
-
getMigrationHistory(address) {
|
|
136
|
-
const rootAddr = this.getRootAddress(address);
|
|
137
|
-
return this.chains.get(rootAddr) || [];
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
module.exports = {
|
|
142
|
-
MigrationNode,
|
|
143
|
-
MigrationChainManager,
|
|
144
|
-
};
|
package/lib/token-cache.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 缓存 token 到内存
|
|
3
|
-
*/
|
|
4
|
-
class Cache {
|
|
5
|
-
constructor(dbAdapter) {
|
|
6
|
-
this.dbAdapter = dbAdapter;
|
|
7
|
-
this.cache = new Map();
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
async get(address) {
|
|
11
|
-
if (this.cache.has(address)) {
|
|
12
|
-
return this.cache.get(address);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const token = await this.dbAdapter.get(address);
|
|
16
|
-
if (token) {
|
|
17
|
-
this.cache.set(address, token);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return token;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
set(address, token) {
|
|
24
|
-
this.cache.set(address, token);
|
|
25
|
-
return token;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
let cache = null;
|
|
30
|
-
|
|
31
|
-
module.exports = {
|
|
32
|
-
getInstance: (dbAdapter) => {
|
|
33
|
-
if (cache !== null) {
|
|
34
|
-
return cache;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
cache = new Cache(dbAdapter);
|
|
38
|
-
return cache;
|
|
39
|
-
},
|
|
40
|
-
};
|
|
@@ -1,358 +0,0 @@
|
|
|
1
|
-
const { BN, isSameDid, fromTokenToUnit } = require('@ocap/util');
|
|
2
|
-
const { toTypeInfo } = require('@arcblock/did');
|
|
3
|
-
const {
|
|
4
|
-
types: { RoleType },
|
|
5
|
-
} = require('@ocap/mcrypto');
|
|
6
|
-
const { createIndexedTokenDistribution } = require('@ocap/indexdb/lib/util');
|
|
7
|
-
const { eachReceipts } = require('@ocap/state/lib/states/tx');
|
|
8
|
-
const { isGasStakeAddress } = require('@ocap/tx-protocols/lib/util');
|
|
9
|
-
const omit = require('lodash/omit');
|
|
10
|
-
|
|
11
|
-
const ZERO = new BN(0);
|
|
12
|
-
|
|
13
|
-
class TokenDistributionManager {
|
|
14
|
-
constructor(resolver) {
|
|
15
|
-
this.resolver = resolver;
|
|
16
|
-
this.indexdb = resolver.indexdb;
|
|
17
|
-
this.isProcessing = false;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
formatDistribution(distribution) {
|
|
21
|
-
const { tokenAddress, account, gas, fee, slashedVault, stake, revokedStake, gasStake, other, txTime } =
|
|
22
|
-
distribution;
|
|
23
|
-
return {
|
|
24
|
-
tokenAddress,
|
|
25
|
-
account: new BN(account || 0),
|
|
26
|
-
gas: new BN(gas || 0),
|
|
27
|
-
fee: new BN(fee || 0),
|
|
28
|
-
slashedVault: new BN(slashedVault || 0),
|
|
29
|
-
stake: new BN(stake || 0),
|
|
30
|
-
revokedStake: new BN(revokedStake || 0),
|
|
31
|
-
gasStake: new BN(gasStake || 0),
|
|
32
|
-
other: new BN(other || 0),
|
|
33
|
-
txTime: txTime || new Date(0).toISOString(),
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
async getDistribution(tokenAddress) {
|
|
38
|
-
const data = await this.indexdb.tokenDistribution.get(tokenAddress);
|
|
39
|
-
return data && createIndexedTokenDistribution(data);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async saveDistribution(distribution, isEnsureLatest = true) {
|
|
43
|
-
const data = createIndexedTokenDistribution(distribution);
|
|
44
|
-
const indexdbDistribution = await this.getDistribution(data.tokenAddress);
|
|
45
|
-
|
|
46
|
-
if (!indexdbDistribution) {
|
|
47
|
-
await this.indexdb.tokenDistribution.insert(data);
|
|
48
|
-
} else {
|
|
49
|
-
// ensure txTime is latest
|
|
50
|
-
if (isEnsureLatest) {
|
|
51
|
-
const latestTime = Math.max(new Date(indexdbDistribution.txTime).getTime(), new Date(data.txTime).getTime());
|
|
52
|
-
data.txTime = new Date(latestTime).toISOString();
|
|
53
|
-
}
|
|
54
|
-
await this.indexdb.tokenDistribution.update(data.tokenAddress, data);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return data;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Calculate token distribution based on all transaction data.
|
|
62
|
-
* This method is usually used to update historical token distribution data
|
|
63
|
-
*
|
|
64
|
-
* @param {string} tokenAddress Token address
|
|
65
|
-
* @param {boolean} force If force is false, only calculate distributions for new transactions after txTime in indexdb. default is false
|
|
66
|
-
* @returns {Promise<Object>}
|
|
67
|
-
*/
|
|
68
|
-
async updateByToken(tokenAddress, force) {
|
|
69
|
-
const { logger, config } = this.resolver;
|
|
70
|
-
|
|
71
|
-
if (this.isProcessing) {
|
|
72
|
-
logger?.logger('Token distribution is already in progress', { tokenAddress, force });
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const distribution = force
|
|
77
|
-
? this.formatDistribution({ tokenAddress })
|
|
78
|
-
: this.formatDistribution((await this.getDistribution(tokenAddress)) || { tokenAddress });
|
|
79
|
-
const isDefaultToken = tokenAddress === config.token.address;
|
|
80
|
-
|
|
81
|
-
logger?.info(`Update distribution by token (${tokenAddress})`, {
|
|
82
|
-
distribution: createIndexedTokenDistribution(distribution),
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
this.isProcessing = true;
|
|
86
|
-
|
|
87
|
-
try {
|
|
88
|
-
if (force && isDefaultToken) {
|
|
89
|
-
this.handleModerator(distribution);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const { next } = await this.resolver.listTransactionsChunks({
|
|
93
|
-
timeFilter: { startDateTime: distribution.txTime },
|
|
94
|
-
// Default token is used for gas payment and may appear in any transaction, so we cannot filter by tokenFilter
|
|
95
|
-
tokenFilter: isDefaultToken ? {} : { tokenFilter: { tokens: [tokenAddress] } },
|
|
96
|
-
});
|
|
97
|
-
let nextData = await next();
|
|
98
|
-
|
|
99
|
-
// Process transactions in chunks and update indexdb
|
|
100
|
-
while (nextData.length) {
|
|
101
|
-
logger?.info('Updating token distribution in chunks', {
|
|
102
|
-
chunkSize: nextData.length,
|
|
103
|
-
startTime: nextData[0].time,
|
|
104
|
-
startHash: nextData[0].hash,
|
|
105
|
-
endTime: nextData[nextData.length - 1].time,
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
const handlePromises = nextData.map((tx) => this.handleTx(tx, distribution));
|
|
109
|
-
await Promise.all(handlePromises);
|
|
110
|
-
|
|
111
|
-
// update indexdb
|
|
112
|
-
await this.saveDistribution(distribution, false);
|
|
113
|
-
nextData = await next();
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// We cannot distinguish between revokedStake and stake from tx receipts here,
|
|
117
|
-
// so we need to read all stake transactions and recalculate token distribution based on their revokeTokens and tokens
|
|
118
|
-
await this.splitStake(distribution);
|
|
119
|
-
await this.saveDistribution(distribution, false);
|
|
120
|
-
|
|
121
|
-
const result = createIndexedTokenDistribution(distribution);
|
|
122
|
-
|
|
123
|
-
logger.info(`Token distribution update completed (${tokenAddress})`, { distribution: result });
|
|
124
|
-
return result;
|
|
125
|
-
} catch (e) {
|
|
126
|
-
logger?.error('Token distribution update failed', { error: e });
|
|
127
|
-
return null;
|
|
128
|
-
} finally {
|
|
129
|
-
this.isProcessing = false;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Split out revokedStake / gasStake from stake
|
|
135
|
-
*
|
|
136
|
-
* @param {Object} distribution
|
|
137
|
-
* @returns {Promise<Object>}
|
|
138
|
-
*/
|
|
139
|
-
async splitStake(distribution) {
|
|
140
|
-
const { logger } = this.resolver;
|
|
141
|
-
const { tokenAddress } = distribution;
|
|
142
|
-
|
|
143
|
-
const { next } = await this.resolver.listStakeChunks();
|
|
144
|
-
|
|
145
|
-
let nextData = await next();
|
|
146
|
-
|
|
147
|
-
// Process transactions in chunks and update indexdb
|
|
148
|
-
while (nextData.length) {
|
|
149
|
-
logger?.info('Updating stake distribution in chunks', {
|
|
150
|
-
chunkSize: nextData.length,
|
|
151
|
-
startTime: nextData[0].renaissanceTime,
|
|
152
|
-
startAddress: nextData[0].address,
|
|
153
|
-
endTime: nextData[nextData.length - 1].renaissanceTime,
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
nextData.forEach((stakeState) => {
|
|
157
|
-
const isGasStake = this.isGasStake(stakeState);
|
|
158
|
-
const token = stakeState.tokens.find((x) => x.address === tokenAddress);
|
|
159
|
-
const revokedToken = stakeState.revokedTokens.find((x) => x.address === tokenAddress);
|
|
160
|
-
const revokedBalance = new BN(revokedToken?.balance || 0);
|
|
161
|
-
const balance = new BN(token?.balance || 0);
|
|
162
|
-
|
|
163
|
-
// stake -->> revokedStake
|
|
164
|
-
distribution.revokedStake = distribution.revokedStake.add(revokedBalance);
|
|
165
|
-
distribution.stake = distribution.stake.sub(revokedBalance);
|
|
166
|
-
|
|
167
|
-
// stake -->> gasStake
|
|
168
|
-
if (isGasStake) {
|
|
169
|
-
distribution.gasStake = distribution.gasStake.add(balance);
|
|
170
|
-
distribution.stake = distribution.stake.sub(balance);
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
// continue
|
|
175
|
-
nextData = await next();
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
return distribution;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Update token distribution by a single transaction.
|
|
183
|
-
* This method is usually used when a tx is completed.
|
|
184
|
-
*
|
|
185
|
-
* @param {Object} tx The transaction object
|
|
186
|
-
* @param {Object} context The transaction context
|
|
187
|
-
* @returns {Promise<Object>} The updated token distributions
|
|
188
|
-
*/
|
|
189
|
-
async updateByTx(tx, context) {
|
|
190
|
-
const { logger } = this.resolver;
|
|
191
|
-
const ctx = omit(context, 'txn');
|
|
192
|
-
|
|
193
|
-
if (this.isProcessing) {
|
|
194
|
-
logger?.logger('Token distribution is already in progress', { tx });
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
const formattedTx = await this.resolver.formatTx(tx);
|
|
199
|
-
const tokens = formattedTx.tokenSymbols.map(({ address }) => address);
|
|
200
|
-
|
|
201
|
-
// Parse all tokens in this transaction
|
|
202
|
-
const results = await Promise.all(
|
|
203
|
-
tokens.map(async (tokenAddress) => {
|
|
204
|
-
const distribution = this.formatDistribution((await this.getDistribution(tokenAddress)) || { tokenAddress });
|
|
205
|
-
await this.handleTx(tx, distribution, { context: ctx, isHandleStake: true });
|
|
206
|
-
const data = await this.saveDistribution(distribution);
|
|
207
|
-
return data;
|
|
208
|
-
})
|
|
209
|
-
);
|
|
210
|
-
|
|
211
|
-
return results;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* Parse token distribution for a single transaction
|
|
216
|
-
* @param {Object} tx
|
|
217
|
-
* @param {String} distribution
|
|
218
|
-
* @param {Object} param
|
|
219
|
-
* @param {Object} param.context
|
|
220
|
-
* @param {Boolean} param.isHandleStake Whether to handle revoked / gas stake tokens
|
|
221
|
-
* @returns {Promise<Object>} token distribution
|
|
222
|
-
*/
|
|
223
|
-
async handleTx(tx, distribution, { context, isHandleStake = false } = {}) {
|
|
224
|
-
if (isHandleStake && !context) {
|
|
225
|
-
throw new Error('FORBIDDEN', 'context is missing, handle revoke stake is not supported without context');
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
const type = tx.tx?.itxJson?._type;
|
|
229
|
-
const receipts = tx.receipts || [];
|
|
230
|
-
const { tokenAddress } = distribution;
|
|
231
|
-
|
|
232
|
-
// Iterate through each transaction receipt and distribute tokens based on account type
|
|
233
|
-
const handlePromises = eachReceipts(receipts, async (address, change) => {
|
|
234
|
-
if (!isSameDid(change.target, tokenAddress)) return;
|
|
235
|
-
// In migrate tx, tokens are frozen for the old account and minted for the new account
|
|
236
|
-
// It behaves like minting new tokens in receipts
|
|
237
|
-
// So we should skip here
|
|
238
|
-
if (change.action === 'migrate') return;
|
|
239
|
-
|
|
240
|
-
const roleType = toTypeInfo(address).role;
|
|
241
|
-
const value = new BN(change.value);
|
|
242
|
-
|
|
243
|
-
// Stake
|
|
244
|
-
if (roleType === RoleType.ROLE_STAKE) {
|
|
245
|
-
if (isHandleStake) {
|
|
246
|
-
const stakeState = await this.getStakeState(address, context);
|
|
247
|
-
const isGasStake = this.isGasStake(stakeState);
|
|
248
|
-
// stake revokedTokens -->> account tokens
|
|
249
|
-
if (type === 'ClaimStakeTx') {
|
|
250
|
-
distribution.revokedStake = distribution.revokedStake.add(value);
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
253
|
-
// stake tokens + stake revokedTokens -->> account tokens
|
|
254
|
-
if (type === 'SlashStakeTx') {
|
|
255
|
-
const beforeState = context.stateSnapshot[address];
|
|
256
|
-
const afterState = context.stakeState;
|
|
257
|
-
if (!beforeState || !afterState) {
|
|
258
|
-
throw new Error('INVALID_TOKEN_DISTRIBUTION', 'stake state is missing on slash stake tx');
|
|
259
|
-
}
|
|
260
|
-
const revokeTokenDiff = new BN(beforeState.revokedTokens[tokenAddress]).sub(
|
|
261
|
-
new BN(afterState.revokedTokens[tokenAddress])
|
|
262
|
-
);
|
|
263
|
-
const tokenDiff = new BN(beforeState.tokens[tokenAddress]).sub(new BN(afterState.tokens[tokenAddress]));
|
|
264
|
-
|
|
265
|
-
distribution.revokedStake = distribution.revokedStake.sub(revokeTokenDiff);
|
|
266
|
-
if (isGasStake) {
|
|
267
|
-
distribution.gasStake = distribution.gasStake.sub(tokenDiff);
|
|
268
|
-
} else {
|
|
269
|
-
distribution.stake = distribution.stake.sub(tokenDiff);
|
|
270
|
-
}
|
|
271
|
-
return;
|
|
272
|
-
}
|
|
273
|
-
// gasStake tokens -->> account tokens
|
|
274
|
-
if (isGasStake) {
|
|
275
|
-
distribution.gasStake = distribution.gasStake.add(value);
|
|
276
|
-
return;
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
distribution.stake = distribution.stake.add(value);
|
|
280
|
-
}
|
|
281
|
-
// Gas
|
|
282
|
-
else if (change.action === 'gas' && value.gt(ZERO)) {
|
|
283
|
-
distribution.gas = distribution.gas.add(value);
|
|
284
|
-
}
|
|
285
|
-
// Fee
|
|
286
|
-
else if (change.action === 'fee' && value.gt(ZERO)) {
|
|
287
|
-
distribution.fee = distribution.fee.add(value);
|
|
288
|
-
}
|
|
289
|
-
// SlashVault
|
|
290
|
-
else if (
|
|
291
|
-
type === 'SlashStakeTx' &&
|
|
292
|
-
value.gt(ZERO) &&
|
|
293
|
-
isSameDid(address, this.resolver.config.vaults.slashedStake)
|
|
294
|
-
) {
|
|
295
|
-
distribution.slashedVault = distribution.slashedVault.add(value);
|
|
296
|
-
}
|
|
297
|
-
// Others: Account
|
|
298
|
-
else {
|
|
299
|
-
distribution.account = distribution.account.add(value);
|
|
300
|
-
}
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
await Promise.all(handlePromises);
|
|
304
|
-
|
|
305
|
-
if (isHandleStake && type === 'RevokeStakeTx') {
|
|
306
|
-
// RevokedStake has no receipts
|
|
307
|
-
// stake tokens -->> stake revokedTokens
|
|
308
|
-
const { address } = tx.tx.itxJson;
|
|
309
|
-
const stakeState = await this.getStakeState(address, context);
|
|
310
|
-
const isGasStake = this.isGasStake(stakeState);
|
|
311
|
-
|
|
312
|
-
tx.tx.itxJson.outputs.forEach((x) => {
|
|
313
|
-
x.tokens
|
|
314
|
-
.filter((change) => isSameDid(change.address, tokenAddress))
|
|
315
|
-
.forEach((change) => {
|
|
316
|
-
const value = new BN(change.value);
|
|
317
|
-
distribution.revokedStake = distribution.revokedStake.add(value);
|
|
318
|
-
if (isGasStake) {
|
|
319
|
-
distribution.gasStake = distribution.gasStake.sub(value);
|
|
320
|
-
} else {
|
|
321
|
-
distribution.stake = distribution.stake.sub(value);
|
|
322
|
-
}
|
|
323
|
-
});
|
|
324
|
-
});
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
distribution.txTime = tx.time;
|
|
328
|
-
|
|
329
|
-
return distribution;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
handleModerator(distribution) {
|
|
333
|
-
const { config } = this.resolver;
|
|
334
|
-
|
|
335
|
-
if (distribution.tokenAddress !== config.token.address) {
|
|
336
|
-
return distribution;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
const value = config.accounts
|
|
340
|
-
.filter((account) => account.balance > 0)
|
|
341
|
-
.map((account) => new BN(account.balance))
|
|
342
|
-
.reduce((cur, balance) => cur.add(balance), ZERO);
|
|
343
|
-
|
|
344
|
-
distribution.account = distribution.account.add(fromTokenToUnit(value.toString()));
|
|
345
|
-
return distribution;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
async getStakeState(address, ctx) {
|
|
349
|
-
const stakeState = await this.resolver.statedb.stake.get(address, ctx);
|
|
350
|
-
return stakeState;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
isGasStake(stakeState) {
|
|
354
|
-
return isGasStakeAddress(stakeState.sender, stakeState.address) && stakeState.message === 'stake-for-gas';
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
module.exports = { TokenDistributionManager };
|