Skip to content

Commit 742d0f1

Browse files
committed
Update service
1 parent a8ec535 commit 742d0f1

File tree

1 file changed

+128
-49
lines changed

1 file changed

+128
-49
lines changed

src/services/accounts/AccountsVestingInfoService.ts

Lines changed: 128 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,13 @@ import { BlockNumberSource, IAccountVestingInfo, IVestingSchedule } from 'src/ty
2323
import { ApiPromiseRegistry } from '../../apiRegistry';
2424
import { assetHubSpecNames } from '../../chains-config';
2525
import { getInclusionBlockNumber } from '../../util/relay/getRelayParentNumber';
26-
import { calculateTotalUnlockable, calculateUnlockable, IVestingSchedule as IVestingCalcSchedule } from '../../util/vesting/vestingCalculations';
26+
import {
27+
calculateTotalVested,
28+
calculateVested,
29+
calculateVestedClaimable,
30+
calculateVestingTotal,
31+
IVestingSchedule as IVestingCalcSchedule,
32+
} from '../../util/vesting/vestingCalculations';
2733
import { AbstractService } from '../AbstractService';
2834
import { MIGRATION_BOUNDARIES, relayToSpecMapping } from '../consts';
2935

@@ -37,14 +43,25 @@ type MigrationState = 'pre-migration' | 'during-migration' | 'post-migration';
3743
*/
3844
const ASSET_HUB_PARA_ID = 1000;
3945

