hats-finance / ether-fi-0x36c3b77853dec9c4a237a692623293223d4b9bc4

Smart Contracts for Ether Fi dapp
1 stars 1 forks source link

`LiquidityPool.batchRegisterAsBnftHolder` function doesn't check if the caller is a registered BNFT holder #47

Open hats-bug-reporter[bot] opened 11 months ago

hats-bug-reporter[bot] commented 11 months ago

Github username: -- Submission hash (on-chain): 0x9fe23589dd836660bbf8dfa5fa57a050ce038d0196420d793aa59ca9c99aaa00 Severity: low

Description: Description\

    function batchRegisterAsBnftHolder(
        bytes32 _depositRoot,
        uint256[] calldata _validatorIds,
        IStakingManager.DepositData[] calldata _registerValidatorDepositData,
        bytes32[] calldata _depositDataRootApproval,
        bytes[] calldata _signaturesForApprovalDeposit
    ) external whenNotPaused {
        require(_validatorIds.length == _registerValidatorDepositData.length && _validatorIds.length == _depositDataRootApproval.length && _validatorIds.length == _signaturesForApprovalDeposit.length, "lengths differ");

        stakingManager.batchRegisterValidators(_depositRoot, _validatorIds, msg.sender, address(this), _registerValidatorDepositData, msg.sender);

        //For each validator, we need to store the deposit data root of the 31 ETH transaction so it is accessible in the approve function
        for(uint256 i; i < _validatorIds.length; i++) {
            depositDataRootForApprovalDeposits[_validatorIds[i]] = _depositDataRootApproval[i];
            emit ValidatorRegistered(_validatorIds[i], _signaturesForApprovalDeposit[i], _registerValidatorDepositData[i].publicKey, _depositDataRootApproval[i]);
        }
    }

Attack Scenario

Attachments

Revised Code

  1. Update LiquidityPool.batchRegisterAsBnftHolder function to check if the caller is a registered BNFT holder:

    function batchRegisterAsBnftHolder(
         bytes32 _depositRoot,
         uint256[] calldata _validatorIds,
         IStakingManager.DepositData[] calldata _registerValidatorDepositData,
         bytes32[] calldata _depositDataRootApproval,
         bytes[] calldata _signaturesForApprovalDeposit
    ) external whenNotPaused {
         require(_validatorIds.length == _registerValidatorDepositData.length && _validatorIds.length == _depositDataRootApproval.length && _validatorIds.length == _signaturesForApprovalDeposit.length, "lengths differ");
    
    +       require(bnftHoldersIndexes[msg.sender].registered, "Not registered");
         stakingManager.batchRegisterValidators(_depositRoot, _validatorIds, msg.sender, address(this), _registerValidatorDepositData, msg.sender);
    
         //For each validator, we need to store the deposit data root of the 31 ETH transaction so it is accessible in the approve function
         for(uint256 i; i < _validatorIds.length; i++) {
               depositDataRootForApprovalDeposits[_validatorIds[i]] = _depositDataRootApproval[i];
               emit ValidatorRegistered(_validatorIds[i], _signaturesForApprovalDeposit[i], _registerValidatorDepositData[i].publicKey, _depositDataRootApproval[i]);
         }
    }
  2. Add a mechanism to enable deregisterd BFT holders from claiming their deposited ethers if they haven't registered any validators.

seongyun-ko commented 11 months ago

Thanks for brining this up! Have you had any chance to test it? I think it will be reverted within the call stakingManager.batchRegisterValidators.