Platform: Code4rena
Start Date: 07/01/2022
Pot Size: $80,000 USDC
Total HM: 21
Participants: 37
Period: 7 days
Judge: 0xean
Total Solo HM: 14
Id: 71
League: ETH
Rank: 17/37
Findings: 7
Award: $896.73
🌟 Selected for report: 0
🚀 Solo Findings: 0
692.4614 INSURE - $242.36
420.423 USDC - $420.42
Ruhum
The Vault.withdrawRedundant()
allows the owner to withdraw funds that are not accounted for. The function has a check that is supposed to stop the owner from withdrawing funds of the vault's underlying token that the vault "knows" about.
But, there's an edge case where the owner is able to take out the vault's whole balance of the underlying token.
Since the owner is able to simply send the tokens back, it's not that big of a deal. But, I think that's behavior that is unintended so I still wanna raise the issue here.
https://github.com/code-423n4/2022-01-insure/blob/main/contracts/Vault.sol#L461
function withdrawRedundant(address _token, address _to) external override onlyOwner { if ( _token == address(token) && balance < IERC20(token).balanceOf(address(this)) ) { uint256 _redundant = IERC20(token).balanceOf(address(this)) - balance; IERC20(token).safeTransfer(_to, _redundant); } else if (IERC20(_token).balanceOf(address(this)) > 0) { IERC20(_token).safeTransfer( _to, IERC20(_token).balanceOf(address(this)) ); } }
If _token == address(token)
and balance == IERC20(token).balanceOf(address(this)
the first if statement is false
. Because it checks whether the balance is <
. So if balance == IERC20(token).balanceOf(address(this)
, which should be default state of the vault, the else if
block is executed. There, the whole balance is sent to _to
.
none
Add another if clause like this:
if ( _token == address(token) && balance == IERC20(token).balanceOf(address(this)) ) { // do nothing }
#0 - oishun1112
2022-01-19T06:34:05Z
🌟 Selected for report: WatchPug
Also found by: Dravee, Ruhum, cmichel, pmerkleplant
149.5717 INSURE - $52.35
90.8114 USDC - $90.81
Ruhum
Some tokens charge a fee for each transfer. USDT, for example, has the possibility of enabling fees at any time. If the vault is used for that kind of token, the internal balance keeping will be wrong. The vault will think that it owns more tokens than it actually does.
The vault would allow users to withdraw, in total, more funds than it actually owns. Meaning at some point someone will lose tokens as there is not enough to cover their share.
state var: https://github.com/code-423n4/2022-01-insure/blob/main/contracts/Vault.sol#L35
Vault doesn't check the number of tokens it actually received from the transfer: https://github.com/code-423n4/2022-01-insure/blob/main/contracts/Vault.sol#L136-L137
https://github.com/code-423n4/2022-01-insure/blob/main/contracts/Vault.sol#L432
https://github.com/code-423n4/2022-01-insure/blob/main/contracts/Vault.sol#L107
It's always an issue when the vault receives funds. If it sends them somewhere else the bookkeeping is correct.
none
One possibility would be to ditch the internal bookkeeping and simply use the balance from the token contract.
#0 - oishun1112
2022-01-19T06:26:18Z
https://github.com/code-423n4/2022-01-insure-findings/issues/236
I was of this idea, but changed my mind by "USDT, for example, has the possibility of enabling fees at any time." token itself might change its behavior. We need to be tolerant of this.
86.5379 INSURE - $30.29
52.5409 USDC - $52.54
Ruhum
Because of the block gas limit, looping over a dynamic array that grows over time might result in a DoS at some point.
Both the PoolTemplate
and the IndexTemplate
have such dynamic arrays. Both don't have any functionality to decrease the size. Meaning, if the array grows too big at some point there's no way to fix the issue. Parts of the protocol simply become unusable. Each function that loops over the array might at some point use more gas than is possible.
The size of both arrays is controlled by the owner of the contracts. Meaning, the user can't force a DoS here. But, there's nothing stopping the owner from increasing the size to a point where a DoS happens.
Here's the SWC entry: https://swcregistry.io/docs/SWC-128
IndexPool:
PoolTemplate:
none
Implement logic that allows breaking up the loop into multiple transactions. Or, avoid looping over a dynamic array at all.
#0 - oishun1112
2022-01-19T06:59:28Z
#1 - oishun1112
2022-01-19T07:00:54Z
6.1284 INSURE - $2.14
3.2174 USDC - $3.22
Ruhum
There's an unused import here: https://github.com/code-423n4/2022-01-insure/blob/main/contracts/Factory.sol#L14
#0 - oishun1112
2022-01-17T07:34:27Z
2.9784 INSURE - $1.04
1.5637 USDC - $1.56
Ruhum
Use != 0
instead of >0
when working with uints. That's a little cheaper. There are multiple spots in the codebase where that can be applied. Just search for > 0
#0 - oishun1112
2022-01-17T07:34:47Z
#1 - 0xean
2022-01-28T00:09:50Z
dupe of #36