Platform: Code4rena
Start Date: 01/07/2022
Pot Size: $75,000 USDC
Total HM: 17
Participants: 105
Period: 7 days
Judge: Jack the Pug
Total Solo HM: 5
Id: 143
League: ETH
Rank: 8/105
Findings: 6
Award: $2,498.36
🌟 Selected for report: 1
🚀 Solo Findings: 0
🌟 Selected for report: 0xNineDec
Also found by: 0x1f8b, 0x29A, 0x52, 0xDjango, 0xdanial, 0xf15ers, Cheeezzyyyy, Chom, Franfran, GalloDaSballo, Green, IllIllI, Meera, Ruhum, bardamu, cccz, codexploder, defsec, hake, hansfriese, horsefacts, hubble, hyh, jonatascm, kebabsec, oyc_109, pashov, rbserver, simon135, tabish, tintin, zzzitron
14.8726 USDC - $14.87
Oracle my return stale price.
Round completeness and the quoted timestamp are not checked to ensure that the reported price is not stale.
roundId
, startedAt
, updatedAt
, and answeredInRound
are omitted from the return result of feed.latestRoundData()
.
(, int256 _price, , , ) = feed.latestRoundData();
Manual Review
(uint80 roundId, int256 price, , uint256 updatedAt, uint80 answeredInRound) = feed.latestRoundData(); require(answeredInRound >= roundId, "ChainLink: Stale price"); require(updatedAt != 0, "ChainLink: Round not complete");
Check return values of answeredInRound
, roundId
and updatedAt
to ensure price is not stale.
#0 - mejango
2022-07-12T18:56:21Z
dup of #138
🌟 Selected for report: horsefacts
Also found by: 0x1f8b, 0x29A, 0x52, 0xf15ers, AlleyCat, Ch_301, Chom, Franfran, IllIllI, Kaiziron, Limbooo, Meera, Ruhum, Sm4rty, apostle0x01, berndartmueller, cccz, cloudjunky, codexploder, cryptphi, delfin454000, durianSausage, fatherOfBlocks, hake, hansfriese, hyh, jonatascm, m_Rassska, oyc_109, peritoflores, rajatbeladiya, rbserver, svskaushik, zzzitron
3.4075 USDC - $3.41
migrate()
might silently fail on ETH/ERC20 transfer.
As outlined in another finding, JBERC20PaymentTerminal._transferFrom()
is unsafe and might not revert on failure. Potentially leading to migrate()
being "successful" even though no assets were transferred.
_to.addToBalanceOf()
doesnt check return value of ETH _payableValue
transfer. If the new addToBalanceOf()
implementation has some error the transaction won't revert and still be recorded as succesful.
// If this terminal's token is ETH, send it in msg.value. uint256 _payableValue = token == JBTokens.ETH ? balance : 0;
Manual Review
Use safeTransfer()/safeTransferFrom()
for ERC20 transfers
Use call
to transfer ETH to another address and check it's return value.
Other instances of this issue are present in (this is not an all-inclusive list): https://github.com/jbx-protocol/juice-contracts-v2-code4rena/blob/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/abstract/JBPayoutRedemptionPaymentTerminal.sol#L1058 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/blob/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/abstract/JBPayoutRedemptionPaymentTerminal.sol#L1232
#0 - mejango
2022-07-12T18:36:58Z
dup #281
Projects using fee-on-transfer tokens will have incorrect accounting and will receive fewer tokens than expected leading up to loss of funds.
pay()
and _pay()
rely on user input for accounting _amount
being transferred.
If token has a fee-on-transfer this amount will be inaccurate and lead to mintTokensOf()
minting the wrong amount as well several other functions transferring the wrong values and emitting events with false values.
function pay( uint256 _projectId, uint256 _amount, address _token, address _beneficiary, uint256 _minReturnedTokens, bool _preferClaimedTokens, string calldata _memo, bytes calldata _metadata ) external payable virtual override isTerminalOf(_projectId) returns (uint256) { _token; // Prevents unused var compiler and natspec complaints. // ETH shouldn't be sent if this terminal's token isn't ETH. if (token != JBTokens.ETH) { if (msg.value > 0) revert NO_MSG_VALUE_ALLOWED(); // Transfer tokens to this terminal from the msg sender. _transferFrom(msg.sender, payable(address(this)), _amount); } // If this terminal's token is ETH, override _amount with msg.value. else _amount = msg.value; return _pay( _amount, msg.sender, _projectId, _beneficiary, _minReturnedTokens, _preferClaimedTokens, _memo, _metadata ); }
Other functions affected:
_distributeToPayoutSplitsOf()
_processFee()
Manual Review
Use the difference between the contract's balance before and after the transfer to account for the right amount of tokens.
#0 - drgorillamd
2022-07-12T19:16:10Z
Duplicate of #304
1929.6275 USDC - $1,929.63
Payouts won't be able to be distributed if one of multiple beneficiaries decides to revert the transaction on recieval.
// If there's a beneficiary, send the funds directly to the beneficiary. Otherwise, send to the msg.sender. _transferFrom( address(this), _split.beneficiary != address(0) ? _split.beneficiary : payable(msg.sender), _netPayoutAmount );
If token used is native ETH or ERC777 a beneficiary can revert the transaction on the callback and DOS _distributeToPayoutSplitsOf()
for all the other beneficiaries.
Manual Review
Have beneficiaries withdraw their benefit instead of sending it to them.
#0 - mejango
2022-07-12T19:57:34Z
by design. project owners bring their own risks and opportunities when setting payout splits. Made clear here https://info.juicebox.money/dev/learn/risks#setting-a-distribution-limit-and-payout-splits
#1 - youngDuckling
2022-07-13T19:36:45Z
A malicious or compromised beneficiary is not exactly under a project owner's control. Implementing the recommended mitigation step would prevent the possibility of DOS while maintaining all privileges of project owner. No risks outlined in link below would be mitigated by the recommended mitigation, thus project owner would still have access to same range of functionalities. https://info.juicebox.money/dev/learn/risks/#setting-a-distribution-limit-and-payout-splits
🌟 Selected for report: IllIllI
Also found by: 0v3rf10w, 0x1f8b, 0x29A, 0xDjango, 0xNazgul, 0xNineDec, 0xdanial, 0xf15ers, Bnke0x0, Ch_301, Chandr, Chom, Funen, GimelSec, Hawkeye, JC, Kaiziron, Lambda, Meera, MiloTruck, Noah3o6, Picodes, ReyAdmirado, Rohan16, Sm4rty, TerrierLover, TomJ, Waze, _Adam, __141345__, asutorufos, aysha, berndartmueller, brgltd, cccz, codexploder, defsec, delfin454000, djxploit, durianSausage, fatherOfBlocks, hake, horsefacts, hubble, jayfromthe13th, joestakey, jonatascm, m_Rassska, oyc_109, pashov, rajatbeladiya, rbserver, robee, sach1r0, sahar, samruna, simon135, svskaushik, zzzitron
90.2101 USDC - $90.21
If projects
is mistakenly set to zero the whole contract would have to be redeployed.
https://github.com/jbx-protocol/juice-contracts-v2-code4rena/blob/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBTokenStore.sol#L165 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/blob/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBSplitsStore.sol#L126 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/blob/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBDirectory.sol#L189 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/blob/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBController.sol#L380 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/blob/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBChainlinkV3PriceFeed.sol#L61
Mappings documentation could be made clearer bby adopting the following representation:
_projectId
=> _domain
=> _group
=> countOf
https://github.com/jbx-protocol/juice-contracts-v2-code4rena/blob/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBSplitsStore.sol#L46-L57
_to
doesnt need to be payableThe specified compiler version pragma solidity 0.8.6
is not up to date.
Older compilers might be susceptible to bugs.
List of known compiler bugs and their severity can be found here: https://etherscan.io/solcbuginfo
🌟 Selected for report: 0xA5DF
Also found by: 0v3rf10w, 0x09GTO, 0x1f8b, 0x29A, 0xDjango, 0xKitsune, 0xNazgul, 0xdanial, 0xf15ers, Aymen0909, Bnke0x0, Ch_301, Cheeezzyyyy, Chom, ElKu, Funen, Hawkeye, IllIllI, JC, JohnSmith, Kaiziron, Lambda, Limbooo, Meera, Metatron, MiloTruck, Noah3o6, Picodes, Randyyy, RedOneN, ReyAdmirado, Rohan16, Saintcode_, Sm4rty, TomJ, Tomio, Tutturu, UnusualTurtle, Waze, _Adam, __141345__, ajtra, apostle0x01, asutorufos, brgltd, c3phas, cRat1st0s, codexploder, defsec, delfin454000, djxploit, durianSausage, exd0tpy, fatherOfBlocks, hake, horsefacts, ignacio, jayfromthe13th, joestakey, jonatascm, kaden, kebabsec, m_Rassska, mektigboy, mrpathfindr, oyc_109, rajatbeladiya, rbserver, rfa, robee, sach1r0, sashik_eth, simon135
38.2308 USDC - $38.23
for
loop optimisationfor (uint256 _i = 0; _i < _permissionIndexes.length; _i++) { uint256 _permissionIndex = _permissionIndexes[_i]; if (_permissionIndex > 255) revert PERMISSION_INDEX_OUT_OF_BOUNDS(); if (((permissionsOf[_operator][_account][_domain] >> _permissionIndex) & 1) == 0) return false; }
Gas could be saved by:
Example:
uint lenght = _permissionIndexes.length; for (uint256 _i = 0; _i < length;) { uint256 _permissionIndex = _permissionIndexes[_i]; if (_permissionIndex > 255) revert PERMISSION_INDEX_OUT_OF_BOUNDS(); if (((permissionsOf[_operator][_account][_domain] >> _permissionIndex) & 1) == 0) return false; unchecked { ++_i; } }
https://github.com/jbx-protocol/juice-contracts-v2-code4rena/blob/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBSingleTokenPaymentTerminalStore.sol#L862 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/blob/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBOperatorStore.sol#L85 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/blob/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/abstract/JBPayoutRedemptionPaymentTerminal.sol#L1008
As outlined _token
variable is not used and should be removed.
https://github.com/jbx-protocol/juice-contracts-v2-code4rena/blob/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/abstract/JBPayoutRedemptionPaymentTerminal.sol#L335