Platform: Code4rena
Start Date: 14/06/2022
Pot Size: $100,000 USDC
Total HM: 26
Participants: 59
Period: 7 days
Judge: GalloDaSballo
Total Solo HM: 9
Id: 133
League: ETH
Rank: 3/59
Findings: 14
Award: $7,689.41
🌟 Selected for report: 3
🚀 Solo Findings: 6
🌟 Selected for report: Soosh
Also found by: 0x1f8b, Ruhum, TerrierLover, WatchPug, cccz, csanuragjain, hake, p4st13r4, zzzitron
782.2807 CANTO - $126.34
126.3383 USDC - $126.34
Anyone can call execute()
and pass in a malicious proposal.
There is no access control for the execute()
function.
Manual Review.
Implement access control to execute()
.
#0 - nivasan1
2022-06-22T20:11:00Z
Duplicate of #26
#1 - GalloDaSballo
2022-08-10T22:18:40Z
Marking as dup of 26 although this report could be argued to not be relevant
320.9326 USDC - $320.93
1987.1989 CANTO - $320.93
getWETHAddress()
still relies on same Comp hard coded address.
Unless WETH address is deployed to an address identical to Comps original address the grantCompinternal()
function wont work or in a pessimistic scenario an attacker might deploy a malicious token to that address and steal users funds.
As stated in the README.md file the changes made to Comptroller.sol
were made "to allow for the granting of any generic ERC-20 token as a reward instead of Comp token"
However, the modification in the grantCompInternal()
function does not remove the reference to Comp()
, it simply renames the parameters while still using the same Comp address at getWETHAddress()
.
The functionality has not changed at all, the only change made was the renaming from COMP to WETH.
Here is a link to the original Comptroller.sol
where the address getCompAddress()
is the same as getWETHAddress()
.
Additionally, the hard coded address in getWETHAddress()
doesn't allow "for the granting of any generic ERC-20 token as a reward" only for WETH.
Manual Review.
As discussed with sponsor I recommend promptly changing the hard coded address. This seems like something really easy to overlook that could lead up to a really costly mistake.
#0 - ecmendenhall
2022-06-21T21:54:46Z
#1 - nivasan1
2022-06-22T20:41:07Z
duplicate of #46
247.5766 USDC - $247.58
1532.982 CANTO - $247.58
Proposals might be falsely deemed as executed when in fact they have not been executed yet.
The queue()
function sets newProposal.executed = true
even though that is not technically true.
For the proposal to be executed it needs to be called via the execute()
function.
Manual Review.
Do not automatically set proposal state to executed when queueing as it doesnt reflect actual state of events.
#0 - nivasan1
2022-06-24T03:21:45Z
duplicate of #39
#1 - GalloDaSballo
2022-08-10T23:45:07Z
While submissions lacks some details, it does get duped up with #39
🌟 Selected for report: hake
978.304 USDC - $978.30
6057.6098 CANTO - $978.30
state()
function cannot view the state from any proposal except for the latest one.
require(proposalCount >= proposalId && proposalId > initialProposalId, "GovernorBravo::state: invalid proposal id");
Currently proposalCount
needs to be bigger or equal to proposalId
.
Assuming proposalId
is incremented linearly in conjunction with proposalCount
, this implies only the most recent proposalId
will pass the require()
check above. All other proposals will not be able to have their states checked via this function.
Manual Review.
Change above function to proposalCount <= proposalId
(assuming proposalId
is set linearly, which currently is not enforced by code).
#0 - GalloDaSballo
2022-08-04T19:30:23Z
The warden has shown how, due to a mistake in logic, only the state
of the latest proposal can be read.
Because the function state
is used in execute
we can conclude that only one proposal can be queue for execution at a time, drastically reducing the availability of the Governor.
For this reason I believe medium severity is appropriate
🌟 Selected for report: hake
6057.6098 CANTO - $978.30
978.304 USDC - $978.30
state()
function cannot be called to view proposal state if proposalId == 0
.
There is no check to prevent queueing a proposalId
with a value of 0 via the queue()
function.
However, in the state()
function there is a check preventing using a proposalId == 0
.
For clarity: initialProposalId
must be zero according to _initiate()
, therefore, proposalId
cannot be 0 according to check below.
function state(uint proposalId) public view returns (ProposalState) { require(proposalCount >= proposalId && proposalId > initialProposalId, "GovernorBravo::state: invalid proposal id");
Manual Review
Implement check to preventing queueing a proposalId == 0
.
#0 - nivasan1
2022-07-21T22:24:15Z
The ProposalId cannot be 0 as the proposal IDs are fixed and will be set via the cosmos-sdk.
#1 - GalloDaSballo
2022-08-04T19:39:12Z
The warden has shown how, through a misconfiguration, a proposal could never be executable due to a revert in state()
While I believe the warden has already shown a remediation that would cover this scenario, I believe the Warden has shown a unique possible situation that can cause the system to stop working as intended.
While the sponsor says the proposalId will never be 0, there is no way to avoid that at the Smart Contract level, meaning that any caller can set the proposal to 0.
For these reasons, I think Medium Severity to be appropriate
🌟 Selected for report: hake
978.304 USDC - $978.30
6057.6098 CANTO - $978.30
Admin can _grantComp()
to any address using any amount and drain the contract.
If admin key gets compromised there is no timelock, no amount boundaries and no address limitations to prevent the assets to be drained immediately to the attackers address.
Manual Review.
There is a few suggestions that could help mitigate this issue:
Implement timelock for _grantComp()
Implement hard coded recipient so funds cannot be arbitrarily sent to any address.
Implement a limit to the amount that can be granted.
Here is a reference to a past submission where this issue has been made by team Watchpug: https://github.com/code-423n4/2022-01-insure-findings/issues/271
#0 - nivasan1
2022-06-23T02:21:29Z
We acknowledge that this is an issue, however we feel that changing the core functionality of compound would be too costly.
#1 - GalloDaSballo
2022-08-10T21:19:30Z
The warden has shown how the Admin could sweep the reward token(in this case WETH) to any address, at any time, for an amount equal to all tokens available to the Comptroller.
Because this is contingent on admin privilege, I think Medium Severity to be more appropriate
🌟 Selected for report: joestakey
Also found by: 0x1f8b, 0x29A, 0x52, 0xDjango, 0xNazgul, 0xf15ers, 0xmint, Bronicle, Dravee, Funen, JMukesh, Limbooo, MadWookie, Picodes, Ruhum, TerrierLover, TomJ, Tutturu, WatchPug, Waze, _Adam, asutorufos, c3phas, catchup, cccz, codexploder, cryptphi, csanuragjain, defsec, fatherOfBlocks, gzeon, hake, hansfriese, hyh, ignacio, k, nxrblsrpr, oyc_109, robee, sach1r0, saian, simon135, technicallyty, zzzitron
687.9945 CANTO - $111.11
215.0341 USDC - $215.03
Using a floating pragma (^) might result in the contract being deployed with a version it was not tested with and might result in bugs that affect the contract system negatively.
Locking the pragma (deleting the ^) helps to ensure that contracts do not accidentally get deployed using an outdated compiler version or a version it was not tested with.
For example testing Comptroller.sol with 0.8.14, but deploying it 0.8.11 as BaseV1-core.sol uses this version
https://github.com/Plex-Engineer/lending-market/blob/755424c1f9ab3f9f0408443e6606f94e4f08a990/contracts/Comptroller.sol#L1394 https://github.com/Plex-Engineer/stableswap/blob/489d010eb99a0885139b2d5ed5a2d826838cc5f9/contracts/BaseV1-core.sol#L2
Code not used from original BaseV1-core.sol
should be removed instead of commented out for better clarity.
https://github.com/Plex-Engineer/stableswap/blob/489d010eb99a0885139b2d5ed5a2d826838cc5f9/contracts/BaseV1-core.sol#L362
It is conventional to capitalise constants. Some constants do not follow this convention. https://github.com/Plex-Engineer/lending-market/blob/755424c1f9ab3f9f0408443e6606f94e4f08a990/contracts/Governance/GovernorBravoDelegate.sol#L9-L12
faialure -> failure. https://github.com/Plex-Engineer/stableswap/blob/489d010eb99a0885139b2d5ed5a2d826838cc5f9/contracts/BaseV1-periphery.sol#L463
Please be more clear with error message linked below. https://github.com/Plex-Engineer/stableswap/blob/489d010eb99a0885139b2d5ed5a2d826838cc5f9/contracts/BaseV1-periphery.sol#L466
Copied from the repo: "the modification of Comptroller to allow for the granting of any generic ERC-20 token as a reward instead of Comp token"
However, as confirmed by sponsor Comptroller.sol
does not intend to be able to grant "any generic ERC-20 token as a reward", only WETH.
#0 - GalloDaSballo
2022-08-02T20:36:18Z
NC
R
R
NC
NC
NC
2R 4NC
🌟 Selected for report: _Adam
Also found by: 0v3rf10w, 0x1f8b, 0x29A, 0xKitsune, 0xNazgul, 0xf15ers, 0xkatana, 0xmint, Chom, Dravee, Fitraldys, Funen, JC, Limbooo, MadWookie, Picodes, Ruhum, TerrierLover, TomJ, Tomio, Waze, ak1, c3phas, catchup, defsec, fatherOfBlocks, gzeon, hake, hansfriese, joestakey, k, oyc_109, rfa, robee, sach1r0, saian, simon135, ynnad
39.6748 USDC - $39.67
396.9199 CANTO - $64.10
for
loop gas optimisationfor (uint i = 0; i < routes.length; i++) { address pair = pairFor(routes[i].from, routes[i].to, routes[i].stable); if (IBaseV1Factory(factory).isPair(pair)) { amounts[i+1] = IBaseV1Pair(pair).getAmountOut(amounts[i], routes[i].from); } }
Gas could be saved by:
Example:
uint length = routes.length; for (uint i; i < length;) { address pair = pairFor(routes[i].from, routes[i].to, routes[i].stable); if (IBaseV1Factory(factory).isPair(pair)) { amounts[i+1] = IBaseV1Pair(pair).getAmountOut(amounts[i], routes[i].from); } unchecked { ++i; } }
Parameters are always initialed to zero as their default value, therefore assigning them a value of zero is a waist of gas.
https://github.com/Plex-Engineer/lending-market/blob/755424c1f9ab3f9f0408443e6606f94e4f08a990/contracts/Governance/GovernorBravoDelegate.sol#L62 https://github.com/Plex-Engineer/lending-market/blob/755424c1f9ab3f9f0408443e6606f94e4f08a990/contracts/Governance/GovernorBravoDelegate.sol#L57
#0 - GalloDaSballo
2022-08-04T00:27:20Z
Less than 100 gas saved