Skip to content

Commit e74f2e5

Browse files
committed
fix(rosetta): use faster balance calculation for chain tip queries
1 parent 32f879f commit e74f2e5

File tree

1 file changed

+38
-20
lines changed

1 file changed

+38
-20
lines changed

src/api/routes/rosetta/account.ts

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,15 @@ export function createRosettaAccountRouter(db: PgStore, chainId: ChainID): expre
3737
.sqlTransaction(async sql => {
3838
let blockQuery: FoundOrNot<DbBlock>;
3939
let blockHash: string = '0x';
40+
let atChainTip: boolean = false;
4041
// we need to return the block height/hash in the response, so we
4142
// need to fetch the block first.
4243
if (
4344
(!blockIdentifier?.hash && !blockIdentifier?.index) ||
4445
(blockIdentifier && blockIdentifier.index <= 0)
4546
) {
4647
blockQuery = await db.getCurrentBlock();
48+
atChainTip = true;
4749
} else if (blockIdentifier.index > 0) {
4850
blockQuery = await db.getBlock({ height: blockIdentifier.index });
4951
} else if (blockIdentifier.hash !== undefined) {
@@ -66,12 +68,32 @@ export function createRosettaAccountRouter(db: PgStore, chainId: ChainID): expre
6668
throw RosettaErrors[RosettaErrorsTypes.invalidBlockHash];
6769
}
6870

69-
const stxBalance = await db.getStxBalanceAtBlock(
70-
accountIdentifier.address,
71-
block.block_height
72-
);
73-
// return spendable balance (liquid) if no sub-account is specified
74-
let balance = (stxBalance.balance - stxBalance.locked).toString();
71+
let balance = 0n;
72+
let locked = 0n;
73+
74+
// Fetch chain tip balance from pre-computed table when possible.
75+
if (atChainTip) {
76+
const stxBalancesResult = await db.v2.getStxHolderBalance({
77+
sql,
78+
stxAddress: accountIdentifier.address,
79+
});
80+
balance = stxBalancesResult.found ? stxBalancesResult.result.balance : 0n;
81+
const stxPoxLockedResult = await db.v2.getStxPoxLockedAtBlock({
82+
sql,
83+
stxAddress: accountIdentifier.address,
84+
blockHeight: block.block_height,
85+
burnBlockHeight: block.burn_block_height,
86+
});
87+
balance = balance - stxPoxLockedResult.locked;
88+
locked = stxPoxLockedResult.locked;
89+
} else {
90+
const stxBalance = await db.getStxBalanceAtBlock(
91+
accountIdentifier.address,
92+
block.block_height
93+
);
94+
balance = stxBalance.balance - stxBalance.locked;
95+
locked = stxBalance.locked;
96+
}
7597

7698
const accountNonceQuery = await db.getAddressNonceAtBlock({
7799
stxAddress: accountIdentifier.address,
@@ -86,12 +108,10 @@ export function createRosettaAccountRouter(db: PgStore, chainId: ChainID): expre
86108
if (subAccountIdentifier !== undefined) {
87109
switch (subAccountIdentifier.address) {
88110
case RosettaConstants.StackedBalance:
89-
const lockedBalance = stxBalance.locked;
90-
balance = lockedBalance.toString();
111+
balance = locked;
91112
break;
92113
case RosettaConstants.SpendableBalance:
93-
const spendableBalance = stxBalance.balance - stxBalance.locked;
94-
balance = spendableBalance.toString();
114+
balance = balance - locked;
95115
break;
96116
case RosettaConstants.VestingLockedBalance:
97117
case RosettaConstants.VestingUnlockedBalance:
@@ -101,11 +121,10 @@ export function createRosettaAccountRouter(db: PgStore, chainId: ChainID): expre
101121
);
102122
if (stxVesting.found) {
103123
const vestingInfo = getVestingInfo(stxVesting.result);
104-
balance = vestingInfo[subAccountIdentifier.address].toString();
105124
extra_metadata[RosettaConstants.VestingSchedule] =
106125
vestingInfo[RosettaConstants.VestingSchedule];
107126
} else {
108-
balance = '0';
127+
balance = 0n;
109128
}
110129
break;
111130
default:
@@ -114,7 +133,7 @@ export function createRosettaAccountRouter(db: PgStore, chainId: ChainID): expre
114133
}
115134
const balances: RosettaAmount[] = [
116135
{
117-
value: balance,
136+
value: balance.toString(),
118137
currency: {
119138
symbol: RosettaConstants.symbol,
120139
decimals: RosettaConstants.decimals,
@@ -166,16 +185,15 @@ export function createRosettaAccountRouter(db: PgStore, chainId: ChainID): expre
166185
return router;
167186
}
168187

169-
function getVestingInfo(info: AddressTokenOfferingLocked): { [key: string]: string | string[] } {
170-
const vestingData: { [key: string]: string | string[] } = {};
188+
function getVestingInfo(info: AddressTokenOfferingLocked) {
171189
const jsonVestingSchedule: string[] = [];
172190
info.unlock_schedule.forEach(schedule => {
173191
const item = { amount: schedule.amount, unlock_height: schedule.block_height };
174192
jsonVestingSchedule.push(JSON.stringify(item));
175193
});
176-
177-
vestingData[RosettaConstants.VestingLockedBalance] = info.total_locked;
178-
vestingData[RosettaConstants.VestingUnlockedBalance] = info.total_unlocked;
179-
vestingData[RosettaConstants.VestingSchedule] = jsonVestingSchedule;
180-
return vestingData;
194+
return {
195+
[RosettaConstants.VestingLockedBalance]: info.total_locked,
196+
[RosettaConstants.VestingUnlockedBalance]: info.total_unlocked,
197+
[RosettaConstants.VestingSchedule]: jsonVestingSchedule,
198+
};
181199
}

0 commit comments

Comments
 (0)