-
Notifications
You must be signed in to change notification settings - Fork 3
Description
Description
The current vote weight calculation for Subnet 77 is not correctly aggregating a user's total stake (alphaBalance). When a user has staked their TAO to multiple validators (hotkeys) on Subnet 77 from a single coldkey, the voting system only considers the stake from one of those validators, not the sum of all of them. This leads to an inaccurate and significantly reduced voting power for users who diversify their stake.
Steps to Reproduce
- Use a single coldkey to stake TAO on two or more different validators within Subnet 77.
- Use the
scripts/vote.tsscript to cast a vote for a liquidity pool. - Observe the
alphaBalanceandweightMultipliercalculated for your vote by inspecting the output of thescripts/pools.tsscript. - The
alphaBalancewill only reflect the stake on one of the validators, not the total stake from the coldkey.
Example:
- A user's coldkey has 1000 ALPHA staked on Validator A and 988 ALPHA staked on Validator B.
- The user votes.
- The system calculates their
alphaBalanceas 988 ALPHA, ignoring the 1000 ALPHA on Validator A. - The expected
alphaBalanceshould be 1988 ALPHA.
Root Cause Analysis
The issue lies within the backend service responsible for processing votes, likely at the /updateVotes endpoint.
The client-side scripts (vote.ts, pools.ts) and the validator logic (validator/index.ts) are working as expected by consuming the data provided by the API; the flaw is in the API's data calculation itself.
Proposed Solution
The backend logic for calculating a voter's alphaBalance needs to be updated to ensure it aggregates stake from all delegations.
- Query All Delegations: When a vote is received from a coldkey, the backend must query the metagraph to get a list of all delegations made by that coldkey on Subnet 77.
- Iterate and Sum: The logic should iterate through each delegation (to each hotkey) and sum the staked amounts.
- Calculate Total Stake: The sum of these stakes is the correct, total
alphaBalancefor the voter. - Apply Weight: This aggregated total stake should then be used to calculate the voter's final
weightMultiplier.
Example Logic
// ...
// Track alpha balances by subnet
const subnetAlphaBalances: Record<number, SubnetAlphaBalance> = {};
for await (const hotkey of hotkeys) {
const res = await api.query.subtensorModule.alpha.entries(
hotkey,
currentAccount.address,
);
for await (const [key, value] of res) {
const netUid = Number(key.toHuman()[2]);
const alpha_share = parseFixedU128(value.toJSON().bits);
const total_hotkey_alpha = (
await api.query.subtensorModule.totalHotkeyAlpha(hotkey, netUid)
).toJSON();
const total_hotkey_shares_raw = (
await api.query.subtensorModule.totalHotkeyShares(hotkey, netUid)
).toJSON();
const total_hotkey_shares = parseFixedU128(
total_hotkey_shares_raw.bits,
);
// This is the core calculation for a single staking position
const alpha =
(alpha_share * total_hotkey_alpha) /
total_hotkey_shares /
1000000000; // Divided by UNIT constant
const tao_in = (
await api.query.subtensorModule.subnetTAO(netUid)
).toString();
const alpha_in = (
await api.query.subtensorModule.subnetAlphaIn(netUid)
).toString();
const price = netUid === 0 ? 1 : Number(tao_in) / Number(alpha_in);
const tao = alpha * price;
if (netUid === 0) {
balanceStakedToRoot += tao;
} else {
totalAlphaBalance += alpha;
totalAlphaTaoValue += tao;
// This part initializes and aggregates the balance for the specific subnet
if (!subnetAlphaBalances[netUid]) {
const subnet = getSubnetById(netUid);
const subnetName = subnet?.name || `Subnet ${netUid}`;
const subnetSymbol = subnet?.symbol || "";
subnetAlphaBalances[netUid] = {
netUid,
name: subnetName,
symbol: subnetSymbol,
alpha: 0,
taoValue: 0,
};
}
// Here is the summation across different hotkeys for the same subnet
subnetAlphaBalances[netUid].alpha += alpha;
subnetAlphaBalances[netUid].taoValue += tao;
}
totalStakedBalance += tao;
}
}
// ...