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: 92/179
Findings: 5
Award: $61.16
🌟 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
You should use call()
instead of transfer()
. If the recipient is a contract it might use more than 2300 gas which will make the transaction fail if the ETH was sent with transfer()
.
https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/
The functions follow the check effects interactions pattern. I couldn't find any reentrancy issues.
none
Use call()
instead.
#0 - KenzoAgada
2022-08-03T14:08:24Z
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
You're already using safeTransferFrom()
for ERC1155 contracts but not for ERC721 ones. You allow scenarios where the user might transfer the bought token to an address that can't handle them. The tokens would be locked up. Because of that I'd rate the issue as MED.
The possibility of reentrancy has to be evaluated. I couldn't find any reentrancy issues until now. The check effects interactions pattern is used within the relevant functions.
none
Use safeTransferFrom()
for ERC721 contracts.
#0 - KenzoAgada
2022-08-03T15:08:39Z
Duplicate of #342
🌟 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
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/core/GolomTrader.sol#L217
In GolomTrader.fillAsk()
the buyer is allowed to send more ETH than necessary to fulfill the order. The contract doesn't have any way of using the excess amount. The funds will be locked up in the contract.
This issue depends on the buyer making a mistake and sending more than necessary. Thus, I'd rate it as MED.
The contract only transfers ETH when payEther()
is called. But, that function is only callable within a function that handles the orders. There it only works with the ETH that was part of that specific order. It never accesses excess ETH.
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/core/GolomTrader.sol#L151
none
Only allow the buyer to send the exact amount of ETH that should be spend: require(msg.value == o.totalAmt * amount + p.paymentAmt, 'mgmtm');
#0 - KenzoAgada
2022-08-04T02:52:10Z
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
According to the docs, the royalty fees should only be able to go up to 10%, see https://docs.golom.io/features/lowest-fees#royalty-fees. But, the contract allows the seller to choose the value freely.
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/core/GolomTrader.sol#L54
There's no validation for the prePayment
value.
Either update the docs or implement the royalty fees as specified.
According to the docs, the protocol is supposed to be launched on other chains as well in the future. The address of the WETH contract is different on other chains. If you don't update the value before deployment you might run into some small isseus.
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/core/GolomTrader.sol#L45
startTime
in RewardDistributor through the constructorThere'll always be an issue that will make you postpone the launch. Setting the value through the constructor will save you the work of modifying the value multiple times.
It allows third parties to track those changes efficiently.
e.g.
🌟 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
cancelOrder()
can be simplifiedSince the iterator can't overflow in most cases because of the conditional check, you can save gas by incrementing it in an unchecked block
For example, in GolomTrader the WETH
variable is never updated. You can set it as a constant to save a lot of gas.
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/core/GolomTrader.sol#L45
The Order
struct in GolomTrader can be packed to save gas.
e.g.
struct Order { address collection; // NFT contract address uint8 v; bool isERC721; // standard of the collection , if 721 then true , if 1155 then false uint256 tokenId; // order for which tokenId of the collection address signer; // maker of order address uint256 orderType; // 0 if selling nft for eth , 1 if offering weth for nft,2 if offering weth for collection with special criteria root uint256 totalAmt; // price value of the trade // total amt maker is willing to give up per unit of amount Payment exchange; // payment agreed by maker of the order to pay on succesful filling of trade this amt is subtracted from totalamt Payment prePayment; // another payment , can be used for royalty, facilating trades uint256 tokenAmt; // token amt useful if standard is 1155 if >1 means whole order can be filled tokenAmt times uint256 refererrAmt; // amt to pay to the address that helps in filling your order bytes32 root; // A merkle root derived from each valid tokenId — set to 0 to indicate a collection-level or tokenId-specific order. address reservedAddress; // if not address(0) , only this address can fill the order uint256 nonce; // nonce of order usefull for cancelling in bulk uint256 deadline; // timestamp till order is valid epoch timestamp in secs bytes32 r; bytes32 s; }
For example, in RewardDistributor, the weth
, rewardToken
, and startTime
variables are only set in the constructor.
In GolomTrader.incrementNonce()
you save to memory for no reason. Removing that will save gas:
function incrementNonce() external nonReentrant {ß emit NonceIncremented(msg.sender, ++nonces[msg.sender]); }
cancelOrder()
can be simplifiedIn GolomTrader.cancelOrder()
you call validateOrder()
to get the hashed order struct. But, the validateOrder()
runs a handful of other operations as well. By calling the _hashOrder()
function directly, you can remove all those excess operations and save gas:
function cancelOrder(Order calldata o) public nonReentrant { require(o.signer == msg.sender); bytes32 hashStruct = _hashOrder(o); filled[hashStruct] = o.tokenAmt + 1; emit OrderCancelled(hashStruct); }