Platform: Code4rena
Start Date: 07/01/2022
Pot Size: $80,000 USDC
Total HM: 21
Participants: 37
Period: 7 days
Judge: 0xean
Total Solo HM: 14
Id: 71
League: ETH
Rank: 23/37
Findings: 1
Award: $349.03
🌟 Selected for report: 7
🚀 Solo Findings: 0
6.1284 INSURE - $2.14
3.2174 USDC - $3.22
Jujic
Consider reviewing all the unused imports and removing them to reduce the size of the contract and thus save some deployment gas.
import "hardhat/console.sol";
Remix
Remove test imports
#0 - oishun1112
2022-01-13T11:08:06Z
28.022 INSURE - $9.81
14.7115 USDC - $14.71
Jujic
Some of the variables can be cached to slightly reduce gas usage
function withdrawRedundant(address _token, address _to) external override onlyOwner { if ( _token == address(token) && balance < IERC20(token).balanceOf(address(this)) ) { uint256 _redundant = IERC20(token).balanceOf(address(this)) - balance; IERC20(token).safeTransfer(_to, _redundant); } else if (IERC20(_token).balanceOf(address(this)) > 0) { IERC20(_token).safeTransfer( _to, IERC20(_token).balanceOf(address(this)) ); } }
Remix
Consider caching those variable for read and make sure write back to storage Example:
bal = IERC20(_token).balanceOf(address(this);
28.022 INSURE - $9.81
14.7115 USDC - $14.71
Jujic
The use of _msgSender() when there is no implementation of a meta transaction mechanism that uses it, such as EIP-2771, very slightly increases gas consumption.
function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; }
Remix
Replace _msgSender() with msg.sender if there is no mechanism to support meta-transactions like EIP-2771 implemented.
🌟 Selected for report: Jujic
62.2711 INSURE - $21.79
32.6923 USDC - $32.69
Jujic
Checking if _amount != 0
before making the transfer call can save gas by avoiding the external call in such situations.
function borrowValue(uint256 _amount, address _to) external onlyMarket override { debts[msg.sender] += _amount; totalDebt += _amount; IERC20(token).safeTransfer(_to, _amount); }
Remix
Add additional check for non zero _amount
.
#0 - 0xHaku
2022-01-22T07:31:28Z
@oishun1112
what error message do we set at require(_amount != 0, "...");
?
#1 - 0xHaku
2022-01-26T10:28:32Z
@oishun1112
there is if (_amount != 0) {
block.
https://github.com/InsureDAO/pool-contracts/blob/e6faa61aa5ebb3fa18c974bd2cfd314f6dedcf8c/contracts/Vault.sol#L224
how to solve ?
#2 - oishun1112
2022-01-31T05:30:44Z
it's already fixed
🌟 Selected for report: Jujic
62.2711 INSURE - $21.79
32.6923 USDC - $32.69
Jujic
function rate() external view returns (uint256) { if (totalSupply() > 0) { return (totalLiquidity() * MAGIC_SCALE_1E6) / totalSupply(); } else { return 0; } }
Remix
Change to:
function rate() external view returns (uint256) { if (totalSupply() != 0) { return (totalLiquidity() * MAGIC_SCALE_1E6) / totalSupply(); }
🌟 Selected for report: Jujic
62.2711 INSURE - $21.79
32.6923 USDC - $32.69
Jujic
Where possible, use equivalent function parameters or local variables in event emits instead of state variables to prevent expensive SLOADs.
marketStatus = MarketStatus.Trading; emit MarketStatusChanged(MarketStatus.Trading); }
Remix
Use equivalent function parameters or local variables in event emits instead of state variables.
🌟 Selected for report: Jujic
Also found by: TomFrenchBlockchain, WatchPug
16.8132 INSURE - $5.88
8.8269 USDC - $8.83
Jujic
Insurance struct in PoolTemplate .sol
can be optimized to reduce 2 storage slot
struct Insurance { uint256 id; //each insuance has their own id uint256 startTime; //timestamp of starttime uint256 endTime; //timestamp of endtime uint256 amount; //insured amount bytes32 target; //target id in bytes32 address insured; //the address holds the right to get insured bool status; //true if insurance is not expired or redeemed }
startTime
and endTime
store block numbers, and 2^48 is being enough for a very long time.
The struct can be changed into:
struct Insurance { uint256 id; //each insuance has their own id uint48 startTime; //timestamp of starttime uint48 endTime; //timestamp of endtime address insured; //the address holds the right to get insured uint256 amount; //insured amount bytes32 target; //target id in bytes32 bool status; //true if insurance is not expired or redeemed }
#0 - oishun1112
2022-01-14T05:49:07Z
there is other issue# that mention about this
#1 - oishun1112
2022-01-28T03:00:30Z
@blue32captain could you put the link of the commit here?
#2 - blue32captain
2022-01-28T07:42:25Z
https://github.com/InsureDAO/pool-contracts/commit/5dc65d3e8ba7d978e40676930be7d286b837e9de I think you've already merged this.
🌟 Selected for report: Jujic
62.2711 INSURE - $21.79
32.6923 USDC - $32.69
Jujic
The constructor is empty. You should remove constructor to save some gas.
constructor() {}
Remove unused constructor
16.8132 INSURE - $5.88
8.8269 USDC - $8.83
Jujic
Each function part of contract's external interface is part of the function dispatch, i.e., every time a contract is called, it goes through a switch statement (a set of eq ... JUMPI blocks in EVM) matching the selector of each externally available functions with the chosen function selector (the first 4 bytes of calldata). This means that any unnecessary function that is part of contract's external interface will lead to more gas for (almost) every single function calls to the contract. There are several cases where constants were made public. This is unnecessary; the constants can simply be readfrom the verified contract, i.e., it is unnecessary to expose it with a public function.
uint256 public constant MAGIC_SCALE_1E6 = 1e6; //internal multiplication scale 1e6 to reduce decimal truncation
Remix
#0 - oishun1112
2022-01-17T08:10:06Z
2.9784 INSURE - $1.04
1.5637 USDC - $1.56
Jujic
!= 0 is a cheaper operation compared to > 0, when dealing with uint.
require( attributions[msg.sender] > 0 && underlyingValue(msg.sender) >= _amount, "ERROR_REPAY_DEBT_BADCONDITOONS" );
Remix
The same situation are in other scope contracts where use > 0.
#0 - oishun1112
2022-01-13T17:53:01Z
#1 - 0xean
2022-01-28T00:09:17Z
dupe of #36
2.4125 INSURE - $0.84
1.2666 USDC - $1.27
Jujic
The local variable used as for loop index need not be initialized to 0 because the default value is 0. Avoiding this anti-pattern can save a few opcodes and therefore a tiny bit of gas.
The same situation are in other scope contracts where loops use.
for (uint256 i = 0; i < _ids.length; i++)
Remix
Remove explicit 0 initialization of for loop index variable. Before: for (uint256 i = 0; After for (uint256 i;
#0 - oishun1112
2022-01-13T16:59:45Z
Jujic
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.
function _accruedPremiums() internal view returns (uint256 _totalValue) { for (uint256 i = 0; i < poolList.length; i++) { if (allocPoints[poolList[i]] > 0) { _totalValue = _totalValue + IPoolTemplate(poolList[i]).pendingPremium(address(this)); } } }
Remix
Caching len = poolList.length
and using the len
instead will save gas.
#0 - oishun1112
2022-01-13T11:32:43Z
16.8132 INSURE - $5.88
8.8269 USDC - $8.83
Jujic
There is the loop that use uint128 for an index parameter (i). It does not give any efficiency, actually, it is the opposite as EVM operates on default of 256-bit values so uint128 is more expensive in this case as it needs a conversion. It only gives improvements in cases where you can pack variables together, e.g. structs.
for (uint128 i = 0; i < 2; i++) {...}
Remix
#0 - oishun1112
2022-01-13T11:06:23Z
6.1284 INSURE - $2.14
3.2174 USDC - $3.22
Jujic
For the arithmetic operations that will never over/underflow, using the unchecked directive (Solidity v0.8 has default overflow/underflow checks) can save some gas from the unnecessary internal over/underflow checks.
if (_allocated > _availableBalance) { uint256 _availableRate = (_availableBalance * MAGIC_SCALE_1E6) / _allocated; uint256 _lockedCredit = _allocated - _availableBalance;
Remix
Consider using 'unchecked' where it is safe to do so.
#0 - oishun1112
2022-01-16T07:27:02Z
🌟 Selected for report: Dravee
Also found by: 0x1f8b, Jujic, TomFrenchBlockchain, csanuragjain, defsec, gzeon, loop, robee
2.9784 INSURE - $1.04
1.5637 USDC - $1.56
Jujic
Address token
is set on initialization, doesn't change afterwards and should be immutable
address public override token; constructor( address _token, address _registry, address _controller, address _ownership ) {...}
Remix
#0 - oishun1112
2022-01-13T14:24:03Z
🌟 Selected for report: 0xngndev
Also found by: Dravee, Jujic, Meta0xNull, defsec, p4st13r4, robee, sirhashalot, tqts
2.9784 INSURE - $1.04
1.5637 USDC - $1.56
Jujic
Revert strings that are longer than 32 bytes require at least one additional mstore, along with additional overhead for computing memory offset, etc.
modifier onlyOwner() { require( ownership.owner() == msg.sender, "Restricted: caller is not allowed to operate" ); _; }
The same situation are in other scope contracts where require use.
Shorten the revert strings to fit in 32 bytes.
#0 - oishun1112
2022-01-13T17:56:19Z
16.8132 INSURE - $5.88
8.8269 USDC - $8.83
Jujic
The solc compiler 0.8 will give the in-built overflow/underflow checks which may give gas savings by avoiding expensive SafeMath.
pragma solidity 0.8.7; import "@openzeppelin/contracts/utils/math/SafeMath.sol";
Remix
Remove safeMath operations
#0 - oishun1112
2022-01-13T11:21:42Z
#1 - 0xean
2022-01-28T00:11:21Z
dupe of #38
2.9784 INSURE - $1.04
1.5637 USDC - $1.56
Jujic
Here you could use unchecked{++i} to save gas since it is more efficient then i++.
for (uint256 i = 0; i < _references.length; i++) {...}
Remix
The same situation are in other scope contracts where loops use.
#0 - oishun1112
2022-01-13T17:55:59Z