Juicebox V2 contest - apostle0x01's results

The decentralized fundraising and treasury protocol.

General Information

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

Juicebox

Findings Distribution

Researcher Performance

Rank: 79/105

Findings: 2

Award: $41.64

🌟 Selected for report: 0

🚀 Solo Findings: 0

Lines of code

https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBERC20PaymentTerminal.sol#L88 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBETHERC20ProjectPayer.sol#L271 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBETHERC20ProjectPayer.sol#L315 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBETHERC20SplitsPayer.sol#L256 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBETHERC20SplitsPayer.sol#L348

Vulnerability details

Unchecked return value for transferFrom calls

Impact - Medium

It is good to add a require() statement that checks the return value of token transfers or to use something like OpenZeppelin’s safeTransferFrom unless one is sure the given token reverts in case of a failure. Failure to do so will cause silent failures of transfers and affect token accounting in contract.

Reference

Similar medium severity issue from Consensys Diligence Audit of Fei Protocol: https://consensys.net/diligence/audits/2021/01/fei-protocol/#unchecked-return-value-for-iweth-transfer-call

Poc

There are several transferFrom calls that do not check the return value (some tokens signal failure by returning false):

./contracts/JBERC20PaymentTerminal.sol:88: : IERC20(token).transferFrom(_from, _to, _amount); ./contracts/JBETHERC20ProjectPayer.sol:271: IERC20(_token).transferFrom(msg.sender, address(this), _amount); ./contracts/JBETHERC20ProjectPayer.sol:315: IERC20(_token).transferFrom(msg.sender, address(this), _amount); ./contracts/JBETHERC20SplitsPayer.sol:256: IERC20(_token).transferFrom(msg.sender, address(this), _amount); ./contracts/JBETHERC20SplitsPayer.sol:348: IERC20(_token).transferFrom(msg.sender, address(this), _amount);

https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBERC20PaymentTerminal.sol#L88 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBETHERC20ProjectPayer.sol#L271 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBETHERC20ProjectPayer.sol#L315 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBETHERC20SplitsPayer.sol#L256 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBETHERC20SplitsPayer.sol#L348

Recommendation

It is usually good to add a require-statement that checks the return value or to use something like safeTransferFrom; unless one is sure the given token reverts in case of a failure.

#0 - mejango

2022-07-12T17:36:44Z

dup of #281

[G-01] No need to explicitly initialize variables with default values

Impact

If a variable is not set/initialized, it is assumed to have the default value (0 for uint, false for bool, address(0) for address…). Explicitly initializing it with its default value is an anti-pattern and wastes gas.

POC

