Joyn contest - robee's results

Launchpad for collaborative web3 media projects with blueprints, building blocks, and community support.

General Information

Platform: Code4rena

Start Date: 30/03/2022

Pot Size: $30,000 USDC

Total HM: 21

Participants: 38

Period: 3 days

Judge: Michael De Luca

Total Solo HM: 10

Id: 104

League: ETH

Joyn

Findings Distribution

Researcher Performance

Rank: 14/38

Findings: 3

Award: $800.22

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: hyh

Also found by: robee

Labels

bug
duplicate
2 (Med Risk)
sponsor confirmed

Awards

362.2521 USDC - $362.25

External Links

Lines of code

https://github.com/code-423n4/2022-03-joyn/blob/main/royalty-vault/contracts/RoyaltyVault.sol

Vulnerability details

the balance of outputToken is checked to be exactly a specified value that is not declared in this specific function. Therefore, a malicious user can transfer to the contract address tiny amount of tokens and the user transactions will always revert.

Potential DoS in RoyaltyVault.sol, 43 Potential DoS in RoyaltyVault.sol, 47 Potential DoS in RoyaltyVault.sol, 51

#0 - sofianeOuafir

2022-04-14T22:50:48Z

This issue could potentially be an issue. However, it needs a bit more details

#1 - deluca-mike

2022-04-22T03:45:31Z

It is unclear to me as well after going over sendToSplitter and incrementWindow several times. I agree with this not being an issue, at least not how it's explained.

#2 - deluca-mike

2022-04-22T04:37:44Z

I'm going to mark this as a low-quality duplicate of #37.

Findings Information

Awards

359.1979 USDC - $359.20

Labels

bug
sponsor confirmed
QA (Quality Assurance)

External Links

Title: Does not validate the input fee parameter Severity: Low Risk

Some fee parameters of functions are not checked for invalid values. Validate the parameters:

RoyaltyVaultFactory.setPlatformFeeRecipient (_platformFeeRecipient) RoyaltyVaultFactory.setPlatformFee (_platformFee) SplitFactory.setPlatformFeeRecipient (_platformFeeRecipient) CoreCollection.initialize (_mintFee) RoyaltyVault.setPlatformFeeRecipient (_platformFeeRecipient) RoyaltyVault.setPlatformFee (_platformFee) SplitFactory.setPlatformFee (_platformFee)

Title: Never used parameters Severity: Low Risk

Those are functions and parameters pairs that the function doesn't use the parameter. In case those functions are external/public this is even worst since the user is required to put value that never used and can misslead him and waste its time.

Splitter.sol: function attemptETHTransfer parameter value isn't used. (attemptETHTransfer is private)

Title: Not verified claimer Severity: Low Risk

If a functions gets as input a claimer param, then it should make sure the claimer address is not address(0). Otherwise it will cause to loss of the funds or access.

ERC721Claimable.sol._claim claimer

Title: Missing commenting Severity: Low Risk

The following functions are missing commenting as describe below: MultiSigWallet.sol, external_call (private), parameters destination, value, dataLength, data not commented SplitProxy.sol, splitter (public), @return is missing MultiSigWallet.sol, external_call (private), @return is missing

Title: Two Steps Verification before Transferring Ownership Severity: Low Risk

The following contracts have a function that allows them an admin to change it to a different address. If the admin accidentally uses an invalid address for which they do not have the private key, then the system gets locked. It is important to have two steps admin change where the first is announcing a pending new admin and the new address should then claim its ownership. A similar issue was reported in a previous contest and was assigned a severity of medium: code-423n4/2021-06-realitycards-findings#105

ICoreCollection.sol

Title: Not verified owner Severity: Low Risk

owner param should be validated to make sure the owner address is not address(0). Otherwise if not given the right input all only owner accessible functions will be unaccessible. MultiSigWallet.sol.replaceOwner owner MultiSigWallet.sol.removeOwner owner MultiSigWallet.sol.addOwner owner MultiSigWallet.sol.replaceOwner newOwner

Title: Duplicates in array Severity: Low Risk

You allow in some arrays to have duplicates. Sometimes you assumes there are no duplicates in the array.

MultiSigWallet.addOwner pushed (owner)

Title: Solidity compiler versions mismatch Severity: Low Risk

The project is compiled with different versions of solidity, which is not recommended because it can lead to undefined behaviors.

Title: Not verified input Severity: Low Risk

