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: 17/179
Findings: 8
Award: $690.97
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: GimelSec
Also found by: 0x52, 0xA5DF, 0xSky, 0xsanson, Bahurum, CertoraInc, GalloDaSballo, JohnSmith, Lambda, MEP, Twpony, arcoun, berndartmueller, cryptphi, hansfriese, kenzo, kyteg, panprog, rajatbeladiya, scaraven, simon135, zzzitron
26.7695 USDC - $26.77
Judge has assessed an item in Issue #744 as High risk. The relevant finding follows:
Due to solidity 0.8 overflow/underflow protection, accessing checkpoints[toTokenId][nCheckpoints - 1] will throw if nCheckpoints == 0.
As it is not possible to insert the initial checkpoint, _writeCheckpoint will always fail. This issue currently prevents adding a delegation.
#0 - dmvt
2022-10-24T14:24:11Z
Duplicate of #630
26.7695 USDC - $26.77
In the delegate
function in VoteEscrowDelegation.sol, there is no verification that a token has been already delegated to an other token.
A malicious user can create several veNFT
tokens by splitting and locking his GOL tokens. By delegating each token to the other ones, he can increase his voting power and make a (malicious) proposal to be accepted.
I have rated this finding as Medium because the governance contract is out-of-scope. But voter fraud can be used to gain ownership, update contract settings, transfer available funds, ... This can be High if the impact on the governance contract can be used.
Note: The whole delegation functionnality is currently unusable because the first checkpoint can never be inserted due to an overflow when getting oldCheckpoint
in _writeCheckpoint()
. A Low-impact finding is related to this bug. This finding assumes that this issue is fixed.
create_lock()
calls, with 100 GOL tokens each time.delegate()
calls necessary)Update the number of veNFT tokens to create to have as much voting power as necessary to make a proposal be accepted.
A check must be added to prevent a token to be delegated several times, to different tokens.
#0 - KenzoAgada
2022-08-02T12:09:47Z
Duplicate of #169
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/vote-escrow/VoteEscrowDelegation.sol#L79-L80 https://github.com/code-423n4/2022-07-golom/blob/main/contracts/vote-escrow/VoteEscrowDelegation.sol#L213-L214
Delegations are tracked using checkpoints stored in the contract's storage.
When delegate()
and removeDelegation()
are used to add a new checkpoint, the previous checkpoint is also modified because the modification is done on Checkpoint storage checkpoint = checkpoints[tokenId][nCheckpoints - 1]
which is the storage of the previous checkpoint, not a temporary Checkpoint in memory.
By modifying the previous checkpoint, a user can add a delegation after a proposal has been submited and have the related delegation votes taken into account.
I have rated this finding as Medium because it allows voter fraud. The impact can be High on the governance contract, but it is out-of-scope.
Note: The whole delegation functionnality is currently unusable because the first checkpoint can never be inserted due to an overflow when getting oldCheckpoint
in _writeCheckpoint()
. A Low-impact finding is related to this bug. This finding assumes that this issue is fixed.
delegate()
, but after the proposal has been submitedgetPriorVotes()
function, called with the proposal timestamp will also return the delegated votes, although they have been delegated after.Use a temporary array stored in memory or modify delegatedTokenIds
after the new checkpoint has been inserted.
#0 - KenzoAgada
2022-08-02T07:58:12Z
Duplicate of #81
🌟 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
Judge has assessed an item in Issue #744 as Medium risk. The relevant finding follows:
The original transfer used to send eth uses a fixed stipend 2300 gas. This was used to prevent reentrancy. However this limit interaction with others contracts that need more than that to process the transaction.
Related: https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/.
In GolomTrader, ether paid to rewards distributor and exchanges will probably be paid to contracts. If one target throw on transfer(), the order will not be processed.
#0 - dmvt
2022-10-21T13:36:26Z
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
Judge has assessed an item in Issue #744 as Medium risk. The relevant finding follows:
#0 - dmvt
2022-10-21T13:37:43Z
Duplicate of #342
🌟 Selected for report: sseefried
Also found by: 0x4non, IllIllI, Jmaxmanblue, JohnSmith, Lambda, arcoun, berndartmueller, cccz, csanuragjain, minhquanym, rbserver, rotcivegaf
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/vote-escrow/VoteEscrowCore.sol#L604
The safeTransferFrom()
function is described in EIP-721 as a function to safely transfer NFTs. As described in EIP-721 and in the NatSpec of the method in VoteEscrowCore.sol, the method must throw if the onERC721Received()
function does not return the expected bytes4 signature.
The implementation of safeTransferFrom()
in VoteEscrowCore.sol does not verify the returned value.
A user may accidentally transfer his NTF to a contract which does not support EIP-721 and permanently lose his asset, although this would have been avoided if the bytes4 signature was verified.
veNFT
to an other addresssafeTransferFrom
because transferFrom
is discouraged (https://github.com/code-423n4/2022-07-golom/blob/main/contracts/vote-escrow/VoteEscrowCore.sol#L106) as no verification is done on the target.to
parameter is set to a contract which do not support EIP-721onERC721Received()
call on the destination targets does not throw because the fallback implementation may (for example) handle the call without error.veNTF
token is transfered and is now lostThe bytes4
data, returned by onERC721Received
, must be verified against the expected signature.
Note that the expected signature is not bytes4(keccak256("onERC721Received(address,address,uint,bytes)"))
as stated in:
The correct signature is bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))
(ie. uint256
instead of uint
)
#0 - KenzoAgada
2022-08-04T02:01:28Z
Duplicate of #577
🌟 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#L216-L267 https://github.com/code-423n4/2022-07-golom/blob/main/contracts/core/GolomTrader.sol#L382-L401
The GolomTrader contract does not check if there are remaining ether after an order has been executed. The remaining ether will be permanently lost as it is not automatically returned and there is no function to withdraw ether from the contract.
In fillAsk
, remaining ether can be due to:
o.exchange.paymentAddress == address(0)
and o.exchange.paymentAmt != 0
(https://github.com/code-423n4/2022-07-golom/blob/main/contracts/core/GolomTrader.sol#L245)o.prePayment.paymentAddress == address(0)
and o.prePayment.paymentAmt != 0
(https://github.com/code-423n4/2022-07-golom/blob/main/contracts/core/GolomTrader.sol#L248)p.paymentAddress == address(0)
and p.paymentAmt != 0
(https://github.com/code-423n4/2022-07-golom/blob/main/contracts/core/GolomTrader.sol#L267)In _settleBalances
, remaining ether can be due to:
o.exchange.paymentAddress == address(0)
and o.exchange.paymentAmt != 0
(https://github.com/code-423n4/2022-07-golom/blob/main/contracts/core/GolomTrader.sol#L385)o.prePayment.paymentAddress == address(0)
and o.prePayment.paymentAmt != 0
(https://github.com/code-423n4/2022-07-golom/blob/main/contracts/core/GolomTrader.sol#L386)p.paymentAddress == address(0)
and p.paymentAmt != 0
(https://github.com/code-423n4/2022-07-golom/blob/main/contracts/core/GolomTrader.sol#L401)During the handling of an order, the sent ether must be tracked and the remaining ether must be sent back:
fillAsk
fillBid
and fillCriteriaBid
A withdraw()
method can also be added for the owner of the contract. This is a good practice, especially when fallback
and receive
are left available, as it is the case here.
#0 - KenzoAgada
2022-08-04T02:49:39Z
fillAsk is duplicate of #75 settleBalances needs to be determined later if considered separate issue
#1 - dmvt
2022-10-12T15:43:57Z
I view this as the same issue ultimately, as did the warden since they only submitted one version. 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
_writeCheckpoint
will fail to insert an initial checkpointDue to solidity 0.8 overflow/underflow protection, accessing checkpoints[toTokenId][nCheckpoints - 1]
will throw if nCheckpoints == 0
.
As it is not possible to insert the initial checkpoint, _writeCheckpoint
will always fail. This issue currently prevents adding a delegation.
When updating a checkpoint already available for the current block, the modification is done on a memory structure, not on the storage. These modifications are lost after the call is finished.
The removeDelegation()
function can only be used to remove a delegation of a token on itself. But there is no function to remove a delegation of a token to an other one.
There is a commented removeDelegationByOwner()
function in the VoteEscrowDelegation.sol file but it is invalid because it removes the wrong element from delegatedTokenIds
(delegatedTokenId
instead of ownerTokenId
).
xxxxRewards()
functions should accept a range of epochThe 3 functions traderRewards()
, exchangeRewards()
and stakerRewards()
will loop over each epoch (1 day), which will consume more and more ressources each day.
The stakerRewards()
will also return an array of epoch
elements which will reach 960 bytes after only a month, and 11.4kB after a year.
These functions should accept a range of epoch to limit resource consumption and avoid running out of gas.
delegate()
and removeDelegation()
do not allow token operatorThe VoteEscrowCore contract supports setting operator addresses on a token through approve()
and setApprovalForAll()
.
For consistency with other operations on tokens, the delegate()
and removeDelegation()
should use _isApprovedOrOwner(msg.sender, tokenIf)
instead of checking ownerOf(tokenId) == msg.sender
validateOrder()
should check if order is filled/cancelled before checking for expirationhttps://github.com/code-423n4/2022-07-golom/blob/main/contracts/core/GolomTrader.sol#L182-L192
If an order has been filled or cancelled, the validateOrder()
will return state 1 (deadline reached) once the order expiration timestamp has been reached, although this is not the expected state.
transfer()
in payEther()
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/core/GolomTrader.sol#L154
The original transfer
used to send eth uses a fixed stipend 2300 gas. This was used to prevent reentrancy. However this limit interaction with others contracts that need more than that to process the transaction.
Related: https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/.
In GolomTrader, ether paid to rewards distributor and exchanges will probably be paid to contracts. If one target throw on transfer()
, the order will not be processed.
Base64 library already available (same code) in @openzeppelin/contracts/utils/Base64.sol
toString
already available (same code) in @openzeppelin/contracts/utils/Strings.sol
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/core/GolomTrader.sol#L405-L440
MerkleProof
library is available in @openzeppelin/contracts/utils/cryptography/MerkleProof.sol
and is already used by the GolomAirdrop contract.
ReentrancyGard
is available in @openzeppelin/contracts/security/ReentrancyGuard.sol
.
safeTransferFrom()
to transfer ERC721For safety and consistency with ERC1155, ERC721 tokens transfered by GolomTrader should use the safeTransferFrom()
function.