Platform: Code4rena
Start Date: 23/05/2022
Pot Size: $50,000 USDC
Total HM: 44
Participants: 99
Period: 5 days
Judge: hickuphh3
Total Solo HM: 11
Id: 129
League: ETH
Rank: 21/99
Findings: 4
Award: $600.18
🌟 Selected for report: 1
🚀 Solo Findings: 0
🌟 Selected for report: xiaoming90
Also found by: 0xNoah, PP1004, hubble, pauliax, reassor, sashik_eth, shenwilly, sseefried
https://github.com/code-423n4/2022-05-rubicon/blob/8c312a63a91193c6a192a9aab44ff980fbfd7741/contracts/rubiconPools/BathToken.sol#L80 https://github.com/code-423n4/2022-05-rubicon/blob/8c312a63a91193c6a192a9aab44ff980fbfd7741/contracts/rubiconPools/BathToken.sol#L642
rewardsVestingWallet (IBathBuddy) helps in releasing or distributing vested bonus tokens during withdrawals. There is currently no function to set it in BathToken contract. rewardsVestingWallet (IBathBuddy) remains as address(0) and is unusable.
Admin can not add BathBuddy. User can not get bonus token incentives.
Manual review
Add the following function to BathToken contract
/// @notice Admin-only function to set a Bath Token's rewardsVestingWallet function setRewardsVestingWallet(address rewardsVestingWallet_) external onlyBathHouse { require(rewardsVestingWallet_ != address(0), "invalid_addr"); rewardsVestingWallet = rewardsVestingWallet_; }
Accordingly, add a new function to BathHouse also.
END
#0 - bghughes
2022-06-03T22:08:19Z
Duplicate of #168
401.6035 USDC - $401.60
https://github.com/code-423n4/2022-05-rubicon/blob/8c312a63a91193c6a192a9aab44ff980fbfd7741/contracts/rubiconPools/BathToken.sol#L270 https://github.com/code-423n4/2022-05-rubicon/blob/8c312a63a91193c6a192a9aab44ff980fbfd7741/contracts/rubiconPools/BathToken.sol#L629 https://github.com/code-423n4/2022-05-rubicon/blob/8c312a63a91193c6a192a9aab44ff980fbfd7741/contracts/peripheral_contracts/BathBuddy.sol#L98-L101
The function setBonusToken allows the same BonusToken to be added more than once to the array bonusTokens.
function setBonusToken(address newBonusERC20) external onlyBathHouse { bonusTokens.push(newBonusERC20); }
If that happens, early withdrawers can get Bonus in multiples of what they actually have right to. Late withdrawers, might not get any Bonus due to shortage.
BathToken.sol, function setBonusToken https://github.com/code-423n4/2022-05-rubicon/blob/8c312a63a91193c6a192a9aab44ff980fbfd7741/contracts/rubiconPools/BathToken.sol#L270-L272
BathToken.sol, function distributeBonusTokenRewards https://github.com/code-423n4/2022-05-rubicon/blob/8c312a63a91193c6a192a9aab44ff980fbfd7741/contracts/rubiconPools/BathToken.sol#L629 2. a. As and when distributeBonusTokenRewards is triggered during a withdraw call, the same bonusToken will be released more than once.
BathBuddy.sol, function release https://github.com/code-423n4/2022-05-rubicon/blob/8c312a63a91193c6a192a9aab44ff980fbfd7741/contracts/peripheral_contracts/BathBuddy.sol#L98-L101 2. b. The release function is called.
Manual review
Add the required validations to avoid duplicate additions of bonus tokens.
function setBonusToken(address newBonusERC20) external onlyBathHouse { require(newBonusERC20 != address(0), "invalid_addr"); if (bonusTokens.length > 0) { for (uint256 index = 0; index < bonusTokens.length; index++) { require (token != newBonusERC20, "token already exists") } } bonusTokens.push(newBonusERC20); }
#0 - HickupHH3
2022-06-23T16:10:20Z
Similar, but different to unbounded tokens case because it's about duplicates.
🌟 Selected for report: Ruhum
Also found by: 0x1f8b, 0x4non, 0xDjango, Dravee, GimelSec, StErMi, berndartmueller, blackscale, catchup, cccz, csanuragjain, defsec, eccentricexit, ellahi, horsefacts, hubble, joestakey, kenzo, pedroais, peritoflores, reassor, rotcivegaf, sashik_eth, shenwilly, sseefried, throttle, xiaoming90
1.8534 USDC - $1.85
https://github.com/code-423n4/2022-05-rubicon/blob/8c312a63a91193c6a192a9aab44ff980fbfd7741/contracts/rubiconPools/BathHouse.sol#L279 https://github.com/code-423n4/2022-05-rubicon/blob/8c312a63a91193c6a192a9aab44ff980fbfd7741/contracts/rubiconPools/BathHouse.sol#L310
Contract BathHouse.sol
function setCancelTimeDelay(uint256 value) function setReserveRatio(uint256 rr) function setBathTokenFeeBPS(address bathToken, uint256 newBPS) function setBathTokenFeeTo(address bathToken, address feeTo)
Contract BathToken.sol
function setFeeBPS(uint256 _feeBPS)
These functions that make critical changes should have a timelock, so that users can see the upcoming changes and have time to react to these changes. Another thing to note is that there is no input validation like lower/upper bound check, address check, etc., currently in these functions.
Stable and trustworthy protocol.
https://consensys.net/diligence/audits/2020/12/1inch-liquidity-protocol/#unpredictable-behavior-for-users-due-to-admin-front-running-or-general-bad-timing https://github.com/code-423n4/2022-01-sandclock-findings/issues/178
Add timelock for critical changes which will effect user experience and decisions.
#0 - bghughes
2022-06-03T21:12:00Z
Duplicate of #349 #344
#1 - HickupHH3
2022-06-23T15:51:09Z
Generic issue about limits and timelock. Grouping it with #21.
🌟 Selected for report: Ruhum
Also found by: 0x1f8b, 0x4non, 0xDjango, Dravee, GimelSec, StErMi, berndartmueller, blackscale, catchup, cccz, csanuragjain, defsec, eccentricexit, ellahi, horsefacts, hubble, joestakey, kenzo, pedroais, peritoflores, reassor, rotcivegaf, sashik_eth, shenwilly, sseefried, throttle, xiaoming90
1.8534 USDC - $1.85
Judge has assessed an item in Issue #447 as Medium risk. The relevant finding follows:
#0 - HickupHH3
2022-06-27T13:37:43Z
L-02: Missing basic validation for parameter _feeBPS (BathToken.sol, function setFeeBPS) dup of #21
🌟 Selected for report: IllIllI
Also found by: 0x1337, 0x1f8b, 0x4non, 0xDjango, 0xKitsune, 0xNazgul, 0xf15ers, ACai, AlleyCat, Bahurum, BouSalman, CertoraInc, Chom, Dravee, ElKu, FSchmoede, Funen, GimelSec, Hawkeye, JC, JMukesh, Kaiziron, MaratCerby, Metatron, PP1004, Picodes, Ruhum, SmartSek, StErMi, TerrierLover, UVvirus, UnusualTurtle, WatchPug, Waze, _Adam, asutorufos, berndartmueller, blackscale, blockdev, broccolirob, c3phas, catchup, cryptphi, csanuragjain, defsec, delfin454000, dipp, eccentricexit, ellahi, fatherOfBlocks, gzeon, hansfriese, horsefacts, hubble, ilan, joestakey, kebabsec, minhquanym, oyc_109, parashar, pauliax, rotcivegaf, sach1r0, sashik_eth, shenwilly, simon135, sorrynotsorry, sseefried, throttle, unforgiven, xiaoming90
54.4382 USDC - $54.44
withdraw(uint256 _shares)
is confusing with EIP-4626 standard function withdraw(uint256 assets, address receiver, address owner)
(BathToken.sol)Contract : BathToken.sol Function : initialize
Either,
Contract : BathToken.sol Function : setFeeBPS
Add the following validation, require(_feeBPS <= 5, "")
As per the whitepaper, to protect Rubicon Pools LPs and for security of the protocol, some amount of pool liquidity should be present in the contract at all times. So, allowing to set the ReserveRation to very close to 0 should be disallowed. A check for a min value like MIN_RR = 20 is recommened to be added.
reserveRatio can be set to 1, which may drain the liquidity from the pool.
Contract : BathHouse.sol Function : setReserveRatio()
define a constant and add a require statement in setReserveRatio() like below
MIN_ReserveRatio = 20 require(rr > 0);
As per the whitepaper, related to FeeTo destination address. "These fees go to the pool, so it is set to the BathToken contract address for a given pool." But there is no check in the setFeeTo() function if the _feeTo address is contract pool address.
Malicious or by error, the FeeTo can be changed to any external address, so LP's will be losing the rewards.
Contract : BathToken.sol
function setFeeTo(address _feeTo) external onlyBathHouse { feeTo = _feeTo; }
Once the feeTo is set during initialization, it should not be configurable.
Compiler warning
Contract : RubiconMarket.sol https://github.com/code-423n4/2022-05-rubicon/blob/8c312a63a91193c6a192a9aab44ff980fbfd7741/contracts/RubiconMarket.sol#L1262-L1270
Remove from interface definition from here, and import from interfaces/IWETH.sol
withdraw(uint256 _shares)
is confusing with EIP-4626 standard function withdraw(uint256 assets, address receiver, address owner)
(BathToken.sol)Confusion
EIP-4626 standard function withdraw(uint256 assets, address receiver, address owner)
expects assets as its first input parameter
Function name withdraw(uint256 _shares)
expects shares as input parameter
Contract : BathToken.sol https://github.com/code-423n4/2022-05-rubicon/blob/8c312a63a91193c6a192a9aab44ff980fbfd7741/contracts/rubiconPools/BathToken.sol#L375
Rename the function withdraw(uint256 _shares)
to
Custom defined TransparentUpgradeableProxy contract is not used anywhere, instead OZ's TransparentUpgradeableProxy is used in BathHouse.sol
Contract : BathHouse.sol https://github.com/code-423n4/2022-05-rubicon/blob/8c312a63a91193c6a192a9aab44ff980fbfd7741/contracts/rubiconPools/BathHouse.sol#L14