46+
/**
47+
* Vesting lock ID used in balances.locks
48+
* This is "vesting " padded to 8 bytes (0x76657374696e6720)
49+
*/
50+
const VESTING_ID = '0x76657374696e6720';
51+
4052
export class AccountsVestingInfoService extends AbstractService {
4153
/**
4254
* Fetch vesting information for an account at a given block.
4355
*
4456
* @param hash `BlockHash` to make call at
4557
* @param address address of the account to get the vesting info of
58+
* @param includeVested whether to calculate and include vested amounts (default: false)
4659
*/
47-
async fetchAccountVestingInfo(hash: BlockHash, address: string): Promise<IAccountVestingInfo> {
60+
async fetchAccountVestingInfo(
61+
hash: BlockHash,
62+
address: string,
63+
includeVested = false,
64+
): Promise<IAccountVestingInfo> {
4865
const { api } = this;
4966

5067
const historicApi = await api.at(hash);
@@ -77,12 +94,23 @@ export class AccountsVestingInfoService extends AbstractService {
7794
const unwrapVesting = vesting.unwrap();
7895
const vestingArray: VestingInfo[] = Array.isArray(unwrapVesting) ? unwrapVesting : [unwrapVesting];
7996

80-
// Calculate unlockable amounts based on chain type and migration state
81-
const unlockableResult = await this.calculateVestingUnlockable(hash, blockNumber, vestingArray);
97+
// If includeVested is not requested, return raw vesting data
98+
if (!includeVested) {
99+
return {
100+
at,
101+
vesting: vestingArray,
102+
};
103+
}
104+
105+
// Get the on-chain vesting lock amount from balances.locks
106+
const vestingLocked = await this.getVestingLocked(historicApi, address);
82107

83-
if (unlockableResult === null) {
84-
// Unable to calculate unlockable (e.g., during migration or missing relay connection)
85-
// Return raw vesting data without unlockable calculations
108+
// Calculate vested amounts based on chain type and migration state
109+
const vestingResult = await this.calculateVestingAmounts(hash, blockNumber, vestingArray, vestingLocked);
110+
111+
if (vestingResult === null) {
112+
// Unable to calculate (e.g., during migration or missing relay connection)
113+
// Return raw vesting data without calculations
86114
return {
87115
at,
88116
vesting: vestingArray,
@@ -91,48 +119,79 @@ export class AccountsVestingInfoService extends AbstractService {
91119

92120
return {
93121
at,
94-
vesting: unlockableResult.schedules,
95-
totalUnlockable: unlockableResult.totalUnlockable,
96-
blockNumberForCalculation: unlockableResult.blockNumberForCalculation,
97-
blockNumberSource: unlockableResult.blockNumberSource,
122+
vesting: vestingResult.schedules,
123+
vestedBalance: vestingResult.vestedBalance,
124+
vestingTotal: vestingResult.vestingTotal,
125+
vestedClaimable: vestingResult.vestedClaimable,
126+
blockNumberForCalculation: vestingResult.blockNumberForCalculation,
127+
blockNumberSource: vestingResult.blockNumberSource,
98128
};
99129
}
100130

101131
/**
102-
* Calculate unlockable amounts for vesting schedules.
132+
* Get the vesting lock amount from balances.locks.
133+
* Returns 0 if no vesting lock exists.
134+
*/
135+
private async getVestingLocked(
136+
historicApi: Awaited<ReturnType<typeof this.api.at>>,
137+
address: string,
138+
): Promise<BN> {
139+
if (!historicApi.query.balances?.locks) {
140+
return new BN(0);
141+
}
142+
143+
const locks = await historicApi.query.balances.locks(address);
144+
145+
for (const lock of locks) {
146+
if (lock.id.toHex() === VESTING_ID) {
147+
return new BN(lock.amount.toString());
148+
}
149+
}
150+
151+
return new BN(0);
152+
}
153+
154+
/**
155+
* Calculate vested amounts for vesting schedules.
103156
* Returns null if calculation cannot be performed (e.g., during migration window).
104157
*/
105-
private async calculateVestingUnlockable(
158+
private async calculateVestingAmounts(
106159
hash: BlockHash,
107160
blockNumber: number,
108161
vestingArray: VestingInfo[],
162+
vestingLocked: BN,
109163
): Promise<{
110164
schedules: IVestingSchedule[];
111-
totalUnlockable: string;
165+
vestedBalance: string;
166+
vestingTotal: string;
167+
vestedClaimable: string;
112168
blockNumberForCalculation: string;
113169
blockNumberSource: BlockNumberSource;
114170
} | null> {
115171
const specName = this.specName;
116172
const isAssetHub = assetHubSpecNames.has(specName);
117173

118174
if (isAssetHub) {
119-
return this.calculateForAssetHub(hash, blockNumber, vestingArray);
175+
return this.calculateForAssetHub(hash, blockNumber, vestingArray, vestingLocked);
120176
} else {
121-
return this.calculateForRelayChain(blockNumber, vestingArray);
177+
return this.calculateForRelayChain(blockNumber, vestingArray, vestingLocked);
122178
}
123179
}
124180

125181
/**
126-
* Calculate unlockable for Asset Hub chains.
182+
* Calculate vested amounts for Asset Hub chains.
127183
* Post-migration: uses relay chain inclusion block number for calculations.
128184
*/
129185
private async calculateForAssetHub(
130186
hash: BlockHash,
131187
blockNumber: number,
132188
vestingArray: VestingInfo[],
189+
vestingLocked: BN,
133190
): Promise<{
134191
schedules: IVestingSchedule[];
135-
totalUnlockable: string;
192+
vestedBalance: string;
193+
vestingTotal: string;
194+
vestedClaimable: string;
136195
blockNumberForCalculation: string;
137196
blockNumberSource: BlockNumberSource;
138197
} | null> {
@@ -147,8 +206,8 @@ export class AccountsVestingInfoService extends AbstractService {
147206
const migrationState = this.getAssetHubMigrationState(blockNumber, boundaries);
148207

149208
if (migrationState === 'during-migration') {
150-
// During migration window, return 0 for unlockable
151-
return this.createZeroUnlockableResult(vestingArray, blockNumber.toString(), 'self');
209+
// During migration window, return 0 for claimable
210+
return this.createZeroClaimableResult(vestingArray, vestingLocked, blockNumber.toString(), 'self');
152211
}
153212

154213
if (migrationState === 'pre-migration') {
@@ -161,39 +220,37 @@ export class AccountsVestingInfoService extends AbstractService {
161220
if (relayApis.length === 0) {
162221
throw new InternalServerError(
163222
'Relay chain connection required for vesting calculations on Asset Hub post-migration. ' +
164-
'Please configure MULTI_CHAIN_URL with a relay chain endpoint.',
223+
'Please configure MULTI_CHAIN_URL with a relay chain endpoint.',
165224
);
166225
}
167226

168227
const rcApi = relayApis[0].api;
169228

170229
// Get the relay chain inclusion block number for this Asset Hub block
171-
const inclusionResult = await getInclusionBlockNumber(
172-
this.api,
173-
rcApi,
174-
hash,
175-
ASSET_HUB_PARA_ID,
176-
);
230+
const inclusionResult = await getInclusionBlockNumber(this.api, rcApi, hash, ASSET_HUB_PARA_ID);
177231

178232
if (!inclusionResult.found || inclusionResult.inclusionBlockNumber === null) {
179233
// Inclusion not found within search depth
180234
// Fall back to relay parent number for calculation
181-
return this.performCalculation(vestingArray, new BN(inclusionResult.relayParentNumber), 'relay');
235+
return this.performCalculation(vestingArray, vestingLocked, new BN(inclusionResult.relayParentNumber), 'relay');
182236
}
183237

184-
return this.performCalculation(vestingArray, new BN(inclusionResult.inclusionBlockNumber), 'relay');
238+
return this.performCalculation(vestingArray, vestingLocked, new BN(inclusionResult.inclusionBlockNumber), 'relay');
185239
}
186240

187241
/**
188-
* Calculate unlockable for relay chains.
242+
* Calculate vested amounts for relay chains.
189243
* Pre-migration: uses the chain's own block number for calculations.
190244
*/
191245
private calculateForRelayChain(
192246
blockNumber: number,
193247
vestingArray: VestingInfo[],
248+
vestingLocked: BN,
194249
): {
195250
schedules: IVestingSchedule[];
196-
totalUnlockable: string;
251+
vestedBalance: string;
252+
vestingTotal: string;
253+
vestedClaimable: string;
197254
blockNumberForCalculation: string;
198255
blockNumberSource: BlockNumberSource;
199256
} | null {
@@ -203,43 +260,46 @@ export class AccountsVestingInfoService extends AbstractService {
203260
if (!assetHubSpec) {
204261
// Not a known relay chain with migration boundaries
205262
// Use single-chain calculation
206-
return this.performCalculation(vestingArray, new BN(blockNumber), 'self');
263+
return this.performCalculation(vestingArray, vestingLocked, new BN(blockNumber), 'self');
207264
}
208265

209266
const boundaries = MIGRATION_BOUNDARIES[assetHubSpec];
210267

211268
if (!boundaries) {
212269
// No boundaries defined, use single-chain calculation
213-
return this.performCalculation(vestingArray, new BN(blockNumber), 'self');
270+
return this.performCalculation(vestingArray, vestingLocked, new BN(blockNumber), 'self');
214271
}
215272

216273
const migrationState = this.getRelayChainMigrationState(blockNumber, boundaries);
217274

218275
if (migrationState === 'during-migration') {
219-
// During migration window, return 0 for unlockable
220-
return this.createZeroUnlockableResult(vestingArray, blockNumber.toString(), 'self');
276+
// During migration window, return 0 for claimable
277+
return this.createZeroClaimableResult(vestingArray, vestingLocked, blockNumber.toString(), 'self');
221278
}
222279

223280
if (migrationState === 'post-migration') {
224281
// Post-migration: vesting no longer exists on relay chain
225-
// Return 0 for unlockable since vesting has migrated
226-
return this.createZeroUnlockableResult(vestingArray, blockNumber.toString(), 'self');
282+
// Return 0 for claimable since vesting has migrated
283+
return this.createZeroClaimableResult(vestingArray, vestingLocked, blockNumber.toString(), 'self');
227284
}
228285

229286
// Pre-migration: use relay chain's own block number
230-
return this.performCalculation(vestingArray, new BN(blockNumber), 'self');
287+
return this.performCalculation(vestingArray, vestingLocked, new BN(blockNumber), 'self');
231288
}
232289

233290
/**
234-
* Perform the actual unlockable calculation for vesting schedules.
291+
* Perform the actual vesting calculation for vesting schedules.
235292
*/
236293
private performCalculation(
237294
vestingArray: VestingInfo[],
295+
vestingLocked: BN,
238296
currentBlock: BN,
239297
source: BlockNumberSource,
240298
): {
241299
schedules: IVestingSchedule[];
242-
totalUnlockable: string;
300+
vestedBalance: string;
301+
vestingTotal: string;
302+
vestedClaimable: string;
243303
blockNumberForCalculation: string;
244304
blockNumberSource: BlockNumberSource;
245305
} {
@@ -250,48 +310,67 @@ export class AccountsVestingInfoService extends AbstractService {
250310
startingBlock: new BN(v.startingBlock.toString()),
251311
}));
252312

253-
// Calculate unlockable for each schedule
313+
// Calculate vested for each schedule
254314
const schedules: IVestingSchedule[] = vestingArray.map((v, idx) => ({
255315
locked: v.locked.toString(),
256316
perBlock: v.perBlock.toString(),
257317
startingBlock: v.startingBlock.toString(),
258-
unlockable: calculateUnlockable(currentBlock, calcSchedules[idx]).toString(),
318+
vested: calculateVested(currentBlock, calcSchedules[idx]).toString(),
259319
}));
260320

261-
const totalUnlockable = calculateTotalUnlockable(currentBlock, calcSchedules);
321+
// Calculate aggregate values
322+
const vestedBalance = calculateTotalVested(currentBlock, calcSchedules);
323+
const vestingTotal = calculateVestingTotal(calcSchedules);
324+
const vestedClaimable = calculateVestedClaimable(vestingLocked, vestingTotal, vestedBalance);
262325

263326
return {
264327
schedules,
265-
totalUnlockable: totalUnlockable.toString(),
328+
vestedBalance: vestedBalance.toString(),
329+
vestingTotal: vestingTotal.toString(),
330+
vestedClaimable: vestedClaimable.toString(),
266331
blockNumberForCalculation: currentBlock.toString(),
267332
blockNumberSource: source,
268333
};
269334
}
270335

271336
/**
272-
* Create a result with zero unlockable for all schedules.
337+
* Create a result with zero claimable for all schedules.
273338
* Used during migration windows or post-migration on relay chain.
274339
*/
275-
private createZeroUnlockableResult(
340+
private createZeroClaimableResult(
276341
vestingArray: VestingInfo[],
342+
_vestingLocked: BN,
277343
blockNumber: string,
278344
source: BlockNumberSource,
279345
): {
280346
schedules: IVestingSchedule[];
281-
totalUnlockable: string;
347+
vestedBalance: string;
348+
vestingTotal: string;
349+
vestedClaimable: string;
282350
blockNumberForCalculation: string;
283351
blockNumberSource: BlockNumberSource;
284352
} {
353+
// Convert VestingInfo to calculation interface
354+
const calcSchedules: IVestingCalcSchedule[] = vestingArray.map((v) => ({
355+
locked: new BN(v.locked.toString()),
356+
perBlock: new BN(v.perBlock.toString()),
357+
startingBlock: new BN(v.startingBlock.toString()),
358+
}));
359+
360+
const vestingTotal = calculateVestingTotal(calcSchedules);
361+
285362
const schedules: IVestingSchedule[] = vestingArray.map((v) => ({
286363
locked: v.locked.toString(),
287364
perBlock: v.perBlock.toString(),
288365
startingBlock: v.startingBlock.toString(),
289-
unlockable: '0',
366+
vested: '0',
290367
}));
291368

292369
return {
293370
schedules,
294-
totalUnlockable: '0',
371+
vestedBalance: '0',
372+
vestingTotal: vestingTotal.toString(),
373+
vestedClaimable: '0',
295374
blockNumberForCalculation: blockNumber,
296375
blockNumberSource: source,
297376
};

0 commit comments

Comments
 (0)