HIGH severity: Onchain governance integration breaks due to inconsistent implementation of voting power. **Description:** OpenZeppelin's [GovernorVotesUpgradeable](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/e3ba7f6a236c55e3fb7e569ecd6043b11d567c3d/contracts/governance/extensions/GovernorVotesUpgradeable.sol#L80) uses `getPastVotes` for all voting power calculations:

The...

Vulnerable code:
/**
     * Read the voting weight from the token's built in snapshot mechanism (see {Governor-_getVotes}).
     */
    function _getVotes(
        address account,
        uint256 timepoint,
        bytes memory /*params*/
    ) internal view virtual override returns (uint256) {
        return token().getPastVotes(account, timepoint);
    }

/**
     * @dev Internal vote casting mechanism: Check that the vote is pending, that it has not been cast yet, retrieve
     * voting weight using {IGovernor-getVotes} and call the {_countVote} internal function.
     *
     * Emits a {IGovernor-VoteCast} event.
     */
    function _castVote(
        uint256 proposalId,
        address account,
        uint8 support,
        string memory reason,
        bytes memory params
    ) internal virtual returns (uint256) {
        _validateStateBitmap(proposalId, _encodeStateBitmap(ProposalState.Active));

@>        uint256 totalWeight = _getVotes(account, proposalSnapshot(proposalId), params);
        uint256 votedWeight = _countVote(proposalId, account, support, totalWeight, params);

       // @more code

        return votedWeight;
    }

describe('getVotes vs getPastVotes inconsistency', () => {
    it('should show discrepancy between current and historical voting power', async () => {
      // Setup: Give user1 1 ETH
      expect(await ctx.wlfi.balanceOf(ctx.core.hhUser1)).to.eq(ONE_ETH_BI);

      // User1 has NOT delegated yet (delegates returns address(0))
      expect(await ctx.wlfi.delegates(ctx.core.hhUser1)).to.eq(ADDRESS_ZERO);

      // Current voting power includes auto-delegation (balance added when delegates == address(0))
      expect(await ctx.wlfi.getVotes(ctx.core.hhUser1)).to.eq(ONE_ETH_BI);

      // Mine a block to create a checkpoint
      await mine();
      const checkpointBlock = await time.latestBlock();

      // Historical voting power at that block should be 0 (no delegation checkpoint exists)
      expect(await ctx.wlfi.getPastVotes(ctx.core.hhUser1, checkpointBlock - 1)).to.eq(ZERO_BI);

      // This shows the inconsistency:
      // - getVotes() returns 1 ETH (auto-includes balance)
      // - getPastVotes() returns 0 (no checkpoint)

      // Now test with vesting tokens
      await ctx.registry.connect(ctx.wlfiOwner).agentBulkInsertLegacyUsers(
        ZERO_BI,
        [ctx.core.hhUser1],
        [ONE_ETH_BI],
        [DEFAULT_CATEGORY],
      );

      // Setup vesting templates with immediate unlock portion
      await ctx.vester.connect(ctx.wlfiOwner).ownerSetCategoryTemplate(DEFAULT_CATEGORY, 0, TEMPLATE_1);
      await ctx.vester.connect(ctx.wlfiOwner).ownerSetCategoryTemplate(DEFAULT_CATEGORY, 1, TEMPLATE_2);
      await ctx.vester.connect(ctx.wlfiOwner).ownerSetCategoryEnabled(DEFAULT_CATEGORY, true);

      const signature = await signWlfiActivationMessage(ctx, ctx.core.hhUser1.address);
      await ctx.wlfi.connect(ctx.core.hhUser1).activateAccount(signature.serialized);

      // User1 now has 0 balance (all in vester) but 1 ETH vesting
      expect(await ctx.wlfi.balanceOf(ctx.core.hhUser1)).to.eq(ZERO_BI);
      expect(await ctx.vester.unclaimed(ctx.core.hhUser1)).to.eq(ONE_ETH_BI);

      // Current voting power includes vesting tokens
      expect(await ctx.wlfi.getVotes(ctx.core.hhUser1)).to.eq(ONE_ETH_BI);

      // Mine another block for checkpoint
      await mine();
      const vestingCheckpointBlock = await time.latestBlock();

      // Historical voting power still 0 (vesting not included in checkpoints)
      expect(await ctx.wlfi.getPastVotes(ctx.core.hhUser1, vestingCheckpointBlock - 1)).to.eq(ZERO_BI);

      // Now let's delegate to self to create a checkpoint
      await ctx.wlfi.connect(ctx.core.hhUser1).delegate(ctx.core.hhUser1);

      // Current voting power still includes vesting (but no more auto-balance since delegated)
      expect(await ctx.wlfi.getVotes(ctx.core.hhUser1)).to.eq(ONE_ETH_BI);

      await mine();
      const delegatedCheckpointBlock = await time.latestBlock();

      // Historical voting power after delegation only shows balance (0), not vesting
      expect(await ctx.wlfi.getPastVotes(ctx.core.hhUser1, delegatedCheckpointBlock - 1)).to.eq(ZERO_BI);

      // Move to after start timestamp to allow claiming
      await advanceTimeToAfterStartTimestamp(ctx);

      // Claim the immediate portion (20% of 1 ETH = 0.2 ETH)
      await ctx.wlfi.connect(ctx.core.hhUser1).claimVest();

      const claimedAmount = parseEther('0.2'); // 20% immediate from TEMPLATE_1
      expect(await ctx.wlfi.balanceOf(ctx.core.hhUser1)).to.eq(claimedAmount);

      await mine();
      const finalCheckpointBlock = await time.latestBlock();

      // Now getPastVotes shows the claimed balance (0.2 ETH)
      expect(await ctx.wlfi.getPastVotes(ctx.core.hhUser1, finalCheckpointBlock - 1)).to.eq(claimedAmount);

      // But getVotes shows balance + remaining vesting (0.2 + 0.8 = 1 ETH)
      const currentVotes = await ctx.wlfi.getVotes(ctx.core.hhUser1);
      const remainingVesting = await ctx.vester.unclaimed(ctx.core.hhUser1);

      expect(remainingVesting).to.eq(parseEther('0.8')); // 80% still vesting
      expect(currentVotes).to.eq(ONE_ETH_BI); // 0.2 claimed + 0.8 vesting = 1 ETH

      // Summary of the bug:
      // 1. Before delegation: getVotes returns 1 ETH, getPastVotes returns 0
      // 2. After delegation but before claim: getVotes returns 1 ETH (vesting), getPastVotes returns 0
      // 3. After claiming 0.2 ETH: getVotes returns 1 ETH (0.2 + 0.8 vesting), getPastVotes returns only 0.2 ETH
      // getPastVotes never includes vesting tokens or auto-delegation balance
    });
  });