external / public functions parameters should be validated to make sure the address is not 0. Otherwise if not given the right input it can mistakenly lead to loss of user funds. MockCollection.sol.setRoyaltyVault _royaltyVault Splitter.sol.setClaimed who CoreCollection.sol.setRoyaltyVault _royaltyVault RoyaltyVault.sol.setPlatformFeeRecipient _platformFeeRecipient

Title: Require with empty message Severity: Low Risk

The following requires are with empty messages. This is very important to add a message for any require. Such that the user has enough information to know the reason of failure:

Solidity file: MultiSigWallet.sol, In line 99 with Empty Require message. Solidity file: CoreMultiSig.sol, In line 21 with Empty Require message.

#0 - sofianeOuafir

2022-04-15T16:02:19Z

high quality report

Findings Information

Awards

78.7653 USDC - $78.77

Labels

bug
G (Gas Optimization)

External Links

Title: More efficient Struct packing of Transaction in the contract MultiSigWallet.sol Severity: GAS

The following structs could change the order of their stored elements to decrease memory uses and number of occupied slots. Therefore will save gas at every store and load from memory.

In MultiSigWallet.sol, Transaction is optimized to: 3 slots from: 4 slots. The new order of types (you choose the actual variables):

1. uint256 2. bytes 3. address 4. bool

Title: Unnecessary equals boolean Severity: GAS

Boolean variables can be checked within conditionals directly without the use of equality operators to true/false.

RoyaltyVault.sol, 55: ) == true, RoyaltyVault.sol, 48: ISplitter(splitterProxy).incrementWindow(splitterShare) == true, MultiSigWallet.sol, 270: if (count == required) return true; RoyaltyVault.sol, 44: IERC20(royaltyAsset).transfer(splitterProxy, splitterShare) == true,

Title: Unnecessary cast Severity: Gas

Collection CoreFactory.sol.addCollection - unnecessary casting Collection(_collection)

Title: Change transferFrom to transfer Severity: GAS

'transferFrom(address(this), , **)' could be replaced by the following more gas efficient 'transfer(, **)' This replacement is more gas efficient and improves the code quality.

CoreCollection.sol, 175 : payableToken.transferFrom(address(this), msg.sender, amount);

Title: State variables that could be set immutable Severity: GAS

In the following files there are state variables that could be set immutable to save gas.

platformFee in SplitFactory.sol platformFee in RoyaltyVaultFactory.sol platformFeeRecipient in RoyaltyVaultFactory.sol royaltyVault in ProxyVault.sol platformFeeRecipient in SplitFactory.sol

Title: Short the following require messages Severity: GAS

The following require messages are of length more than 32 and we think are short enough to short them into exactly 32 characters such that it will be placed in one slot of memory and the require function will cost less gas. The list:

Solidity file: CoreCollection.sol, In line 204, Require message length to shorten: 35, The message: CoreCollection: Hashed Proof is set Solidity file: RoyaltyVault.sol, In line 47, Require message length to shorten: 35, The message: Failed to increment splitter window

Title: Prefix increments are cheaper than postfix increments Severity: GAS

Prefix increments are cheaper than postfix increments. Further more, using unchecked {++x} is even more gas efficient, and the gas saving accumulates every iteration and can make a real change There is no risk of overflow caused by increamenting the iteration index in for loops (the ++i in for (uint256 i = 0; i < numIterations; ++i)). But increments perform overflow checks that are not necessary in this case. These functions use not using prefix increments (++x) or not using the unchecked keyword:

change to prefix increment and unchecked: Splitter.sol, i, 274 change to prefix increment and unchecked: MultiSigWallet.sol, i, 98 change to prefix increment and unchecked: MultiSigWallet.sol, i, 357 change to prefix increment and unchecked: MultiSigWallet.sol, i, 393 change to prefix increment and unchecked: MultiSigWallet.sol, i, 314 change to prefix increment and unchecked: CoreFactory.sol, i, 79 change to prefix increment and unchecked: MultiSigWallet.sol, i, 363 change to prefix increment and unchecked: MultiSigWallet.sol, i, 127 change to prefix increment and unchecked: MultiSigWallet.sol, i, 147 change to prefix increment and unchecked: MultiSigWallet.sol, i, 384 change to prefix increment and unchecked: MultiSigWallet.sol, i, 268 change to prefix increment and unchecked: Splitter.sol, i, 50 change to prefix increment and unchecked: MultiSigWallet.sol, i, 330 change to prefix increment and unchecked: CoreCollection.sol, i, 279