./contracts/abstract/JBPayoutRedemptionPaymentTerminal.sol:594: for (uint256 _i = 0; _i < _heldFeeLength; ) { ./contracts/abstract/JBPayoutRedemptionPaymentTerminal.sol:1008: for (uint256 _i = 0; _i < _splits.length; ) { ./contracts/abstract/JBPayoutRedemptionPaymentTerminal.sol:1396: for (uint256 _i = 0; _i < _heldFeesLength; ) { ./contracts/JBController.sol:913: for (uint256 _i = 0; _i < _splits.length; _i++) { ./contracts/JBETHERC20SplitsPayer.sol:466: for (uint256 i = 0; i < _splits.length; i++) { ./contracts/JBFundingCycleStore.sol:724: for (uint256 i = 0; i < _discountMultiple; i++) { ./contracts/JBOperatorStore.sol:85: for (uint256 _i = 0; _i < _permissionIndexes.length; _i++) { ./contracts/JBOperatorStore.sol:135: for (uint256 _i = 0; _i < _operatorData.length; _i++) { ./contracts/JBOperatorStore.sol:165: for (uint256 _i = 0; _i < _indexes.length; _i++) { ./contracts/JBSingleTokenPaymentTerminalStore.sol:862: for (uint256 _i = 0; _i < _terminals.length; _i++) ./contracts/JBSplitsStore.sol:165: for (uint256 _i = 0; _i < _groupedSplitsLength; ) { ./contracts/JBSplitsStore.sol:204: for (uint256 _i = 0; _i < _currentSplits.length; _i++) { ./contracts/JBSplitsStore.sol:211: for (uint256 _j = 0; _j < _splits.length; _j++) { ./contracts/JBSplitsStore.sol:229: for (uint256 _i = 0; _i < _splits.length; _i++) { ./contracts/JBSplitsStore.sol:304: for (uint256 _i = 0; _i < _splitCount; _i++) {

https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/abstract/JBPayoutRedemptionPaymentTerminal.sol#L594 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/abstract/JBPayoutRedemptionPaymentTerminal.sol#L1008 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/abstract/JBPayoutRedemptionPaymentTerminal.sol#L1396 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBController.sol#L913 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBETHERC20SplitsPayer.sol#L466 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBFundingCycleStore.sol#L724 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBOperatorStore.sol#L85 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBOperatorStore.sol#L135 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBOperatorStore.sol#L165 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBSingleTokenPaymentTerminalStore.sol#L862 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBSplitsStore.sol#L165 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBSplitsStore.sol#L204 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBSplitsStore.sol#L211 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBSplitsStore.sol#L229 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBSplitsStore.sol#L304

Mitigation

As an example: for for (uint256 _i = 0; _i < _heldFeeLength; ) should be replaced with for for (uint256 _i ; _i < _heldFeeLength; ){

[G-02] For-Loops

Impact

An array's length should be cached to save gas in for-loops Reading array length at each iteration of the loop takes 6 gas (3 for mload and 3 to place memory_offset) in the stack.

Caching the array length in the stack saves around 3 gas per iteration.

Here, I suggest storing the array's length in a variable before the for-loop, and use it instead:

abstract/JBPayoutRedemptionPaymentTerminal.sol:1008: for (uint256 _i = 0; _i < _splits.length; ) { JBController.sol:913: for (uint256 _i = 0; _i < _splits.length; _i++) { JBController.sol:1014: for (uint256 _i; _i < _fundAccessConstraints.length; _i++) { JBDirectory.sol:139: for (uint256 _i; _i < _terminalsOf[_projectId].length; _i++) { JBDirectory.sol:167: for (uint256 _i; _i < _terminalsOf[_projectId].length; _i++) JBDirectory.sol:275: for (uint256 _i; _i < _terminals.length; _i++) JBDirectory.sol:276: for (uint256 _j = _i + 1; _j < _terminals.length; _j++) JBETHERC20SplitsPayer.sol:466: for (uint256 i = 0; i < _splits.length; i++) { JBOperatorStore.sol:85: for (uint256 _i = 0; _i < _permissionIndexes.length; _i++) { JBOperatorStore.sol:135: for (uint256 _i = 0; _i < _operatorData.length; _i++) { JBOperatorStore.sol:165: for (uint256 _i = 0; _i < _indexes.length; _i++) { JBSingleTokenPaymentTerminalStore.sol:862: for (uint256 _i = 0; _i < _terminals.length; _i++) JBSplitsStore.sol:204: for (uint256 _i = 0; _i < _currentSplits.length; _i++) { JBSplitsStore.sol:211: for (uint256 _j = 0; _j < _splits.length; _j++) { JBSplitsStore.sol:229: for (uint256 _i = 0; _i < _splits.length; _i++) {

https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/abstract/JBPayoutRedemptionPaymentTerminal.sol#L1008 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBController.sol#L913 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBController.sol#L1014 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBDirectory.sol#L139 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBDirectory.sol#L167 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBDirectory.sol#L275 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBDirectory.sol#L276 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBETHERC20SplitsPayer.sol#L466 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBOperatorStore.sol#L85 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBOperatorStore.sol#L135 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBOperatorStore.sol#L165 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBSingleTokenPaymentTerminalStore.sol#L862 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBSplitsStore.sol#L204 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBSplitsStore.sol#L211 https://github.com/jbx-protocol/juice-contracts-v2-code4rena/tree/828bf2f3e719873daa08081cfa0d0a6deaa5ace5/contracts/JBSplitsStore.sol#L229

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter