Skip to content

Incorrect ALPHA balance calculations #2

@cisterciansis

Description

@cisterciansis

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

  1. Use a single coldkey to stake TAO on two or more different validators within Subnet 77.
  2. Use the scripts/vote.ts script to cast a vote for a liquidity pool.
  3. Observe the alphaBalance and weightMultiplier calculated for your vote by inspecting the output of the scripts/pools.ts script.
  4. The alphaBalance will 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 alphaBalance as 988 ALPHA, ignoring the 1000 ALPHA on Validator A.
  • The expected alphaBalance should 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.

  1. 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.
  2. Iterate and Sum: The logic should iterate through each delegation (to each hotkey) and sum the staked amounts.
  3. Calculate Total Stake: The sum of these stakes is the correct, total alphaBalance for the voter.
  4. 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;
          }
        }
// ...

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions