In the current implementation, depositTokenFlashloanFeeAmount is not excluded when calculating excess depositToken. Therefore, the stream creator can call recoverTokens(depositToken, recipient) and retrieve depositTokenFlashloanFeeAmount if there are any.
As a result:
When the protocol governance calls claimFees() and claim accumulated depositTokenFlashloanFeeAmount, it may fail due to insufficient balance of depositToken.
Or, part of users' funds (depositToken) will be transferred to the protocol governance as fees, causing some users unable to withdraw or can only withdraw part of their deposits.
PoC
Given:
feeEnabled: true
feePercent: 10 (0.1%)
Alice deposited 1,000,000 depositToken;
Bob called flashloan() and borrowed 1,000,000 depositToken, then repaid 1,001,000;
Charlie deposited 1,000 depositToken;
After endDepositLock, Alice called claimDepositTokens() and withdrawn 1,000,000 depositToken;
streamCreator called recoverTokens(depositToken, recipient) and retrieved 1,000 depositToken (2,000 - (1,001,000 - 1,000,000));
governance called claimFees() and retrieved another 1,000 depositToken;
Charlie tries to claimDepositTokens() but since the current balanceOf depositToken is 0, the transcation always fails, and Charlie loses all the depositToken.
Handle
WatchPug
Vulnerability details
https://github.com/code-423n4/2021-11-streaming/blob/56d81204a00fc949d29ddd277169690318b36821/Streaming/src/Locke.sol#L654-L654
In the current implementation,
depositTokenFlashloanFeeAmount
is not excluded when calculatingexcess
depositToken. Therefore, the stream creator can callrecoverTokens(depositToken, recipient)
and retrievedepositTokenFlashloanFeeAmount
if there are any.As a result:
governance
callsclaimFees()
and claim accumulateddepositTokenFlashloanFeeAmount
, it may fail due to insufficient balance of depositToken.governance
as fees, causing some users unable to withdraw or can only withdraw part of their deposits.PoC
Given:
feeEnabled
: truefeePercent
: 10 (0.1%)1,000,000
depositToken;flashloan()
and borrowed1,000,000
depositToken, then repaid1,001,000
;1,000
depositToken;endDepositLock
, Alice calledclaimDepositTokens()
and withdrawn1,000,000
depositToken;streamCreator
calledrecoverTokens(depositToken, recipient)
and retrieved1,000
depositToken(2,000 - (1,001,000 - 1,000,000))
;governance
calledclaimFees()
and retrieved another1,000
depositToken;claimDepositTokens()
but since the current balanceOf depositToken is0
, the transcation always fails, and Charlie loses all the depositToken.Recommendation
Change to: