Platform: Code4rena
Start Date: 21/12/2023
Pot Size: $90,500 USDC
Total HM: 10
Participants: 39
Period: 18 days
Judge: LSDan
Total Solo HM: 5
Id: 315
League: ETH
Rank: 8/39
Findings: 2
Award: $793.49
š Selected for report: 1
š Solo Findings: 0
š Selected for report: IllIllI
Also found by: 0x11singh99, 0xA5DF, 0xMilenov, 0xTheC0der, 7ashraf, Bauchibred, EV_om, Kaysoft, Sathish9098, SpicyMeatball, cheatc0d3, erebus, hash, imare, immeas, joaovwfreire, lil_eth, lsaudit, oakcobalt, para8956, peanuts, rvierdiiev, slvDev, trachev
352.4857 USDC - $352.49
Note: I've removed the instances reported by the winning bot and 4naly3er
Issue | Instances | |
---|---|---|
[Nā01] | else -block not required | 1 |
[Nā02] | public functions not called by the contract should be declared external instead | 2 |
[Nā03] | Common functions should be refactored to a common base contract | 8 |
[Nā04] | Consider making contracts Upgradeable | 22 |
[Nā05] | Consider moving duplicated strings to constants | 4 |
[Nā06] | Consider using delete rather than assigning zero/false to clear values | 3 |
[Nā07] | Consider using descriptive constant s when passing zero as a function argument | 22 |
[Nā08] | Consider using named function arguments | 19 |
[Nā09] | Consider using named returns | 63 |
[Nā10] | Constants in comparisons should appear on the left side | 10 |
[Nā11] | Contract timekeeping will break earlier than the Ethereum network itself will stop working | 1 |
[Nā12] | Contracts/libraries/interfaces should each be defined in separate files | 12 |
[Nā13] | Function state mutability can be restricted to view | 6 |
[Nā14] | Inconsistent spacing in comments | 2 |
[Nā15] | Interfaces should be defined in separate files from their usage | 1 |
[Nā16] | Named imports of parent contracts are missing | 17 |
[Nā17] | NatSpec: Event @param tag is missing | 149 |
[Nā18] | NatSpec: Function @param tag is missing | 1 |
[Nā19] | NatSpec: Function @return tag is missing | 8 |
[Nā20] | Not using the named return variables anywhere in the function is confusing | 4 |
[Nā21] | Style guide: Local and state variables should be named using lowerCamelCase | 1 |
[Nā22] | Style guide: Non-external /public variable names should begin with an underscore | 3 |
[Nā23] | Style guide: Top-level declarations should be separated by at least two lines | 2 |
[Nā24] | Unnecessary cast | 3 |
[Nā25] | Unused error definition | 9 |
[Nā26] | Unused public contract variable | 9 |
[Nā27] | Use bytes.concat() on bytes instead of abi.encodePacked() for clearer semantic meaning | 1 |
[Nā28] | Use bit shifts to represent powers of two | 7 |
[Nā29] | Use of override is unnecessary | 14 |
Total: 404 instances over 29 issues
else
-block not requiredOne level of nesting can be removed by not having an else
block when the if
-block returns, and if (foo) { return 1; } else { return 2; }
becomes if (foo) { return 1; } return 2;
. A following else if
can become if
There is one instance of this issue:
File: contracts/veOLAS.sol 265 if (tStep == block.timestamp) { 266 lastPoint.blockNumber = uint64(block.number); 267 lastPoint.balance = curSupply; 268: break;
GitHub: 265
public
functions not called by the contract should be declared external
insteadContracts are allowed to override their parents' functions and change the visibility from external
to public
.
There are 2 instances of this issue:
File: contracts/veOLAS.sol 761: function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
GitHub: 761
File: contracts/GenericRegistry.sol 135: function tokenURI(uint256 unitId) public view virtual override returns (string memory) {
GitHub: 135
The functions below have the same implementation as is seen in other files. The functions should be refactored into functions of a common base contract
There are 8 instances of this issue:
<details> <summary>see instances</summary>File: contracts/GenericManager.sol 20 function changeOwner(address newOwner) external virtual { 21 // Check for the ownership 22 if (msg.sender != owner) { 23 revert OwnerOnly(msg.sender, owner); 24 } 25 26 // Check for the zero address 27 if (newOwner == address(0)) { 28 revert ZeroAddress(); 29 } 30 31 owner = newOwner; 32 emit OwnerUpdated(newOwner); 33: }
GitHub: 20
File: contracts/GenericRegistry.sol 37 function changeOwner(address newOwner) external virtual { 38 // Check for the ownership 39 if (msg.sender != owner) { 40 revert OwnerOnly(msg.sender, owner); 41 } 42 43 // Check for the zero address 44 if (newOwner == address(0)) { 45 revert ZeroAddress(); 46 } 47 48 owner = newOwner; 49 emit OwnerUpdated(newOwner); 50: }
GitHub: 37
File: contracts/Depository.sol 123 function changeOwner(address newOwner) external { 124 // Check for the contract ownership 125 if (msg.sender != owner) { 126 revert OwnerOnly(msg.sender, owner); 127 } 128 129 // Check for the zero address 130 if (newOwner == address(0)) { 131 revert ZeroAddress(); 132 } 133 134 owner = newOwner; 135 emit OwnerUpdated(newOwner); 136: } 143 function changeManagers(address _tokenomics, address _treasury) external { 144 // Check for the contract ownership 145 if (msg.sender != owner) { 146 revert OwnerOnly(msg.sender, owner); 147 } 148 149 // Change Tokenomics contract address 150 if (_tokenomics != address(0)) { 151 tokenomics = _tokenomics; 152 emit TokenomicsUpdated(_tokenomics); 153 } 154 // Change Treasury contract address 155 if (_treasury != address(0)) { 156 treasury = _treasury; 157 emit TreasuryUpdated(_treasury); 158 } 159: }
File: contracts/Dispenser.sol 46 function changeOwner(address newOwner) external { 47 // Check for the contract ownership 48 if (msg.sender != owner) { 49 revert OwnerOnly(msg.sender, owner); 50 } 51 52 // Check for the zero address 53 if (newOwner == address(0)) { 54 revert ZeroAddress(); 55 } 56 57 owner = newOwner; 58 emit OwnerUpdated(newOwner); 59: }
GitHub: 46
File: contracts/DonatorBlacklist.sol 36 function changeOwner(address newOwner) external { 37 // Check for the contract ownership 38 if (msg.sender != owner) { 39 revert OwnerOnly(msg.sender, owner); 40 } 41 42 // Check for the zero address 43 if (newOwner == address(0)) { 44 revert ZeroAddress(); 45 } 46 47 owner = newOwner; 48 emit OwnerUpdated(newOwner); 49: }
GitHub: 36
File: contracts/Tokenomics.sol 404 function changeOwner(address newOwner) external { 405 // Check for the contract ownership 406 if (msg.sender != owner) { 407 revert OwnerOnly(msg.sender, owner); 408 } 409 410 // Check for the zero address 411 if (newOwner == address(0)) { 412 revert ZeroAddress(); 413 } 414 415 owner = newOwner; 416 emit OwnerUpdated(newOwner); 417: }
GitHub: 404
File: contracts/Treasury.sol 137 function changeOwner(address newOwner) external { 138 // Check for the contract ownership 139 if (msg.sender != owner) { 140 revert OwnerOnly(msg.sender, owner); 141 } 142 143 // Check for the zero address 144 if (newOwner == address(0)) { 145 revert ZeroAddress(); 146 } 147 148 owner = newOwner; 149 emit OwnerUpdated(newOwner); 150: }
GitHub: 137
</details>Upgradeable
This allows for bugs to be fixed in production, at the expense of significantly increasing centralization.
There are 22 instances of this issue:
<details> <summary>see instances</summary>File: contracts/OLAS.sol 17: contract OLAS is ERC20 {
GitHub: 17
File: contracts/Timelock.sol 9: contract Timelock is TimelockController {
GitHub: 9
File: contracts/bridges/BridgedERC20.sol 18: contract BridgedERC20 is ERC20 {
GitHub: 18
File: contracts/bridges/FxERC20ChildTunnel.sol 24: contract FxERC20ChildTunnel is FxBaseChildTunnel {
GitHub: 24
File: contracts/bridges/FxERC20RootTunnel.sol 24: contract FxERC20RootTunnel is FxBaseRootTunnel {
GitHub: 24
File: contracts/bridges/FxGovernorTunnel.sol 46: contract FxGovernorTunnel is IFxMessageProcessor {
GitHub: 46
File: contracts/bridges/HomeMediator.sol 46: contract HomeMediator {
GitHub: 46
File: contracts/multisigs/GuardCM.sol 88: contract GuardCM {
GitHub: 88
File: contracts/veOLAS.sol 86: contract veOLAS is IErrors, IVotes, IERC20, IERC165 {
GitHub: 86
File: contracts/wveOLAS.sol 130 contract wveOLAS { 131: // veOLAS address
GitHub: 130
File: contracts/AgentRegistry.sol 9 contract AgentRegistry is UnitRegistry { 10: // Component registry
GitHub: 9
File: contracts/ComponentRegistry.sol 8 contract ComponentRegistry is UnitRegistry { 9: // Component registry version number
GitHub: 8
File: contracts/RegistriesManager.sol 9 contract RegistriesManager is GenericManager { 10: // Component registry address
GitHub: 9
File: contracts/multisigs/GnosisSafeMultisig.sol 24 contract GnosisSafeMultisig { 25: // Selector of the Gnosis Safe setup function
GitHub: 24
File: contracts/multisigs/GnosisSafeSameAddressMultisig.sol 50 contract GnosisSafeSameAddressMultisig { 51 // Default data size to be parsed as an address of a Gnosis Safe multisig proxy address 52: // This exact size suggests that all the changes to the multisig have been performed and only validation is needed
GitHub: 50
File: contracts/Depository.sol 62: contract Depository is IErrorsTokenomics {
GitHub: 62
File: contracts/Dispenser.sol 11: contract Dispenser is IErrorsTokenomics {
GitHub: 11
File: contracts/DonatorBlacklist.sol 20: contract DonatorBlacklist {
GitHub: 20
File: contracts/GenericBondCalculator.sol 20 contract GenericBondCalculator { 21: // OLAS contract address
GitHub: 20
File: contracts/Tokenomics.sol 118: contract Tokenomics is TokenomicsConstants, IErrorsTokenomics {
GitHub: 118
File: contracts/TokenomicsProxy.sol 26 contract TokenomicsProxy { 27: // Code position in storage is keccak256("PROXY_TOKENOMICS") = "0xbd5523e7c3b6a94aa0e3b24d1120addc2f95c7029e097b466b2bedc8d4b4362f"
GitHub: 26
File: contracts/Treasury.sol 39: contract Treasury is IErrorsTokenomics {
GitHub: 39
</details>There are 4 instances of this issue:
File: contracts/AgentRegistry.sol 13: string public constant VERSION = "1.0.0";
GitHub: 13
File: contracts/ComponentRegistry.sol 10: string public constant VERSION = "1.0.0";
GitHub: 10
File: contracts/Depository.sol 77: string public constant VERSION = "1.0.1";
GitHub: 77
File: contracts/TokenomicsConstants.sol 11: string public constant VERSION = "1.0.1";
GitHub: 11
delete
rather than assigning zero/false to clear valuesThe delete
keyword more closely matches the semantics of what is being done, and draws more attention to the changing of state, which may lead to a more thorough audit of its associated logic
There are 3 instances of this issue:
File: contracts/GenericManager.sol 53: paused = false;
GitHub: 53
File: contracts/Treasury.sol 455: success = false; 518: mapEnabledTokens[token] = false;
constant
s when passing zero as a function argumentPassing zero as a function argument can sometimes result in a security issue (e.g. passing zero as the slippage parameter). Consider using a constant
variable with a descriptive name, so it's clear that the argument is intentionally being used, and for the right reasons.
There are 22 instances of this issue:
File: contracts/veOLAS.sol 139: mapSupplyPoints[0] = PointVoting(0, 0, uint64(block.timestamp), uint64(block.number), 0); 139: mapSupplyPoints[0] = PointVoting(0, 0, uint64(block.timestamp), uint64(block.number), 0); 139: mapSupplyPoints[0] = PointVoting(0, 0, uint64(block.timestamp), uint64(block.number), 0); 215: lastPoint = PointVoting(0, 0, uint64(block.timestamp), uint64(block.number), 0); 215: lastPoint = PointVoting(0, 0, uint64(block.timestamp), uint64(block.number), 0); 215: lastPoint = PointVoting(0, 0, uint64(block.timestamp), uint64(block.number), 0); 321: _checkpoint(address(0), LockedBalance(0, 0), LockedBalance(0, 0), uint128(supply)); 321: _checkpoint(address(0), LockedBalance(0, 0), LockedBalance(0, 0), uint128(supply)); 321: _checkpoint(address(0), LockedBalance(0, 0), LockedBalance(0, 0), uint128(supply)); 321: _checkpoint(address(0), LockedBalance(0, 0), LockedBalance(0, 0), uint128(supply)); 321: _checkpoint(address(0), LockedBalance(0, 0), LockedBalance(0, 0), uint128(supply)); 396: _depositFor(account, amount, 0, lockedBalance, DepositType.DEPOSIT_FOR_TYPE); 478: _depositFor(msg.sender, amount, 0, lockedBalance, DepositType.INCREASE_LOCK_AMOUNT); 506: _depositFor(msg.sender, 0, unlockTime, lockedBalance, DepositType.INCREASE_UNLOCK_TIME); 517: mapLockedBalances[msg.sender] = LockedBalance(0, 0); 517: mapLockedBalances[msg.sender] = LockedBalance(0, 0); 528: _checkpoint(msg.sender, lockedBalance, LockedBalance(0, 0), uint128(supplyAfter)); 528: _checkpoint(msg.sender, lockedBalance, LockedBalance(0, 0), uint128(supplyAfter)); 650: (point, minPointNumber) = _findPointByBlock(blockNumber, address(0)); 728: (PointVoting memory sPoint, ) = _findPointByBlock(blockNumber, address(0));
GitHub: 139, 139, 139, 215, 215, 215, 321, 321, 321, 321, 321, 396, 478, 506, 517, 517, 528, 528, 650, 728
File: contracts/Tokenomics.sol 329: uint256 _inflationPerSecond = getInflationForYear(0) / zeroYearSecondsLeft;
GitHub: 329
File: contracts/Treasury.sol 408: revert TransferFailed(address(0), address(this), account, accountRewards);
GitHub: 408
When calling functions in external contracts with multiple arguments, consider using named function parameters, rather than positional ones.
There are 19 instances of this issue:
<details> <summary>see instances</summary>File: contracts/bridges/FxERC20RootTunnel.sol 77: IERC20(rootToken).mint(to, amount);
GitHub: 77
File: contracts/wveOLAS.sol 197: uPoint = IVEOLAS(ve).getUserPoint(account, idx); 216: balance = IVEOLAS(ve).getPastVotes(account, blockNumber); 236: balance = IVEOLAS(ve).balanceOfAt(account, blockNumber);
File: contracts/RegistriesManager.sol 39: unitId = IRegistry(componentRegistry).create(unitOwner, unitHash, dependencies); 41: unitId = IRegistry(agentRegistry).create(unitOwner, unitHash, dependencies); 52: success = IRegistry(componentRegistry).updateHash(msg.sender, unitId, unitHash); 54: success = IRegistry(agentRegistry).updateHash(msg.sender, unitId, unitHash);
File: contracts/multisigs/GnosisSafeMultisig.sol 106: multisig = IGnosisSafeProxyFactory(gnosisSafeProxyFactory).createProxyWithNonce(gnosisSafe, safeParams, nonce);
GitHub: 106
File: contracts/Depository.sol 320: payout = IGenericBondCalculator(bondCalculator).calculatePayoutOLAS(tokenAmount, product.priceLP); 337: ITreasury(treasury).depositTokenForOLAS(msg.sender, tokenAmount, token, payout);
File: contracts/Dispenser.sol 99: (reward, topUp) = ITokenomics(tokenomics).accountOwnerIncentives(msg.sender, unitTypes, unitIds); 104: success = ITreasury(treasury).withdrawToAccount(msg.sender, reward, topUp);
File: contracts/Tokenomics.sol 716 (uint256 numServiceUnits, uint32[] memory serviceUnitIds) = IServiceRegistry(serviceRegistry). 717: getUnitIdsOfService(IServiceRegistry.UnitType(unitType), serviceIds[i]);
GitHub: 716
File: contracts/Treasury.sol 228: if (IToken(token).allowance(account, address(this)) < tokenAmount) { 229: revert InsufficientAllowance(IToken(token).allowance((account), address(this)), tokenAmount); 242: IOLAS(olas).mint(msg.sender, olasMintAmount); 298: ITokenomics(tokenomics).trackServiceDonations(msg.sender, serviceIds, amounts, msg.value); 416: IOLAS(olas).mint(account, accountTopUps);
GitHub: 228, 229, 242, 298, 416
</details>Using named returns makes the code more self-documenting, makes it easier to fill out NatSpec, and in some cases can save gas. The cases below are where there currently is at most one return statement, which is ideal for named returns.
There are 63 instances of this issue:
<details> <summary>see instances</summary>File: contracts/GovernorOLAS.sol 33 function state(uint256 proposalId) public view override(IGovernor, Governor, GovernorTimelockControl) 34 returns (ProposalState) 35: { 45 function propose( 46 address[] memory targets, 47 uint256[] memory values, 48 bytes[] memory calldatas, 49 string memory description 50 ) public override(IGovernor, Governor, GovernorCompatibilityBravo) returns (uint256) 51: { 57 function proposalThreshold() public view override(Governor, GovernorSettings) returns (uint256) 58: { 85 function _cancel( 86 address[] memory targets, 87 uint256[] memory values, 88 bytes[] memory calldatas, 89 bytes32 descriptionHash 90 ) internal override(Governor, GovernorTimelockControl) returns (uint256) 91: { 97 function _executor() internal view override(Governor, GovernorTimelockControl) returns (address) 98: { 105 function supportsInterface(bytes4 interfaceId) public view override(IERC165, Governor, GovernorTimelockControl) 106 returns (bool) 107: {
GitHub: 33, 45, 57, 85, 97, 105
File: contracts/OLAS.sol 91: function inflationControl(uint256 amount) public view returns (bool) { 128: function decreaseAllowance(address spender, uint256 amount) external returns (bool) { 145: function increaseAllowance(address spender, uint256 amount) external returns (bool) {
File: contracts/bridges/HomeMediator.sol 6: function messageSender() external view returns (address);
GitHub: 6
File: contracts/interfaces/IERC20.sol 9: function balanceOf(address account) external view returns (uint256); 13: function totalSupply() external view returns (uint256); 19: function allowance(address owner, address spender) external view returns (uint256); 25: function approve(address spender, uint256 amount) external returns (bool); 31: function transfer(address to, uint256 amount) external returns (bool); 38: function transferFrom(address from, address to, uint256 amount) external returns (bool);
File: contracts/multisigs/GuardCM.sol 7: function state(uint256 proposalId) external returns (ProposalState);
GitHub: 7
File: contracts/veOLAS.sol 164: function getUserPoint(address account, uint256 idx) external view returns (PointVoting memory) { 633: function getVotes(address account) public view override returns (uint256) { 719: function totalSupply() public view override returns (uint256) { 738: function totalSupplyLockedAtT(uint256 ts) public view returns (uint256) { 745: function totalSupplyLocked() public view returns (uint256) { 752: function getPastTotalSupply(uint256 blockNumber) public view override returns (uint256) { 761: function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { 767: function transfer(address to, uint256 amount) external virtual override returns (bool) { 772: function approve(address spender, uint256 amount) external virtual override returns (bool) { 777: function transferFrom(address from, address to, uint256 amount) external virtual override returns (bool) { 782 function allowance(address owner, address spender) external view virtual override returns (uint256) 783: { 788 function delegates(address account) external view virtual override returns (address) 789: {
GitHub: 164, 633, 719, 738, 745, 752, 761, 767, 772, 777, 782, 788
File: contracts/wveOLAS.sol 98: function supportsInterface(bytes4 interfaceId) external view returns (bool); 101: function allowance(address owner, address spender) external view returns (uint256); 104: function delegates(address account) external view returns (address); 292: function supportsInterface(bytes4 interfaceId) external view returns (bool) { 297: function transfer(address, uint256) external returns (bool) { 302: function approve(address, uint256) external returns (bool) { 307: function transferFrom(address, address, uint256) external returns (bool) { 312: function allowance(address, address) external view returns (uint256) { 317: function delegates(address) external view returns (address) {
GitHub: 98, 101, 104, 292, 297, 302, 307, 312, 317
File: contracts/GenericRegistry.sol 72: function exists(uint256 unitId) external view virtual returns (bool) { 129: function _getUnitHash(uint256 unitId) internal view virtual returns (bytes32); 135: function tokenURI(uint256 unitId) public view virtual override returns (string memory) {
File: contracts/UnitRegistry.sol 270: function _getUnitHash(uint256 unitId) internal view override returns (bytes32) {
GitHub: 270
File: contracts/interfaces/IRegistry.sol 16 function create( 17 address unitOwner, 18 bytes32 unitHash, 19 uint32[] memory dependencies 20: ) external returns (uint256); 48: function totalSupply() external view returns (uint256);
File: contracts/multisigs/GnosisSafeSameAddressMultisig.sol 8: function getOwners() external view returns (address[] memory); 12: function getThreshold() external view returns (uint256);
File: contracts/interfaces/IOLAS.sol 12: function timeLaunch() external view returns (uint256);
GitHub: 12
File: contracts/interfaces/IServiceRegistry.sol 14: function exists(uint256 serviceId) external view returns (bool);
GitHub: 14
File: contracts/interfaces/IToken.sol 9: function balanceOf(address account) external view returns (uint256); 14: function ownerOf(uint256 tokenId) external view returns (address); 18: function totalSupply() external view returns (uint256); 24: function transfer(address to, uint256 amount) external returns (bool); 30: function allowance(address owner, address spender) external view returns (uint256); 36: function approve(address spender, uint256 amount) external returns (bool); 43: function transferFrom(address from, address to, uint256 amount) external returns (bool);
GitHub: 9, 14, 18, 24, 30, 36, 43
File: contracts/interfaces/ITokenomics.sol 8: function effectiveBond() external pure returns (uint256); 11: function checkpoint() external returns (bool); 30: function reserveAmountForBondProgram(uint256 amount) external returns(bool); 51: function serviceRegistry() external view returns (address);
File: contracts/interfaces/IUniswapV2Pair.sol 6: function totalSupply() external view returns (uint); 7: function token0() external view returns (address); 8: function token1() external view returns (address);
File: contracts/interfaces/IVotingEscrow.sol 8: function getVotes(address account) external view returns (uint256);
GitHub: 8
</details>Doing so will prevent typo bugs where the first '!', '>', '<', or '=' at the beginning of the operator is missing.
There are 10 instances of this issue:
File: contracts/GenericBondCalculator.sol 82: if (token0 == olas || token1 == olas) { 82: if (token0 == olas || token1 == olas) { 84: if (token0 == olas) {
File: contracts/Tokenomics.sol 296: if (uint32(_epochLen) < MIN_EPOCH_LENGTH) { 301: if (uint32(_epochLen) > ONE_YEAR) { 511: if (uint72(_devsPerCapital) > MIN_PARAM_VALUE) { 519: if (uint72(_codePerDev) > MIN_PARAM_VALUE) { 536: if (uint32(_epochLen) >= MIN_EPOCH_LENGTH && uint32(_epochLen) <= ONE_YEAR) { 536: if (uint32(_epochLen) >= MIN_EPOCH_LENGTH && uint32(_epochLen) <= ONE_YEAR) { 902: if (diffNumSeconds < curEpochLen || diffNumSeconds > ONE_YEAR) {
GitHub: 296, 301, 511, 519, 536, 536, 902
When a timestamp is downcast from uint256
to uint32
, the value will wrap in the year 2106, and the contracts will break. Other downcasts will have different endpoints. Consider whether your contract is intended to live past the size of the type being used.
There is one instance of this issue:
File: contracts/Tokenomics.sol 325: uint256 zeroYearSecondsLeft = uint32(_timeLaunch + ONE_YEAR - block.timestamp);
GitHub: 325
This helps to make tracking changes across commits easier, among other reasons. They can be combined into a single file later by importing multiple contracts, and using that file as the common import.
There are 12 instances of this issue:
File: contracts/bridges/FxGovernorTunnel.sol /// @audit FxGovernorTunnel,IFxMessageProcessor 5: interface IFxMessageProcessor { /// @audit FxGovernorTunnel,IFxMessageProcessor 46: contract FxGovernorTunnel is IFxMessageProcessor {
File: contracts/bridges/HomeMediator.sol /// @audit IAMB,HomeMediator 5: interface IAMB { /// @audit IAMB,HomeMediator 46: contract HomeMediator {
File: contracts/multisigs/GuardCM.sol /// @audit GuardCM,IGovernor 6: interface IGovernor { /// @audit GuardCM,IGovernor 88: contract GuardCM {
File: contracts/wveOLAS.sol /// @audit IVEOLAS,wveOLAS 13 interface IVEOLAS { 14 /// @dev Gets the total number of supply points. 15: /// @return numPoints Number of supply points. /// @audit IVEOLAS,wveOLAS 130 contract wveOLAS { 131: // veOLAS address
File: contracts/multisigs/GnosisSafeMultisig.sol /// @audit IGnosisSafeProxyFactory,GnosisSafeMultisig 5 interface IGnosisSafeProxyFactory { 6 /// @dev Allows to create new proxy contact and execute a message call to the new proxy within one transaction. 7 /// @param _singleton Address of singleton contract. 8 /// @param initializer Payload for message call sent to new proxy contract. 9: /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract. /// @audit IGnosisSafeProxyFactory,GnosisSafeMultisig 24 contract GnosisSafeMultisig { 25: // Selector of the Gnosis Safe setup function
File: contracts/multisigs/GnosisSafeSameAddressMultisig.sol /// @audit GnosisSafeSameAddressMultisig,IGnosisSafe 5 interface IGnosisSafe { 6 /// @dev Gets set of owners. 7: /// @return Set of Safe owners. /// @audit GnosisSafeSameAddressMultisig,IGnosisSafe 50 contract GnosisSafeSameAddressMultisig { 51 // Default data size to be parsed as an address of a Gnosis Safe multisig proxy address 52: // This exact size suggests that all the changes to the multisig have been performed and only validation is needed
view
Note that when overriding, mutability is allowed to be changed to be more strict than the parent function's mutability
There are 6 instances of this issue:
File: contracts/multisigs/GuardCM.sol 189: function _verifyData(address target, bytes memory data, uint256 chainId) internal {
GitHub: 189
File: contracts/wveOLAS.sol 297: function transfer(address, uint256) external returns (bool) { 302: function approve(address, uint256) external returns (bool) { 307: function transferFrom(address, address, uint256) external returns (bool) { 322 function delegate(address) external 323: { 328 function delegateBySig(address, uint256, uint256, uint8, bytes32, bytes32) external 329: {
GitHub: 297, 302, 307, 322, 328
Some lines use // x
and some use //x
. The instances below point out the usages that don't follow the majority, within each file
There are 2 instances of this issue:
File: contracts/Tokenomics.sol 873: ///$result == true && (block.timestamp - timeLaunch) / ONE_YEAR == old(currentYear)
GitHub: 873
File: contracts/Treasury.sol 464: ///if_succeeds {:msg "slashed funds check"} IServiceRegistry(ITokenomics(tokenomics).serviceRegistry()).slashedFunds() >= minAcceptedETH
GitHub: 464
The interfaces below should be defined in separate files, so that it's easier for future projects to import them, and to avoid duplication later on if they need to be used elsewhere in the project
There is one instance of this issue:
File: contracts/bridges/FxGovernorTunnel.sol 5: interface IFxMessageProcessor {
GitHub: 5
There are 17 instances of this issue:
<details> <summary>see instances</summary>File: contracts/OLAS.sol /// @audit ERC20 17: contract OLAS is ERC20 {
GitHub: 17
File: contracts/Timelock.sol /// @audit TimelockController 9: contract Timelock is TimelockController {
GitHub: 9
File: contracts/veOLAS.sol /// @audit IErrors /// @audit IVotes /// @audit IERC20 /// @audit IERC165 86: contract veOLAS is IErrors, IVotes, IERC20, IERC165 {
File: contracts/AgentRegistry.sol /// @audit UnitRegistry 9: contract AgentRegistry is UnitRegistry {
GitHub: 9
File: contracts/ComponentRegistry.sol /// @audit UnitRegistry 8: contract ComponentRegistry is UnitRegistry {
GitHub: 8
File: contracts/GenericManager.sol /// @audit IErrorsRegistries 8: abstract contract GenericManager is IErrorsRegistries {
GitHub: 8
File: contracts/GenericRegistry.sol /// @audit IErrorsRegistries /// @audit ERC721 9: abstract contract GenericRegistry is IErrorsRegistries, ERC721 {
File: contracts/RegistriesManager.sol /// @audit GenericManager 9: contract RegistriesManager is GenericManager {
GitHub: 9
File: contracts/UnitRegistry.sol /// @audit GenericRegistry 8: abstract contract UnitRegistry is GenericRegistry {
GitHub: 8
File: contracts/Dispenser.sol /// @audit IErrorsTokenomics 11: contract Dispenser is IErrorsTokenomics {
GitHub: 11
File: contracts/Tokenomics.sol /// @audit TokenomicsConstants /// @audit IErrorsTokenomics 118: contract Tokenomics is TokenomicsConstants, IErrorsTokenomics {
File: contracts/Treasury.sol /// @audit IErrorsTokenomics 39: contract Treasury is IErrorsTokenomics {
GitHub: 39
</details>@param
tag is missingThere are 149 instances of this issue:
<details> <summary>see instances</summary>File: contracts/OLAS.sol /// @audit Missing '@param minter' 13 14 /// @title OLAS - Smart contract for the OLAS token. 15 /// @author AL 16 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 17 contract OLAS is ERC20 { 18: event MinterUpdated(address indexed minter); /// @audit Missing '@param owner' 14 /// @title OLAS - Smart contract for the OLAS token. 15 /// @author AL 16 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 17 contract OLAS is ERC20 { 18 event MinterUpdated(address indexed minter); 19: event OwnerUpdated(address indexed owner);
File: contracts/bridges/BridgedERC20.sol /// @audit Missing '@param owner' 14 15 /// @title BridgedERC20 - Smart contract for bridged ERC20 token 16 /// @dev Bridged token contract is owned by the bridge mediator contract, and thus the token representation from 17 /// another chain must be minted and burned solely by the bridge mediator contract. 18 contract BridgedERC20 is ERC20 { 19: event OwnerUpdated(address indexed owner);
GitHub: 14
File: contracts/bridges/FxERC20ChildTunnel.sol /// @audit Missing '@param childToken' /// @audit Missing '@param rootToken' /// @audit Missing '@param from' /// @audit Missing '@param to' /// @audit Missing '@param amount' 20 /// @title FxERC20ChildTunnel - Smart contract for the L2 token management part 21 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 22 /// @author Andrey Lebedev - <andrey.lebedev@valory.xyz> 23 /// @author Mariapia Moscatiello - <mariapia.moscatiello@valory.xyz> 24 contract FxERC20ChildTunnel is FxBaseChildTunnel { 25: event FxDepositERC20(address indexed childToken, address indexed rootToken, address from, address indexed to, uint256 amount); /// @audit Missing '@param rootToken' /// @audit Missing '@param childToken' /// @audit Missing '@param from' /// @audit Missing '@param to' /// @audit Missing '@param amount' 21 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 22 /// @author Andrey Lebedev - <andrey.lebedev@valory.xyz> 23 /// @author Mariapia Moscatiello - <mariapia.moscatiello@valory.xyz> 24 contract FxERC20ChildTunnel is FxBaseChildTunnel { 25 event FxDepositERC20(address indexed childToken, address indexed rootToken, address from, address indexed to, uint256 amount); 26: event FxWithdrawERC20(address indexed rootToken, address indexed childToken, address from, address indexed to, uint256 amount);
GitHub: 20, 20, 20, 20, 20, 21, 21, 21, 21, 21
File: contracts/bridges/FxERC20RootTunnel.sol /// @audit Missing '@param childToken' /// @audit Missing '@param rootToken' /// @audit Missing '@param from' /// @audit Missing '@param to' /// @audit Missing '@param amount' 20 /// @title FxERC20RootTunnel - Smart contract for the L1 token management part 21 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 22 /// @author Andrey Lebedev - <andrey.lebedev@valory.xyz> 23 /// @author Mariapia Moscatiello - <mariapia.moscatiello@valory.xyz> 24 contract FxERC20RootTunnel is FxBaseRootTunnel { 25: event FxDepositERC20(address indexed childToken, address indexed rootToken, address from, address indexed to, uint256 amount); /// @audit Missing '@param rootToken' /// @audit Missing '@param childToken' /// @audit Missing '@param from' /// @audit Missing '@param to' /// @audit Missing '@param amount' 21 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 22 /// @author Andrey Lebedev - <andrey.lebedev@valory.xyz> 23 /// @author Mariapia Moscatiello - <mariapia.moscatiello@valory.xyz> 24 contract FxERC20RootTunnel is FxBaseRootTunnel { 25 event FxDepositERC20(address indexed childToken, address indexed rootToken, address from, address indexed to, uint256 amount); 26: event FxWithdrawERC20(address indexed rootToken, address indexed childToken, address from, address indexed to, uint256 amount);
GitHub: 20, 20, 20, 20, 20, 21, 21, 21, 21, 21
File: contracts/bridges/FxGovernorTunnel.sol /// @audit Missing '@param sender' /// @audit Missing '@param value' 42 43 /// @title FxGovernorTunnel - Smart contract for the governor child tunnel bridge implementation 44 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 45 /// @author AL 46 contract FxGovernorTunnel is IFxMessageProcessor { 47: event FundsReceived(address indexed sender, uint256 value); /// @audit Missing '@param rootMessageSender' 43 /// @title FxGovernorTunnel - Smart contract for the governor child tunnel bridge implementation 44 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 45 /// @author AL 46 contract FxGovernorTunnel is IFxMessageProcessor { 47 event FundsReceived(address indexed sender, uint256 value); 48: event RootGovernorUpdated(address indexed rootMessageSender); /// @audit Missing '@param stateId' /// @audit Missing '@param rootMessageSender' /// @audit Missing '@param data' 44 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 45 /// @author AL 46 contract FxGovernorTunnel is IFxMessageProcessor { 47 event FundsReceived(address indexed sender, uint256 value); 48 event RootGovernorUpdated(address indexed rootMessageSender); 49: event MessageReceived(uint256 indexed stateId, address indexed rootMessageSender, bytes data);
GitHub: 42, 42, 43, 44, 44, 44
File: contracts/bridges/HomeMediator.sol /// @audit Missing '@param sender' /// @audit Missing '@param value' 42 43 /// @title HomeMediator - Smart contract for the governor home (gnosis chain) bridge implementation 44 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 45 /// @author AL 46 contract HomeMediator { 47: event FundsReceived(address indexed sender, uint256 value); /// @audit Missing '@param foreignMessageSender' 43 /// @title HomeMediator - Smart contract for the governor home (gnosis chain) bridge implementation 44 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 45 /// @author AL 46 contract HomeMediator { 47 event FundsReceived(address indexed sender, uint256 value); 48: event ForeignGovernorUpdated(address indexed foreignMessageSender); /// @audit Missing '@param foreignMessageSender' /// @audit Missing '@param data' 44 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 45 /// @author AL 46 contract HomeMediator { 47 event FundsReceived(address indexed sender, uint256 value); 48 event ForeignGovernorUpdated(address indexed foreignMessageSender); 49: event MessageReceived(address indexed foreignMessageSender, bytes data);
File: contracts/multisigs/GuardCM.sol /// @audit Missing '@param governor' 84 85 /// @title GuardCM - Smart contract for Gnosis Safe community multisig (CM) guard 86 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 87 /// @author Andrey Lebedev - <andrey.lebedev@valory.xyz> 88 contract GuardCM { 89: event GovernorUpdated(address indexed governor); /// @audit Missing '@param targets' /// @audit Missing '@param selectors' /// @audit Missing '@param chainIds' /// @audit Missing '@param statuses' 85 /// @title GuardCM - Smart contract for Gnosis Safe community multisig (CM) guard 86 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 87 /// @author Andrey Lebedev - <andrey.lebedev@valory.xyz> 88 contract GuardCM { 89 event GovernorUpdated(address indexed governor); 90: event SetTargetSelectors(address[] indexed targets, bytes4[] indexed selectors, uint256[] chainIds, bool[] statuses); /// @audit Missing '@param bridgeMediatorL1s' /// @audit Missing '@param bridgeMediatorL2s' /// @audit Missing '@param chainIds' 86 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 87 /// @author Andrey Lebedev - <andrey.lebedev@valory.xyz> 88 contract GuardCM { 89 event GovernorUpdated(address indexed governor); 90 event SetTargetSelectors(address[] indexed targets, bytes4[] indexed selectors, uint256[] chainIds, bool[] statuses); 91: event SetBridgeMediators(address[] indexed bridgeMediatorL1s, address[] indexed bridgeMediatorL2s, uint256[] chainIds); /// @audit Missing '@param proposalId' 87 /// @author Andrey Lebedev - <andrey.lebedev@valory.xyz> 88 contract GuardCM { 89 event GovernorUpdated(address indexed governor); 90 event SetTargetSelectors(address[] indexed targets, bytes4[] indexed selectors, uint256[] chainIds, bool[] statuses); 91 event SetBridgeMediators(address[] indexed bridgeMediatorL1s, address[] indexed bridgeMediatorL2s, uint256[] chainIds); 92: event GovernorCheckProposalIdChanged(uint256 indexed proposalId); /// @audit Missing '@param account' 88 contract GuardCM { 89 event GovernorUpdated(address indexed governor); 90 event SetTargetSelectors(address[] indexed targets, bytes4[] indexed selectors, uint256[] chainIds, bool[] statuses); 91 event SetBridgeMediators(address[] indexed bridgeMediatorL1s, address[] indexed bridgeMediatorL2s, uint256[] chainIds); 92 event GovernorCheckProposalIdChanged(uint256 indexed proposalId); 93: event GuardPaused(address indexed account);
GitHub: 84, 85, 85, 85, 85, 86, 86, 86, 87, 88
File: contracts/veOLAS.sol /// @audit Missing '@param account' /// @audit Missing '@param amount' /// @audit Missing '@param locktime' /// @audit Missing '@param depositType' /// @audit Missing '@param ts' 89 CREATE_LOCK_TYPE, 90 INCREASE_LOCK_AMOUNT, 91 INCREASE_UNLOCK_TIME 92 } 93 94: event Deposit(address indexed account, uint256 amount, uint256 locktime, DepositType depositType, uint256 ts); /// @audit Missing '@param account' /// @audit Missing '@param amount' /// @audit Missing '@param ts' 90 INCREASE_LOCK_AMOUNT, 91 INCREASE_UNLOCK_TIME 92 } 93 94 event Deposit(address indexed account, uint256 amount, uint256 locktime, DepositType depositType, uint256 ts); 95: event Withdraw(address indexed account, uint256 amount, uint256 ts); /// @audit Missing '@param previousSupply' /// @audit Missing '@param currentSupply' 91 INCREASE_UNLOCK_TIME 92 } 93 94 event Deposit(address indexed account, uint256 amount, uint256 locktime, DepositType depositType, uint256 ts); 95 event Withdraw(address indexed account, uint256 amount, uint256 ts); 96: event Supply(uint256 previousSupply, uint256 currentSupply);
GitHub: 89, 89, 89, 89, 89, 90, 90, 90, 91, 91
File: contracts/GenericManager.sol /// @audit Missing '@param owner' 4 import "./interfaces/IErrorsRegistries.sol"; 5 6 /// @title Generic Manager - Smart contract for generic registry manager template 7 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 8 abstract contract GenericManager is IErrorsRegistries { 9: event OwnerUpdated(address indexed owner); /// @audit Missing '@param owner' 5 6 /// @title Generic Manager - Smart contract for generic registry manager template 7 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 8 abstract contract GenericManager is IErrorsRegistries { 9 event OwnerUpdated(address indexed owner); 10: event Pause(address indexed owner); /// @audit Missing '@param owner' 6 /// @title Generic Manager - Smart contract for generic registry manager template 7 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 8 abstract contract GenericManager is IErrorsRegistries { 9 event OwnerUpdated(address indexed owner); 10 event Pause(address indexed owner); 11: event Unpause(address indexed owner);
File: contracts/GenericRegistry.sol /// @audit Missing '@param owner' 5 import "./interfaces/IErrorsRegistries.sol"; 6 7 /// @title Generic Registry - Smart contract for generic registry template 8 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 9 abstract contract GenericRegistry is IErrorsRegistries, ERC721 { 10: event OwnerUpdated(address indexed owner); /// @audit Missing '@param manager' 6 7 /// @title Generic Registry - Smart contract for generic registry template 8 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 9 abstract contract GenericRegistry is IErrorsRegistries, ERC721 { 10 event OwnerUpdated(address indexed owner); 11: event ManagerUpdated(address indexed manager); /// @audit Missing '@param baseURI' 7 /// @title Generic Registry - Smart contract for generic registry template 8 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 9 abstract contract GenericRegistry is IErrorsRegistries, ERC721 { 10 event OwnerUpdated(address indexed owner); 11 event ManagerUpdated(address indexed manager); 12: event BaseURIChanged(string baseURI);
File: contracts/UnitRegistry.sol /// @audit Missing '@param unitId' /// @audit Missing '@param uType' /// @audit Missing '@param unitHash' 4 import "./GenericRegistry.sol"; 5 6 /// @title Unit Registry - Smart contract for registering generalized units / units 7 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 8 abstract contract UnitRegistry is GenericRegistry { 9: event CreateUnit(uint256 unitId, UnitType uType, bytes32 unitHash); /// @audit Missing '@param unitId' /// @audit Missing '@param uType' /// @audit Missing '@param unitHash' 5 6 /// @title Unit Registry - Smart contract for registering generalized units / units 7 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 8 abstract contract UnitRegistry is GenericRegistry { 9 event CreateUnit(uint256 unitId, UnitType uType, bytes32 unitHash); 10: event UpdateUnitHash(uint256 unitId, UnitType uType, bytes32 unitHash);
File: contracts/Depository.sol /// @audit Missing '@param owner' 58 59 /// @title Bond Depository - Smart contract for OLAS Bond Depository 60 /// @author AL 61 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 62 contract Depository is IErrorsTokenomics { 63: event OwnerUpdated(address indexed owner); /// @audit Missing '@param tokenomics' 59 /// @title Bond Depository - Smart contract for OLAS Bond Depository 60 /// @author AL 61 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 62 contract Depository is IErrorsTokenomics { 63 event OwnerUpdated(address indexed owner); 64: event TokenomicsUpdated(address indexed tokenomics); /// @audit Missing '@param treasury' 60 /// @author AL 61 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 62 contract Depository is IErrorsTokenomics { 63 event OwnerUpdated(address indexed owner); 64 event TokenomicsUpdated(address indexed tokenomics); 65: event TreasuryUpdated(address indexed treasury); /// @audit Missing '@param bondCalculator' 61 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 62 contract Depository is IErrorsTokenomics { 63 event OwnerUpdated(address indexed owner); 64 event TokenomicsUpdated(address indexed tokenomics); 65 event TreasuryUpdated(address indexed treasury); 66: event BondCalculatorUpdated(address indexed bondCalculator); /// @audit Missing '@param token' /// @audit Missing '@param productId' /// @audit Missing '@param owner' /// @audit Missing '@param bondId' 62 contract Depository is IErrorsTokenomics { 63 event OwnerUpdated(address indexed owner); 64 event TokenomicsUpdated(address indexed tokenomics); 65 event TreasuryUpdated(address indexed treasury); 66 event BondCalculatorUpdated(address indexed bondCalculator); 67: event CreateBond(address indexed token, uint256 indexed productId, address indexed owner, uint256 bondId, /// @audit Missing '@param amountOLAS' /// @audit Missing '@param tokenAmount' /// @audit Missing '@param maturity' 63 event OwnerUpdated(address indexed owner); 64 event TokenomicsUpdated(address indexed tokenomics); 65 event TreasuryUpdated(address indexed treasury); 66 event BondCalculatorUpdated(address indexed bondCalculator); 67 event CreateBond(address indexed token, uint256 indexed productId, address indexed owner, uint256 bondId, 68: uint256 amountOLAS, uint256 tokenAmount, uint256 maturity); /// @audit Missing '@param productId' /// @audit Missing '@param owner' /// @audit Missing '@param bondId' 64 event TokenomicsUpdated(address indexed tokenomics); 65 event TreasuryUpdated(address indexed treasury); 66 event BondCalculatorUpdated(address indexed bondCalculator); 67 event CreateBond(address indexed token, uint256 indexed productId, address indexed owner, uint256 bondId, 68 uint256 amountOLAS, uint256 tokenAmount, uint256 maturity); 69: event RedeemBond(uint256 indexed productId, address indexed owner, uint256 bondId); /// @audit Missing '@param token' /// @audit Missing '@param productId' /// @audit Missing '@param supply' /// @audit Missing '@param priceLP' 65 event TreasuryUpdated(address indexed treasury); 66 event BondCalculatorUpdated(address indexed bondCalculator); 67 event CreateBond(address indexed token, uint256 indexed productId, address indexed owner, uint256 bondId, 68 uint256 amountOLAS, uint256 tokenAmount, uint256 maturity); 69 event RedeemBond(uint256 indexed productId, address indexed owner, uint256 bondId); 70: event CreateProduct(address indexed token, uint256 indexed productId, uint256 supply, uint256 priceLP, /// @audit Missing '@param vesting' 66 event BondCalculatorUpdated(address indexed bondCalculator); 67 event CreateBond(address indexed token, uint256 indexed productId, address indexed owner, uint256 bondId, 68 uint256 amountOLAS, uint256 tokenAmount, uint256 maturity); 69 event RedeemBond(uint256 indexed productId, address indexed owner, uint256 bondId); 70 event CreateProduct(address indexed token, uint256 indexed productId, uint256 supply, uint256 priceLP, 71: uint256 vesting); /// @audit Missing '@param token' /// @audit Missing '@param productId' /// @audit Missing '@param supply' 67 event CreateBond(address indexed token, uint256 indexed productId, address indexed owner, uint256 bondId, 68 uint256 amountOLAS, uint256 tokenAmount, uint256 maturity); 69 event RedeemBond(uint256 indexed productId, address indexed owner, uint256 bondId); 70 event CreateProduct(address indexed token, uint256 indexed productId, uint256 supply, uint256 priceLP, 71 uint256 vesting); 72: event CloseProduct(address indexed token, uint256 indexed productId, uint256 supply);
GitHub: 58, 59, 60, 61, 62, 62, 62, 62, 63, 63, 63, 64, 64, 64, 65, 65, 65, 65, 66, 67, 67, 67
File: contracts/Dispenser.sol /// @audit Missing '@param owner' 7 8 /// @title Dispenser - Smart contract for distributing incentives 9 /// @author AL 10 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 11 contract Dispenser is IErrorsTokenomics { 12: event OwnerUpdated(address indexed owner); /// @audit Missing '@param tokenomics' 8 /// @title Dispenser - Smart contract for distributing incentives 9 /// @author AL 10 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 11 contract Dispenser is IErrorsTokenomics { 12 event OwnerUpdated(address indexed owner); 13: event TokenomicsUpdated(address indexed tokenomics); /// @audit Missing '@param treasury' 9 /// @author AL 10 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 11 contract Dispenser is IErrorsTokenomics { 12 event OwnerUpdated(address indexed owner); 13 event TokenomicsUpdated(address indexed tokenomics); 14: event TreasuryUpdated(address indexed treasury); /// @audit Missing '@param owner' /// @audit Missing '@param reward' /// @audit Missing '@param topUp' 10 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 11 contract Dispenser is IErrorsTokenomics { 12 event OwnerUpdated(address indexed owner); 13 event TokenomicsUpdated(address indexed tokenomics); 14 event TreasuryUpdated(address indexed treasury); 15: event IncentivesClaimed(address indexed owner, uint256 reward, uint256 topUp);
File: contracts/DonatorBlacklist.sol /// @audit Missing '@param owner' 16 17 /// @title DonatorBlacklist - Smart contract for donator address blacklisting 18 /// @author AL 19 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 20 contract DonatorBlacklist { 21: event OwnerUpdated(address indexed owner); /// @audit Missing '@param account' /// @audit Missing '@param status' 17 /// @title DonatorBlacklist - Smart contract for donator address blacklisting 18 /// @author AL 19 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 20 contract DonatorBlacklist { 21 event OwnerUpdated(address indexed owner); 22: event DonatorBlacklistStatus(address indexed account, bool status);
File: contracts/Tokenomics.sol /// @audit Missing '@param owner' 114 115 /// @title Tokenomics - Smart contract for tokenomics logic with incentives for unit owners and discount factor regulations for bonds. 116 /// @author AL 117 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 118 contract Tokenomics is TokenomicsConstants, IErrorsTokenomics { 119: event OwnerUpdated(address indexed owner); /// @audit Missing '@param treasury' 115 /// @title Tokenomics - Smart contract for tokenomics logic with incentives for unit owners and discount factor regulations for bonds. 116 /// @author AL 117 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 118 contract Tokenomics is TokenomicsConstants, IErrorsTokenomics { 119 event OwnerUpdated(address indexed owner); 120: event TreasuryUpdated(address indexed treasury); /// @audit Missing '@param depository' 116 /// @author AL 117 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 118 contract Tokenomics is TokenomicsConstants, IErrorsTokenomics { 119 event OwnerUpdated(address indexed owner); 120 event TreasuryUpdated(address indexed treasury); 121: event DepositoryUpdated(address indexed depository); /// @audit Missing '@param dispenser' 117 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 118 contract Tokenomics is TokenomicsConstants, IErrorsTokenomics { 119 event OwnerUpdated(address indexed owner); 120 event TreasuryUpdated(address indexed treasury); 121 event DepositoryUpdated(address indexed depository); 122: event DispenserUpdated(address indexed dispenser); /// @audit Missing '@param epochLen' 118 contract Tokenomics is TokenomicsConstants, IErrorsTokenomics { 119 event OwnerUpdated(address indexed owner); 120 event TreasuryUpdated(address indexed treasury); 121 event DepositoryUpdated(address indexed depository); 122 event DispenserUpdated(address indexed dispenser); 123: event EpochLengthUpdated(uint256 epochLen); /// @audit Missing '@param effectiveBond' 119 event OwnerUpdated(address indexed owner); 120 event TreasuryUpdated(address indexed treasury); 121 event DepositoryUpdated(address indexed depository); 122 event DispenserUpdated(address indexed dispenser); 123 event EpochLengthUpdated(uint256 epochLen); 124: event EffectiveBondUpdated(uint256 effectiveBond); /// @audit Missing '@param idf' 120 event TreasuryUpdated(address indexed treasury); 121 event DepositoryUpdated(address indexed depository); 122 event DispenserUpdated(address indexed dispenser); 123 event EpochLengthUpdated(uint256 epochLen); 124 event EffectiveBondUpdated(uint256 effectiveBond); 125: event IDFUpdated(uint256 idf); /// @audit Missing '@param epochNumber' /// @audit Missing '@param devsPerCapital' /// @audit Missing '@param codePerDev' 121 event DepositoryUpdated(address indexed depository); 122 event DispenserUpdated(address indexed dispenser); 123 event EpochLengthUpdated(uint256 epochLen); 124 event EffectiveBondUpdated(uint256 effectiveBond); 125 event IDFUpdated(uint256 idf); 126: event TokenomicsParametersUpdateRequested(uint256 indexed epochNumber, uint256 devsPerCapital, uint256 codePerDev, /// @audit Missing '@param epsilonRate' /// @audit Missing '@param epochLen' /// @audit Missing '@param veOLASThreshold' 122 event DispenserUpdated(address indexed dispenser); 123 event EpochLengthUpdated(uint256 epochLen); 124 event EffectiveBondUpdated(uint256 effectiveBond); 125 event IDFUpdated(uint256 idf); 126 event TokenomicsParametersUpdateRequested(uint256 indexed epochNumber, uint256 devsPerCapital, uint256 codePerDev, 127: uint256 epsilonRate, uint256 epochLen, uint256 veOLASThreshold); /// @audit Missing '@param epochNumber' 123 event EpochLengthUpdated(uint256 epochLen); 124 event EffectiveBondUpdated(uint256 effectiveBond); 125 event IDFUpdated(uint256 idf); 126 event TokenomicsParametersUpdateRequested(uint256 indexed epochNumber, uint256 devsPerCapital, uint256 codePerDev, 127 uint256 epsilonRate, uint256 epochLen, uint256 veOLASThreshold); 128: event TokenomicsParametersUpdated(uint256 indexed epochNumber); /// @audit Missing '@param epochNumber' /// @audit Missing '@param rewardComponentFraction' 124 event EffectiveBondUpdated(uint256 effectiveBond); 125 event IDFUpdated(uint256 idf); 126 event TokenomicsParametersUpdateRequested(uint256 indexed epochNumber, uint256 devsPerCapital, uint256 codePerDev, 127 uint256 epsilonRate, uint256 epochLen, uint256 veOLASThreshold); 128 event TokenomicsParametersUpdated(uint256 indexed epochNumber); 129: event IncentiveFractionsUpdateRequested(uint256 indexed epochNumber, uint256 rewardComponentFraction, /// @audit Missing '@param rewardAgentFraction' /// @audit Missing '@param maxBondFraction' /// @audit Missing '@param topUpComponentFraction' /// @audit Missing '@param topUpAgentFraction' 125 event IDFUpdated(uint256 idf); 126 event TokenomicsParametersUpdateRequested(uint256 indexed epochNumber, uint256 devsPerCapital, uint256 codePerDev, 127 uint256 epsilonRate, uint256 epochLen, uint256 veOLASThreshold); 128 event TokenomicsParametersUpdated(uint256 indexed epochNumber); 129 event IncentiveFractionsUpdateRequested(uint256 indexed epochNumber, uint256 rewardComponentFraction, 130: uint256 rewardAgentFraction, uint256 maxBondFraction, uint256 topUpComponentFraction, uint256 topUpAgentFraction); /// @audit Missing '@param epochNumber' 126 event TokenomicsParametersUpdateRequested(uint256 indexed epochNumber, uint256 devsPerCapital, uint256 codePerDev, 127 uint256 epsilonRate, uint256 epochLen, uint256 veOLASThreshold); 128 event TokenomicsParametersUpdated(uint256 indexed epochNumber); 129 event IncentiveFractionsUpdateRequested(uint256 indexed epochNumber, uint256 rewardComponentFraction, 130 uint256 rewardAgentFraction, uint256 maxBondFraction, uint256 topUpComponentFraction, uint256 topUpAgentFraction); 131: event IncentiveFractionsUpdated(uint256 indexed epochNumber); /// @audit Missing '@param componentRegistry' 127 uint256 epsilonRate, uint256 epochLen, uint256 veOLASThreshold); 128 event TokenomicsParametersUpdated(uint256 indexed epochNumber); 129 event IncentiveFractionsUpdateRequested(uint256 indexed epochNumber, uint256 rewardComponentFraction, 130 uint256 rewardAgentFraction, uint256 maxBondFraction, uint256 topUpComponentFraction, uint256 topUpAgentFraction); 131 event IncentiveFractionsUpdated(uint256 indexed epochNumber); 132: event ComponentRegistryUpdated(address indexed componentRegistry); /// @audit Missing '@param agentRegistry' 128 event TokenomicsParametersUpdated(uint256 indexed epochNumber); 129 event IncentiveFractionsUpdateRequested(uint256 indexed epochNumber, uint256 rewardComponentFraction, 130 uint256 rewardAgentFraction, uint256 maxBondFraction, uint256 topUpComponentFraction, uint256 topUpAgentFraction); 131 event IncentiveFractionsUpdated(uint256 indexed epochNumber); 132 event ComponentRegistryUpdated(address indexed componentRegistry); 133: event AgentRegistryUpdated(address indexed agentRegistry); /// @audit Missing '@param serviceRegistry' 129 event IncentiveFractionsUpdateRequested(uint256 indexed epochNumber, uint256 rewardComponentFraction, 130 uint256 rewardAgentFraction, uint256 maxBondFraction, uint256 topUpComponentFraction, uint256 topUpAgentFraction); 131 event IncentiveFractionsUpdated(uint256 indexed epochNumber); 132 event ComponentRegistryUpdated(address indexed componentRegistry); 133 event AgentRegistryUpdated(address indexed agentRegistry); 134: event ServiceRegistryUpdated(address indexed serviceRegistry); /// @audit Missing '@param blacklist' 130 uint256 rewardAgentFraction, uint256 maxBondFraction, uint256 topUpComponentFraction, uint256 topUpAgentFraction); 131 event IncentiveFractionsUpdated(uint256 indexed epochNumber); 132 event ComponentRegistryUpdated(address indexed componentRegistry); 133 event AgentRegistryUpdated(address indexed agentRegistry); 134 event ServiceRegistryUpdated(address indexed serviceRegistry); 135: event DonatorBlacklistUpdated(address indexed blacklist); /// @audit Missing '@param epochCounter' /// @audit Missing '@param treasuryRewards' /// @audit Missing '@param accountRewards' /// @audit Missing '@param accountTopUps' 131 event IncentiveFractionsUpdated(uint256 indexed epochNumber); 132 event ComponentRegistryUpdated(address indexed componentRegistry); 133 event AgentRegistryUpdated(address indexed agentRegistry); 134 event ServiceRegistryUpdated(address indexed serviceRegistry); 135 event DonatorBlacklistUpdated(address indexed blacklist); 136: event EpochSettled(uint256 indexed epochCounter, uint256 treasuryRewards, uint256 accountRewards, uint256 accountTopUps); /// @audit Missing '@param implementation' 132 event ComponentRegistryUpdated(address indexed componentRegistry); 133 event AgentRegistryUpdated(address indexed agentRegistry); 134 event ServiceRegistryUpdated(address indexed serviceRegistry); 135 event DonatorBlacklistUpdated(address indexed blacklist); 136 event EpochSettled(uint256 indexed epochCounter, uint256 treasuryRewards, uint256 accountRewards, uint256 accountTopUps); 137: event TokenomicsImplementationUpdated(address indexed implementation);
GitHub: 114, 115, 116, 117, 118, 119, 120, 121, 121, 121, 122, 122, 122, 123, 124, 124, 125, 125, 125, 125, 126, 127, 128, 129, 130, 131, 131, 131, 131, 132
File: contracts/Treasury.sol /// @audit Missing '@param owner' 35 /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz> 36 /// Invariant does not support a failing call() function while transferring ETH when using the CEI pattern: 37 /// revert TransferFailed(address(0), address(this), to, tokenAmount); 38 /// invariant {:msg "broken conservation law"} address(this).balance == ETHFromServices + ETHOwned; 39 contract Treasury is IErrorsTokenomics { 40: event OwnerUpdated(address indexed owner); /// @audit Missing '@param tokenomics' 36 /// Invariant does not support a failing call() function while transferring ETH when using the CEI pattern: 37 /// revert TransferFailed(address(0), address(this), to, tokenAmount); 38 /// invariant {:msg "broken conservation law"} address(this).balance == ETHFromServices + ETHOwned; 39 contract Treasury is IErrorsTokenomics { 40 event OwnerUpdated(address indexed owner); 41: event TokenomicsUpdated(address indexed tokenomics); /// @audit Missing '@param depository' 37 /// revert TransferFailed(address(0), address(this), to, tokenAmount); 38 /// invariant {:msg "broken conservation law"} address(this).balance == ETHFromServices + ETHOwned; 39 contract Treasury is IErrorsTokenomics { 40 event OwnerUpdated(address indexed owner); 41 event TokenomicsUpdated(address indexed tokenomics); 42: event DepositoryUpdated(address indexed depository); /// @audit Missing '@param dispenser' 38 /// invariant {:msg "broken conservation law"} address(this).balance == ETHFromServices + ETHOwned; 39 contract Treasury is IErrorsTokenomics { 40 event OwnerUpdated(address indexed owner); 41 event TokenomicsUpdated(address indexed tokenomics); 42 event DepositoryUpdated(address indexed depository); 43: event DispenserUpdated(address indexed dispenser); /// @audit Missing '@param account' /// @audit Missing '@param token' /// @audit Missing '@param tokenAmount' /// @audit Missing '@param olasAmount' 39 contract Treasury is IErrorsTokenomics { 40 event OwnerUpdated(address indexed owner); 41 event TokenomicsUpdated(address indexed tokenomics); 42 event DepositoryUpdated(address indexed depository); 43 event DispenserUpdated(address indexed dispenser); 44: event DepositTokenFromAccount(address indexed account, address indexed token, uint256 tokenAmount, uint256 olasAmount); /// @audit Missing '@param sender' /// @audit Missing '@param serviceIds' /// @audit Missing '@param amounts' /// @audit Missing '@param donation' 40 event OwnerUpdated(address indexed owner); 41 event TokenomicsUpdated(address indexed tokenomics); 42 event DepositoryUpdated(address indexed depository); 43 event DispenserUpdated(address indexed dispenser); 44 event DepositTokenFromAccount(address indexed account, address indexed token, uint256 tokenAmount, uint256 olasAmount); 45: event DonateToServicesETH(address indexed sender, uint256[] serviceIds, uint256[] amounts, uint256 donation); /// @audit Missing '@param token' /// @audit Missing '@param to' /// @audit Missing '@param tokenAmount' 41 event TokenomicsUpdated(address indexed tokenomics); 42 event DepositoryUpdated(address indexed depository); 43 event DispenserUpdated(address indexed dispenser); 44 event DepositTokenFromAccount(address indexed account, address indexed token, uint256 tokenAmount, uint256 olasAmount); 45 event DonateToServicesETH(address indexed sender, uint256[] serviceIds, uint256[] amounts, uint256 donation); 46: event Withdraw(address indexed token, address indexed to, uint256 tokenAmount); /// @audit Missing '@param token' 42 event DepositoryUpdated(address indexed depository); 43 event DispenserUpdated(address indexed dispenser); 44 event DepositTokenFromAccount(address indexed account, address indexed token, uint256 tokenAmount, uint256 olasAmount); 45 event DonateToServicesETH(address indexed sender, uint256[] serviceIds, uint256[] amounts, uint256 donation); 46 event Withdraw(address indexed token, address indexed to, uint256 tokenAmount); 47: event EnableToken(address indexed token); /// @audit Missing '@param token' 43 event DispenserUpdated(address indexed dispenser); 44 event DepositTokenFromAccount(address indexed account, address indexed token, uint256 tokenAmount, uint256 olasAmount); 45 event DonateToServicesETH(address indexed sender, uint256[] serviceIds, uint256[] amounts, uint256 donation); 46 event Withdraw(address indexed token, address indexed to, uint256 tokenAmount); 47 event EnableToken(address indexed token); 48: event DisableToken(address indexed token); /// @audit Missing '@param sender' /// @audit Missing '@param amount' 44 event DepositTokenFromAccount(address indexed account, address indexed token, uint256 tokenAmount, uint256 olasAmount); 45 event DonateToServicesETH(address indexed sender, uint256[] serviceIds, uint256[] amounts, uint256 donation); 46 event Withdraw(address indexed token, address indexed to, uint256 tokenAmount); 47 event EnableToken(address indexed token); 48 event DisableToken(address indexed token); 49: event ReceiveETH(address indexed sender, uint256 amount); /// @audit Missing '@param ETHOwned' /// @audit Missing '@param ETHFromServices' 45 event DonateToServicesETH(address indexed sender, uint256[] serviceIds, uint256[] amounts, uint256 donation); 46 event Withdraw(address indexed token, address indexed to, uint256 tokenAmount); 47 event EnableToken(address indexed token); 48 event DisableToken(address indexed token); 49 event ReceiveETH(address indexed sender, uint256 amount); 50: event UpdateTreasuryBalances(uint256 ETHOwned, uint256 ETHFromServices); /// @audit Missing '@param amount' 48 event DisableToken(address indexed token); 49 event ReceiveETH(address indexed sender, uint256 amount); 50 event UpdateTreasuryBalances(uint256 ETHOwned, uint256 ETHFromServices); 51 event PauseTreasury(); 52 event UnpauseTreasury(); 53: event MinAcceptedETHUpdated(uint256 amount);
GitHub: 35, 36, 37, 38, 39, 39, 39, 39, 40, 40, 40, 40, 41, 41, 41, 42, 43, 44, 44, 45, 45, 48
</details>@param
tag is missingThere is one instance of this issue:
File: contracts/GovernorOLAS.sol /// @audit Missing '@param quorumFraction' 17 IVotes governanceToken, 18 TimelockController timelock, 19 uint256 initialVotingDelay, 20 uint256 initialVotingPeriod, 21 uint256 initialProposalThreshold, 22: uint256 quorumFraction
GitHub: 17
@return
tag is missingThere are 8 instances of this issue:
File: contracts/bridges/HomeMediator.sol /// @audit Missing '@return ' 6: function messageSender() external view returns (address);
GitHub: 6
File: contracts/multisigs/GuardCM.sol /// @audit Missing '@return ' 7: function state(uint256 proposalId) external returns (ProposalState);
GitHub: 7
File: contracts/interfaces/IUniswapV2Pair.sol /// @audit Missing '@return ' 6: function totalSupply() external view returns (uint); /// @audit Missing '@return ' 7: function token0() external view returns (address); /// @audit Missing '@return ' 8: function token1() external view returns (address); /// @audit Missing '@return reserve0' /// @audit Missing '@return reserve1' /// @audit Missing '@return blockTimestampLast' 9: function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
Consider changing the variable to be an unnamed one, since the variable is never assigned, nor is it returned by name. If the optimizer is not turned on, leaving the code as it is will also waste gas for the stack variable.
There are 4 instances of this issue:
File: contracts/UnitRegistry.sol /// @audit numDependencies /// @audit dependencies 160 function getDependencies(uint256 unitId) external view virtual 161 returns (uint256 numDependencies, uint32[] memory dependencies) 162: { /// @audit numHashes 171 function getUpdatedHashes(uint256 unitId) external view virtual 172 returns (uint256 numHashes, bytes32[] memory unitHashes) 173: {
File: contracts/Depository.sol /// @audit priceLP 491: function getCurrentPriceLP(address token) external view returns (uint256 priceLP) {
GitHub: 491
The Solidity style guide says to use mixedCase for function argument names
There is one instance of this issue:
File: contracts/bridges/HomeMediator.sol 62: constructor(address _AMBContractProxyHome, address _foreignGovernor) {
GitHub: 62
external
/public
variable names should begin with an underscoreAccording to the Solidity Style Guide, non-external
/public
variable names should begin with an underscore
There are 3 instances of this issue:
File: contracts/veOLAS.sol 99: uint64 internal constant WEEK = 1 weeks; 101: uint256 internal constant MAXTIME = 4 * 365 * 86400; 103: int128 internal constant IMAXTIME = 4 * 365 * 86400;
And functions within contracts should be separate by a single line
There are 2 instances of this issue:
File: contracts/multisigs/GuardCM.sol 4 import {Enum} from "@gnosis.pm/safe-contracts/contracts/common/Enum.sol"; 5 6: interface IGovernor {
GitHub: 4
File: contracts/wveOLAS.sol 11 } 12 13: interface IVEOLAS {
GitHub: 11
The variable is being cast to its own type
There are 3 instances of this issue:
File: contracts/veOLAS.sol /// @audit uint256 /// @audit uint256 225: block_slope = (1e18 * uint256(block.number - lastPoint.blockNumber)) / uint256(block.timestamp - lastPoint.ts);
File: contracts/UnitRegistry.sol /// @audit uint256 185: subComponentIds = mapSubComponents[uint256(unitId)];
GitHub: 185
error
definitionNote that there may be cases where an error superficially appears to be used, but this is only because there are multiple definitions of the error in different files. In such cases, the error definition should be moved into a separate file. The instances below are the unused definitions.
There are 9 instances of this issue:
File: contracts/interfaces/IErrors.sol 9: error OwnerOnly(address sender, address owner); 18: error NonZeroValue(); 23: error WrongArrayLength(uint256 numValues1, uint256 numValues2); 41: error InsufficientAllowance(uint256 provided, uint256 expected);
File: contracts/interfaces/IErrorsRegistries.sol 29: error WrongArrayLength(uint256 numValues1, uint256 numValues2); 43: error WrongThreshold(uint256 currentThreshold, uint256 minThreshold, uint256 maxThreshold); 95: error UnauthorizedMultisig(address multisig); 114: error TransferFailed(address token, address from, address to, uint256 value);
File: contracts/multisigs/GnosisSafeSameAddressMultisig.sol 19: error ZeroAddress();
GitHub: 19
public
contract variableNote that there may be cases where a variable superficially appears to be used, but this is only because there are multiple definitions of the variable in different files. In such cases, the variable definition should be moved into a separate file. The instances below are the unused variables.
There are 9 instances of this issue:
File: contracts/AgentRegistry.sol 13: string public constant VERSION = "1.0.0";
GitHub: 13
File: contracts/ComponentRegistry.sol 10: string public constant VERSION = "1.0.0";
GitHub: 10
File: contracts/Depository.sol 77: string public constant VERSION = "1.0.1";
GitHub: 77
File: contracts/Tokenomics.sol 217: mapping(uint256 => uint256) public mapServiceAmounts; 219: mapping(address => uint256) public mapOwnerRewards; 221: mapping(address => uint256) public mapOwnerTopUps;
File: contracts/TokenomicsConstants.sol 11: string public constant VERSION = "1.0.1"; 14: bytes32 public constant PROXY_TOKENOMICS = 0xbd5523e7c3b6a94aa0e3b24d1120addc2f95c7029e097b466b2bedc8d4b4362f;
File: contracts/TokenomicsProxy.sol 28: bytes32 public constant PROXY_TOKENOMICS = 0xbd5523e7c3b6a94aa0e3b24d1120addc2f95c7029e097b466b2bedc8d4b4362f;
GitHub: 28
bytes.concat()
on bytes instead of abi.encodePacked()
for clearer semantic meaningStarting with version 0.8.4, Solidity has the bytes.concat()
function, which allows one to concatenate a list of bytes/strings, without extra padding. Using this function rather than abi.encodePacked()
makes the intended operation more clear, leading to less reviewer confusion.
There is one instance of this issue:
File: contracts/GenericRegistry.sol 139 return string(abi.encodePacked(baseURI, CID_PREFIX, _toHex16(bytes16(unitHash)), 140: _toHex16(bytes16(unitHash << 128))));
GitHub: 139
Use bit shifts in an immutable variable rather than long hex strings for powers of two, for readability (e.g. 0x1000000000000000000000000
-> 1 << 96
)
There are 7 instances of this issue:
File: contracts/Tokenomics.sol 550: tokenomicsParametersUpdated = tokenomicsParametersUpdated | 0x01; 599: tokenomicsParametersUpdated = tokenomicsParametersUpdated | 0x02; 942: tokenomicsParametersUpdated = tokenomicsParametersUpdated | 0x04; 973: if (tokenomicsParametersUpdated & 0x02 == 0x02) { 973: if (tokenomicsParametersUpdated & 0x02 == 0x02) { 987: if (tokenomicsParametersUpdated & 0x01 == 0x01) { 987: if (tokenomicsParametersUpdated & 0x01 == 0x01) {
GitHub: 550, 599, 942, 973, 973, 987, 987
override
is unnecessaryStarting with Solidity version 0.8.8, using the override
keyword when the function solely overrides an interface function, and the function doesn't exist in multiple base contracts, is unnecessary.
There are 14 instances of this issue:
File: contracts/GovernorOLAS.sol 33 function state(uint256 proposalId) public view override(IGovernor, Governor, GovernorTimelockControl) 34 returns (ProposalState) 35: { 45 function propose( 46 address[] memory targets, 47 uint256[] memory values, 48 bytes[] memory calldatas, 49 string memory description 50 ) public override(IGovernor, Governor, GovernorCompatibilityBravo) returns (uint256) 51: { 57 function proposalThreshold() public view override(Governor, GovernorSettings) returns (uint256) 58: { 68 function _execute( 69 uint256 proposalId, 70 address[] memory targets, 71 uint256[] memory values, 72 bytes[] memory calldatas, 73 bytes32 descriptionHash 74 ) internal override(Governor, GovernorTimelockControl) 75: { 85 function _cancel( 86 address[] memory targets, 87 uint256[] memory values, 88 bytes[] memory calldatas, 89 bytes32 descriptionHash 90 ) internal override(Governor, GovernorTimelockControl) returns (uint256) 91: { 97 function _executor() internal view override(Governor, GovernorTimelockControl) returns (address) 98: { 105 function supportsInterface(bytes4 interfaceId) public view override(IERC165, Governor, GovernorTimelockControl) 106 returns (bool) 107: {
GitHub: 33, 45, 57, 68, 85, 97, 105
File: contracts/bridges/FxGovernorTunnel.sol 107: function processMessageFromRoot(uint256 stateId, address rootMessageSender, bytes memory data) external override {
GitHub: 107
File: contracts/veOLAS.sol 607: function balanceOf(address account) public view override returns (uint256 balance) { 719: function totalSupply() public view override returns (uint256) { 767: function transfer(address to, uint256 amount) external virtual override returns (bool) { 772: function approve(address spender, uint256 amount) external virtual override returns (bool) { 777: function transferFrom(address from, address to, uint256 amount) external virtual override returns (bool) { 782 function allowance(address owner, address spender) external view virtual override returns (uint256) 783: {
#0 - thebrittfactor
2024-01-02T16:44:42Z
Content submitted prior to audit close.
#1 - c4-pre-sort
2024-01-10T14:44:41Z
alex-ppg marked the issue as sufficient quality report
#2 - c4-judge
2024-01-18T21:54:24Z
dmvt marked the issue as grade-a
#3 - c4-judge
2024-01-20T18:33:46Z
dmvt marked the issue as selected for report
#4 - c4-sponsor
2024-01-22T18:13:39Z
kupermind (sponsor) disputed
#5 - kupermind
2024-01-22T18:14:37Z
@c4-judge This report contains no issue and no valuable insights. Therefore we suggest not having this into the final report.
š Selected for report: 0xAnah
Also found by: 0x11singh99, 0xA5DF, IllIllI, JCK, Raihan, Sathish9098, alphacipher, c3phas, hunter_w3b, naman1778
440.995 USDC - $441.00
Note: I've removed the instances reported by the winning bot and 4naly3er
Issue | Instances | Total Gas Saved | |
---|---|---|---|
[Gā01] | Argument validation should be at the top of the function/block | 41 | - |
[Gā02] | Mappings are cheaper to use than storage arrays | 3 | 6300 |
[Gā03] | State variables only set in the constructor should be declared immutable | 1 | 2097 |
[Gā04] | Emitting index ed constant s wastes gas | 10 | 3750 |
[Gā05] | Assembly: Use scratch space for building calldata | 39 | 8580 |
[Gā06] | Use += for arrays | 4 | 796 |
[Gā07] | Assembly: Avoid fetching a low-level call's return data by using assembly | 2 | 318 |
[Gā08] | Avoid transferring amounts of zero in order to save gas | 2 | 200 |
[Gā09] | Combine mapping s referenced in the same function by the same key | 7 | 294 |
[Gā10] | Functions guaranteed to revert when called by normal users can be marked payable | 56 | 1176 |
[Gā11] | internal functions only called once can be inlined to save gas | 2 | 40 |
[Gā12] | Assembly: Use scratch space when building emitted events with two data arguments | 10 | 150 |
[Gā13] | >= costs less gas than > | 51 | 153 |
[Gā14] | Consider using ERC721A over ERC721 | 3 | - |
[Gā15] | Consider using solady 's token contracts to save gas | 3 | - |
[Gā16] | Consider using solady's FixedPointMathLib | 23 | - |
[Gā17] | Initializers can be marked payable | 1 | - |
[Gā18] | Memory-safe annotation missing | 6 | - |
[Gā19] | Multiple address /ID mappings can be combined into a single mapping of an address /ID to a struct | 4 | - |
[Gā20] | Multiple accesses of a memory /calldata array should use a local variable cache | 84 | - |
[Gā21] | Multiple accesses of a storage array should use a local variable cache | 2 | 84 |
[Gā22] | Reduce deployment costs by tweaking contracts' metadata | 23 | - |
[Gā23] | Stack variable is only used once | 32 | 96 |
[Gā24] | Split revert checks to save gas | 19 | 38 |
[Gā25] | Update OpenZeppelin dependency to save gas | 8 | - |
[Gā26] | Use short-circuit evaluation to avoid external calls | 1 | - |
Total: 437 instances over 26 issues with 24072 gas saved
Gas totals are estimates based on data from the Ethereum Yellowpaper. The estimates use the lower bounds of ranges and count two iterations of each for
-loop. All values above are runtime, not deployment, values; deployment values are listed in the individual issue descriptions. The table above as well as its gas numbers do not include any of the excluded findings.
Checks that involve constants should come before checks that involve state variables, function calls, and calculations. By doing these checks first, the function is able to revert before wasting a Gcoldsload (2100 gas*) in a function that may ultimately revert in the unhappy case.
There are 41 instances of this issue:
<details> <summary>see instances</summary>File: contracts/OLAS.sol 48 if (newOwner == address(0)) { 49 revert ZeroAddress(); 50: } 63 if (newMinter == address(0)) { 64 revert ZeroAddress(); 65: }
File: contracts/bridges/BridgedERC20.sol 37 if (newOwner == address(0)) { 38 revert ZeroAddress(); 39: }
GitHub: 37
File: contracts/veOLAS.sol 379 if (amount == 0) { 380 revert ZeroValue(); 381: } 392 if (amount > type(uint96).max) { 393 revert Overflow(amount, type(uint96).max); 394: } 449 if (amount > type(uint96).max) { 450 revert Overflow(amount, type(uint96).max); 451: } 461 if (amount == 0) { 462 revert ZeroValue(); 463: } 474 if (amount > type(uint96).max) { 475 revert Overflow(amount, type(uint96).max); 476: }
GitHub: 379, 392, 449, 461, 474
File: contracts/GenericManager.sol 27 if (newOwner == address(0)) { 28 revert ZeroAddress(); 29: }
GitHub: 27
File: contracts/GenericRegistry.sol 44 if (newOwner == address(0)) { 45 revert ZeroAddress(); 46: } 60 if (newManager == address(0)) { 61 revert ZeroAddress(); 62: } 85 if (bytes(bURI).length == 0) { 86 revert ZeroValue(); 87: }
File: contracts/UnitRegistry.sol 64 if(unitOwner == address(0)) { 65 revert ZeroAddress(); 66: } 69 if (unitHash == 0) { 70 revert ZeroValue(); 71: } 139 if (unitHash == 0) { 140 revert ZeroValue(); 141: }
File: contracts/Depository.sol 111 if (_olas == address(0) || _tokenomics == address(0) || _treasury == address(0) || _bondCalculator == address(0)) { 112 revert ZeroAddress(); 113: } 130 if (newOwner == address(0)) { 131 revert ZeroAddress(); 132: } 190 if (priceLP == 0) { 191 revert ZeroValue(); 192: } 195 if (priceLP > type(uint160).max) { 196 revert Overflow(priceLP, type(uint160).max); 197: } 200 if (supply == 0) { 201 revert ZeroValue(); 202: } 205 if (supply > type(uint96).max) { 206 revert Overflow(supply, type(uint96).max); 207: } 210 if (vesting < MIN_VESTING) { 211 revert LowerThan(vesting, MIN_VESTING); 212: }
GitHub: 111, 130, 190, 195, 200, 205, 210
File: contracts/Dispenser.sol 36 if (_tokenomics == address(0) || _treasury == address(0)) { 37 revert ZeroAddress(); 38: } 53 if (newOwner == address(0)) { 54 revert ZeroAddress(); 55: }
File: contracts/DonatorBlacklist.sol 43 if (newOwner == address(0)) { 44 revert ZeroAddress(); 45: } 63 if (accounts.length != statuses.length) { 64 revert WrongArrayLength(accounts.length, statuses.length); 65: }
File: contracts/Tokenomics.sol 283 if (_olas == address(0) || _treasury == address(0) || _depository == address(0) || _dispenser == address(0) || 284 _ve == address(0) || _componentRegistry == address(0) || _agentRegistry == address(0) || 285 _serviceRegistry == address(0)) { 286 revert ZeroAddress(); 287: } 296 if (uint32(_epochLen) < MIN_EPOCH_LENGTH) { 297 revert LowerThan(_epochLen, MIN_EPOCH_LENGTH); 298: } 301 if (uint32(_epochLen) > ONE_YEAR) { 302 revert Overflow(_epochLen, ONE_YEAR); 303: } 391 if (implementation == address(0)) { 392 revert ZeroAddress(); 393: } 411 if (newOwner == address(0)) { 412 revert ZeroAddress(); 413: } 576 if (_rewardComponentFraction + _rewardAgentFraction > 100) { 577 revert WrongAmount(_rewardComponentFraction + _rewardAgentFraction, 100); 578: } 581 if (_maxBondFraction + _topUpComponentFraction + _topUpAgentFraction > 100) { 582 revert WrongAmount(_maxBondFraction + _topUpComponentFraction + _topUpAgentFraction, 100); 583: } 1094 if (unitTypes.length != unitIds.length) { 1095 revert WrongArrayLength(unitTypes.length, unitIds.length); 1096: }
GitHub: 283, 296, 301, 391, 411, 576, 581, 1094
File: contracts/Treasury.sol 100 if (_olas == address(0) || _tokenomics == address(0) || _depository == address(0) || _dispenser == address(0)) { 101 revert ZeroAddress(); 102: } 144 if (newOwner == address(0)) { 145 revert ZeroAddress(); 146: } 189 if (_minAcceptedETH == 0) { 190 revert ZeroValue(); 191: } 194 if (_minAcceptedETH > type(uint96).max) { 195 revert Overflow(_minAcceptedETH, type(uint96).max); 196: } 320 if (to == address(this)) { 321 revert TransferFailed(token, address(this), to, tokenAmount); 322: } 325 if (tokenAmount == 0) { 326 revert ZeroValue(); 327: } 494 if (token == address(0)) { 495 revert ZeroAddress(); 496: }
GitHub: 100, 144, 189, 194, 320, 325, 494
</details>When using storage arrays, solidity adds an internal lookup of the array's length (a Gcoldsload 2100 gas) to ensure you don't read past the array's end. You can avoid this lookup by using a mapping
and storing the number of entries in a separate storage variable. In cases where you have sentinel values (e.g. 'zero' means invalid), you can avoid length checks
There are 3 instances of this issue:
File: contracts/veOLAS.sol 119: mapping(address => PointVoting[]) public mapUserPoints;
GitHub: 119
File: contracts/UnitRegistry.sol 29: mapping(uint256 => bytes32[]) public mapUnitIdHashes; 31: mapping(uint256 => uint32[]) public mapSubComponents;
immutable
Avoids a Gsset (20000 gas) in the constructor, and replaces the first access in each transaction (Gcoldsload - 2100 gas) and each access thereafter (Gwarmacces - 100 gas) with a PUSH32
(3 gas).
While string
s are not value types, and therefore cannot be immutable
/constant
if not hard-coded outside of the constructor, the same behavior can be achieved by making the current contract abstract
with virtual
functions for the string
accessors, and having a child contract override the functions with the hard-coded implementation-specific values.
There is one instance of this issue:
File: contracts/Treasury.sol 65: address public olas;
GitHub: 65
index
ed constant
s wastes gasEvery indexed
event parameter adds an extra Glogtopic
(375 gas). You can avoid this extra cost, in cases where you're emitting a constant, by creating a second version of the event, which doesn't have the indexed parameter (and have users look to the contract's variables for its value instead), and using the new event in the cases shown below.
There are 10 instances of this issue:
File: contracts/bridges/FxERC20ChildTunnel.sol /// @audit arg0 is `indexed rootToken` /// @audit arg1 is `indexed childToken` 84: emit FxWithdrawERC20(rootToken, childToken, from, to, amount); /// @audit arg0 is `indexed childToken` /// @audit arg1 is `indexed rootToken` 107: emit FxDepositERC20(childToken, rootToken, msg.sender, to, amount);
File: contracts/bridges/FxERC20RootTunnel.sol /// @audit arg0 is `indexed childToken` /// @audit arg1 is `indexed rootToken` 79: emit FxDepositERC20(childToken, rootToken, from, to, amount); /// @audit arg0 is `indexed rootToken` /// @audit arg1 is `indexed childToken` 106: emit FxWithdrawERC20(rootToken, childToken, msg.sender, to, amount);
File: contracts/Treasury.sol /// @audit arg0 is `indexed token` 337: emit Withdraw(ETH_TOKEN_ADDRESS, to, tokenAmount); /// @audit arg0 is `indexed token` 405: emit Withdraw(ETH_TOKEN_ADDRESS, account, accountRewards);
If an external call's calldata can fit into two or fewer words, use the scratch space to build the calldata, rather than allowing Solidity to do a memory expansion.
There are 39 instances of this issue:
<details> <summary>see instances</summary>File: contracts/bridges/FxERC20ChildTunnel.sol 79: bool success = IERC20(childToken).transfer(to, amount);
GitHub: 79
File: contracts/bridges/FxERC20RootTunnel.sol 77: IERC20(rootToken).mint(to, amount); 104: IERC20(rootToken).burn(amount);
File: contracts/multisigs/GuardCM.sol 545: ProposalState state = IGovernor(governor).state(governorCheckProposalId);
GitHub: 545
File: contracts/veOLAS.sol 534: IERC20(token).transfer(msg.sender, amount);
GitHub: 534
File: contracts/wveOLAS.sol 164: sPoint = IVEOLAS(ve).mapSupplyPoints(idx); 171: slopeChange = IVEOLAS(ve).mapSlopeChanges(ts); 178: pv = IVEOLAS(ve).getLastUserPoint(account); 185: userNumPoints = IVEOLAS(ve).getNumUserPoints(account); 195: uint256 userNumPoints = IVEOLAS(ve).getNumUserPoints(account); 197: uPoint = IVEOLAS(ve).getUserPoint(account, idx); 204: balance = IVEOLAS(ve).getVotes(account); 216: balance = IVEOLAS(ve).getPastVotes(account, blockNumber); 224: balance = IVEOLAS(ve).balanceOf(account); 236: balance = IVEOLAS(ve).balanceOfAt(account, blockNumber); 244: unlockTime = IVEOLAS(ve).lockedEnd(account); 257: supplyAt = IVEOLAS(ve).totalSupplyAt(blockNumber); 266: PointVoting memory sPoint = IVEOLAS(ve).mapSupplyPoints(numPoints); 269: vPower = IVEOLAS(ve).totalSupplyLockedAtT(ts); 286: vPower = IVEOLAS(ve).getPastTotalSupply(blockNumber); 293: return IVEOLAS(ve).supportsInterface(interfaceId);
GitHub: 164, 171, 178, 185, 195, 197, 204, 216, 224, 236, 244, 257, 266, 269, 286, 293
File: contracts/AgentRegistry.sol 60: (subComponentIds, ) = IRegistry(componentRegistry).getLocalSubComponents(uint256(unitId));
GitHub: 60
File: contracts/Depository.sol 221: if (!ITreasury(treasury).isEnabled(token)) { 226: if (!ITokenomics(tokenomics).reserveAmountForBondProgram(supply)) { 262: ITokenomics(tokenomics).refundFromBondProgram(supply); 320: payout = IGenericBondCalculator(bondCalculator).calculatePayoutOLAS(tokenAmount, product.priceLP); 390: IToken(olas).transfer(msg.sender, payout); 492: return IGenericBondCalculator(bondCalculator).getCurrentPriceLP(token);
GitHub: 221, 226, 262, 320, 390, 492
File: contracts/Tokenomics.sol 708: address serviceOwner = IToken(serviceRegistry).ownerOf(serviceIds[i]); 709: topUpEligible = (IVotingEscrow(ve).getVotes(serviceOwner) >= veOLASThreshold || 710: IVotingEscrow(ve).getVotes(donator) >= veOLASThreshold) ? true : false; 801: if (bList != address(0) && IDonatorBlacklist(bList).isDonatorBlacklisted(donator)) { 810: if (!IServiceRegistry(serviceRegistry).exists(serviceIds[i])) { 1063: if (incentives[1] == 0 || ITreasury(treasury).rebalanceTreasury(incentives[1])) {
GitHub: 708, 709, 710, 801, 810, 1063
File: contracts/Treasury.sol 228: if (IToken(token).allowance(account, address(this)) < tokenAmount) { 229: revert InsufficientAllowance(IToken(token).allowance((account), address(this)), tokenAmount); 242: IOLAS(olas).mint(msg.sender, olasMintAmount); 362: success = IToken(token).transfer(to, tokenAmount); 416: IOLAS(olas).mint(account, accountTopUps);
GitHub: 228, 229, 242, 362, 416
</details>+=
for arraysUsing +=
for arrays saves 199 gas due to not having to re-fetch the array's length
There are 4 instances of this issue:
File: contracts/Tokenomics.sol 915: incentives[1] = (incentives[0] * tp.epochPoint.rewardTreasuryFraction) / 100; 917: incentives[2] = (incentives[0] * tp.unitPoints[0].rewardUnitFraction) / 100; 918: incentives[3] = (incentives[0] * tp.unitPoints[1].rewardUnitFraction) / 100; 965: incentives[4] = effectiveBond + incentives[4] - curMaxBond;
Even if you don't assign the call's second return value, it still gets copied to memory. Use assembly instead to prevent this and save 159 gas:
(bool success,) = payable(receiver).call{gas: gas, value: value}("");
-> bool success; assembly { success := call(gas, receiver, value, 0, 0, 0, 0) }
There are 2 instances of this issue:
File: contracts/multisigs/GnosisSafeSameAddressMultisig.sol 118: (bool success, ) = multisig.call(payload);
GitHub: 118
File: contracts/TokenomicsProxy.sol 48: (bool success, ) = tokenomics.delegatecall(tokenomicsData);
GitHub: 48
Skipping the external call when nothing will be transferred, will save at least 100 gas
There are 2 instances of this issue:
File: contracts/veOLAS.sol 534: IERC20(token).transfer(msg.sender, amount);
GitHub: 534
File: contracts/Treasury.sol 235: bool success = IToken(token).transferFrom(account, address(this), tokenAmount);
GitHub: 235
mapping
s referenced in the same function by the same keyReads and subsequent writes can also be cheaper when a function requires both values and they both fit in the same storage slot (i.e. runtime gas savings). Even if the values can't be packed, if both fields are accessed in the same function (as is the case for these instances), combining them 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 7 instances of this issue:
File: contracts/UnitRegistry.sol /// @audit combine into a `struct`: mapSubComponents,mapUnits 49 function create(address unitOwner, bytes32 unitHash, uint32[] memory dependencies) 50 external virtual returns (uint256 unitId) 51 { 52 // Reentrancy guard 53 if (_locked > 1) { 54 revert ReentrancyGuard(); 55 } 56 _locked = 2; 57 58 // Check for the manager privilege for a unit creation 59 if (manager != msg.sender) { 60 revert ManagerOnly(msg.sender, manager); 61 } 62 63 // Checks for a non-zero owner address 64 if(unitOwner == address(0)) { 65 revert ZeroAddress(); 66 } 67 68 // Check for the non-zero hash value 69 if (unitHash == 0) { 70 revert ZeroValue(); 71 } 72 73 // Check for dependencies validity: must be already allocated, must not repeat 74 unitId = totalSupply; 75 _checkDependencies(dependencies, uint32(unitId)); 76 77 // Unit with Id = 0 is left empty not to do additional checks for the index zero 78 unitId++; 79 80 // Initialize the unit and mint its token 81 Unit storage unit = mapUnits[unitId]; 82 unit.unitHash = unitHash; 83 unit.dependencies = dependencies; 84 85 // Update the map of subcomponents with calculated subcomponents for the new unit Id 86 // In order to get the correct set of subcomponents, we need to differentiate between the callers of this function 87 // Self contract (unit registry) can only call subcomponents calculation from the component level 88 uint32[] memory subComponentIds = _calculateSubComponents(UnitType.Component, dependencies); 89 // We need to add a current component Id to the set of subcomponents if the unit is a component 90 // For example, if component 3 (c3) has dependencies of [c1, c2], then the subcomponents will return [c1, c2]. 91 // The resulting set will be [c1, c2, c3]. So we write into the map of component subcomponents: c3=>[c1, c2, c3]. 92 // This is done such that the subcomponents start getting explored, and when the agent calls its subcomponents, 93 // it would have [c1, c2, c3] right away instead of adding c3 manually and then (for services) checking 94 // if another agent also has c3 as a component dependency. The latter will consume additional computation. 95 if (unitType == UnitType.Component) { 96 uint256 numSubComponents = subComponentIds.length; 97 uint32[] memory addSubComponentIds = new uint32[](numSubComponents + 1); 98 for (uint256 i = 0; i < numSubComponents; ++i) { 99 addSubComponentIds[i] = subComponentIds[i]; 100 } 101 // Adding self component Id 102 addSubComponentIds[numSubComponents] = uint32(unitId); 103 subComponentIds = addSubComponentIds; 104 } 105 mapSubComponents[unitId] = subComponentIds; 106 107 // Set total supply to the unit Id number 108 totalSupply = unitId; 109 // Safe mint is needed since contracts can create units as well 110 _safeMint(unitOwner, unitId); 111 112 emit CreateUnit(unitId, unitType, unitHash); 113 _locked = 1; 114: }
GitHub: 49
File: contracts/Tokenomics.sol /// @audit combine into a `struct`: mapEpochTokenomics,unitPoints 264 function initializeTokenomics( 265 address _olas, 266 address _treasury, 267 address _depository, 268 address _dispenser, 269 address _ve, 270 uint256 _epochLen, 271 address _componentRegistry, 272 address _agentRegistry, 273 address _serviceRegistry, 274 address _donatorBlacklist 275 ) external 276 { 277 // Check if the contract is already initialized 278 if (owner != address(0)) { 279 revert AlreadyInitialized(); 280 } 281 282 // Check for at least one zero contract address 283 if (_olas == address(0) || _treasury == address(0) || _depository == address(0) || _dispenser == address(0) || 284 _ve == address(0) || _componentRegistry == address(0) || _agentRegistry == address(0) || 285 _serviceRegistry == address(0)) { 286 revert ZeroAddress(); 287 } 288 289 // Initialize storage variables 290 owner = msg.sender; 291 _locked = 1; 292 epsilonRate = 1e17; 293 veOLASThreshold = 10_000e18; 294 295 // Check that the epoch length has at least a practical minimal value 296 if (uint32(_epochLen) < MIN_EPOCH_LENGTH) { 297 revert LowerThan(_epochLen, MIN_EPOCH_LENGTH); 298 } 299 300 // Check that the epoch length is not bigger than one year 301 if (uint32(_epochLen) > ONE_YEAR) { 302 revert Overflow(_epochLen, ONE_YEAR); 303 } 304 305 // Assign other input variables 306 olas = _olas; 307 treasury = _treasury; 308 depository = _depository; 309 dispenser = _dispenser; 310 ve = _ve; 311 epochLen = uint32(_epochLen); 312 componentRegistry = _componentRegistry; 313 agentRegistry = _agentRegistry; 314 serviceRegistry = _serviceRegistry; 315 donatorBlacklist = _donatorBlacklist; 316 317 // Time launch of the OLAS contract 318 uint256 _timeLaunch = IOLAS(_olas).timeLaunch(); 319 // Check that the tokenomics contract is initialized no later than one year after the OLAS token is deployed 320 if (block.timestamp >= (_timeLaunch + ONE_YEAR)) { 321 revert Overflow(_timeLaunch + ONE_YEAR, block.timestamp); 322 } 323 // Seconds left in the deployment year for the zero year inflation schedule 324 // This value is necessary since it is different from a precise one year time, as the OLAS contract started earlier 325 uint256 zeroYearSecondsLeft = uint32(_timeLaunch + ONE_YEAR - block.timestamp); 326 // Calculating initial inflation per second: (mintable OLAS from getInflationForYear(0)) / (seconds left in a year) 327 // Note that we lose precision here dividing by the number of seconds right away, but to avoid complex calculations 328 // later we consider it less error-prone and sacrifice at most 6 insignificant digits (or 1e-12) of OLAS per year 329 uint256 _inflationPerSecond = getInflationForYear(0) / zeroYearSecondsLeft; 330 inflationPerSecond = uint96(_inflationPerSecond); 331 timeLaunch = uint32(_timeLaunch); 332 333 // The initial epoch start time is the end time of the zero epoch 334 mapEpochTokenomics[0].epochPoint.endTime = uint32(block.timestamp); 335 336 // The epoch counter starts from 1 337 epochCounter = 1; 338 TokenomicsPoint storage tp = mapEpochTokenomics[1]; 339 340 // Setting initial parameters and fractions 341 devsPerCapital = 1e18; 342 tp.epochPoint.idf = 1e18; 343 344 // Reward fractions 345 // 0 stands for components and 1 for agents 346 // The initial target is to distribute around 2/3 of incentives reserved to fund owners of the code 347 // for components royalties and 1/3 for agents royalties 348 tp.unitPoints[0].rewardUnitFraction = 83; 349 tp.unitPoints[1].rewardUnitFraction = 17; 350 // tp.epochPoint.rewardTreasuryFraction is essentially equal to zero 351 352 // We consider a unit of code as n agents or m components. 353 // Initially we consider 1 unit of code as either 2 agents or 1 component. 354 // E.g. if we have 2 profitable components and 2 profitable agents, this means there are (2 x 2.0 + 2 x 1.0) / 3 = 2 355 // units of code. 356 // We assume that during one epoch the developer can contribute with one piece of code (1 component or 2 agents) 357 codePerDev = 1e18; 358 359 // Top-up fractions 360 uint256 _maxBondFraction = 50; 361 tp.epochPoint.maxBondFraction = uint8(_maxBondFraction); 362 tp.unitPoints[0].topUpUnitFraction = 41; 363 tp.unitPoints[1].topUpUnitFraction = 9; 364 365 // Calculate initial effectiveBond based on the maxBond during the first epoch 366 // maxBond = inflationPerSecond * epochLen * maxBondFraction / 100 367 uint256 _maxBond = (_inflationPerSecond * _epochLen * _maxBondFraction) / 100; 368 maxBond = uint96(_maxBond); 369 effectiveBond = uint96(_maxBond); 370: } /// @audit combine into a `struct`: mapUnitIncentives,unitPoints 650 function _finalizeIncentivesForUnitId(uint256 epochNum, uint256 unitType, uint256 unitId) internal { 651 // Gets the overall amount of unit rewards for the unit's last epoch 652 // The pendingRelativeReward can be zero if the rewardUnitFraction was zero in the first place 653 // Note that if the rewardUnitFraction is set to zero at the end of epoch, the whole pending reward will be zero 654 // reward = (pendingRelativeReward * rewardUnitFraction) / 100 655 uint256 totalIncentives = mapUnitIncentives[unitType][unitId].pendingRelativeReward; 656 if (totalIncentives > 0) { 657 totalIncentives *= mapEpochTokenomics[epochNum].unitPoints[unitType].rewardUnitFraction; 658 // Add to the final reward for the last epoch 659 totalIncentives = mapUnitIncentives[unitType][unitId].reward + totalIncentives / 100; 660 mapUnitIncentives[unitType][unitId].reward = uint96(totalIncentives); 661 // Setting pending reward to zero 662 mapUnitIncentives[unitType][unitId].pendingRelativeReward = 0; 663 } 664 665 // Add to the final top-up for the last epoch 666 totalIncentives = mapUnitIncentives[unitType][unitId].pendingRelativeTopUp; 667 // The pendingRelativeTopUp can be zero if the service owner did not stake enough veOLAS 668 // The topUpUnitFraction was checked before and if it were zero, pendingRelativeTopUp would be zero as well 669 if (totalIncentives > 0) { 670 // Summation of all the unit top-ups and total amount of top-ups per epoch 671 // topUp = (pendingRelativeTopUp * totalTopUpsOLAS * topUpUnitFraction) / (100 * sumUnitTopUpsOLAS) 672 totalIncentives *= mapEpochTokenomics[epochNum].epochPoint.totalTopUpsOLAS; 673 totalIncentives *= mapEpochTokenomics[epochNum].unitPoints[unitType].topUpUnitFraction; 674 uint256 sumUnitIncentives = uint256(mapEpochTokenomics[epochNum].unitPoints[unitType].sumUnitTopUpsOLAS) * 100; 675 totalIncentives = mapUnitIncentives[unitType][unitId].topUp + totalIncentives / sumUnitIncentives; 676 mapUnitIncentives[unitType][unitId].topUp = uint96(totalIncentives); 677 // Setting pending top-up to zero 678 mapUnitIncentives[unitType][unitId].pendingRelativeTopUp = 0; 679 } 680: } /// @audit combine into a `struct`: mapNewUnits,mapUnitIncentives,unitPoints 687 function _trackServiceDonations(address donator, uint256[] memory serviceIds, uint256[] memory amounts, uint256 curEpoch) internal { 688 // Component / agent registry addresses 689 address[] memory registries = new address[](2); 690 (registries[0], registries[1]) = (componentRegistry, agentRegistry); 691 692 // Check all the unit fractions and identify those that need accounting of incentives 693 bool[] memory incentiveFlags = new bool[](4); 694 incentiveFlags[0] = (mapEpochTokenomics[curEpoch].unitPoints[0].rewardUnitFraction > 0); 695 incentiveFlags[1] = (mapEpochTokenomics[curEpoch].unitPoints[1].rewardUnitFraction > 0); 696 incentiveFlags[2] = (mapEpochTokenomics[curEpoch].unitPoints[0].topUpUnitFraction > 0); 697 incentiveFlags[3] = (mapEpochTokenomics[curEpoch].unitPoints[1].topUpUnitFraction > 0); 698 699 // Get the number of services 700 uint256 numServices = serviceIds.length; 701 // Loop over service Ids to calculate their partial contributions 702 for (uint256 i = 0; i < numServices; ++i) { 703 // Check if the service owner or donator stakes enough OLAS for its components / agents to get a top-up 704 // If both component and agent owner top-up fractions are zero, there is no need to call external contract 705 // functions to check each service owner veOLAS balance 706 bool topUpEligible; 707 if (incentiveFlags[2] || incentiveFlags[3]) { 708 address serviceOwner = IToken(serviceRegistry).ownerOf(serviceIds[i]); 709 topUpEligible = (IVotingEscrow(ve).getVotes(serviceOwner) >= veOLASThreshold || 710 IVotingEscrow(ve).getVotes(donator) >= veOLASThreshold) ? true : false; 711 } 712 713 // Loop over component and agent Ids 714 for (uint256 unitType = 0; unitType < 2; ++unitType) { 715 // Get the number and set of units in the service 716 (uint256 numServiceUnits, uint32[] memory serviceUnitIds) = IServiceRegistry(serviceRegistry). 717 getUnitIdsOfService(IServiceRegistry.UnitType(unitType), serviceIds[i]); 718 // Service has to be deployed at least once to be able to receive donations, 719 // otherwise its components and agents are undefined 720 if (numServiceUnits == 0) { 721 revert ServiceNeverDeployed(serviceIds[i]); 722 } 723 // Record amounts data only if at least one incentive unit fraction is not zero 724 if (incentiveFlags[unitType] || incentiveFlags[unitType + 2]) { 725 // The amount has to be adjusted for the number of units in the service 726 uint96 amount = uint96(amounts[i] / numServiceUnits); 727 // Accumulate amounts for each unit Id 728 for (uint256 j = 0; j < numServiceUnits; ++j) { 729 // Get the last epoch number the incentives were accumulated for 730 uint256 lastEpoch = mapUnitIncentives[unitType][serviceUnitIds[j]].lastEpoch; 731 // Check if there were no donations in previous epochs and set the current epoch 732 if (lastEpoch == 0) { 733 mapUnitIncentives[unitType][serviceUnitIds[j]].lastEpoch = uint32(curEpoch); 734 } else if (lastEpoch < curEpoch) { 735 // Finalize unit rewards and top-ups if there were pending ones from the previous epoch 736 // Pending incentives are getting finalized during the next epoch the component / agent 737 // receives donations. If this is not the case before claiming incentives, the finalization 738 // happens in the accountOwnerIncentives() where the incentives are issued 739 _finalizeIncentivesForUnitId(lastEpoch, unitType, serviceUnitIds[j]); 740 // Change the last epoch number 741 mapUnitIncentives[unitType][serviceUnitIds[j]].lastEpoch = uint32(curEpoch); 742 } 743 // Sum the relative amounts for the corresponding components / agents 744 if (incentiveFlags[unitType]) { 745 mapUnitIncentives[unitType][serviceUnitIds[j]].pendingRelativeReward += amount; 746 } 747 // If eligible, add relative top-up weights in the form of donation amounts. 748 // These weights will represent the fraction of top-ups for each component / agent relative 749 // to the overall amount of top-ups that must be allocated 750 if (topUpEligible && incentiveFlags[unitType + 2]) { 751 mapUnitIncentives[unitType][serviceUnitIds[j]].pendingRelativeTopUp += amount; 752 mapEpochTokenomics[curEpoch].unitPoints[unitType].sumUnitTopUpsOLAS += amount; 753 } 754 } 755 } 756 757 // Record new units and new unit owners 758 for (uint256 j = 0; j < numServiceUnits; ++j) { 759 // Check if the component / agent is used for the first time 760 if (!mapNewUnits[unitType][serviceUnitIds[j]]) { 761 mapNewUnits[unitType][serviceUnitIds[j]] = true; 762 mapEpochTokenomics[curEpoch].unitPoints[unitType].numNewUnits++; 763 // Check if the owner has introduced component / agent for the first time 764 // This is done together with the new unit check, otherwise it could be just a new unit owner 765 address unitOwner = IToken(registries[unitType]).ownerOf(serviceUnitIds[j]); 766 if (!mapNewOwners[unitOwner]) { 767 mapNewOwners[unitOwner] = true; 768 mapEpochTokenomics[curEpoch].epochPoint.numNewOwners++; 769 } 770 } 771 } 772 } 773 } 774: }
File: contracts/Treasury.sol /// @audit combine into a `struct`: mapEnabledTokens,mapTokenReserves 212 function depositTokenForOLAS(address account, uint256 tokenAmount, address token, uint256 olasMintAmount) external { 213 // Check for the depository access 214 if (depository != msg.sender) { 215 revert ManagerOnly(msg.sender, depository); 216 } 217 218 // Check if the token is authorized by the registry 219 if (!mapEnabledTokens[token]) { 220 revert UnauthorizedToken(token); 221 } 222 223 // Increase the amount of LP token reserves 224 uint256 reserves = mapTokenReserves[token] + tokenAmount; 225 mapTokenReserves[token] = reserves; 226 227 // Uniswap allowance implementation does not revert with the accurate message, need to check before the transfer is engaged 228 if (IToken(token).allowance(account, address(this)) < tokenAmount) { 229 revert InsufficientAllowance(IToken(token).allowance((account), address(this)), tokenAmount); 230 } 231 232 // Transfer tokens from account to treasury and add to the token treasury reserves 233 // We assume that authorized LP tokens in the protocol are safe as they are enabled via the governance 234 // UniswapV2ERC20 realization has a standard transferFrom() function that returns a boolean value 235 bool success = IToken(token).transferFrom(account, address(this), tokenAmount); 236 if (!success) { 237 revert TransferFailed(token, account, address(this), tokenAmount); 238 } 239 240 // Mint specified number of OLAS tokens corresponding to tokens bonding deposit 241 // The olasMintAmount is guaranteed by the product supply limit, which is limited by the effectiveBond 242 IOLAS(olas).mint(msg.sender, olasMintAmount); 243 244 emit DepositTokenFromAccount(account, token, tokenAmount, olasMintAmount); 245: } /// @audit combine into a `struct`: mapEnabledTokens,mapTokenReserves 313 function withdraw(address to, uint256 tokenAmount, address token) external returns (bool success) { 314 // Check for the contract ownership 315 if (msg.sender != owner) { 316 revert OwnerOnly(msg.sender, owner); 317 } 318 319 // Check that the withdraw address is not treasury itself 320 if (to == address(this)) { 321 revert TransferFailed(token, address(this), to, tokenAmount); 322 } 323 324 // Check for the zero withdraw amount 325 if (tokenAmount == 0) { 326 revert ZeroValue(); 327 } 328 329 // ETH address is taken separately, and all the LP tokens must be validated with corresponding token reserves 330 if (token == ETH_TOKEN_ADDRESS) { 331 uint256 amountOwned = ETHOwned; 332 // Check if treasury has enough amount of owned ETH 333 if (amountOwned >= tokenAmount) { 334 // This branch is used to transfer ETH to a specified address 335 amountOwned -= tokenAmount; 336 ETHOwned = uint96(amountOwned); 337 emit Withdraw(ETH_TOKEN_ADDRESS, to, tokenAmount); 338 // Send ETH to the specified address 339 (success, ) = to.call{value: tokenAmount}(""); 340 if (!success) { 341 revert TransferFailed(ETH_TOKEN_ADDRESS, address(this), to, tokenAmount); 342 } 343 } else { 344 // Insufficient amount of treasury owned ETH 345 revert LowerThan(tokenAmount, amountOwned); 346 } 347 } else { 348 // Only approved token reserves can be used for redemptions 349 if (!mapEnabledTokens[token]) { 350 revert UnauthorizedToken(token); 351 } 352 // Decrease the global LP token reserves record 353 uint256 reserves = mapTokenReserves[token]; 354 if (reserves >= tokenAmount) { 355 reserves -= tokenAmount; 356 mapTokenReserves[token] = reserves; 357 358 emit Withdraw(token, to, tokenAmount); 359 // Transfer LP tokens 360 // We assume that LP tokens enabled in the protocol are safe by default 361 // UniswapV2ERC20 realization has a standard transfer() function 362 success = IToken(token).transfer(to, tokenAmount); 363 if (!success) { 364 revert TransferFailed(token, address(this), to, tokenAmount); 365 } 366 } else { 367 // Insufficient amount of LP tokens 368 revert LowerThan(tokenAmount, reserves); 369 } 370 } 371: } /// @audit combine into a `struct`: mapEnabledTokens,mapTokenReserves 507 function disableToken(address token) external { 508 // Check for the contract ownership 509 if (msg.sender != owner) { 510 revert OwnerOnly(msg.sender, owner); 511 } 512 513 if (mapEnabledTokens[token]) { 514 // The reserves of a token must be zero in order to disable it 515 if (mapTokenReserves[token] > 0) { 516 revert NonZeroValue(); 517 } 518 mapEnabledTokens[token] = false; 519 emit DisableToken(token); 520 } 521: }
payable
If a function modifier such as onlyOwner
is used, or a function checks msg.sender
some other way, the function will revert if a normal user tries to pay the function. Marking the function as payable
will lower the gas cost for legitimate callers because the compiler will not include checks for whether a payment was provided. The extra opcodes avoided are CALLVALUE
(2),DUP1
(3),ISZERO
(3),PUSH2
(3),JUMPI
(10),PUSH1
(3),DUP1
(3),REVERT
(0),JUMPDEST
(1),POP
(2), which costs an average of about 21 gas per call to the function, in addition to the extra deployment cost
There are 56 instances of this issue:
<details> <summary>see instances</summary>File: contracts/OLAS.sol 43: function changeOwner(address newOwner) external { 58: function changeMinter(address newMinter) external { 75: function mint(address account, uint256 amount) external {
File: contracts/bridges/BridgedERC20.sol 30: function changeOwner(address newOwner) external { 48: function mint(address account, uint256 amount) external { 59: function burn(uint256 amount) external {
File: contracts/bridges/FxGovernorTunnel.sol 81: function changeRootGovernor(address newRootGovernor) external { 107: function processMessageFromRoot(uint256 stateId, address rootMessageSender, bytes memory data) external override {
File: contracts/bridges/HomeMediator.sol 81: function changeForeignGovernor(address newForeignGovernor) external { 105: function processMessageFromForeign(bytes memory data) external {
File: contracts/multisigs/GuardCM.sol 154: function changeGovernor(address newGovernor) external { 170: function changeGovernorCheckProposalId(uint256 proposalId) external { 441 function setTargetSelectorChainIds( 442 address[] memory targets, 443 bytes4[] memory selectors, 444 uint256[] memory chainIds, 445 bool[] memory statuses 446: ) external { 495 function setBridgeMediatorChainIds( 496 address[] memory bridgeMediatorL1s, 497 address[] memory bridgeMediatorL2s, 498 uint256[] memory chainIds 499: ) external { 560: function unpause() external {
GitHub: 154, 170, 441, 495, 560
File: contracts/GenericManager.sol 20: function changeOwner(address newOwner) external virtual { 36: function pause() external virtual { 47: function unpause() external virtual {
File: contracts/GenericRegistry.sol 37: function changeOwner(address newOwner) external virtual { 54: function changeManager(address newManager) external virtual { 78: function setBaseURI(string memory bURI) external virtual {
File: contracts/UnitRegistry.sol 49 function create(address unitOwner, bytes32 unitHash, uint32[] memory dependencies) 50 external virtual returns (uint256 unitId) 51: { 121 function updateHash(address unitOwner, uint256 unitId, bytes32 unitHash) external virtual 122 returns (bool success) 123: {
File: contracts/Depository.sol 123: function changeOwner(address newOwner) external { 143: function changeManagers(address _tokenomics, address _treasury) external { 163: function changeBondCalculator(address _bondCalculator) external { 183: function create(address token, uint256 priceLP, uint256 supply, uint256 vesting) external returns (uint256 productId) { 244: function close(uint256[] memory productIds) external returns (uint256[] memory closedProductIds) { 356: function redeem(uint256[] memory bondIds) external returns (uint256 payout) {
GitHub: 123, 143, 163, 183, 244, 356
File: contracts/Dispenser.sol 46: function changeOwner(address newOwner) external { 64: function changeManagers(address _tokenomics, address _treasury) external {
File: contracts/DonatorBlacklist.sol 36: function changeOwner(address newOwner) external { 56: function setDonatorsStatuses(address[] memory accounts, bool[] memory statuses) external returns (bool success) {
File: contracts/Tokenomics.sol 384: function changeTokenomicsImplementation(address implementation) external { 404: function changeOwner(address newOwner) external { 423: function changeManagers(address _treasury, address _depository, address _dispenser) external { 450: function changeRegistries(address _componentRegistry, address _agentRegistry, address _serviceRegistry) external { 474: function changeDonatorBlacklist(address _donatorBlacklist) external { 497 function changeTokenomicsParameters( 498 uint256 _devsPerCapital, 499 uint256 _codePerDev, 500 uint256 _epsilonRate, 501 uint256 _epochLen, 502 uint256 _veOLASThreshold 503 ) external 504: { 562 function changeIncentiveFractions( 563 uint256 _rewardComponentFraction, 564 uint256 _rewardAgentFraction, 565 uint256 _maxBondFraction, 566 uint256 _topUpComponentFraction, 567 uint256 _topUpAgentFraction 568 ) external 569: { 609: function reserveAmountForBondProgram(uint256 amount) external returns (bool success) { 630: function refundFromBondProgram(uint256 amount) external { 788 function trackServiceDonations( 789 address donator, 790 uint256[] memory serviceIds, 791 uint256[] memory amounts, 792 uint256 donationETH 793: ) external { 1085 function accountOwnerIncentives(address account, uint256[] memory unitTypes, uint256[] memory unitIds) external 1086 returns (uint256 reward, uint256 topUp) 1087: {
GitHub: 384, 404, 423, 450, 474, 497, 562, 609, 630, 788, 1085
File: contracts/Treasury.sol 137: function changeOwner(address newOwner) external { 156: function changeManagers(address _tokenomics, address _depository, address _dispenser) external { 182: function changeMinAcceptedETH(uint256 _minAcceptedETH) external { 212: function depositTokenForOLAS(address account, uint256 tokenAmount, address token, uint256 olasMintAmount) external { 313: function withdraw(address to, uint256 tokenAmount, address token) external returns (bool success) { 387 function withdrawToAccount(address account, uint256 accountRewards, uint256 accountTopUps) external 388 returns (bool success) 389: { 428: function rebalanceTreasury(uint256 treasuryRewards) external returns (bool success) { 466: function drainServiceSlashedFunds() external returns (uint256 amount) { 487: function enableToken(address token) external { 507: function disableToken(address token) external { 531: function pause() external { 542: function unpause() external {
GitHub: 137, 156, 182, 212, 313, 387, 428, 466, 487, 507, 531, 542
</details>internal
functions only called once can be inlined to save gasNot inlining costs 20 to 40 gas because of two extra JUMP
instructions and additional stack operations needed for function calls. The inliner can do it only for 'simple' cases:
Now to get back to the point why we require the routine to be simple: As soon as you do more complicated things like for example branching, calling external contracts, the Common Subexpression Eliminator cannot re-construct the code anymore or does not do full symbolic evaluation of the expressions.
https://soliditylang.org/blog/2021/03/02/saving-gas-with-simple-inliner/
Therefore, the instances below contain branching or use op-codes with side-effects
There are 2 instances of this issue:
File: contracts/bridges/FxERC20ChildTunnel.sol 70 function _processMessageFromRoot( 71 uint256 /* stateId */, 72 address sender, 73 bytes memory message 74: ) internal override validateSender(sender) {
GitHub: 70
File: contracts/bridges/FxERC20RootTunnel.sol 72: function _processMessageFromChild(bytes memory message) internal override {
GitHub: 72
Using the scratch space for more than one, but at most two words worth of data (non-indexed arguments) will save gas over needing Solidity's abi memory expansion used for emitting normally.
There are 10 instances of this issue:
File: contracts/bridges/FxERC20ChildTunnel.sol 84: emit FxWithdrawERC20(rootToken, childToken, from, to, amount); 107: emit FxDepositERC20(childToken, rootToken, msg.sender, to, amount);
File: contracts/bridges/FxERC20RootTunnel.sol 79: emit FxDepositERC20(childToken, rootToken, from, to, amount); 106: emit FxWithdrawERC20(rootToken, childToken, msg.sender, to, amount);
File: contracts/veOLAS.sol 368: emit Supply(supplyBefore, supplyAfter); 530: emit Withdraw(msg.sender, amount, block.timestamp); 531: emit Supply(supplyBefore, supplyAfter);
File: contracts/Dispenser.sol 112: emit IncentivesClaimed(msg.sender, reward, topUp);
GitHub: 112
File: contracts/Treasury.sol 244: emit DepositTokenFromAccount(account, token, tokenAmount, olasMintAmount); 452: emit UpdateTreasuryBalances(amountETHOwned, amountETHFromServices);
>=
costs less gas than >
The compiler uses opcodes GT
and ISZERO
for solidity code that uses >
, but only requires LT
for >=
, which saves 3 gas. If <
is being used, the condition can be inverted. In cases where a for-loop is being used, one can count down rather than up, in order to use the optimal operator
There are 51 instances of this issue:
<details> <summary>see instances</summary>File: contracts/OLAS.sol 108: for (uint256 i = 0; i < numYears; ++i) {
GitHub: 108
File: contracts/bridges/FxGovernorTunnel.sol 125: for (uint256 i = 0; i < dataLength;) { 153: for (uint256 j = 0; j < payloadLength; ++j) {
File: contracts/bridges/HomeMediator.sol 125: for (uint256 i = 0; i < dataLength;) { 153: for (uint256 j = 0; j < payloadLength; ++j) {
File: contracts/multisigs/GuardCM.sol 212: for (uint256 i = 0; i < data.length;) { 237: for (uint256 j = 0; j < payloadLength; ++j) { 273: for (uint256 i = 0; i < payload.length; ++i) { 292: for (uint256 i = 0; i < bridgePayload.length; ++i) { 318: for (uint256 i = 0; i < payload.length; ++i) { 340: for (uint256 i = 0; i < payload.length; ++i) { 360: for (uint i = 0; i < targets.length; ++i) { 458: for (uint256 i = 0; i < targets.length; ++i) { 511: for (uint256 i = 0; i < chainIds.length; ++i) {
GitHub: 212, 237, 273, 292, 318, 340, 360, 458, 511
File: contracts/veOLAS.sol 232: for (uint256 i = 0; i < 255; ++i) { 561: for (uint256 i = 0; i < 128; ++i) { 693: for (uint256 i = 0; i < 255; ++i) {
File: contracts/AgentRegistry.sol 40: for (uint256 iDep = 0; iDep < dependencies.length; ++iDep) {
GitHub: 40
File: contracts/ComponentRegistry.sol 29: for (uint256 iDep = 0; iDep < dependencies.length; ++iDep) {
GitHub: 29
File: contracts/UnitRegistry.sol 98: for (uint256 i = 0; i < numSubComponents; ++i) { 211: for (uint32 i = 0; i < numUnits; ++i) { 227: for (counter = 0; counter < maxNumComponents; ++counter) { 234: for (uint32 i = 0; i < numUnits; ++i) { 236: for (; processedComponents[i] < numComponents[i]; ++processedComponents[i]) { 262: for (uint32 i = 0; i < counter; ++i) {
GitHub: 98, 211, 227, 234, 236, 262
File: contracts/multisigs/GnosisSafeMultisig.sol 79: for (uint256 i = 0; i < payloadLength; ++i) {
GitHub: 79
File: contracts/multisigs/GnosisSafeSameAddressMultisig.sol 113: for (uint256 i = 0; i < payloadLength; ++i) { 139: for (uint256 i = 0; i < numOwners; ++i) {
File: contracts/Depository.sol 255: for (uint256 i = 0; i < numProducts; ++i) { 274: for (uint256 i = 0; i < numClosedProducts; ++i) { 357: for (uint256 i = 0; i < bondIds.length; ++i) { 402: for (uint256 i = 0; i < numProducts; ++i) { 413: for (uint256 i = 0; i < numProducts; ++i) { 448: for (uint256 i = 0; i < numBonds; ++i) { 468: for (uint256 i = 0; i < numBonds; ++i) {
GitHub: 255, 274, 357, 402, 413, 448, 468
File: contracts/DonatorBlacklist.sol 67: for (uint256 i = 0; i < accounts.length; ++i) {
GitHub: 67
File: contracts/Tokenomics.sol 702: for (uint256 i = 0; i < numServices; ++i) { 714: for (uint256 unitType = 0; unitType < 2; ++unitType) { 728: for (uint256 j = 0; j < numServiceUnits; ++j) { 758: for (uint256 j = 0; j < numServiceUnits; ++j) { 808: for (uint256 i = 0; i < numServices; ++i) { 978: for (uint256 i = 0; i < 2; ++i) { 1104: for (uint256 i = 0; i < 2; ++i) { 1110: for (uint256 i = 0; i < unitIds.length; ++i) { 1132: for (uint256 i = 0; i < unitIds.length; ++i) { 1174: for (uint256 i = 0; i < 2; ++i) { 1180: for (uint256 i = 0; i < unitIds.length; ++i) { 1202: for (uint256 i = 0; i < unitIds.length; ++i) {
GitHub: 702, 714, 728, 758, 808, 978, 1104, 1110, 1132, 1174, 1180, 1202
File: contracts/TokenomicsConstants.sol 55: for (uint256 i = 0; i < numYears; ++i) { 92: for (uint256 i = 1; i < numYears; ++i) {
File: contracts/Treasury.sol 276: for (uint256 i = 0; i < numServices; ++i) {
GitHub: 276
</details>ERC721A
over ERC721
ERC721A is much more gas-efficient for minting multiple NFTs in the same transaction
There are 3 instances of this issue:
File: contracts/AgentRegistry.sol 9 contract AgentRegistry is UnitRegistry { 10: // Component registry
GitHub: 9
File: contracts/ComponentRegistry.sol 8 contract ComponentRegistry is UnitRegistry { 9: // Component registry version number
GitHub: 8
File: contracts/UnitRegistry.sol 8: abstract contract UnitRegistry is GenericRegistry {
GitHub: 8
solady
's token contracts to save gasThey're written using heavily-optimized assembly, and will your users a lot of gas
There are 3 instances of this issue:
File: contracts/AgentRegistry.sol /// @audit ERC721 9 contract AgentRegistry is UnitRegistry { 10: // Component registry
GitHub: 9
File: contracts/ComponentRegistry.sol /// @audit ERC721 8 contract ComponentRegistry is UnitRegistry { 9: // Component registry version number
GitHub: 8
File: contracts/UnitRegistry.sol /// @audit ERC721 8: abstract contract UnitRegistry is GenericRegistry {
GitHub: 8
FixedPointMathLib
Saves gas, and works to avoid unnecessary overflows.
There are 23 instances of this issue:
File: contracts/OLAS.sol 101: uint256 numYears = (block.timestamp - timeLaunch) / oneYear; 109: supplyCap += (supplyCap * maxMintCapFraction) / 100;
File: contracts/veOLAS.sol 225: block_slope = (1e18 * uint256(block.number - lastPoint.blockNumber)) / uint256(block.timestamp - lastPoint.ts); 258: lastPoint.blockNumber = initialPoint.blockNumber + uint64((block_slope * uint256(tStep - initialPoint.ts)) / 1e18); 433: unlockTime = ((block.timestamp + unlockTime) / WEEK) * WEEK; 487: unlockTime = ((block.timestamp + unlockTime) / WEEK) * WEEK; 565: uint256 mid = (minPointNumber + maxPointNumber + 1) / 2; 664: blockTime += (dt * (blockNumber - point.blockNumber)) / dBlock;
GitHub: 225, 258, 433, 487, 565, 664
File: contracts/GenericBondCalculator.sol 89: priceLP = (reserve1 * 1e18) / totalSupply;
GitHub: 89
File: contracts/Tokenomics.sol 367: uint256 _maxBond = (_inflationPerSecond * _epochLen * _maxBondFraction) / 100; 915: incentives[1] = (incentives[0] * tp.epochPoint.rewardTreasuryFraction) / 100; 917: incentives[2] = (incentives[0] * tp.unitPoints[0].rewardUnitFraction) / 100; 918: incentives[3] = (incentives[0] * tp.unitPoints[1].rewardUnitFraction) / 100; 925: uint256 numYears = (block.timestamp - timeLaunch) / ONE_YEAR; 951: incentives[4] = (inflationPerEpoch * tp.epochPoint.maxBondFraction) / 100; 1010: numYears = (block.timestamp + curEpochLen - timeLaunch) / ONE_YEAR; 1023: curMaxBond = (inflationPerEpoch * nextEpochPoint.epochPoint.maxBondFraction) / 100; 1030: curMaxBond = (curEpochLen * curInflationPerSecond * nextEpochPoint.epochPoint.maxBondFraction) / 100; 1054: incentives[5] = (inflationPerEpoch * tp.unitPoints[0].topUpUnitFraction) / 100; 1056: incentives[6] = (inflationPerEpoch * tp.unitPoints[1].topUpUnitFraction) / 100;
GitHub: 367, 915, 917, 918, 925, 951, 1010, 1023, 1030, 1054, 1056
File: contracts/TokenomicsConstants.sol 56: supplyCap += (supplyCap * maxMintCapFraction) / 100; 93: supplyCap += (supplyCap * maxMintCapFraction) / 100; 97: inflationAmount = (supplyCap * maxMintCapFraction) / 100;
payable
Payable functions cost less gas to execute, since the compiler does not have to add extra checks to ensure that a payment wasn't provided. An initializer can safely be marked as payable, since only the deployer would be able to pass funds, and the project itself would not pass any funds.
There is one instance of this issue:
File: contracts/Tokenomics.sol 264 function initializeTokenomics( 265 address _olas, 266 address _treasury, 267 address _depository, 268 address _dispenser, 269 address _ve, 270 uint256 _epochLen, 271 address _componentRegistry, 272 address _agentRegistry, 273 address _serviceRegistry, 274 address _donatorBlacklist 275 ) external 276: {
GitHub: 264
Use assembly ("memory-safe") { ... }
for the assembly blocks below since they dont't read or modify memory, and therefore are memory-safe. This will help the optimizer to create more optimal gas-efficient code. Use the comment variant if prior to Solidity version 0.8.13
There are 6 instances of this issue:
File: contracts/bridges/FxGovernorTunnel.sol 129 // solhint-disable-next-line no-inline-assembly 130 assembly { 131 // First 20 bytes is the address (160 bits) 132 i := add(i, 20) 133 target := mload(add(data, i)) 134 // Offset the data by 12 bytes of value (96 bits) 135 i := add(i, 12) 136 value := mload(add(data, i)) 137 // Offset the data by 4 bytes of payload length (32 bits) 138 i := add(i, 4) 139 payloadLength := mload(add(data, i)) 140: }
GitHub: 129
File: contracts/bridges/HomeMediator.sol 129 // solhint-disable-next-line no-inline-assembly 130 assembly { 131 // First 20 bytes is the address (160 bits) 132 i := add(i, 20) 133 target := mload(add(data, i)) 134 // Offset the data by 12 bytes of value (96 bits) 135 i := add(i, 12) 136 value := mload(add(data, i)) 137 // Offset the data by 4 bytes of payload length (32 bits) 138 i := add(i, 4) 139 payloadLength := mload(add(data, i)) 140: }
GitHub: 129
File: contracts/multisigs/GuardCM.sol 215 // solhint-disable-next-line no-inline-assembly 216 assembly { 217 // First 20 bytes is the address (160 bits) 218 i := add(i, 20) 219 target := mload(add(data, i)) 220 // Offset the data by 12 bytes of value (96 bits) and by 4 bytes of payload length (32 bits) 221 i := add(i, 16) 222 payloadLength := mload(add(data, i)) 223: }
GitHub: 215
File: contracts/multisigs/GnosisSafeMultisig.sol 56 // Read the first 144 bytes of data 57 assembly { 58 // Read all the addresses first (80 bytes) 59 let offset := 20 60 to := mload(add(data, offset)) 61 offset := add(offset, 20) 62 fallbackHandler := mload(add(data, offset)) 63 offset := add(offset, 20) 64 paymentToken := mload(add(data, offset)) 65 offset := add(offset, 20) 66 paymentReceiver := mload(add(data, offset)) 67 68 // Read all the uints (64 more bytes, a total of 144 bytes) 69 offset := add(offset, 32) 70 payment := mload(add(data, offset)) 71 offset := add(offset, 32) 72 nonce := mload(add(data, offset)) 73: }
GitHub: 56
File: contracts/multisigs/GnosisSafeSameAddressMultisig.sol 97 // Read the proxy multisig address (20 bytes) and the multisig-related data 98 assembly { 99 multisig := mload(add(data, DEFAULT_DATA_LENGTH)) 100: }
GitHub: 97
File: contracts/TokenomicsProxy.sol 55 fallback() external { 56 assembly { 57 let tokenomics := sload(PROXY_TOKENOMICS) 58 // Otherwise continue with the delegatecall to the tokenomics implementation 59 calldatacopy(0, 0, calldatasize()) 60 let success := delegatecall(gas(), tokenomics, 0, calldatasize(), 0, 0) 61 returndatacopy(0, 0, returndatasize()) 62 if eq(success, 0) { 63 revert(0, returndatasize()) 64 } 65 return(0, returndatasize()) 66: }
GitHub: 55
address
/ID mappings can be combined into a single mapping
of an address
/ID to a struct
Saves a storage slot for each of the removed mappings (i.e. this finding is not about runtime savings). The instances below refer to both mappings using the same key in the same function, so the mappings are related.
There are 4 instances of this issue:
File: contracts/UnitRegistry.sol /// @audit combine into a `struct`: mapSubComponents,mapUnits 8: abstract contract UnitRegistry is GenericRegistry {
GitHub: 8
File: contracts/Tokenomics.sol /// @audit combine into a `struct`: mapUnitIncentives,unitPoints /// @audit combine into a `struct`: mapEpochTokenomics,unitPoints /// @audit combine into a `struct`: mapNewUnits,mapUnitIncentives,unitPoints 118: contract Tokenomics is TokenomicsConstants, IErrorsTokenomics {
memory
/calldata
array should use a local variable cacheThe instances below point to the second+ access of a value inside a memory
/calldata
array, within a function. Caching avoids recalculating the array offsets into memory
/calldata
There are 84 instances of this issue:
<details> <summary>see instances</summary>File: contracts/multisigs/GuardCM.sol /// @audit targets...[] 376: _verifyData(targets[i], callDatas[i], block.chainid); /// @audit targets...[] 476: uint256 targetSelectorChainId = uint256(uint160(targets[i])); /// @audit selectors...[] 478: targetSelectorChainId |= uint256(uint32(selectors[i])) << 160; /// @audit chainIds...[] 480: targetSelectorChainId |= chainIds[i] << 192; /// @audit bridgeMediatorL2s...[] 525: uint256 bridgeMediatorL2ChainId = uint256(uint160(bridgeMediatorL2s[i])); /// @audit bridgeMediatorL1s...[] 528: mapBridgeMediatorL1L2ChainIds[bridgeMediatorL1s[i]] = bridgeMediatorL2ChainId;
GitHub: 376, 476, 478, 480, 525, 528
File: contracts/AgentRegistry.sol /// @audit dependencies...[] 44: lastId = dependencies[iDep];
GitHub: 44
File: contracts/ComponentRegistry.sol /// @audit dependencies...[] 33: lastId = dependencies[iDep];
GitHub: 33
File: contracts/UnitRegistry.sol /// @audit components...[] 214: numComponents[i] = uint32(components[i].length); /// @audit numComponents...[] 215: maxNumComponents += numComponents[i];
File: contracts/Depository.sol /// @audit productIds...[] 266: ids[numClosedProducts] = productIds[i]; /// @audit bondIds...[] 360: bool matured = block.timestamp >= mapUserBonds[bondIds[i]].maturity; /// @audit bondIds...[] 364: revert BondNotRedeemable(bondIds[i]); /// @audit bondIds...[] 368: if (mapUserBonds[bondIds[i]].account != msg.sender) { /// @audit bondIds...[] 369: revert OwnerOnly(msg.sender, mapUserBonds[bondIds[i]].account); /// @audit bondIds...[] 376: uint256 productId = mapUserBonds[bondIds[i]].productId; /// @audit bondIds...[] 379: delete mapUserBonds[bondIds[i]]; /// @audit bondIds...[] 380: emit RedeemBond(productId, msg.sender, bondIds[i]);
GitHub: 266, 360, 364, 368, 369, 376, 379, 380
File: contracts/DonatorBlacklist.sol /// @audit accounts...[] 73: mapBlacklistedDonators[accounts[i]] = statuses[i]; /// @audit accounts...[] /// @audit statuses...[] 74: emit DonatorBlacklistStatus(accounts[i], statuses[i]);
File: contracts/Tokenomics.sol /// @audit incentiveFlags...[] /// @audit incentiveFlags...[] 707: if (incentiveFlags[2] || incentiveFlags[3]) { /// @audit serviceIds...[] 721: revert ServiceNeverDeployed(serviceIds[i]); /// @audit serviceUnitIds...[] 733: mapUnitIncentives[unitType][serviceUnitIds[j]].lastEpoch = uint32(curEpoch); /// @audit serviceUnitIds...[] 739: _finalizeIncentivesForUnitId(lastEpoch, unitType, serviceUnitIds[j]); /// @audit serviceUnitIds...[] 741: mapUnitIncentives[unitType][serviceUnitIds[j]].lastEpoch = uint32(curEpoch); /// @audit serviceUnitIds...[] 745: mapUnitIncentives[unitType][serviceUnitIds[j]].pendingRelativeReward += amount; /// @audit serviceUnitIds...[] 751: mapUnitIncentives[unitType][serviceUnitIds[j]].pendingRelativeTopUp += amount; /// @audit serviceUnitIds...[] 765: address unitOwner = IToken(registries[unitType]).ownerOf(serviceUnitIds[j]); /// @audit incentives...[] 915: incentives[1] = (incentives[0] * tp.epochPoint.rewardTreasuryFraction) / 100; /// @audit incentives...[] 917: incentives[2] = (incentives[0] * tp.unitPoints[0].rewardUnitFraction) / 100; /// @audit incentives...[] 918: incentives[3] = (incentives[0] * tp.unitPoints[1].rewardUnitFraction) / 100; /// @audit incentives...[] 963: if (incentives[4] > curMaxBond) { /// @audit incentives...[] /// @audit incentives...[] 965: incentives[4] = effectiveBond + incentives[4] - curMaxBond; /// @audit incentives...[] 966: effectiveBond = uint96(incentives[4]); /// @audit incentives...[] 1041: if (incentives[0] > 0) { /// @audit incentives...[] 1043: uint256 idf = _calculateIDF(incentives[1], tp.epochPoint.numNewOwners); /// @audit incentives...[] /// @audit incentives...[] 1052: uint256 accountRewards = incentives[2] + incentives[3]; /// @audit incentives...[] /// @audit incentives...[] 1060: uint256 accountTopUps = incentives[5] + incentives[6]; /// @audit incentives...[] /// @audit incentives...[] 1063: if (incentives[1] == 0 || ITreasury(treasury).rebalanceTreasury(incentives[1])) { /// @audit incentives...[] 1065: emit EpochSettled(eCounter, incentives[1], accountRewards, accountTopUps); /// @audit unitTypes...[] /// @audit unitTypes...[] 1117: if (unitIds[i] <= lastIds[unitTypes[i]] || unitIds[i] > registriesSupply[unitTypes[i]]) { /// @audit unitTypes...[] 1118: revert WrongUnitId(unitIds[i], unitTypes[i]); /// @audit unitTypes...[] /// @audit unitIds...[] 1120: lastIds[unitTypes[i]] = unitIds[i]; /// @audit unitTypes...[] /// @audit unitIds...[] 1123: address unitOwner = IToken(registries[unitTypes[i]]).ownerOf(unitIds[i]); /// @audit unitTypes...[] /// @audit unitIds...[] 1139: _finalizeIncentivesForUnitId(lastEpoch, unitTypes[i], unitIds[i]); /// @audit unitTypes...[] /// @audit unitIds...[] 1141: mapUnitIncentives[unitTypes[i]][unitIds[i]].lastEpoch = 0; /// @audit unitTypes...[] /// @audit unitIds...[] 1145: reward += mapUnitIncentives[unitTypes[i]][unitIds[i]].reward; /// @audit unitTypes...[] /// @audit unitIds...[] 1146: mapUnitIncentives[unitTypes[i]][unitIds[i]].reward = 0; /// @audit unitTypes...[] /// @audit unitIds...[] 1148: topUp += mapUnitIncentives[unitTypes[i]][unitIds[i]].topUp; /// @audit unitTypes...[] /// @audit unitIds...[] 1149: mapUnitIncentives[unitTypes[i]][unitIds[i]].topUp = 0; /// @audit unitTypes...[] /// @audit unitTypes...[] 1187: if (unitIds[i] <= lastIds[unitTypes[i]] || unitIds[i] > registriesSupply[unitTypes[i]]) { /// @audit unitTypes...[] 1188: revert WrongUnitId(unitIds[i], unitTypes[i]); /// @audit unitTypes...[] /// @audit unitIds...[] 1190: lastIds[unitTypes[i]] = unitIds[i]; /// @audit unitTypes...[] /// @audit unitIds...[] 1193: address unitOwner = IToken(registries[unitTypes[i]]).ownerOf(unitIds[i]); /// @audit unitTypes...[] /// @audit unitIds...[] 1209: uint256 totalIncentives = mapUnitIncentives[unitTypes[i]][unitIds[i]].pendingRelativeReward; /// @audit unitTypes...[] 1211: totalIncentives *= mapEpochTokenomics[lastEpoch].unitPoints[unitTypes[i]].rewardUnitFraction; /// @audit unitTypes...[] /// @audit unitIds...[] 1216: totalIncentives = mapUnitIncentives[unitTypes[i]][unitIds[i]].pendingRelativeTopUp; /// @audit unitTypes...[] 1221: totalIncentives *= mapEpochTokenomics[lastEpoch].unitPoints[unitTypes[i]].topUpUnitFraction; /// @audit unitTypes...[] 1222: uint256 sumUnitIncentives = uint256(mapEpochTokenomics[lastEpoch].unitPoints[unitTypes[i]].sumUnitTopUpsOLAS) * 100; /// @audit unitTypes...[] /// @audit unitIds...[] 1229: reward += mapUnitIncentives[unitTypes[i]][unitIds[i]].reward; /// @audit unitTypes...[] /// @audit unitIds...[] 1231: topUp += mapUnitIncentives[unitTypes[i]][unitIds[i]].topUp;
GitHub: 707, 707, 721, 733, 739, 741, 745, 751, 765, 915, 917, 918, 963, 965, 965, 966, 1041, 1043, 1052, 1052, 1060, 1060, 1063, 1063, 1065, 1117, 1117, 1118, 1120, 1120, 1123, 1123, 1139, 1139, 1141, 1141, 1145, 1145, 1146, 1146, 1148, 1148, 1149, 1149, 1187, 1187, 1188, 1190, 1190, 1193, 1193, 1209, 1209, 1211, 1216, 1216, 1221, 1222, 1229, 1229, 1231, 1231
File: contracts/Treasury.sol /// @audit amounts...[] 280: totalAmount += amounts[i];
GitHub: 280
</details>The instances below point to the second+ access of a value inside a storage array, within a function. Caching an array index value in a local storage
or calldata
variable when the value is accessed multiple times, saves ~42 gas per access due to not having to recalculate the array's keccak256 hash (Gkeccak256
- 30 gas) and that calculation's associated stack operations.
There are 2 instances of this issue:
File: contracts/veOLAS.sol /// @audit mapUserPoints...[] 148: pv = mapUserPoints[account][lastPointNumber - 1]; /// @audit mapUserPoints...[] 596: PointVoting memory uPoint = mapUserPoints[account][pointNumber - 1];
See this link, at its bottom, for full details
There are 23 instances of this issue:
<details> <summary>see instances</summary>File: contracts/GovernorOLAS.sol 15: contract GovernorOLAS is Governor, GovernorSettings, GovernorCompatibilityBravo, GovernorVotes, GovernorVotesQuorumFraction, GovernorTimelockControl {
GitHub: 15
File: contracts/OLAS.sol 17: contract OLAS is ERC20 {
GitHub: 17
File: contracts/Timelock.sol 9: contract Timelock is TimelockController {
GitHub: 9
File: contracts/bridges/BridgedERC20.sol 18: contract BridgedERC20 is ERC20 {
GitHub: 18
File: contracts/bridges/FxERC20ChildTunnel.sol 24: contract FxERC20ChildTunnel is FxBaseChildTunnel {
GitHub: 24
File: contracts/bridges/FxERC20RootTunnel.sol 24: contract FxERC20RootTunnel is FxBaseRootTunnel {
GitHub: 24
File: contracts/bridges/FxGovernorTunnel.sol 46: contract FxGovernorTunnel is IFxMessageProcessor {
GitHub: 46
File: contracts/bridges/HomeMediator.sol 46: contract HomeMediator {
GitHub: 46
File: contracts/multisigs/GuardCM.sol 88: contract GuardCM {
GitHub: 88
File: contracts/veOLAS.sol 86: contract veOLAS is IErrors, IVotes, IERC20, IERC165 {
GitHub: 86
File: contracts/wveOLAS.sol 130 contract wveOLAS { 131: // veOLAS address
GitHub: 130
File: contracts/AgentRegistry.sol 9 contract AgentRegistry is UnitRegistry { 10: // Component registry
GitHub: 9
File: contracts/ComponentRegistry.sol 8 contract ComponentRegistry is UnitRegistry { 9: // Component registry version number
GitHub: 8
File: contracts/RegistriesManager.sol 9 contract RegistriesManager is GenericManager { 10: // Component registry address
GitHub: 9
File: contracts/multisigs/GnosisSafeMultisig.sol 24 contract GnosisSafeMultisig { 25: // Selector of the Gnosis Safe setup function
GitHub: 24
File: contracts/multisigs/GnosisSafeSameAddressMultisig.sol 50 contract GnosisSafeSameAddressMultisig { 51 // Default data size to be parsed as an address of a Gnosis Safe multisig proxy address 52: // This exact size suggests that all the changes to the multisig have been performed and only validation is needed
GitHub: 50
File: contracts/Depository.sol 62: contract Depository is IErrorsTokenomics {
GitHub: 62
File: contracts/Dispenser.sol 11: contract Dispenser is IErrorsTokenomics {
GitHub: 11
File: contracts/DonatorBlacklist.sol 20: contract DonatorBlacklist {
GitHub: 20
File: contracts/GenericBondCalculator.sol 20 contract GenericBondCalculator { 21: // OLAS contract address
GitHub: 20
File: contracts/Tokenomics.sol 118: contract Tokenomics is TokenomicsConstants, IErrorsTokenomics {
GitHub: 118
File: contracts/TokenomicsProxy.sol 26 contract TokenomicsProxy { 27: // Code position in storage is keccak256("PROXY_TOKENOMICS") = "0xbd5523e7c3b6a94aa0e3b24d1120addc2f95c7029e097b466b2bedc8d4b4362f"
GitHub: 26
File: contracts/Treasury.sol 39: contract Treasury is IErrorsTokenomics {
GitHub: 39
</details>If the variable is only accessed once, it's cheaper to use the assigned value directly that one time, and save the 3 gas the extra stack assignment would spend
There are 32 instances of this issue:
<details> <summary>see instances</summary>File: contracts/OLAS.sol 92: uint256 remainder = inflationRemainder(); 99: uint256 _totalSupply = totalSupply;
File: contracts/bridges/FxERC20ChildTunnel.sol 76: (address from, address to, uint256 amount) = abi.decode(message, (address, address, uint256)); 79: bool success = IERC20(childToken).transfer(to, amount); 97: bytes memory message = abi.encode(msg.sender, to, amount); 102: bool success = IERC20(childToken).transferFrom(msg.sender, address(this), amount);
File: contracts/bridges/FxERC20RootTunnel.sol 74: (address from, address to, uint256 amount) = abi.decode(message, (address, address, uint256)); 93: bytes memory message = abi.encode(msg.sender, to, amount); 98: bool success = IERC20(rootToken).transferFrom(msg.sender, address(this), amount);
File: contracts/multisigs/GuardCM.sol 297: (bytes memory l2Message) = abi.decode(bridgePayload, (bytes)); 323: (address fxGovernorTunnel, bytes memory l2Message) = abi.decode(payload, (address, bytes));
File: contracts/veOLAS.sol 677: (, uint256 blockTime) = _getBlockTime(blockNumber); 753: (PointVoting memory sPoint, uint256 blockTime) = _getBlockTime(blockNumber);
File: contracts/wveOLAS.sol 195: uint256 userNumPoints = IVEOLAS(ve).getNumUserPoints(account); 265: uint256 numPoints = IVEOLAS(ve).totalNumPoints();
File: contracts/multisigs/GnosisSafeMultisig.sol 98 (address to, address fallbackHandler, address paymentToken, address payable paymentReceiver, uint256 payment, 99: uint256 nonce, bytes memory payload) = _parseData(data); 102 bytes memory safeParams = abi.encodeWithSelector(GNOSIS_SAFE_SETUP_SELECTOR, owners, threshold, 103: to, payload, fallbackHandler, paymentToken, payment, paymentReceiver);
File: contracts/multisigs/GnosisSafeSameAddressMultisig.sol 103: bytes32 multisigProxyHash = keccak256(multisig.code); 118: (bool success, ) = multisig.call(payload);
File: contracts/GenericBondCalculator.sol 76: address token1 = pair.token1();
GitHub: 76
File: contracts/Tokenomics.sol 325: uint256 zeroYearSecondsLeft = uint32(_timeLaunch + ONE_YEAR - block.timestamp); 674: uint256 sumUnitIncentives = uint256(mapEpochTokenomics[epochNum].unitPoints[unitType].sumUnitTopUpsOLAS) * 100; 841: UD60x18 codeUnits = UD60x18.wrap(codePerDev); 846: UD60x18 fpDevsPerCapital = UD60x18.wrap(devsPerCapital); 848: UD60x18 fpNumNewOwners = convert(numNewOwners); 1052: uint256 accountRewards = incentives[2] + incentives[3]; 1060: uint256 accountTopUps = incentives[5] + incentives[6];
GitHub: 325, 674, 841, 846, 848, 1052, 1060
File: contracts/TokenomicsConstants.sol 33 uint96[10] memory supplyCaps = [ 34 529_659_000_00e16, 35 569_913_084_00e16, 36 641_152_219_50e16, 37 708_500_141_72e16, 38 771_039_876_00e16, 39 828_233_282_97e16, 40 879_860_040_11e16, 41 925_948_139_65e16, 42 966_706_331_40e16, 43 1_000_000_000e18 44: ]; 70 uint88[10] memory inflationAmounts = [ 71 3_159_000_00e16, 72 40_254_084_00e16, 73 71_239_135_50e16, 74 67_347_922_22e16, 75 62_539_734_28e16, 76 57_193_406_97e16, 77 51_626_757_14e16, 78 46_088_099_54e16, 79 40_758_191_75e16, 80 33_293_668_60e16 81: ];
File: contracts/TokenomicsProxy.sol 48: (bool success, ) = tokenomics.delegatecall(tokenomicsData);
GitHub: 48
</details>File: contracts/Treasury.sol 224: uint256 reserves = mapTokenReserves[token] + tokenAmount; 235: bool success = IToken(token).transferFrom(account, address(this), tokenAmount);
revert
checks to save gasSplitting the conditions into two separate checks saves 2 gas
There are 19 instances of this issue:
<details> <summary>see instances</summary>File: contracts/bridges/FxERC20ChildTunnel.sol 39 if (_fxChild == address(0) || _childToken == address(0) || _rootToken == address(0)) { 40 revert ZeroAddress(); 41: }
GitHub: 39
File: contracts/bridges/FxERC20RootTunnel.sol 42 if (_checkpointManager == address(0) || _fxRoot == address(0) || _childToken == address(0) || 43 _rootToken == address(0)) { 44 revert ZeroAddress(); 45: }
GitHub: 42
File: contracts/bridges/FxGovernorTunnel.sol 64 if (_fxChild == address(0) || _rootGovernor == address(0)) { 65 revert ZeroAddress(); 66: }
GitHub: 64
File: contracts/bridges/HomeMediator.sol 64 if (_AMBContractProxyHome == address(0) || _foreignGovernor == address(0)) { 65 revert ZeroAddress(); 66: }
GitHub: 64
File: contracts/multisigs/GuardCM.sol 144 if (_timelock == address(0) || _multisig == address(0) || _governor == address(0)) { 145 revert ZeroAddress(); 146: } 453 if (targets.length != selectors.length || targets.length != statuses.length || targets.length != chainIds.length) { 454 revert WrongArrayLength(targets.length, selectors.length, statuses.length, chainIds.length); 455: } 506 if (bridgeMediatorL1s.length != bridgeMediatorL2s.length || bridgeMediatorL1s.length != chainIds.length) { 507 revert WrongArrayLength(bridgeMediatorL1s.length, bridgeMediatorL2s.length, chainIds.length, chainIds.length); 508: } 513 if (bridgeMediatorL1s[i] == address(0) || bridgeMediatorL2s[i] == address(0)) { 514 revert ZeroAddress(); 515: }
File: contracts/wveOLAS.sol 147 if (_ve == address(0) || _token == address(0)) { 148 revert ZeroAddress(); 149: }
GitHub: 147
File: contracts/AgentRegistry.sol 41 if (dependencies[iDep] < (lastId + 1) || dependencies[iDep] > componentTotalSupply) { 42 revert ComponentNotFound(dependencies[iDep]); 43: }
GitHub: 41
File: contracts/ComponentRegistry.sol 30 if (dependencies[iDep] < (lastId + 1) || dependencies[iDep] > maxComponentId) { 31 revert ComponentNotFound(dependencies[iDep]); 32: }
GitHub: 30
File: contracts/Depository.sol 111 if (_olas == address(0) || _tokenomics == address(0) || _treasury == address(0) || _bondCalculator == address(0)) { 112 revert ZeroAddress(); 113: } 363 if (pay == 0 || !matured) { 364 revert BondNotRedeemable(bondIds[i]); 365: }
File: contracts/Dispenser.sol 36 if (_tokenomics == address(0) || _treasury == address(0)) { 37 revert ZeroAddress(); 38: }
GitHub: 36
File: contracts/GenericBondCalculator.sol 31 if (_olas == address(0) || _tokenomics == address(0)) { 32 revert ZeroAddress(); 33: }
GitHub: 31
File: contracts/Tokenomics.sol 283 if (_olas == address(0) || _treasury == address(0) || _depository == address(0) || _dispenser == address(0) || 284 _ve == address(0) || _componentRegistry == address(0) || _agentRegistry == address(0) || 285 _serviceRegistry == address(0)) { 286 revert ZeroAddress(); 287: } 1117 if (unitIds[i] <= lastIds[unitTypes[i]] || unitIds[i] > registriesSupply[unitTypes[i]]) { 1118 revert WrongUnitId(unitIds[i], unitTypes[i]); 1119: } 1187 if (unitIds[i] <= lastIds[unitTypes[i]] || unitIds[i] > registriesSupply[unitTypes[i]]) { 1188 revert WrongUnitId(unitIds[i], unitTypes[i]); 1189: }
File: contracts/Treasury.sol 100 if (_olas == address(0) || _tokenomics == address(0) || _depository == address(0) || _dispenser == address(0)) { 101 revert ZeroAddress(); 102: }
GitHub: 100
</details>Every release contains new gas optimizations. Use the latest version to take advantage of this
There are 8 instances of this issue:
File: contracts/GovernorOLAS.sol 5: import {Governor, IGovernor} from "@openzeppelin/contracts/governance/Governor.sol"; 7: import {GovernorCompatibilityBravo} from "@openzeppelin/contracts/governance/compatibility/GovernorCompatibilityBravo.sol"; 8: import {GovernorVotes, IVotes} from "@openzeppelin/contracts/governance/extensions/GovernorVotes.sol"; 9: import {GovernorVotesQuorumFraction} from "@openzeppelin/contracts/governance/extensions/GovernorVotesQuorumFraction.sol"; 10: import {GovernorTimelockControl, TimelockController} from "@openzeppelin/contracts/governance/extensions/GovernorTimelockControl.sol";
File: contracts/Timelock.sol 4: import "@openzeppelin/contracts/governance/TimelockController.sol";
GitHub: 4
File: contracts/veOLAS.sol 4: import "@openzeppelin/contracts/governance/utils/IVotes.sol"; 5: import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
By evaluating expressions involving constants, literals, and local variables before ones involving external calls, you can avoid unnecessarily executing the calls in the unhappy path.
There is one instance of this issue:
File: contracts/Tokenomics.sol /// @audit move the expression involving `getVotes()` to the right 709 topUpEligible = (IVotingEscrow(ve).getVotes(serviceOwner) >= veOLASThreshold || 710: IVotingEscrow(ve).getVotes(donator) >= veOLASThreshold) ? true : false;
GitHub: 709
#0 - thebrittfactor
2024-01-02T16:43:35Z
Content submitted prior to audit close.
#1 - c4-pre-sort
2024-01-10T13:41:56Z
alex-ppg marked the issue as sufficient quality report
#2 - c4-judge
2024-01-18T21:53:32Z
dmvt marked the issue as grade-b
#3 - c4-judge
2024-01-20T16:28:52Z
dmvt marked the issue as grade-a