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: 98/179
Findings: 4
Award: $56.64
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: cloudjunky
Also found by: 0x1f8b, 0x4non, 0x52, 0xDjango, 0xHarry, 0xNazgul, 0xNineDec, 0xf15ers, 0xsanson, 0xsolstars, 8olidity, Bnke0x0, CertoraInc, Chom, Deivitto, Dravee, GalloDaSballo, GimelSec, IllIllI, Jmaxmanblue, JohnSmith, Jujic, Kenshin, Krow10, Lambda, MEP, Noah3o6, RedOneN, Ruhum, StErMi, StyxRave, TomJ, Treasure-Seeker, TrungOre, _Adam, __141345__, arcoun, asutorufos, bardamu, bearonbike, bin2chen, brgltd, bulej93, c3phas, cRat1st0s, carlitox477, cccz, codexploder, cryptonue, cryptphi, cthulhu_cult, dharma09, dipp, djxploit, durianSausage, ellahi, giovannidisiena, hansfriese, horsefacts, hyh, immeas, indijanc, jayjonah8, jayphbee, joestakey, kenzo, kyteg, ladboy233, minhquanym, navinavu, obront, oyc_109, peritoflores, rbserver, reassor, rokinot, rotcivegaf, saian, scaraven, shenwilly, simon135, sseefried, teddav, zzzitron
0.0037 USDC - $0.00
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/core/GolomTrader.sol#L154
Using transfer will forward 2300 gas. A smart contract receiving the ether can run out of gas, which would make the payment impossible. Using call will forward all the gas.
154: payable(payAddress).transfer(payAmt); // royalty transfer to royaltyaddress
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/core/GolomTrader.sol#L154
Replace transfer with call. Note that the return of call needs to be checked.
$ git diff --no-index GolomTrader.sol.orig GolomTrader.sol diff --git a/GolomTrader.sol.orig b/GolomTrader.sol index 70a18ad..d8cd7aa 100644 --- a/GolomTrader.sol.orig +++ b/GolomTrader.sol @@ -151,7 +151,8 @@ contract GolomTrader is Ownable, ReentrancyGuard { function payEther(uint256 payAmt, address payAddress) internal { if (payAmt > 0) { // if royalty has to be paid - payable(payAddress).transfer(payAmt); // royalty transfer to royaltyaddress + (bool sent, ) = payAddress.call{value: payAmt}(""); // royalty transfer to royaltyaddress + require(sent); } }
#0 - KenzoAgada
2022-08-03T14:02:54Z
Duplicate of #343
🌟 Selected for report: TomJ
Also found by: 0x4non, 0x52, 0xDjango, 0xNazgul, 0xf15ers, 0xsanson, 8olidity, Bnke0x0, CertoraInc, Ch_301, Chom, Dravee, GalloDaSballo, GimelSec, JC, Jujic, Kenshin, Kumpa, Lambda, M0ndoHEHE, PaludoX0, RedOneN, Ruhum, Sm4rty, Treasure-Seeker, TrungOre, Twpony, Waze, _Adam, __141345__, apostle0x01, arcoun, benbaessler, bin2chen, brgltd, cccz, cloudjunky, cryptonue, djxploit, ellahi, erictee, hansfriese, i0001, minhquanym, oyc_109, peritoflores, rbserver, reassor, rokinot, rotcivegaf, saian, shenwilly, sseefried
0.1513 USDC - $0.15
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/core/GolomTrader.sol#L236 https://github.com/code-423n4/2022-07-golom/blob/main/contracts/core/GolomTrader.sol#L301 https://github.com/code-423n4/2022-07-golom/blob/main/contracts/core/GolomTrader.sol#L361
OpenZeppelin recommends using safeTransferFrom
instead of transferFrom
.
Using safeTransferFrom
is also recommended since some NFTs have logic in the onERC721Received
function which is only triggered with safeTransferFrom
. Example
transferFrom
.transferFrom
will not check if the contract recipients are aware of the ERC721 protocol to prevent tokens from being locked forever.The instances bellow should make of the safeTransferFrom
from OpenZeppelin instead of transferFrom
.
File: contracts/core/GolomTrader.sol 236: ERC721(o.collection).transferFrom(o.signer, receiver, o.tokenId); 301: nftcontract.transferFrom(msg.sender, o.signer, o.tokenId); 361: nftcontract.transferFrom(msg.sender, o.signer, tokenId);
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/core/GolomTrader.sol
#0 - KenzoAgada
2022-08-03T15:04:40Z
Duplicate of #342
🌟 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
Locking the pragma will make sure that the contract does not get deployed using outdated compiler versions.
File: 2: pragma solidity ^0.8.11;
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/governance/GolomToken.sol
Comments with console.logs should be removed from the codebase.
File: contracts/rewards/RewardDistributor.sol 99: //console.log(block.timestamp,epoch,fee);
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/rewards/RewardDistributor.sol
File: contracts/rewards/RewardDistributor.sol function traderRewards(address addr) public view returns ( uint256 ){ uint256 reward = 0; for (uint256 index = 0; index < epoch; index++) { reward = reward + (rewardTrader[index] * feesTrader[addr][index]) / epochTotalFee[index]; } return (reward); }
Add a spacing before and after the else keyword
File: contracts/rewards/RewardDistributor.sol#L194 194: }else{
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/rewards/RewardDistributor.sol#L194
State variables that are not used inside the contract should be removed.
File: contracts/core/GolomTrader.sol 72: address public governance;
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/core/GolomTrader.sol#L72
File: contracts/core/GolomTrader.sol 279: function fillBid( 312: function cancelOrder(Order calldata o) public nonReentrant { 334: function fillCriteriaBid(
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/core/GolomTrader.sol
File: contracts/rewards/RewardDistributor.sol 98: function addFee(address[2] memory addr, uint256 fee) public onlyTrader { 141: function traderClaim(address addr, uint256[] memory epochs) public { 155: function exchangeClaim(address addr, uint256[] memory epochs) public { 172: function multiStakerClaim(uint256[] memory tokenids, uint256[] memory epochs) public { 215: function stakerRewards(uint256 tokenid) public view returns ( 254: function traderRewards(address addr) public view returns ( 296: function exchangeRewards(address addr) public view returns (
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/rewards/RewardDistributor.sol
🌟 Selected for report: JohnSmith
Also found by: 0x1f8b, 0xA5DF, 0xDjango, 0xKitsune, 0xLovesleep, 0xNazgul, 0xSmartContract, 0xmatt, 0xsam, Aymen0909, Bnke0x0, CRYP70, Chandr, Chinmay, CodingNameKiki, Deivitto, Dravee, ElKu, Fitraldys, Funen, GalloDaSballo, Green, IllIllI, JC, Jmaxmanblue, Junnon, Kaiziron, Kenshin, Krow10, Maxime, Migue, MiloTruck, Noah3o6, NoamYakov, Randyyy, RedOneN, ReyAdmirado, Rohan16, Rolezn, Ruhum, Sm4rty, StyxRave, TomJ, Tomio, _Adam, __141345__, ajtra, ak1, apostle0x01, asutorufos, async, benbaessler, brgltd, c3phas, cRat1st0s, carlitox477, delfin454000, djxploit, durianSausage, ellahi, erictee, fatherOfBlocks, gerdusx, gogo, hyh, jayfromthe13th, jayphbee, joestakey, kaden, kenzo, kyteg, ladboy233, lucacez, m_Rassska, mics, minhquanym, oyc_109, pfapostol, rbserver, reassor, rfa, robee, rokinot, sach1r0, saian, samruna, sashik_eth, simon135, supernova, tofunmi, zuhaibmohd
21.3211 USDC - $21.32
Saves about 5 gas per loop.
File: contracts/core/GolomTrader.soI 415: for (uint256 i = 0; i < proof.length; i++) {
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/core/GolomTrader.sol
File: contracts/rewards/RewardDistributor.sol 143: for (uint256 index = 0; index < epochs.length; index++) { 157: for (uint256 index = 0; index < epochs.length; index++) { 180: for (uint256 tindex = 0; tindex < tokenids.length; tindex++) { 183: for (uint256 index = 0; index < epochs.length; index++) { 226: for (uint256 index = 0; index < epoch; index++) { 258: for (uint256 index = 0; index < epoch; index++) { 273: for (uint256 index = 0; index < epoch; index++) {
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/rewards/RewardDistributor.sol
File: contracts/vote-escrow/TokenUriHelper.sol 138: digits++;
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/vote-escrow/TokenUriHelper.sol
File: contracts/vote-escrow/VoteEscrowDelegation.sol 171: for (uint256 index = 0; index < delegated.length; index++) { 189: for (uint256 index = 0; index < delegatednft.length; index++) { 199: for (uint256 i; i < _array.length; i++) {
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/vote-escrow/VoteEscrowDelegation.sol
The solidity compiler will apply arithmetic checks for the increment step during for loops. This can be disabled since the value being incremented won't surpass the upper bound that's checked on the break condition.
Adding uncheck can save 30-40 gas per loop.
File: contracts/core/GolomTrader.soI 415: for (uint256 i = 0; i < proof.length; i++) {
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/core/GolomTrader.sol
File: contracts/rewards/RewardDistributor.sol 143: for (uint256 index = 0; index < epochs.length; index++) { 157: for (uint256 index = 0; index < epochs.length; index++) { 180: for (uint256 tindex = 0; tindex < tokenids.length; tindex++) { 183: for (uint256 index = 0; index < epochs.length; index++) { 226: for (uint256 index = 0; index < epoch; index++) { 258: for (uint256 index = 0; index < epoch; index++) { 273: for (uint256 index = 0; index < epoch; index++) {
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/rewards/RewardDistributor.sol
File: contracts/vote-escrow/VoteEscrowDelegation.sol 171: for (uint256 index = 0; index < delegated.length; index++) { 189: for (uint256 index = 0; index < delegatednft.length; index++) { 199: for (uint256 i; i < _array.length; i++) {
File: contracts/vote-escrow/VoteEscrowCore.sol 1044: for (uint256 i = 0; i < 128; ++i) { 1115: for (uint256 i = 0; i < 128; ++i) { 1167: for (uint256 i = 0; i < 255; ++i) {
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/vote-escrow/VoteEscrowCore.sol
Instead of computing array.length for every iteration, the value for array.length should be cached before the loop to save gas.
File: contracts/core/GolomTrader.soI 415: for (uint256 i = 0; i < proof.length; i++) {
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/core/GolomTrader.sol
File: contracts/rewards/RewardDistributor.sol 143: for (uint256 index = 0; index < epochs.length; index++) { 157: for (uint256 index = 0; index < epochs.length; index++) { 180: for (uint256 tindex = 0; tindex < tokenids.length; tindex++) { 183: for (uint256 index = 0; index < epochs.length; index++) {
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/rewards/RewardDistributor.sol
File: contracts/vote-escrow/VoteEscrowDelegation.sol 171: for (uint256 index = 0; index < delegated.length; index++) { 189: for (uint256 index = 0; index < delegatednft.length; index++) { 199: for (uint256 i; i < _array.length; i++) {
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/vote-escrow/VoteEscrowDelegation.sol
File: contracts/core/GolomTrader.soI 415: for (uint256 i = 0; i < proof.length; i++) {
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/core/GolomTrader.sol
File: contracts/rewards/RewardDistributor.sol 142: uint256 reward = 0; 143: for (uint256 index = 0; index < epochs.length; index++) { 156: uint256 reward = 0; 157: for (uint256 index = 0; index < epochs.length; index++) { 175: uint256 reward = 0; 176: uint256 rewardEth = 0; 180: for (uint256 tindex = 0; tindex < tokenids.length; tindex++) { 183: for (uint256 index = 0; index < epochs.length; index++) { 222: uint256 reward = 0; 223: uint256 rewardEth = 0; 226: for (uint256 index = 0; index < epoch; index++) { 257: uint256 reward = 0; 258: for (uint256 index = 0; index < epoch; index++) { 272: uint256 reward = 0; 273: for (uint256 index = 0; index < epoch; index++) {
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/rewards/RewardDistributor.sol
File: contracts/vote-escrow/VoteEscrowDelegation.sol 147: uint256 lower = 0; 170: uint256 votes = 0; 171: for (uint256 index = 0; index < delegated.length; index++) { 188: uint256 votes = 0; 189: for (uint256 index = 0; index < delegatednft.length; index++) {
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/vote-escrow/VoteEscrowDelegation.sol
File: contracts/vote-escrow/VoteEscrowCore.sol 735: uint256 block_slope = 0; // dblock/dt 745: for (uint256 i = 0; i < 255; ++i) { 1042: uint256 _min = 0; 1044: for (uint256 i = 0; i < 128; ++i) { 1113: uint256 _min = 0; 1115: for (uint256 i = 0; i < 128; ++i) { 1133: uint256 d_block = 0; 1134: uint256 d_t = 0; 1167: for (uint256 i = 0; i < 255; ++i) { 1211: uint256 dt = 0;
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/vote-escrow/VoteEscrowCore.sol
Custom errors reduce the cost to deploy and call a function.
File: contracts/core/GolomTrader.sol 177: require(signaturesigner == o.signer, 'invalid signature'); 211: require( 217: require(msg.value >= o.totalAmt * amount + p.paymentAmt, 'mgmtm'); 220: require(msg.sender == o.reservedAddress); 222: require(o.orderType == 0, 'invalid orderType'); 226: require(status == 3, 'order not valid'); 227: require(amountRemaining >= amount, 'order already filled'); 235: require(amount == 1, 'only 1 erc721 at 1 time'); 285: require( 291: require(msg.sender == o.reservedAddress); 293: require(o.orderType == 1); 295: require(status == 3); 296: require(amountRemaining >= amount); 299: require(amount == 1, 'only 1 erc721 at 1 time'); 313: require(o.signer == msg.sender); 342: require(o.totalAmt >= o.exchange.paymentAmt + o.prePayment.paymentAmt + o.refererrAmt); 345: require(msg.sender == o.reservedAddress); 347: require(o.orderType == 2); 349: require(status == 3); 350: require(amountRemaining >= amount); 359: require(amount == 1, 'only 1 erc721 at 1 time'); 426: revert('invalid proof'); 455: require(amount == 1, 'only 1 erc721 at 1 time');
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/core/GolomTrader.sol
File: contracts/governance/GolomToken.sol 24: require(msg.sender == minter, 'GolomToken: only reward distributor can enable'); 43: require(!isAirdropMinted, 'already minted'); 51: require(!isGenesisRewardMinted, 'already minted'); 69: require(minterEnableDate <= block.timestamp, 'GolomToken: wait for timelock');
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/governance/GolomToken.sol
File: contracts/rewards/RewardDistributor.sol 88: require(msg.sender == trader); 144: require(epochs[index] < epoch); 158: require(epochs[index] < epoch); 173: require(address(ve) != address(0), ' VE not added yet'); 181: require(tokenowner == ve.ownerOf(tokenids[tindex]), 'Can only claim for a single Address together'); 184: require(epochs[index] < epoch, 'cant claim for future epochs'); 185: require(claimed[tokenids[tindex]][epochs[index]] == 0, 'cant claim if already claimed'); 220: require(address(ve) != address(0), ' VE not added yet'); 292: require(traderEnableDate <= block.timestamp, 'RewardDistributor: time not over yet'); 309: require(voteEscrowEnableDate <= block.timestamp, 'RewardDistributor: time not over yet');
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/rewards/RewardDistributor.sol
File: contracts/vote-escrow/VoteEscrowCore.sol 360: require(_entered_state == _not_entered); 538: require(attachments[_tokenId] == 0 && !voted[_tokenId], 'attached'); 540: require(_isApprovedOrOwner(_sender, _tokenId)); 608: revert('ERC721: transfer to non ERC721Receiver implementer'); 646: require(owner != address(0)); 648: require(_approved != owner); 652: require(senderIsOwner || senderIsApprovedForAll); 869: require(msg.sender == voter); 874: require(msg.sender == voter); 879: require(msg.sender == voter); 884: require(msg.sender == voter); 889: require(msg.sender == voter); 894: require(attachments[_from] == 0 && !voted[_from], 'attached'); 895: require(_from != _to); 896: require(_isApprovedOrOwner(msg.sender, _from)); 897: require(_isApprovedOrOwner(msg.sender, _to)); 927: require(_value > 0); // dev: need non-zero value 928: require(_locked.amount > 0, 'No existing lock found'); 929: require(_locked.end > block.timestamp, 'Cannot add to expired lock. Withdraw'); 944: require(_value > 0); // dev: need non-zero value 945: require(unlock_time > block.timestamp, 'Can only lock until time in the future'); 946: require(unlock_time <= block.timestamp + MAXTIME, 'Voting lock can be 4 years max'); 982: require(_locked.amount > 0, 'No existing lock found'); 983: require(_locked.end > block.timestamp, 'Cannot add to expired lock. Withdraw'); 996: require(_locked.end > block.timestamp, 'Lock expired'); 997: require(_locked.amount > 0, 'Nothing is locked'); 998: require(unlock_time > _locked.end, 'Can only increase lock duration'); 999: require(unlock_time <= block.timestamp + MAXTIME, 'Voting lock can be 4 years max'); 1008: require(attachments[_tokenId] == 0 && !voted[_tokenId], 'attached'); 1011: require(block.timestamp >= _locked.end, "The lock didn't expire"); 1082: require(idToOwner[_tokenId] != address(0), 'Query for nonexistent token'); 1227: require(_isApprovedOrOwner(msg.sender, _tokenId), 'caller is not owner nor approved');
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/vote-escrow/VoteEscrowCore.sol
File: contracts/vote-escrow/VoteEscrowDelegation.sol 72: require(ownerOf(tokenId) == msg.sender, 'VEDelegation: Not allowed'); 73: require(this.balanceOfNFT(tokenId) >= MIN_VOTING_POWER_REQUIRED, 'VEDelegation: Need more voting power'); 99: require(_delegatedTokenIds.length < 500, 'VVDelegation: Cannot stake more'); 130: require(blockNumber < block.number, 'VEDelegation: not yet determined'); 186: require(blockNumber < block.number, 'VEDelegation: not yet determined'); 211: require(ownerOf(tokenId) == msg.sender, 'VEDelegation: Not allowed'); 239: require(attachments[_tokenId] == 0 && !voted[_tokenId], 'attached'); 245: require(_isApprovedOrOwner(_sender, _tokenId));
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/vote-escrow/VoteEscrowDelegation.sol
File: contracts/vote-escrow/VoteEscrowCore.sol#L868-L891 function setVoter(address _voter) external { require(msg.sender == voter); voter = _voter; } function voting(uint256 _tokenId) external { require(msg.sender == voter); voted[_tokenId] = true; } function abstain(uint256 _tokenId) external { require(msg.sender == voter); voted[_tokenId] = false; } function attach(uint256 _tokenId) external { require(msg.sender == voter); attachments[_tokenId] = attachments[_tokenId] + 1; } function detach(uint256 _tokenId) external { require(msg.sender == voter); attachments[_tokenId] = attachments[_tokenId] - 1; }
File: contracts/vote-escrow/VoteEscrowCore.sol 499: ownerToNFTokenCount[_to] += 1; 512: ownerToNFTokenCount[_from] -= 1;
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/vote-escrow/VoteEscrowCore.sol