Assessment reports>EtherFi>Threat Model>batchPartialWithdraw

Function: batchPartialWithdraw(uint256[] _validatorIds)

Calls partialWithdraw for multiple validator IDs.

Inputs

  • _validatorIds

    • Control: Fully controlled.

    • Constraints: No specific constraints.

    • Impact: Validator IDs for which partial withdrawals need to be executed.

Branches and code coverage

Intended branches

  • Calls this.partialWithdraw(_validatorIds[i]) for each validator ID.

  • The partialWithdraw function updates the EtherFiNode for the validator ID.

  • The partialWithdraw function calls claimQueuedWithdrawals to claim queued withdrawals from EigenPod.

  • Distributes the payouts to all the entities.

Negative behavior

  • Revert if numExitRequestsByTnft for any etherFiNode is nonzero.

  • Revert if balance of any etherFiNode is less than 16 Ether.

Function call analysis

  • this.partialWithdraw(_validatorIds[i]) -> this._updateEtherFiNode(_validatorId) -> IEtherFiNode(etherfiNode).version()

    • What is controllable? Not controllable.

    • If the return value is controllable, how is it used and how can it go wrong? Retrieves the version of the EtherFiNode related to the given validator.

    • What happens if it reverts, reenters, or does other unusual control flow? If this call reverts, it might indicate an issue with the EtherFiNode or an unexpected state.

  • this.partialWithdraw(_validatorIds[i]) -> this._updateEtherFiNode(_validatorId) -> IEtherFiNode(etherfiNode).DEPRECATED_exitRequestTimestamp()

    • What is controllable? Not controllable.

    • If the return value is controllable, how is it used and how can it go wrong? Retrieves the deprecated exit-request timestamp.

    • What happens if it reverts, reenters, or does other unusual control flow? N/A.

  • this.partialWithdraw(_validatorIds[i]) -> this._updateEtherFiNode(_validatorId) -> IEtherFiNode(etherfiNode).DEPRECATED_exitTimestamp()

    • What is controllable? Not controllable.

    • If the return value is controllable, how is it used and how can it go wrong? Retrieves the deprecated exit timestamp.

    • What happens if it reverts, reenters, or does other unusual control flow? N/A

  • this.partialWithdraw(_validatorIds[i]) -> this._updateEtherFiNode(_validatorId) -> IEtherFiNode(etherfiNode).DEPRECATED_phase()

    • What is controllable? Not controllable.

    • If the return value is controllable, how is it used and how can it go wrong? Retrieves the deprecated phase.

    • What happens if it reverts, reenters, or does other unusual control flow? N/A.

  • this.partialWithdraw(_validatorIds[i]) -> this._updateEtherFiNode(_validatorId) -> IEtherFiNode(etherfiNode).migrateVersion(_validatorId)

    • What is controllable? _validatorId.

    • If the return value is controllable, how is it used and how can it go wrong? Migrates the version related to the given validator.

    • What happens if it reverts, reenters, or does other unusual control flow? If this call reverts, it might indicate an issue with migrating the version — no reentrancy scenario.

  • this.partialWithdraw(_validatorIds[i]) -> IEtherFiNode(etherfiNode).claimQueuedWithdrawals(this.maxEigenlayerWithdrawals, False)

    • What is controllable? Not controllable.

    • If the return value is controllable, how is it used and how can it go wrong? Claims queued withdrawals in the EtherFiNode related to the given validator.

    • What happens if it reverts, reenters, or does other unusual control flow? If this call reverts, it might indicate an issue with claiming withdrawals — no reentrancy scenario.

  • this.partialWithdraw(_validatorIds[i]) -> this. _getTotalRewardsPayoutsFromSafe(_validatorId, True) -> IEtherFiNode(etherfiNode).numExitRequestsByTnft()

    • What is controllable? Not controllable.

    • If the return value is controllable, how is it used and how can it go wrong? Retrieves the number of exit requests by TNFT in the EtherFiNode related to the given validator.

    • What happens if it reverts, reenters, or does other unusual control flow? N/A.

  • this.partialWithdraw(_validatorIds[i]) -> this. _getTotalRewardsPayoutsFromSafe(_validatorId, True) -> IEtherFiNode(etherfiNode).getRewardsPayouts(this.validatorInfos[_validatorId] .exitRequestTimestamp, this.stakingRewardsSplit)

    • What is controllable? Not controllable.

    • If the return value is controllable, how is it used and how can it go wrong? Retrieves rewards payouts from the EtherFiNode related to the given validator and returns the split between different entities.

    • What happens if it reverts, reenters, or does other unusual control flow? N/A.

  • this.partialWithdraw(_validatorIds[i]) -> this._distributePayouts(etherfiNode, _validatorId, toTreasury, toOperator, toTnft, toBnft) -> IEtherFiNode(_etherfiNode).withdrawFunds(this.treasuryContract, _toTreasury, this.auctionManager.getBidOwner(_validatorId), _toOperator, this.tnft.ownerOf(_validatorId), _toTnft, this.bnft.ownerOf(_validatorId), _toBnft)

    • What is controllable? Not controllable.

    • If the return value is controllable, how is it used and how can it go wrong? Withdraws funds from the EtherFiNode related to the given validator and distributes them.

    • What happens if it reverts, reenters, or does other unusual control flow? If this call reverts, it might indicate an issue with withdrawing funds; the checks-effects-interactions pattern is followed.

  • this.partialWithdraw(_validatorIds[i]) -> this._distributePayouts(etherfiNode, _validatorId, toTreasury, toOperator, toTnft, toBnft) -> this.auctionManager.getBidOwner(_validatorId)

    • What is controllable? _validatorId.

    • If the return value is controllable, how is it used and how can it go wrong? Retrieves the bid owner from the auction manager related to the given validator.

    • What happens if it reverts, reenters, or does other unusual control flow? N/A.

  • this.partialWithdraw(_validatorIds[i]) -> this._distributePayouts(etherfiNode, _validatorId, toTreasury, toOperator, toTnft, toBnft) -> this.tnft.ownerOf(_validatorId)

    • What is controllable? _validatorId.

    • If the return value is controllable, how is it used and how can it go wrong? Retrieves the owner of the TNFT related to the given validator.

    • What happens if it reverts, reenters, or does other unusual control flow? N/A.

  • this.partialWithdraw(_validatorIds[i]) -> this._distributePayouts(etherfiNode, _validatorId, toTreasury, toOperator, toTnft, toBnft) -> this.bnft.ownerOf(_validatorId)

    • What is controllable? _validatorId.

    • If the return value is controllable, how is it used and how can it go wrong? Retrieves the owner of the BNFT related to the given validator.

    • What happens if it reverts, reenters, or does other unusual control flow? N/A.

Zellic © 2025Back to top ↑