This issue involves the LendingPool contract incorrectly using msg.sender instead of its own address (address(this)) as the owner parameter when calling the vault’s withdrawal function. Since the vault recognizes address(this) as the entity that holds the shares (due to the deposit transaction), passing msg.sender violates the expected ownership logic. As a result, the vault either reverts—if it strictly checks ownership—or silently allows withdrawals under incorrect assumptions, potentially compromising the security model of the protocol.
Within the LendingPool contract, the _withdrawFromVault function incorrectly passes msg.sender rather than address(this) as the owner parameter when invoking the vault’s withdraw function. During the deposit phase, address(this) is designated as the share owner. Consequently, calling withdraw with msg.sender causes the vault to reject the transaction if it strictly verifies share ownership. This inconsistency interrupts the expected rebalancing and withdrawal process. If the vault does not validate ownership thoroughly, there is a risk that unauthorized addresses could withdraw shares, compromising the protocol's security model.
If the vault enforces correct share ownership, any attempt to withdraw using msg.sender instead of address(this) will fail, preventing the LendingPool from accessing necessary liquidity and breaking the rebalancing mechanism. In cases where the vault does not strictly check the owner parameter, an unauthorized address could potentially withdraw shares, compromising the protocol’s security and allowing unintended funds transfer.
The _withdrawFromVault function currently calls:
However, the typical curveVault.withdraw(...) signature expects two critical parameters:
recipient: the address that will receive the withdrawn tokens.
owner: the address holding the shares in the vault, thus authorized to initiate the withdrawal.
When depositing, the contract registers address(this) (the LendingPool contract itself) as the owner of the shares, because the code calls curveVault.deposit(amount, address(this)).
Therefore, when withdrawing, the owner should also be address(this).
In the current code, msg.sender is incorrectly used as the owner. This inconsistency can cause the vault to reject the transaction or, if it unexpectedly accepts it, compromise the security assumption that only address(this) (the legitimate depositor) can withdraw those funds.
Below is a simplified excerpt highlighting the deposit and withdrawal logic to the vault (_depositIntoVault and _withdrawFromVault) with the finding marked by (!) for clarity:
In _depositIntoVault, the LendingPool deposits funds, thereby becoming the owner of those shares in the vault (curveVault).
In _withdrawFromVault, the withdrawal must be initiated by the same entity that owns those shares. If msg.sender is used, the vault will not recognize the caller as the legitimate owner.
This flaw can cause the withdraw(...) call to fail if the vault strictly checks share ownership. Even if it does not fail due to some vault-specific quirk, it breaks the security assumption that only address(this) can manage the deposited funds.
A user triggers a function (e.g., _rebalanceLiquidity()) that internally attempts to withdraw funds by calling _withdrawFromVault.
The internal call uses curveVault.withdraw(amount, address(this), msg.sender, 0, new address[](0));.
The vault detects that msg.sender is not the actual owner of the shares, causing the transaction to fail.
Result: Liquidity becomes unavailable, and the rebalancing mechanism breaks.
In a rarer situation, if the vault does not validate ownership properly, it could introduce inconsistencies in share accounting.
The test confirms that when the LendingPool tries to withdraw from the MockVault, it incorrectly uses msg.sender instead of address(this) as the owner parameter. Consequently, the vault reverts with "WrongOwner()". This validates the logic flaw: only the entity that deposited (i.e., address(this)) can withdraw those shares, ensuring consistent ownership.
MockVault is a simplified contract that mimics a typical vault’s deposit and withdrawal mechanisms. It assigns "shares" upon deposit and expects the owner parameter in the withdraw function to match address(this). If a different address attempts to withdraw, it fails with "WrongOwner()". This setup allows to isolate and confirm the incorrect owner parameter usage in the LendingPool.
TestLendingPool inherits from the actual LendingPool and exposes the normally internal functions (_depositIntoVault and _withdrawFromVault) as public (testDepositIntoVault and testWithdrawFromVault). This lets the test directly call those methods and verify that using msg.sender instead of address(this) triggers the expected error in the MockVault.
Add the following Mock to contracts/mocks/MockVault.sol
Add the following Mock to contracts/mocks/TestLendingPool.sol
Add the following test to test/unit/core/pools/LendingPool/LendingPool.test.js
This test conclusively demonstrates that the withdrawal mechanism fails if the vault expects address(this) as the owner, yet the LendingPool passes msg.sender instead. The vault reverts with "WrongOwner()", confirming the misalignment in ownership parameters and validating the logical error in _withdrawFromVault.
Manual Code Review was the primary approach, focusing on the vault interaction code paths and parameter usage during internal deposit and withdrawal processes.
Ensuring the LendingPool contract always uses its own address (address(this)) instead of msg.sender when withdrawing from the vault will resolve the ownership discrepancy.
Remove the call that passes msg.sender as the owner.
Add address(this) as the correct owner parameter to maintain consistent ownership logic with the deposit flow.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.