Platform: Code4rena
Start Date: 26/07/2022
Pot Size: $75,000 USDC
Total HM: 29
Participants: 179
Period: 6 days
Judge: LSDan
Total Solo HM: 6
Id: 148
League: ETH
Rank: 25/179
Findings: 5
Award: $483.43
🌟 Selected for report: 0
🚀 Solo Findings: 0
26.7695 USDC - $26.77
An attacker can multiply his voting power for a single tokenId by 499 times by self-delegating. (can at least multiple by 499 times (https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/vote-escrow/VoteEscrowDelegation.sol#L99) )
VoteEscrow.create_lock_for()
and gets a tokenId
VoteEscrowDelegation.delegate(tokenId, tokenId)
3. https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/vote-escrow/VoteEscrowDelegation.sol#L71VoteEscrowDelegation.delegate(tokenId, tokenId)
for same tokenId. This time we have a checkpoint for this tokenId and the if statement is true
checkpoint.delegatedTokenIds
arraygetVotes()
call would call uint256[] memory delegated = _getCurrentDelegated(tokenId);
and get the checkpoints[tokenId][nCheckpoints - 1].delegatedTokenIds
that hold our same tokenId multiple times
votes = votes + this.balanceOfNFT(delegated[index]);
whereas delegated[index]
is always the same tokenId => this is the same as votes = this.balanceOfNFT(tokenId) * delegated.length
(whereas delegated.length
is max 499) => attacker can multiple his own voting power by 499 times votes = this.balanceOfNFT(tokenId) * 499
VSC
#0 - KenzoAgada
2022-08-02T12:01:05Z
Duplicate of #169
131.6127 USDC - $131.61
After reaching a totalSupply of over 1 Billion $GOLOM tokens, the platform fees (0.5% of the trade amount in ETH) can not be redeemed by the stakers anymore and are locked in the RewardDistributor contract.
fillAsk()
/ fillBid()
/ fillCriteriaBid()
from the GolomTrader contract will trigger the distributor.addFee()
that sends the 0.5% protocol fees to the RewardDistributor contract
if
statement of the addFee()
in the RewardDistributor function if (rewardToken.totalSupply() > 1000000000 * 10**18) {
is true and therefore returns directly (no increase in epoch and no fee is set/added to epochTotalFee[epoch]
VSC
addFee()
function directly swaps the ETH to WETH. This also allows to rescue ETH funds if they are accidentally sent to this contract. ORaddFee()
call from the GolomTrader smart contract if the $GOLOM has reached full supply.#0 - KenzoAgada
2022-08-03T12:14:02Z
Duplicate of #320
🌟 Selected for report: zzzitron
Also found by: GimelSec, GiveMeTestEther, berndartmueller, sseefried
285.3609 USDC - $285.36
https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/governance/GolomToken.sol#L65-L72 https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/core/GolomTrader.sol#L444 https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/rewards/RewardDistributor.sol#L298
Time lock special case: After the <b>time lock address</b> is set back to the <b>0x0</b> address the <b>1 days</b> time lock period doesn't take effect and can be set by the onlyOwner anytime to a different address. There are also no events triggered by time lock functions such that user could listen & react to these events.
Affected contracts.
Example GolomToken.sol:
/// @notice sets the minter with timelock, once setup admin needs to call executeSetMinter() /// @param _minter Address of the new minter function setMinter(address _minter) external onlyOwner { pendingMinter = _minter; minterEnableDate = block.timestamp + 1 days; }
/// @notice Executes the set minter function after the timelock /// @dev If being called first time, there won't be any timelock function executeSetMinter() external onlyOwner { if (minter == address(0)) { minter = pendingMinter; } else { require(minterEnableDate <= block.timestamp, 'GolomToken: wait for timelock'); minter = pendingMinter; } }
VSC
#0 - 0xsaruman
2022-08-20T11:42:19Z
setting to 0x000 will ring alarms and timelock serves the purpose
#1 - 0xsaruman
2022-08-20T12:03:43Z
#2 - dmvt
2022-10-17T11:07:46Z
duplicate of #698
🌟 Selected for report: AuditsAreUS
Also found by: 0xSky, CertoraInc, GimelSec, GiveMeTestEther, Green, Lambda, Ruhum, RustyRabbit, Treasure-Seeker, Twpony, arcoun, bin2chen, cccz, codexploder, cryptonue, dipp, horsefacts, jayphbee, joestakey, minhquanym, obront, peritoflores, rbserver, reassor, rotcivegaf, scaraven, ych18
4.5163 USDC - $4.52
msg.value send to GolomoTrader.sol
by calling fillAsk
can be greater than the necessary amount and get therefore locked in the GolomTrader because it is not accounted for in the payEther()
calls
VSC
>=
use ==
comparison#0 - KenzoAgada
2022-08-04T02:50:28Z
Duplicate of #75
🌟 Selected for report: IllIllI
Also found by: 0x1f8b, 0x4non, 0x52, 0xA5DF, 0xDjango, 0xLovesleep, 0xNazgul, 0xNineDec, 0xSmartContract, 0xackermann, 0xc0ffEE, 0xf15ers, 0xmatt, 0xsanson, 0xsolstars, 8olidity, AuditsAreUS, Bahurum, Bnke0x0, CRYP70, CertoraInc, Ch_301, Chom, CryptoMartian, Deivitto, DevABDee, Dravee, ElKu, Franfran, Funen, GalloDaSballo, GimelSec, GiveMeTestEther, Green, JC, Jmaxmanblue, JohnSmith, Jujic, Junnon, Kenshin, Krow10, Kumpa, Lambda, MEP, Maxime, MiloTruck, Mohandes, NoamYakov, Picodes, RedOneN, Rohan16, Rolezn, Ruhum, RustyRabbit, Sm4rty, Soosh, StErMi, StyxRave, Tadashi, TomJ, Treasure-Seeker, TrungOre, Waze, _Adam, __141345__, ajtra, ak1, apostle0x01, arcoun, asutorufos, async, benbaessler, berndartmueller, bin2chen, brgltd, c3phas, cRat1st0s, carlitox477, chatch, codetilda, codexploder, cryptonue, cryptphi, csanuragjain, cthulhu_cult, delfin454000, dipp, dirk_y, djxploit, ellahi, exd0tpy, fatherOfBlocks, giovannidisiena, hansfriese, horsefacts, hyh, idkwhatimdoing, indijanc, jayfromthe13th, jayphbee, joestakey, kenzo, kyteg, lucacez, luckypanda, mics, minhquanym, obront, oyc_109, pedr02b2, rajatbeladiya, rbserver, reassor, robee, rokinot, rotcivegaf, sach1r0, saian, saneryee, sashik_eth, scaraven, shenwilly, simon135, sseefried, supernova, teddav, ych18, zuhaibmohd, zzzitron
35.1687 USDC - $35.17
RewardDistributor.stakerRewards()
fucntion call will always result in a division by zero error if there is an epoch such that ve.totalSupplyAt(epochBeginTime[index])
is zero (no staked amount).
This also affects RewardDistributor.multiStakerClaim()
in a limited fashion, because a caller can set the epochs
as a functions parameter. But this can therefore also break the assumptions of other protocol integrations that are using this function. (e.g. claim all epoch rewards between the highest epoch plus 1 of the last call to the current (epoch-1)).
RewardDistributor.stakerRewards()
: Iterates from epoch 0 to (epoch-1) and calls ve.totalSupplyAt(epochBeginTime[index])
. If there is at least one epoch w/o any staked amount this will revert with a division by 0
VSC
#0 - 0xsaruman
2022-08-20T11:20:10Z
team will stake initially that means atleast for 4 years this wont be the case
#1 - dmvt
2022-10-18T09:58:39Z
Downgrading to QA. The team needs to deploy the contracts and initialize the system correctly, but as long as they do this problem will be avoided.