Platform: Code4rena
Start Date: 07/04/2023
Pot Size: $47,000 USDC
Total HM: 20
Participants: 120
Period: 6 days
Judge: GalloDaSballo
Total Solo HM: 4
Id: 230
League: ETH
Rank: 70/120
Findings: 1
Award: $31.00
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: AkshaySrivastav
Also found by: 0x5rings, 0xbepresent, ABA, Bauchibred, BenRai, DadeKuma, ElKu, RaymondFam, Rolezn, adriro, btk, chaduke, devscrooge, dingo2077, minhtrng, nemveer, p0wd3r, rbserver, ulqiorra
30.9954 USDC - $31.00
Factory.sol L27 EthRouter.sol L32
Solmate’s SafeTransferLib, which is often used to interact with non-compliant/unsafe ERC20 tokens, does not check whether the ERC20 contract exists. The following code will not revert in case the token doesn’t exist (yet).
This is stated in the Solmate library: https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol#L9
Add a contract exist control in functions
Low level calls return success if called on a destructed contract. See OpenZeppelin’s Address.so which checks address.code.length
There is 1 instance of this issue:
function execute(address target, bytes memory data) public payable onlyOwner returns (bytes memory) { // call the target with the value and data (bool success, bytes memory returnData) = target.call{value: msg.value}(data); // if the call succeeded return the return data if (success) return returnData; // if we got an error bubble up the error message if (returnData.length > 0) { // solhint-disable-next-line no-inline-assembly assembly { let returnData_size := mload(returnData) revert(add(32, returnData), returnData_size) } } else { revert(); } }
If the target
address doesn't exist, the call will return true and the function won't revert.
Check before any low-level call that the address actually exists, for example before the low level call in the callERC20 function you can check that the address is a contract by checking its code size.
The royalRegisrty
address is an immutable one once set, advisably minimal checks like the zero address check should be implemented to ensure that the address does not mistakenly get set to a wrong one.
safeTransferOwnership
instead of transferOwnership
functionFactory.sol L26 Factory.sol L37 transferOwnership function is used to change Ownership from Owned.sol.
Use a two structure transferOwnership which is safer. safeTransferOwnership,is known to be more secure due to its two-stage ownership transfer.
Use Ownable2Step.sol
EthRouter.buy/sell()
possible OOG errorEthRouter.buy() EthRouter.sell()
Both functions takes an array and makes a lot of computation on each iteration, a length limit should be provided to the passed in array length
Factory.sol
missing Zero address checksMultiple instances within contracts in scope:
Lack of zero address checks procedure for critical operations leaves them error-prone. Consider adding zero address checks on the critical functions.
Multiple instances in code:
PrivatePool.sumWeightsAndValidateProof()
These array memory parameter can be problematic if not used properly , if the array is very large it may overlap over other part of memory.
check array length before using it
PrivatePool.setVirtualReserves()
PrivatePool.setVirtualReserves()
Minimal checks should be added to check that the virtual reserves shouldn't be set to 0
PrivatePool.initialize()
and constructor
[constructor] (https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/PrivatePool.sol#L157-L200)
Some variables are immutable after being set in the constructor/initializer, so additional checks like zero address, defaulting to 0, would produce a leap in protective layers.
#0 - GalloDaSballo
2023-05-01T06:42:42Z
safeTransferOwnership
instead of transferOwnership
functionEthRouter.buy/sell()
possible OOG errorFactory.sol
missing Zero address checksPrivatePool.setVirtualReserves()
PrivatePool.initialize()
and constructor
#1 - c4-judge
2023-05-01T09:17:03Z
GalloDaSballo marked the issue as grade-b