MEDIUM severity: Curators will lose reward for an epoch if they lose ownership of vault after epoch but before distribution. **Description:** The `Rewards` contract calculates and assigns curator shares for each epoch by calling `VaultTokenized(vault).owner()` to determine the curator (vault owner) who should receive rewards. However, this fetches the current owner of the vault at the time of reward distribution, not...

Vulnerable code:
// SPDX-License-Identifier: BUSL-1.1
// SPDX-FileCopyrightText: Copyright 2024 ADDPHO
pragma solidity 0.8.25;

interface IVaultTokenized {
    function collateral() external view returns (address);
    function delegator() external view returns (address);
    function activeBalanceOfAt(address, uint48, bytes calldata) external view returns (uint256);
    function activeSharesOfAt(address, uint48, bytes calldata) external view returns (uint256);
    function activeSharesAt(uint48, bytes calldata) external view returns (uint256);
    function owner() external view returns (address);
}

contract MockVault is IVaultTokenized {
    address private _collateral;
    address private _delegator;
    address private _owner;
    mapping(address => uint256) public activeBalance;
    mapping(uint48 => uint256) private _totalActiveShares;

    constructor(address collateralAddress, address delegatorAddress, address owner_) {
        _collateral = collateralAddress;
        _delegator = delegatorAddress;
        _owner = owner_;
    }

    function collateral() external view override returns (address) {
        return _collateral;
    }

    function delegator() external view override returns (address) {
        return _delegator;
    }

    function activeBalanceOfAt(address account, uint48, bytes calldata) public view override returns (uint256) {
        return activeBalance[account];
    }

    function setActiveBalance(address account, uint256 balance) public {
        activeBalance[account] = balance;
    }

    function owner() public view override returns (address) {
        return _owner;
    }

    function activeSharesOfAt(address account, uint48, bytes calldata) public view override returns (uint256) {
        return 100;
    }

    function activeSharesAt(uint48 timestamp, bytes calldata) public view override returns (uint256) {
        uint256 totalShares = _totalActiveShares[timestamp];
        return totalShares > 0 ? totalShares : 200; // Default to 200 if not explicitly set
    }

    function setTotalActiveShares(uint48 timestamp, uint256 totalShares) public {
        _totalActiveShares[timestamp] = totalShares;
    }

    // Added function to transfer ownership
    function transferOwnership(address newOwner) public {
        require(msg.sender == _owner, "Only owner can transfer ownership");
        require(newOwner != address(0), "New owner cannot be zero address");
        _owner = newOwner;
    }
}

function test_curatorSharesAssignedToCurrentOwnerInsteadOfHistorical() public {
	uint48 epoch = 1;
	uint256 uptime = 4 hours;

	// Step 1: Setup stakes for epoch 1 to generate curator shares
	_setupStakes(epoch, uptime);

	// Step 2: Get a vault and its initial owner (Owner A)
	address vault = vaultManager.vaults(0);
	address ownerA = MockVault(vault).owner();
	address ownerB = makeAddr("OwnerB");

	// Step 3: Warp to after epoch 1 ends
	uint256 epoch2Start = middleware.getEpochStartTs(epoch + 1);
	vm.warp(epoch2Start);

	// Step 4: Transfer ownership from Owner A to Owner B
	vm.prank(ownerA);
	MockVault(vault).transferOwnership(ownerB);

	// Step 5: Warp to a time when rewards can be distributed (after 2 epochs)
	uint256 distributionTime = middleware.getEpochStartTs(epoch + 3);
	vm.warp(distributionTime);

	// Step 6: Distribute rewards for epoch 1
	address[] memory operators = middleware.getAllOperators();
	vm.prank(REWARDS_DISTRIBUTOR_ROLE);
	rewards.distributeRewards(epoch, uint48(operators.length));

	// Step 7: Verify curator shares assignment
	uint256 curatorShareA = rewards.curatorShares(epoch, ownerA);
	uint256 curatorShareB = rewards.curatorShares(epoch, ownerB);

	assertEq(curatorShareA, 0, "Owner A should have no curator shares for epoch 1");
	assertGt(curatorShareB, 0, "Owner B should have curator shares for epoch 1");

	// Step 8: Owner B claims curator rewards
	vm.prank(ownerB);
	rewards.claimCuratorFee(address(rewardsToken), ownerB);
	uint256 ownerBBalance = rewardsToken.balanceOf(ownerB);
	assertGt(ownerBBalance, 0, "Owner B should have received curator rewards");

	// Step 9: Owner A attempts to claim and reverts
	vm.prank(ownerA);
	vm.expectRevert(abi.encodeWithSelector(IRewards.NoRewardsToClaim.selector, ownerA));
	rewards.claimCuratorFee(address(rewardsToken), ownerA);
}