Title: Use bytes32 instead of string to save gas whenever possible Severity: GAS

Use bytes32 instead of string to save gas whenever possible. String is a dynamic data structure and therefore is more gas consuming then bytes32. CoreCollection.sol (L27), string public HASHED_PROOF = "";

Title: Use calldata instead of memory Severity: GAS

Use calldata instead of memory for function parameters In some cases, having function arguments in calldata instead of memory is more optimal.

CoreCollection.setCollectionMeta (_collectionName) MultiSigWallet.external_call (data) SplitFactory.createSplit (_splitId) CoreCollection.initialize (_collectionName) CoreFactory.addCollection (_projectId) CoreCollection.initialize (_collectionSymbol) CoreCollection.initialize (_collectionURI) CoreCollection.setCollectionMeta (_collectionSymbol) CoreFactory.getProject (_projectId)

Title: Consider inline the following functions to save gas Severity: GAS

You can inline the following functions instead of writing a specific function to save gas. (see https://github.com/code-423n4/2021-11-nested-findings/issues/167 for a similar issue.) CoreCollection.sol, _baseURI, { return _baseUri; } Splitter.sol, getClaimHash, { return keccak256(abi.encodePacked(who, window)); } ERC721Claimable.sol, getNode, { return keccak256(abi.encodePacked(who, claimableAmount)); }

Title: Unnecessary index init Severity: GAS

In for loops you initialize the index to start from 0, but it already initialized to 0 in default and this assignment cost gas. It is more clear and gas efficient to declare without assigning 0 and will have the same meaning:

MultiSigWallet.sol, 127 MultiSigWallet.sol, 330 CoreCollection.sol, 279 Splitter.sol, 50 Splitter.sol, 274 MultiSigWallet.sol, 314 MultiSigWallet.sol, 98 MultiSigWallet.sol, 268 MultiSigWallet.sol, 147

Title: Unused state variables Severity: GAS

Unused state variables are gas consuming at deployment (since they are located in storage) and are a bad code practice. Removing those variables will decrease deployment gas cost and improve code quality. This is a full list of all the unused storage variables we found in your code base.

VaultStorage.sol, platformFeeRecipient SplitStorage.sol, balanceForWindow SplitStorage.sol, depositedInWindow Splitter.sol, PERCENTAGE_SCALE SplitStorage.sol, merkleRoot ERC721Payable.sol, mintFee SplitStorage.sol, _splitter ERC721Payable.sol, splitFactory SplitStorage.sol, claimed ERC721Payable.sol, isForSale SplitStorage.sol, splitAsset SplitStorage.sol, currentWindow VaultStorage.sol, royaltyAsset VaultStorage.sol, splitterProxy

Title: Unnecessary functions Severity: GAS

The following functions are not used at all. Therefore you can remove them to save deployment gas and improve code clearness. ERC721Claimable.sol, _claim ERC721Claimable.sol, _setMerkelRoot CoreCollection.sol, _baseURI Splitter.sol, attemptETHTransfer Splitter.sol, amountFromPercent ERC721Payable.sol, _handlePayment

Title: Inline one time use functions Severity: GAS

The following functions are used exactly once. Therefore you can inline them and save gas and improve code clearness.

MultiSigWallet.sol, addTransaction CoreCollection.sol, _beforeTokenTransfer CoreCollection.sol, batchMint ERC721Claimable.sol, getNode MultiSigWallet.sol, external_call

Title: Unnecessary array boundaries check when loading an array element twice Severity: GAS

There are places in the code (especially in for-each loops) that loads the same array element more than once. In such cases, only one array boundaries check should take place, and the rest are unnecessary. Therefore, this array element should be cached in a local variable and then be loaded again using this local variable, skipping the redundant second array boundaries check: MultiSigWallet.sol.constructor - double load of owners[i] MultiSigWallet.sol.constructor - double load of _owners[i]

Title: Internal functions to private Severity: GAS

The following functions could be set private to save gas and improve code quality:

MultiSigWallet.sol, addTransaction CoreCollection.sol, _beforeTokenTransfer ERC721Claimable.sol, _claim ERC721Claimable.sol, _setMerkelRoot CoreCollection.sol, _baseURI ERC721Payable.sol, _handlePayment

Title: Caching array length can save gas Severity: GAS

Caching the array length is more gas efficient. This is because access to a local variable in solidity is more efficient than query storage / calldata / memory. We recommend to change from:

for (uint256 i=0; i<array.length; i++) { ... }

to:

uint len = array.length for (uint256 i=0; i<len; i++) { ... } MultiSigWallet.sol, owners, 147 MultiSigWallet.sol, owners, 314 Splitter.sol, proof, 274 MultiSigWallet.sol, _owners, 98 MultiSigWallet.sol, owners, 268 CoreFactory.sol, _collections, 79 MultiSigWallet.sol, owners, 357

Title: Unnecessary constructor Severity: GAS

The following constructors are empty. (A similar issue https://github.com/code-423n4/2021-11-fei-findings/issues/12)

CoreCollection.sol.constructor MockSplitFactory.sol.constructor MyNFT.sol.constructor CoreMultiSig.sol.constructor

Title: Use != 0 instead of > 0 Severity: GAS

Using != 0 is slightly cheaper than > 0. (see https://github.com/code-423n4/2021-12-maple-findings/issues/75 for similar issue)

CoreCollection.sol, 146: change 'amount > 0' to 'amount != 0' CoreMultiSig.sol, 21: change 'balance > 0' to 'balance != 0' Splitter.sol, 164: change 'royaltyAmount > 0' to 'royaltyAmount != 0' RoyaltyVault.sol, 35: change 'balance > 0' to 'balance != 0'

Title: Unused inheritance Severity: GAS

Some of your contract inherent contracts but aren't use them at all. We recommend not to inherent those contracts. MockRoyaltyVault.sol; the inherited contracts RoyaltyVault not used ProxyVault.sol; the inherited contracts ProxyVault not used MockSplitter.sol; the inherited contracts Splitter not used

Title: Public functions to external Severity: GAS

The following functions could be set external to save gas and improve code quality. External call cost is less expensive than of public functions.

ERC721Claimable.sol, canClaim CoreCollection.sol, name MultiSigWallet.sol, replaceOwner CoreCollection.sol, symbol MultiSigWallet.sol, removeOwner MultiSigWallet.sol, revokeConfirmation MultiSigWallet.sol, getTransactionIds MultiSigWallet.sol, addOwner Splitter.sol, isClaimed MyNFT.sol, mintNFT MultiSigWallet.sol, isConfirmed ERC721Claimable.sol, claimableSet CoreCollection.sol, baseURI MultiSigWallet.sol, getConfirmationCount Splitter.sol, incrementWindow MultiSigWallet.sol, submitTransaction MultiSigWallet.sol, getConfirmations RoyaltyVault.sol, getSplitter RoyaltyVault.sol, supportsInterface MultiSigWallet.sol, getTransactionCount MultiSigWallet.sol, getOwners ERC721Claimable.sol, processProof

Title: Unused imports Severity: GAS

In the following files there are contract imports that aren't used Import of unnecessary files costs deployment gas (and is a bad coding practice that is important to ignore)

CoreCollection.sol, line 7, import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; RoyaltyVaultFactory.sol, line 3, import {ProxyVault} from "./ProxyVault.sol"; CoreProxy.sol, line 5, import {ICoreFactory} from "../interfaces/ICoreFactory.sol"; MockCollection.sol, line 3, import {ICoreCollection} from "../../interfaces/ICoreCollection.sol";

#0 - sofianeOuafir

2022-04-15T16:22:17Z

high quality report

#1 - deluca-mike

2022-04-22T06:52:10Z

  • "MultiSigWallet.sol, 270: if (count == required) return true;" is not valid, since you don't want to return count == required in the for loop.
  • "Collection CoreFactory.sol.addCollection - unnecessary casting Collection(_collection)" is unclear.
  • "In the following files there are state variables that could be set immutable to save gas." Not really since immutable is usually not compatible with the proxy pattern used here, and would prevent upgradeability.
  • "Unused state variables" lacks understanding of the current proxy pattern
  • "Unnecessary functions" not all of them
  • "Inline one time use functions" not all of them, like _beforeTokenTransfer
  • "The following functions could be set private to save gas and improve code quality" not all of them
  • "Unnecessary constructor" nope
  • "Unused inheritance" really seems like this warden is not understanding the proxy pattern, or mocks
  • "Public functions to external" not all of them
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