LOW severity: `totalMPAccrued` accrual should start from first stake. **Description:** The StakeManager contract initializes `lastMPUpdatedTime` to `block.timestamp` during deployment. This timestamp is used to calculate MP accrual periods in the `StakeManager::_totalMP` function. However, when the first user stakes, `lastMPUpdatedTime` is not updated to the current.....

Vulnerable code:
function stake(
        uint256 amount,
        uint256 lockPeriod,
        uint256 currentLockUntil
    )
        external
        onlyNotEmergencyMode
        whenNotPaused
        onlyTrustedCodehash
        onlyRegisteredVault
        returns (uint256 newLockUntil)
    {
        if (amount == 0) {
            revert StakeManager__AmountCannotBeZero();
        }

@>      _updateGlobalState();
        _updateVault(msg.sender, true);
        ...
    }

    function _updateGlobalState() internal virtual {
@>      _updateGlobalMP();
        _updateRewardIndex();
    }

    function _updateGlobalMP() internal {
        uint256 newTotalMPAccrued = _totalMP();
@>      if (newTotalMPAccrued > totalMPAccrued) {
            totalMPAccrued = newTotalMPAccrued;
            lastMPUpdatedTime = block.timestamp;
        }
    }

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

import { StakeManagerTest } from "./StakeManagerBase.t.sol";
import { MultiplierPointMath } from "../../src/math/MultiplierPointMath.sol";

contract MPAccrualAfterDelayTest is StakeManagerTest {
    function setUp() public virtual override {
        super.setUp();
    }

    /**
     *
     * Scenario:
     * 1. Contract is initialized at T0, lastMPUpdatedTime = T0
     * 2. 1 year passes (no one stakes)
     * 3. At T1 (T0 + 1 year) Alice makes the first stake
     * 4. During first stake, lastMPUpdatedTime does NOT update to T1 (remains T0)
     *    Reason: totalMaxMP == 0, so _totalMP() returns totalMPAccrued (0)
     *            and condition (newTotalMPAccrued > totalMPAccrued) is false
     * 5. Another 1 year passes
     * 6. At T2 (T0 + 2 years) update is called
     * 7. MP accrues for period (T2 - T0) = 2 years instead of (T2 - T1) = 1 year
     */
    function test_CRITICAL_MPAccrualIncludesTimeBeforeFirstStake() public {
        uint256 stakeAmount = 1000e18;
        uint256 YEAR = 365 days;

        uint256 initTime = block.timestamp;

        vm.warp(block.timestamp + YEAR);
        uint256 firstStakeTime = block.timestamp;

        _stake(alice, stakeAmount, 0);

        // lastMPUpdatedTime did not update to first stake time
        assertEq(
            streamer.lastMPUpdatedTime(),
            initTime
        );

        uint256 expectedInitialMP = stakeAmount;

        vm.warp(block.timestamp + YEAR);
        uint256 updateTime = block.timestamp;

        _updateVault(alice);

        // Calculate expected MP
        uint256 mpFor1Year = MultiplierPointMath._accrueMP(stakeAmount, YEAR);
        uint256 expectedTotalMP = expectedInitialMP + mpFor1Year;

        // Calculate actual MP
        uint256 mpFor2Years = MultiplierPointMath._accrueMP(stakeAmount, updateTime - initTime);
        uint256 wrongTotalMP = expectedInitialMP + mpFor2Years;

        // MP accrued for 2 years instead of 1 year
        assertEq(
            streamer.totalMPAccrued(),
            wrongTotalMP,
            "BUG: MP accrued for 2 years (since init) instead of 1 year (since first stake)"
        );

        emit log_named_uint("Expected MP after 1 year", expectedTotalMP);
        emit log_named_uint("Actual MP (wrong)", wrongTotalMP);
        emit log_named_uint("Overcount (MP for extra year)", wrongTotalMP - expectedTotalMP);
        emit log_named_uint("Time since init (2 years)", updateTime - initTime);
        emit log_named_uint("Time since first stake (1 year)", updateTime - firstStakeTime);
    }
}

function _updateGlobalMP() internal {
        uint256 newTotalMPAccrued = _totalMP();
+       lastMPUpdatedTime = block.timestamp;
        if (newTotalMPAccrued > totalMPAccrued) {
            totalMPAccrued = newTotalMPAccrued;
-           lastMPUpdatedTime = block.timestamp;
        }
    }