Arcade.xyz - Aymen0909's results

The first of its kind Web3 platform to enable liquid lending markets for NFTs.

General Information

Platform: Code4rena

Start Date: 21/07/2023

Pot Size: $90,500 USDC

Total HM: 8

Participants: 60

Period: 7 days

Judge: 0xean

Total Solo HM: 2

Id: 264

League: ETH

Arcade.xyz

Findings Distribution

Researcher Performance

Rank: 51/60

Findings: 1

Award: $42.55

Gas:
grade-b

🌟 Selected for report: 0

πŸš€ Solo Findings: 0

Findings Information

🌟 Selected for report: K42

Also found by: Aymen0909, Raihan, SM3_SS, Sathish9098, c3phas, dharma09, excalibor, jeffy, kaveyjoe

Labels

bug
G (Gas Optimization)
grade-b
G-03

Awards

42.5548 USDC - $42.55

External Links

Summary

IssueInstances
1State variables should be packed to save storage slots2
2Multiple address/IDs mappings can be combined into a single mapping of an address/id to a struct2
3storage variable should be cached into memory variables instead of re-reading them5
4Use unchecked blocks to save gas2

Findings

1- State variables should be packed to save storage slots :

State variables inside contract should be packed if possible to save storage slots and thus will save gas cost.

There are 2 instances of this issue:

  • Instance 1 :
File: token/ArcadeToken.sol 94 address public minter; 97 uint256 public mintingAllowedAfter;

As the variable mintingAllowedAfter represent a timestamp value, it can't realistically overflow 2^96, so it should be packed with the address variable minter (which only takes 20 bytes), this will save 1 storage slots, the code should be modified as follow :

/// @notice Minter contract address responsible for minting future tokens address public minter; /// @notice The timestamp after which minting may occur uint96 public mintingAllowedAfter;
  • Instance 2 :
File: token/ArcadeTokenDistributor.sol 34 bool public treasurySent; 39 bool public devPartnerSent; 44 bool public communityRewardsSent; 49 bool public communityAirdropSent; 54 bool public vestingTeamSent; 59 bool public vestingPartnerSent;

All the variables in the code above are of type boolean which only takes 1 bit in storage, so they can all be packed together to save gas save 5 storage slots, the code should be rearranged as follows :

/// @notice The Arcade Token contract to be used in token distribution. IArcadeToken public arcadeToken; /// @notice A flag to indicate if the treasury has already been transferred to bool public treasurySent; /// @notice A flag to indicate if the token development partner has already been transferred to. bool public devPartnerSent; /// @notice A flag to indicate if the token development partner has already been transferred to. bool public devPartnerSent; /// @notice A flag to indicate if the community airdrop contract has already been transferred to bool public communityAirdropSent; /// @notice A flag to indicate if the launch partners have already been transferred to bool public vestingTeamSent; /// @notice A flag to indicate if the Arcade team has already been transferred to bool public vestingPartnerSent; /// @notice 25.5% of initial distribution is for the treasury uint256 public constant treasuryAmount = 25_500_000 ether; /// @notice 0.6% of initial distribution is for the token development partner uint256 public constant devPartnerAmount = 600_000 ether; /// @notice 15% of initial distribution is for the community rewards pool uint256 public constant communityRewardsAmount = 15_000_000 ether; /// @notice 10% of initial distribution is for the community airdrop contract uint256 public constant communityAirdropAmount = 10_000_000 ether; /// @notice 16.2% of initial distribution is for the Arcade team uint256 public constant vestingTeamAmount = 16_200_000 ether; /// @notice 32.7% of initial distribution is for Arcade's launch partners uint256 public constant vestingPartnerAmount = 32_700_000 ether;

2- Multiple address/IDs mappings can be combined into a single mapping of an address/id to a struct :

Saves a storage slot for the mapping. Depending on the circumstances and sizes of types, can avoid a Gsset (20000 gas) per mapping combined. Reads and subsequent writes can also be cheaper when a function requires both values and they both fit in the same storage slot. Finally, if both fields are accessed in the same function, can save ~42 gas per access due to not having to recalculate the key's keccak256 hash (Gkeccak256 - 30 gas) and that calculation's associated stack operations.

There are 2 instances of this issue :

File: ReputationBadge.sol Line 51-58

52:     mapping(uint256 => bytes32) public claimRoots;
55:     mapping(uint256 => uint48) public claimExpirations;
58:     mapping(uint256 => uint256) public mintPrices;

File: ArcadeTreasury.sol Line 58-65

59:     mapping(address => uint48) public lastAllowanceSet;
62:     mapping(address => SpendThreshold) public spendThresholds;
65:     mapping(address => uint256) public gscAllowance;

3- storage variable should be cached into memory variables instead of re-reading them :

The instances below point to the second+ access of a state variable within a function, the caching of a state variable replaces each Gwarmaccess (100 gas) with a much cheaper stack read, thus saves 100gas for each instance.

There are 5 instance of this issue :

File: ARCDVestingVault.sol Line 145-148

In the code linked above the value of grant.delegatee is read multiple times (3) from storage and it's value does not change so it should be cached into a memory variable in order to save gas (using delegatee value) by avoiding multiple reads from storage.

File: ARCDVestingVault.sol Line 145-148

In the code linked above the value of grant.delegatee is read multiple times (3) from storage and it's value does not change so it should be cached into a memory variable in order to save gas by avoiding multiple reads from storage.

File: ARCDVestingVault.sol Line 262-269

In the code linked above the value of grant.delegatee is read multiple times (4) from storage and it's value does not change so it should be cached into a memory variable in order to save gas by avoiding multiple reads from storage.

File: ARCDVestingVault.sol Line 268-281

In the code linked above the value of grant.latestVotingPower is read multiple times (4) from storage and it's value does not change so it should be cached into a memory variable in order to save gas by avoiding multiple reads from storage.

File: ARCDVestingVault.sol Line 344-356

In the code linked above the value of grant.delegatee is read multiple times (3) from storage and it's value does not change so it should be cached into a memory variable in order to save gas by avoiding multiple reads from storage.

4- Use unchecked blocks to save gas :

Solidity version 0.8+ comes with implicit overflow and underflow checks on unsigned integers. When an overflow or an underflow isn’t possible (as an example, when a comparison is made before the arithmetic operation), some gas can be saved by using an unchecked block.

There are 2 instances of this issue:

File: ARCDVestingVault.sol Line 141

unassigned.data -= amount;

In the code linked above the operation cannot underflow because of the check in line 115.

File: ARCDVestingVault.sol Line 215

unassigned.data -= amount;

In the code linked above the operation cannot underflow because of the check in line 213.

#0 - c4-judge

2023-08-10T22:56:33Z

0xean marked the issue as grade-b

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