Skip to content

Instantly share code, notes, and snippets.

@ChaseTheLight01
Created March 23, 2024 07:24
Show Gist options
  • Save ChaseTheLight01/5fbcc2cc2f426a3c7d08505d995caa95 to your computer and use it in GitHub Desktop.
Save ChaseTheLight01/5fbcc2cc2f426a3c7d08505d995caa95 to your computer and use it in GitHub Desktop.
LightChaserV3_Cantina_Venus

LightChaser-V3

Generated for: Cantina : Venus

Generated on: 2024-03-23

Total findings: 174

Total HIGH findings: 0

Total Medium findings: 1

Total Low findings: 27

Total Gas findings: 64

Total Refactoring findings: 0

Total NonCritical findings: 82

Total Disputed findings: 0

Summary for MEDIUM findings

Number Details Instances
[MEDIUM-1] Privileged functions can create points of failure 9

Summary for Low findings

Number Details Instances
[Low-1] Gas grief possible on unsafe external calls 2
[Low-2] Code does not follow the best practice of check-effects-interaction 2
[Low-3] Comparisons against msg.sender and address(this) can easily be bypassed 2
[Low-4] Non payable fallback function can break compatibility 1
[Low-5] Usage of ecrecover is vulnerable to signature malleability 1
[Low-6] Low level calls in solidity versions preceding 0.8.14 can result in an optimiser bug 3
[Low-7] Experimental functionality should not be used in production code 1
[Low-8] Ownable2Step should be used in place of Ownable 1
[Low-9] The call abi.encodeWithSignature is not safe from typographical errors 1
[Low-10] Empty fallback functions can cause gas issues 1
[Low-11] Uses of EIP712 does not include a version string 1
[Low-12] Initializer function can be front run 1
[Low-13] Missing zero address check in constructor 5
[Low-14] Use of onlyOwner functions can be lost 1
[Low-15] Remaining eth may not be refunded to users 1
[Low-16] Critical functions should have a timelock 4
[Low-17] Missing contract-existence checks before low-level calls 1
[Low-18] Use of abi.encodePacked with dynamic types inside keccak256 4
[Low-19] Vulnerable version of openzeppelin contracts used 1
[Low-20] Contract contains payable functions but no withdraw/sweep function 5
[Low-21] State variables not capped at reasonable values 3
[Low-22] The use of deprecated AccessControl functions 1
[Low-23] Uses of EIP712 does not include a salt 1
[Low-24] Off-by-one timestamp error 3
[Low-25] Owner can renounce while system is paused 1
[Low-26] Numbers downcast to addresses may result in collisions 1
[Low-27] Delegate call can fail 2

Summary for NonCritical findings

Number Details Instances
[NonCritical-1] Having chainId as a parameter can introduce cross chain replay attacks 2
[NonCritical-2] Use solidity time variables instead of using literals 2
[NonCritical-3] Storage Write Removal Bug On Conditional Early Termination 1
[NonCritical-4] Some if-statement can be converted to a ternary 6
[NonCritical-5] Events may be emitted out of order due to code not follow the best practice of check-effects-interaction 2
[NonCritical-6] Consider using time variables when defining time related variables 3
[NonCritical-7] Unnecessary struct attribute prefix 3
[NonCritical-8] Contracts should have all public/external functions exposed by interfaces 104
[NonCritical-9] Using abi.encodePacked can result in hash collision when used in hashing functions 1
[NonCritical-10] Consider making private state variables internal to increase flexibility 2
[NonCritical-11] Using zero as a parameter 8
[NonCritical-12] Consider implementing two-step procedure for updating protocol addresses 6
[NonCritical-13] Prefer skip over revert model in iteration 1
[NonCritical-14] Floating pragma should be avoided 1
[NonCritical-15] Interfaces should be declared in a separate file 2
[NonCritical-16] Events regarding state variable changes should emit the previous state variable value 4
[NonCritical-17] In functions which accept an address as a parameter, there should be a zero address check to prevent bugs 39
[NonCritical-18] Enum values should be used in place of constant array indexes 1
[NonCritical-19] Default address values are manually set 3
[NonCritical-20] Revert statements within external and public functions can be used to perform DOS attacks 1
[NonCritical-21] Reverts should use customer errors instead of strings 1
[NonCritical-22] Functions which are either public or external should not have a preceding _ in their name 15
[NonCritical-23] Functions which are either private or internal should have a preceding _ in their name 25
[NonCritical-24] Contract lines should not be longer than 120 characters for readability 51
[NonCritical-25] Avoid updating storage when the value hasn't changed 4
[NonCritical-26] Specific imports should be used where possible so only used code is imported 8
[NonCritical-27] Old Solidity version 4
[NonCritical-28] Not all event definitions are utilizing indexed variables. 38
[NonCritical-29] It is convention to make the array size of __gap 50 1
[NonCritical-30] uint/int variables should have the bit size defined explicitly 70
[NonCritical-31] Functions within contracts are not ordered according to the solidity style guide 7
[NonCritical-32] Double type casts create complexity within the code 1
[NonCritical-33] Emits without msg.sender parameter 14
[NonCritical-34] Interface imports should be declared first 6
[NonCritical-35] Multiple mappings can be replaced with a single struct mapping 8
[NonCritical-36] Constants should be on the left side of the comparison 5
[NonCritical-37] Interface names should have an I as the first character 5
[NonCritical-38] Initialize functions do not emit an event 3
[NonCritical-39] Both immutable and constant state variables should be CONSTANT_CASE 3
[NonCritical-40] Consider using named mappings 10
[NonCritical-41] Use a single file for system wide constants 6
[NonCritical-42] Use of non-named numeric constants 7
[NonCritical-43] Redundant else statement 6
[NonCritical-44] Inconsistent usage of int/uint with int256/uint256 in contract/abstract/library/interface 7
[NonCritical-45] Use immutable not constant for keccak state variables 2
[NonCritical-46] Consider adding emergency-stop functionality 19
[NonCritical-47] Employ Explicit Casting to Bytes or Bytes32 for Enhanced Code Clarity and Meaning 8
[NonCritical-48] Non-assembly method available 1
[NonCritical-49] Empty bytes check is missing 19
[NonCritical-50] Top level declarations should be separated by two blank lines 13
[NonCritical-51] Assembly block creates dirty bits 1
[NonCritical-52] Whitespace in expressions 3
[NonCritical-53] Use of override is unnecessary 2
[NonCritical-54] No access control on receive/payable fallback 1
[NonCritical-55] Cyclomatic complexity in functions 7
[NonCritical-56] Contract uses both require()/revert() as well as custom errors 2
[NonCritical-57] function names should be lowerCamelCase 3
[NonCritical-58] Consider bounding input array length 9
[NonCritical-59] Missing events in sensitive functions 3
[NonCritical-60] Consider implementing EIP-5267 to securely describe EIP-712 domains being used 1
[NonCritical-61] Ensure block.timestamp is only used in long time intervals 1
[NonCritical-62] Contracts with only unimplemented functions can be labeled as abstract 9
[NonCritical-63] A event should be emitted if a non immutable state variable is set in a constructor 8
[NonCritical-64] Superfluous parameter can only be one value 1
[NonCritical-65] Consider only defining one library/interface/contract per sol file 4
[NonCritical-66] Using Low-Level Call for Transfers 1
[NonCritical-67] Long numbers should include underscores to improve readability and prevent typos 1
[NonCritical-68] package.json name variable should only consist of lowercase letters and underscores 1
[NonCritical-69] package.json missing/empty description 1
[NonCritical-70] Avoid revertible function calls in a constructor 2
[NonCritical-71] Avoid declaring variables with the names of defined functions within the project 4
[NonCritical-72] Consider using the Upgradeable version of the OpenZeppelin libraries/contracts 6
[NonCritical-73] Simplify complex require statements 2
[NonCritical-74] Constructors should emit an event 12
[NonCritical-75] Contract and Abstract files should have a fixed compiler version 14
[NonCritical-76] Function call in event emit 6
[NonCritical-77] Consider using AccessControlDefaultAdminRules rather than AccessControl 1
[NonCritical-78] Errors should have parameters 2
[NonCritical-79] Consider using OpenZeppelins SafeCall library when making calls to arbitrary contracts 3
[NonCritical-80] Memory-safe annotation missing 1
[NonCritical-81] Constant state variables defined more than once 12
[NonCritical-82] Function control structures do not follow the Solidity Style Guide 2

Summary for Gas findings

Number Details Instances Gas
[Gas-1] State variables used within a function more than once should be cached to save gas 1 1200
[Gas-2] The result of a function call should be cached rather than re-calling the function 2 800
[Gas-3] Multiple accesses of the same mapping/array key/index should be cached 3 1134
[Gas-4] bytes.concat() can be used in place of abi.encodePacked 1 0.0
[Gas-5] Shortcircuit rules can be be used to optimize some gas usage 2 50400
[Gas-6] Function calls within for loops 11 0.0
[Gas-7] For loops in public or external functions should be avoided due to high gas costs and possible DOS 18 0.0
[Gas-8] Stack variable cost less than structs while used in emiting event 3 81
[Gas-9] Use assembly to write address/contract storage values 36 0.0
[Gas-10] Avoid caching global vars used once within the function 2 48
[Gas-11] There is a 32 byte length threshold for error strings, strings longer than this consume more gas 77 83006
[Gas-12] Public functions not used internally can be marked as external to save gas 28 0.0
[Gas-13] Calldata should be used in place of memory function parameters when not mutated 5 325
[Gas-14] Usage of smaller uint/int types causes overhead 33 59895
[Gas-15] Use != 0 instead of > 0 4 48
[Gas-16] Integer increments by one can be unchecked to save on gas fees 19 43320
[Gas-17] Use byte32 in place of string 4 0.0
[Gas-18] Default bool values are manually reset 2 0.0
[Gas-19] Default int values are manually reset 1 0.0
[Gas-20] Mappings used within a function more than once should be cached to save gas 4 1600
[Gas-21] Use assembly to check for the zero address 17 0.0
[Gas-22] Structs can be packed into fewer storage slots 2 10000
[Gas-23] Use bitmap to save gas 8 4480
[Gas-24] Use assembly hashing 10 0.0
[Gas-25] Use assembly to emit events 47 83942
[Gas-26] Use assembly in place of abi.decode to extract calldata values more efficiently 4 0.0
[Gas-27] Counting down in for statements is more gas efficient 15 0.0
[Gas-28] Using private rather than public for constants and immutables, saves gas 15 0.0
[Gas-29] Mark Functions That Revert For Normal Users As payable 9 2025
[Gas-30] Function names can be optimized 23 67712
[Gas-31] Consider migrating require statements to custom errors 129 232974
[Gas-32] Refactor event to avoid emitting empty data 4 6000
[Gas-33] Avoid indexing dynamic types 2 0.0
[Gas-34] Where a value is casted more than once, consider caching the result to save gas 3 0.0
[Gas-35] The following mappings can be replaced with a bit mask 1 0.0
[Gas-36] Unnecessary casting as variable is already of the same type 2 88
[Gas-37] Simple checks for zero uint can be done using assembly to save gas 13 1014
[Gas-38] Using nested if to save gas 2 132
[Gas-39] Consider splitting complex if statements into multiple steps 4 0.0
[Gas-40] Stack variable cost less than state variables while used in emiting event 14 1764
[Gas-41] Stack variable cost less than mappings while used in emiting event 3 81
[Gas-42] Avoid emitting event on every iteration 1 750
[Gas-43] Low level call can be optimized with assembly 7 12152
[Gas-44] Constants are cheaper than Enums 4 320000
[Gas-45] ++X costs slightly less gas than X++ (same with --) 2 20
[Gas-46] Solidity versions 0.8.19 and above are more gas efficient 3 12000
[Gas-47] Variable declared within iteration 1 0.0
[Gas-48] Calling .length in a for loop wastes gas 8 6208
[Gas-49] Internal functions only used once can be inlined to save gas 8 1920
[Gas-50] Constructors can be marked as payable to save deployment gas 12 0.0
[Gas-51] Use assembly scratch space to build calldata for external calls 3 1980
[Gas-52] Use assembly scratch space to build calldata for event emits 29 185020
[Gas-53] Assigning to structs can be more efficient 5 3250
[Gas-54] There are comparisons to boolean literals (true and false), these can be simplified to save gas 2 72
[Gas-55] Only emit event in setter function if the state variable was changed 4 0.0
[Gas-56] It is a waste of GAS to emit variable literals 4 128
[Gas-57] Use OZ Array.unsafeAccess() to avoid repeated array length checks 30 1890000
[Gas-58] State variable read in a loop 3 260730
[Gas-59] Write direct outcome, instead of performing mathematical operations for constant state variables 3 0.0
[Gas-60] It is more efficient to use block.timestamp directly rather than calling a function to return it 2 0.0
[Gas-61] Use of memory instead of storage for struct/array state variables 3 18900
[Gas-62] Public functions not called internally 28 0.0
[Gas-63] do-while is cheaper than for-loops when the initial check can be skipped 16 44800
[Gas-64] Empty blocks should be removed or emit something 3 0.0

[MEDIUM-1] Privileged functions can create points of failure

Number of instances found

9

Resolution

Ensure such accounts are protected and consider implementing multi sig to prevent a single point of failure

Findings

Findings are labeled with ' <= FOUND'

Click to show findings

https://github.com/VenusProtocol/governance-contracts/tree/2068398975982bab787168cfd58eca4921e3caad/contracts/Cross-chain/BaseOmnichainControllerSrc.sol#L93-L93

93:     function setAccessControlManager(address accessControlManager_) external onlyOwner  // <= FOUND

https://github.com/VenusProtocol/governance-contracts/tree/2068398975982bab787168cfd58eca4921e3caad/contracts/Cross-chain/BaseOmnichainControllerDest.sol#L48-L48

48:     function setMaxDailyReceiveLimit(uint256 limit_) external onlyOwner  // <= FOUND

https://github.com/VenusProtocol/governance-contracts/tree/2068398975982bab787168cfd58eca4921e3caad/contracts/Cross-chain/BaseOmnichainControllerDest.sol#L57-L57

57:     function pause() external onlyOwner  // <= FOUND

https://github.com/VenusProtocol/governance-contracts/tree/2068398975982bab787168cfd58eca4921e3caad/contracts/Cross-chain/BaseOmnichainControllerDest.sol#L65-L65

65:     function unpause() external onlyOwner  // <= FOUND

https://github.com/VenusProtocol/governance-contracts/tree/2068398975982bab787168cfd58eca4921e3caad/contracts/Cross-chain/BaseOmnichainControllerSrc.sol#L93-L93

93:     function setAccessControlManager(address accessControlManager_) external onlyOwner  // <= FOUND

https://github.com/VenusProtocol/governance-contracts/tree/2068398975982bab787168cfd58eca4921e3caad/contracts/Cross-chain/OmnichainExecutorOwner.sol#L69-L69

69:     function upsertSignature(string[] calldata signatures_, bool[] calldata active_) external onlyOwner  // <= FOUND

https://github.com/VenusProtocol/governance-contracts/tree/2068398975982bab787168cfd58eca4921e3caad/contracts/Cross-chain/OmnichainGovernanceExecutor.sol#L146-L146

146:     function setSrcChainId(uint16 srcChainId_) external onlyOwner  // <= FOUND

https://github.com/VenusProtocol/governance-contracts/tree/2068398975982bab787168cfd58eca4921e3caad/contracts/Cross-chain/OmnichainGovernanceExecutor.sol#L157-L157

157:     function addTimelocks(ITimelock[] memory timelocks_) external onlyOwner  // <= FOUND

https://github.com/VenusProtocol/governance-contracts/tree/2068398975982bab787168cfd58eca4921e3caad/contracts/Cross-chain/OmnichainProposalSender.sol#L221-L221

214:     function fallbackWithdraw(
215:         address to_,
216:         uint256 pId_,
217:         uint16 remoteChainId_,
218:         bytes calldata payload_,
219:         bytes calldata adapterParams_,
220:         uint256 originalValue_
221:     ) external onlyOwner nonReentrant  // <= FOUND

[Low-1] Gas grief possible on unsafe external calls

Resolution

In Solidity, the use of low-level call methods can expose contracts to gas griefing attacks. The potential problem arises when the callee contract returns a large amount of data. This data is allocated in the memory of the calling contract, which pays for the gas costs. If the callee contract intentionally returns an enormous amount of data, the gas costs can skyrocket, causing the transaction to fail due to an Out of Gas error. Therefore, it's advisable to limit the use of call when interacting with untrusted contracts, or ensure that the callee's returned data size is capped or known in advance to prevent unexpected high gas costs.

Num of instances: 2

Findings

Click to show findings

['66']

66:     function delegateTo(address callee, bytes memory data) internal { // <= FOUND
67:         (bool success, bytes memory returnData) = callee.delegatecall(data); // <= FOUND
68:         assembly {
69:             if eq(success, 0) {
70:                 revert(add(returnData, 0x20), returndatasize)
71:             }
72:         }
73:     }

['66']

66:     function delegateTo(address callee, bytes memory data) internal { // <= FOUND
67:         (bool success, bytes memory returnData) = callee.delegatecall(data); // <= FOUND
68:         assembly {
69:             if eq(success, 0) {
70:                 revert(add(returnData, 0x20), returndatasize)
71:             }
72:         }
73:     }

[Low-2] Code does not follow the best practice of check-effects-interaction

Resolution

The "check-effects-interaction" pattern is a best practice in smart contract development, emphasizing the order of operations in functions to prevent reentrancy attacks. Violations arise when a function interacts with external contracts before settling internal state changes or checks. This misordering can expose the contract to potential threats. To adhere to this pattern, first ensure all conditions or checks are satisfied, then update any internal states, and only after these steps, interact with external contracts or addresses. Rearranging operations to this recommended sequence bolsters contract security and aligns with established best practices in the Ethereum community.

Num of instances: 2

Findings

Click to show findings

['498']

498:     function _initiate(address governorAlpha) external { // <= FOUND
499:         require(msg.sender == admin, "GovernorBravo::_initiate: admin only");
500:         require(initialProposalId == 0, "GovernorBravo::_initiate: can only initiate once");
501:         proposalCount = GovernorAlphaInterface(governorAlpha).proposalCount(); // <= FOUND
502:         initialProposalId = proposalCount;
503:         for (uint256 i; i < uint8(ProposalType.CRITICAL) + 1; ++i) {
504:             proposalTimelocks[i].acceptAdmin();
505:         }
506:     }

['445']

445:     function _initiate(address governorAlpha) external { // <= FOUND
446:         require(msg.sender == admin, "GovernorBravo::_initiate: admin only");
447:         require(initialProposalId == 0, "GovernorBravo::_initiate: can only initiate once");
448:         proposalCount = GovernorAlphaInterface(governorAlpha).proposalCount(); // <= FOUND
449:         initialProposalId = proposalCount;
450:         timelock.acceptAdmin();
451:     }

[Low-3] Comparisons against msg.sender and address(this) can easily be bypassed

Resolution

Comparisons using msg.sender and address(this) can be exploited when contracts call their own functions. If an attacker influences the calldata during such a self-call, they can bypass checks expecting msg.sender to be an external address. Instead, msg.sender becomes the contract's own address. This can circumvent security checks designed for external calls or disrupt logic that uses msg.sender for accounting or differentiation. To counter this, contracts should not solely rely on msg.sender checks and should be wary of potential recursive calls. It's crucial to implement additional validation mechanisms and conduct thorough tests to prevent unintended call behaviors.

Num of instances: 2

Findings

Click to show findings

['91']

91:         require(msg.sender == address(this), "Timelock::setDelay: Call must come from Timelock."); // <= FOUND

['141']

141:         require(msg.sender == address(this), "Timelock::setPendingAdmin: Call must come from Timelock."); // <= FOUND

[Low-4] Non payable fallback function can break compatibility

Resolution

Reason: In Solidity, a defined fallback function that is not declared as payable can cause compatibility issues when value is transferred with incorrect data. If the contract is meant to receive Ether but the fallback function isn't payable, the contract will reject the transaction, resulting in a failed operation. This could lead to unintentional loss of funds or incompatible interactions with other contracts.

Resolution: To ensure broad compatibility and safe receipt of Ether, the fallback function should be declared as payable. This allows the contract to accept Ether transfers, regardless of the data sent along with the transaction. However, it's important to note that this should be complemented by proper checks and controls in the contract to avoid potential attacks or misuse. Always exercise caution when dealing with payable functions, and consider using the latest Solidity version to take advantage of the receive() function for explicit Ether transfers.

Num of instances: 1

Findings

Click to show findings

['54']

54:     fallback(bytes calldata data_) external returns (bytes memory) {
55:         string memory fun = functionRegistry[msg.sig];
56:         require(bytes(fun).length != 0, "Function not found");
57:         _checkAccessAllowed(fun);
58:         (bool ok, bytes memory res) = address(OMNICHAIN_GOVERNANCE_EXECUTOR).call(data_);
59:         require(ok, "call failed");
60:         return res;
61:     }

[Low-5] Usage of ecrecover is vulnerable to signature malleability

Resolution

The ecrecover function in Ethereum smart contracts is vulnerable to signature malleability due to the nature of Ethereum signatures and the ECDSA (Elliptic Curve Digital Signature Algorithm) used. Ethereum signatures include an additional field, v (the recovery identifier), which can have multiple valid values for the same signer and message. This field, which ranges from 27 to 30, helps to determine the correct public key associated with a signature. The vulnerability arises because there are multiple valid s values for any given r in a signature, meaning (r, s) and (r, n - s) can both be valid for the same message and signing key. The ecrecover function, which is used to verify signatures and recover addresses, must account for these different valid s values, leading to potential signature malleability issues.

Num of instances: 1

Findings

Click to show findings

['326']

326:         address signatory = ecrecover(digest, v, r, s); // <= FOUND

[Low-6] Low level calls in solidity versions preceding 0.8.14 can result in an optimiser bug

Resolution

In Solidity versions 0.8.13 and 0.8.14, a known optimizer bug presents potential risks when a variable is used in a separate assembly block from the one in which it was stored. Specifically, the 'mstore' operation could be optimized out due to this bug, leading to the use of uninitialized memory. Although the current code does not exhibit this risky pattern of execution, it does utilize 'mstore' within assembly blocks, which introduces a vulnerability risk for future code modifications. As a preventative measure, it is advisable to avoid the usage of the afflicted Solidity versions, 0.8.13 and 0.8.14. Instead, consider utilizing a version that is not impacted by this optimizer bug to prevent potential memory initialization issues in your smart contract.

Num of instances: 3

Findings

Click to show findings

['68']

68:         assembly { // <= FOUND
69:             if eq(success, 0) {
70:                 revert(add(returnData, 0x20), returndatasize)
71:             }
72:         }

['84']

84:         assembly { // <= FOUND
85:             let free_mem_ptr := mload(0x40)
86:             returndatacopy(free_mem_ptr, 0, returndatasize)
87: 
88:             switch success
89:             case 0 {
90:                 revert(free_mem_ptr, returndatasize)
91:             }
92:             default {
93:                 return(free_mem_ptr, returndatasize)
94:             }
95:         }

['384']

384:         assembly { // <= FOUND
385:             chainId := chainid()
386:         }

[Low-7] Experimental functionality should not be used in production code

Resolution

Experimental pragma features should not be used within production code due to their unstable and untested nature. These features are still under development and have not undergone rigorous scrutiny, making them susceptible to bugs, security vulnerabilities, and potential breaking changes in future Solidity releases.

Num of instances: 1

Findings

Click to show findings

['2']

2: pragma experimental ABIEncoderV2; // <= FOUND

[Low-8] Ownable2Step should be used in place of Ownable

Resolution

Ownable2Step further prevents risks posed by centralised privileges as there is a smaller likelihood of the owner being wrongfully changed

Num of instances: 1

Findings

Click to show findings

['17']

17: contract BaseOmnichainControllerSrc is Ownable, Pausable  // <= FOUND

[Low-9] The call abi.encodeWithSignature is not safe from typographical errors

Resolution

When you use abi.encodeWithSignature in Solidity, you're creating an ABI-encoded string representing a function call. This method relies on you, the programmer, correctly typing the function signature (i.e., the function name and types of arguments) as a string.

For instance, if you want to call a function foo(uint256, address), you'd write abi.encodeWithSignature("foo(uint256,address)", arg1, arg2). If there's a typographical error like abi.encodeWithSignature("foo(uinnt256,address)", arg1, arg2), the Solidity compiler will not raise an error because the string is syntactically correct. However, this will fail at runtime because there's no function with that incorrect signature.

This makes abi.encodeWithSignature less safe than directly calling the function or using interfaces to call functions on other contracts. An effective resolution would be using a Solidity interface. An interface provides compile-time type checking, ensuring that you're calling existing functions with the correct parameters.

Num of instances: 1

Findings

Click to show findings

['27']

25:         delegateTo(
26:             implementation_,
27:             abi.encodeWithSignature( // <= FOUND
28:                 "initialize(address,address,uint256,uint256,uint256,address)",
29:                 timelock_,
30:                 xvsVault_,
31:                 votingPeriod_,
32:                 votingDelay_,
33:                 proposalThreshold_,
34:                 guardian_
35:             )
36:         );

[Low-10] Empty fallback functions can cause gas issues

Resolution

An empty fallback() function in a Solidity contract can cause issues, as it might inadvertently accept Ether transfers without any mechanism to manage or allocate the received funds. Consequently, Ether could become permanently locked within the contract, causing potential financial losses and diminishing user trust.

To rectify this situation, developers should implement appropriate logic within the fallback() function to handle incoming Ether transfers. This may include updating internal balances, emitting events for transparency, or implementing access control mechanisms to restrict Ether transfers to authorized parties

Num of instances: 1

Findings

Click to show findings

['82']

82:     fallback() external payable {}

[Low-11] Uses of EIP712 does not include a version string

Resolution

It is standard for uses of EIP712 to include a version string, not doing so can cause future incompatibilities

Num of instances: 1

Findings

Click to show findings

['107']

105:     
106:     bytes32 public constant DOMAIN_TYPEHASH =
107:         keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); // <= FOUND

[Low-12] Initializer function can be front run

Resolution

In Solidity contract deployment, not making the initialize() function call atomic with the contract creation can leave a vulnerability window. A malicious actor could exploit this time gap and call initialize() before the intended initialization. This action could disrupt the contract's setup, potentially necessitating a full contract re-deployment to ensure proper initialization. To mitigate such risks, it's advised to use a factory contract. This factory contract can be programmed to deploy and initialize a new contract in a single atomic transaction, closing the window of vulnerability and ensuring correct and secure contract initialization.

Num of instances: 1

Findings

Click to show findings

['43']

43:     function initialize(address accessControlManager_) external initializer  // <= FOUND

[Low-13] Missing zero address check in constructor

Resolution

In Solidity, constructors often take address parameters to initialize important components of a contract, such as owner or linked contracts. However, without a check, there's a risk that an address parameter could be mistakenly set to the zero address (0x0). This could occur due to a mistake or oversight during contract deployment. A zero address in a crucial role can cause serious issues, as it cannot perform actions like a normal address, and any funds sent to it are irretrievable. Therefore, it's crucial to include a zero address check in constructors to prevent such potential problems. If a zero address is detected, the constructor should revert the transaction.

Num of instances: 5

Findings

Click to show findings

['13']

12:     constructor(
13:         address timelock_, // <= FOUND
14:         address xvsVault_, // <= FOUND
15:         address admin_, // <= FOUND
16:         address implementation_, // <= FOUND
17:         uint votingPeriod_,
18:         uint votingDelay_,
19:         uint proposalThreshold_,
20:         address guardian_ // <= FOUND
21:     ) public {
22:         
23:         admin = msg.sender;
24: 
25:         delegateTo(
26:             implementation_,
27:             abi.encodeWithSignature(
28:                 "initialize(address,address,uint256,uint256,uint256,address)",
29:                 timelock_,
30:                 xvsVault_,
31:                 votingPeriod_,
32:                 votingDelay_,
33:                 proposalThreshold_,
34:                 guardian_
35:             )
36:         );
37: 
38:         _setImplementation(implementation_);
39: 
40:         admin = admin_;
41:     }

['136']

136:     constructor(address timelock_, address xvs_, address guardian_) public { // <= FOUND
137:         timelock = TimelockInterface(timelock_);
138:         xvs = XVSInterface(xvs_);
139:         guardian = guardian_;
140:     }

['136']

136:     constructor(address timelock_, address xvs_, address guardian_, uint256 lastProposalId_) public { // <= FOUND
137:         timelock = TimelockInterface(timelock_);
138:         xvs = XVSInterface(xvs_);
139:         guardian = guardian_;
140:         proposalCount = lastProposalId_;
141:     }

['13']

12:     constructor(
13:         address timelock_, // <= FOUND
14:         address xvsVault_, // <= FOUND
15:         address admin_, // <= FOUND
16:         address implementation_, // <= FOUND
17:         uint votingPeriod_,
18:         uint votingDelay_,
19:         uint proposalThreshold_,
20:         address guardian_ // <= FOUND
21:     ) public {
22:         
23:         admin = msg.sender;
24: 
25:         delegateTo(
26:             implementation_,
27:             abi.encodeWithSignature(
28:                 "initialize(address,address,uint256,uint256,uint256,address)",
29:                 timelock_,
30:                 xvsVault_,
31:                 votingPeriod_,
32:                 votingDelay_,
33:                 proposalThreshold_,
34:                 guardian_
35:             )
36:         );
37: 
38:         _setImplementation(implementation_);
39: 
40:         admin = admin_;
41:     }

['72']

72:     constructor(address admin_, uint delay_) public { // <= FOUND
73:         require(delay_ >= MINIMUM_DELAY, "Timelock::constructor: Delay must exceed minimum delay.");
74:         require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay.");
75: 
76:         admin = admin_;
77:         delay = delay_;
78:     }

[Low-14] Use of onlyOwner functions can be lost

Resolution

In Solidity, renouncing ownership of a contract essentially transfers ownership to the zero address. This is an irreversible operation and has considerable security implications. If the renounceOwnership function is used, the contract will lose the ability to perform any operations that are limited to the owner. This can be problematic if there are any bugs, flaws, or unexpected events that require owner intervention to resolve. Therefore, in some instances, it is better to disable or omit the renounceOwnership function, and instead implement a secure transferOwnership function. This way, if necessary, ownership can be transferred to a new, trusted party without losing the potential for administrative intervention.

Num of instances: 1

Findings

Click to show findings

['17']

17: contract BaseOmnichainControllerSrc is Ownable, Pausable  // <= FOUND

[Low-15] Remaining eth may not be refunded to users

Resolution

When a contract function accepts Ethereum and executes a .call() or similar function that also forwards Ethereum value, it's important to check for and refund any remaining balance. This is because some of the supplied value may not be used during the call execution due to gas constraints, a revert in the called contract, or simply because not all the value was needed.

If you do not account for this remaining balance, it can become "locked" in the contract. It's crucial to either return the remaining balance to the sender or handle it in a way that ensures it is not permanently stuck. Neglecting to do so can lead to loss of funds and degradation of the contract's reliability. Furthermore, it's good practice to ensure fairness and trust with your users by returning unused funds.

Num of instances: 1

Findings

Click to show findings

['217']

217:     function executeTransaction(
218:         address target,
219:         uint256 value,
220:         string calldata signature,
221:         bytes calldata data,
222:         uint256 eta
223:     ) public payable returns (bytes memory) { // <= FOUND
224:         require(msg.sender == admin, "Timelock::executeTransaction: Call must come from admin.");
225: 
226:         bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
227:         require(queuedTransactions[txHash], "Timelock::executeTransaction: Transaction hasn't been queued.");
228:         require(getBlockTimestamp() >= eta, "Timelock::executeTransaction: Transaction hasn't surpassed time lock.");
229:         require(getBlockTimestamp() <= eta + GRACE_PERIOD(), "Timelock::executeTransaction: Transaction is stale.");
230: 
231:         delete (queuedTransactions[txHash]);
232: 
233:         bytes memory callData;
234: 
235:         if (bytes(signature).length == 0) {
236:             callData = data;
237:         } else {
238:             callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data);
239:         }
240: 
241:         
242:         (bool success, bytes memory returnData) = target.call{ value: value }(callData); // <= FOUND
243:         require(success, "Timelock::executeTransaction: Transaction execution reverted.");
244: 
245:         emit ExecuteTransaction(txHash, target, value, signature, data, eta);
246: 
247:         return returnData;
248:     }

[Low-16] Critical functions should have a timelock

Resolution

Critical functions, especially those affecting protocol parameters or user funds, are potential points of failure or exploitation. To mitigate risks, incorporating a timelock on such functions can be beneficial. A timelock requires a waiting period between the time an action is initiated and when it's executed, giving stakeholders time to react, potentially vetoing malicious or erroneous changes. To implement, integrate a smart contract like OpenZeppelin's TimelockController or build a custom mechanism. This ensures governance decisions or administrative changes are transparent and allows for community or multi-signature interventions, enhancing protocol security and trustworthiness.

Num of instances: 4

Findings

Click to show findings

['93']

93:     function setAccessControlManager(address accessControlManager_) external onlyOwner  // <= FOUND

['48']

48:     function setMaxDailyReceiveLimit(uint256 limit_) external onlyOwner  // <= FOUND

['93']

93:     function setAccessControlManager(address accessControlManager_) external onlyOwner  // <= FOUND

['146']

146:     function setSrcChainId(uint16 srcChainId_) external onlyOwner  // <= FOUND

[Low-17] Missing contract-existence checks before low-level calls

Resolution

Low-level calls in Solidity, when made to addresses without contract code, don't fail but return a successful status. This behavior can be misleading, leading to unintended consequences in dApps. Ignoring this can potentially mean acting on false positive results. To address this, apart from the conventional zero-address check, developers should verify the existence of contract code at the target address by ensuring that the code length at the specified address (<address>.code.length) is greater than zero. By doing so, it provides a more robust validation before executing low-level calls, safeguarding against unintentional interactions with empty addresses.

Num of instances: 1

Findings

Click to show findings

['217']

217:     function executeTransaction(
218:         address target,
219:         uint256 value,
220:         string calldata signature,
221:         bytes calldata data,
222:         uint256 eta
223:     ) public payable returns (bytes memory) {
224:         require(msg.sender == admin, "Timelock::executeTransaction: Call must come from admin.");
225: 
226:         bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
227:         require(queuedTransactions[txHash], "Timelock::executeTransaction: Transaction hasn't been queued.");
228:         require(getBlockTimestamp() >= eta, "Timelock::executeTransaction: Transaction hasn't surpassed time lock.");
229:         require(getBlockTimestamp() <= eta + GRACE_PERIOD(), "Timelock::executeTransaction: Transaction is stale.");
230: 
231:         delete (queuedTransactions[txHash]);
232: 
233:         bytes memory callData;
234: 
235:         if (bytes(signature).length == 0) {
236:             callData = data;
237:         } else {
238:             callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data);
239:         }
240: 
241:         
242:         (bool success, bytes memory returnData) = target.call{ value: value }(callData); // <= FOUND
243:         require(success, "Timelock::executeTransaction: Transaction execution reverted.");
244: 
245:         emit ExecuteTransaction(txHash, target, value, signature, data, eta);
246: 
247:         return returnData;
248:     }

[Low-18] Use of abi.encodePacked with dynamic types inside keccak256

Resolution

Using abi.encodePacked with dynamic types for hashing functions like keccak256 can be risky due to the potential for hash collisions. This function concatenates arguments tightly, without padding, which might lead to different inputs producing the same hash. This is especially problematic with dynamic types, where the boundaries between inputs can blur. To mitigate this, use abi.encode instead. abi.encode pads its arguments to 32 bytes, creating clear distinctions between different inputs and significantly reducing the chance of hash collisions. This approach ensures more reliable and collision-resistant hashing, crucial for maintaining data integrity and security in smart contracts.

Num of instances: 4

Findings

Click to show findings

['78']

78:         bytes32 role = keccak256(abi.encodePacked(contractAddress, functionSig)); // <= FOUND

['110']

110:         bytes32 role = keccak256(abi.encodePacked(msg.sender, functionSig)); // <= FOUND

['115']

115:             role = keccak256(abi.encodePacked(address(0), functionSig)); // <= FOUND

['325']

325:         bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); // <= FOUND

[Low-19] Vulnerable version of openzeppelin contracts used

Resolution

OpenZeppelin versions of 4.9.2 and below are vulnerable to exploits, please consider upgrading to 4.9.3 or above. See: https://security.snyk.io/package/npm/@openzeppelin%2Fcontracts for more details

Num of instances: 1

Findings

Click to show findings

['[1](project_package.txt: 1-62)']

1: {
2:   "name": "@venusprotocol/governance-contracts",
3:   "description": "",
4:   "version": "1.4.0",
5:   "author": "",
6:   "files": [
7:     "artifacts",
8:     "dist",
9:     "deploy",
10:     "deployments",
11:     "contracts"
12:   ],
13:   "keywords": [
14:     "blockchain",
15:     "ethers",
16:     "ethereum",
17:     "hardhat",
18:     "smart-contracts",
19:     "solidity",
20:     "template",
21:     "typescript",
22:     "typechain"
23:   ],
24:   "packageManager": "yarn@3.2.0",
25:   "publishConfig": {
26:     "access": "public"
27:   },
28:   "scripts": {
29:     "compile": "hardhat compile",
30:     "test": "hardhat test",
31:     "build": "rm -rf dist && tsc --declaration && hardhat compile",
32:     "publish:dist": "yarn build && cd dist && yarn publish --access public",
33:     "lint": "yarn lint:ts && yarn lint:sol && yarn prettier:check",
34:     "lint:ts": "eslint --ignore-path ./.eslintignore --ext .js,.ts .",
35:     "lint:sol": "solhint \"contracts/**/*.sol\"",
36:     "lint:sol:fix": "prettier --write \"contracts/**/*.sol\"",
37:     "prettier": "prettier  --write \"**/*.{js,json,md,ts,yaml,yml,sol}\"",
38:     "prettier:check": "prettier --check \"**/*.{js,json,md,ts,yaml,yml,sol}\"",
39:     "docgen": "hardhat docgen",
40:     "prepare": "husky install"
41:   },
42:   "dependencies": {
43:     "@venusprotocol/solidity-utilities": "1.3.0",
44:     "hardhat-deploy-ethers": "^0.3.0-beta.13",
45:     "module-alias": "^2.2.2"
46:   },
47:   "devDependencies": {
48:     "@commitlint/cli": "^17.4.4",
49:     "@commitlint/config-conventional": "^17.4.4",
50:     "@defi-wonderland/smock": "^2.3.4",
51:     "@ethersproject/abi": "^5.7.0",
52:     "@ethersproject/abstract-signer": "^5.7.0",
53:     "@ethersproject/bignumber": "^5.7.0",
54:     "@ethersproject/bytes": "^5.7.0",
55:     "@ethersproject/providers": "^5.7.2",
56:     "@layerzerolabs/solidity-examples": "^1.0.0",
57:     "@nomicfoundation/hardhat-chai-matchers": "^1.0.4",
58:     "@nomicfoundation/hardhat-network-helpers": "^1.0.6",
59:     "@nomicfoundation/hardhat-toolbox": "^2.0.0",
60:     "@nomiclabs/hardhat-ethers": "^2.2.1",
61:     "@nomiclabs/hardhat-etherscan": "^3.1.2",
62:     "@openzeppelin/contracts": "^4.8.2", // <= FOUND
63:     "@openzeppelin/contracts-upgradeable": "^4.8.2",
64:     "@openzeppelin/hardhat-upgrades": "^1.22.1",
65:     "@semantic-release/changelog": "^6.0.2",
66:     "@semantic-release/git": "^10.0.1",
67:     "@trivago/prettier-plugin-sort-imports": "^4.0.0",
68:     "@typechain/ethers-v5": "^10.1.1",
69:     "@typechain/hardhat": "^6.1.4",
70:     "@types/chai": "^4.3.4",
71:     "@types/fs-extra": "^9.0.13",
72:     "@types/mocha": "^10.0.0",
73:     "@types/node": "^18.16.3",
74:     "@typescript-eslint/eslint-plugin": "^5.44.0",
75:     "@typescript-eslint/parser": "^5.44.0",
76:     "@venusprotocol/venus-protocol": "7.4.0",
77:     "bignumber.js": "^9.1.1",
78:     "chai": "^4.3.7",
79:     "commitizen": "^4.2.5",
80:     "cross-env": "^7.0.3",
81:     "cz-conventional-changelog": "^3.3.0",
82:     "dotenv": "^16.0.3",
83:     "eslint": "^8.28.0",
84:     "eslint-config-prettier": "^8.5.0",
85:     "ethers": "^5.7.2",
86:     "fs-extra": "^10.1.0",
87:     "hardhat": "^2.16.1",
88:     "hardhat-dependency-compiler": "^1.1.3",
89:     "hardhat-deploy": "^0.11.14",
90:     "hardhat-docgen": "^1.3.0",
91:     "hardhat-gas-reporter": "^1.0.9",
92:     "husky": "^8.0.3",
93:     "lint-staged": "^13.0.4",
94:     "lodash": "^4.17.21",
95:     "mocha": "^10.1.0",
96:     "pinst": "^3.0.0",
97:     "prettier": "^2.8.4",
98:     "prettier-plugin-solidity": "^1.1.2",
99:     "semantic-release": "^19.0.3",
100:     "shx": "^0.3.4",
101:     "solhint": "^3.4.0",
102:     "solhint-plugin-prettier": "^0.0.5",
103:     "solidity-coverage": "^0.8.4",
104:     "solidity-docgen": "^0.6.0-beta.34",
105:     "ts-generator": "^0.1.1",
106:     "ts-node": "^10.9.1",
107:     "tsconfig-paths": "^4.1.2",
108:     "typechain": "^8.1.1",
109:     "typescript": "^4.9.3"
110:   },
111:   "_moduleAliases": {

[Low-20] Contract contains payable functions but no withdraw/sweep function

Resolution

In smart contract development, particularly for Ethereum, having payable functions without a corresponding withdraw or sweep function can lead to potential issues. Payable functions allow the contract to receive Ether, but without a mechanism to withdraw these funds, the Ether can become locked within the contract indefinitely. This situation might be intentional in some cases (like a burn function), but generally, it’s a design oversight. A withdraw or sweep function is necessary to transfer Ether out of the contract to a specific address, typically the owner's or a designated recipient. Without this, the contract lacks flexibility in managing its funds, potentially leading to lost or inaccessible Ether.

Num of instances: 5

Findings

Click to show findings

['4']

4: contract GovernorAlpha 

['4']

4: contract GovernorAlpha2 

['19']

19: contract OmnichainProposalSender is ReentrancyGuard, BaseOmnichainControllerSrc 

['10']

10: contract Timelock 

['12']

12: contract TimelockV8 

[Low-21] State variables not capped at reasonable values

Resolution

Setting boundaries on state variables in smart contracts is essential for maintaining system integrity and user protection. Without caps on values, variables could reach extremes that exploit or disrupt contract functionality, leading to potential loss or unintended consequences for users. Implementing checks for minimum and maximum permissible values can prevent such issues, ensuring variables remain within a safe and reasonable range. This practice guards against attacks aimed at destabilizing the contract, such as griefing, where attackers intentionally cause distress by exploiting vulnerabilities. Proper validation promotes contract reliability, user trust, and a healthier ecosystem by mitigating risks associated with unbounded state changes.

Num of instances: 3

Findings

Click to show findings

['460']

458:     function _setProposalMaxOperations(uint proposalMaxOperations_) external {
459:         require(msg.sender == admin, "GovernorBravo::_setProposalMaxOperations: admin only");
460:         uint oldProposalMaxOperations = proposalMaxOperations; // <= FOUND
461:         proposalMaxOperations = proposalMaxOperations_; // <= FOUND
462: 
463:         emit ProposalMaxOperationsUpdated(oldProposalMaxOperations, proposalMaxOperations_);
464:     }

['50']

48:     function setMaxDailyReceiveLimit(uint256 limit_) external onlyOwner {
49:         emit SetMaxDailyReceiveLimit(maxDailyReceiveLimit, limit_);
50:         maxDailyReceiveLimit = limit_; // <= FOUND
51:     }

['65']

63:     function setMaxDailyLimit(uint16 chainId_, uint256 limit_) external {
64:         _ensureAllowed("setMaxDailyLimit(uint16,uint256)");
65:         emit SetMaxDailyLimit(chainId_, chainIdToMaxDailyLimit[chainId_], limit_); // <= FOUND
66:         chainIdToMaxDailyLimit[chainId_] = limit_; // <= FOUND
67:     }

[Low-22] The use of deprecated AccessControl functions

Resolution

The '_setupRole' function is deprecated in current OZ AccessControl implementations so it is advisable to not use it to maintain compatability.

Num of instances: 1

Findings

Click to show findings

['66']

64:         
65:         
66:         _setupRole(DEFAULT_ADMIN_ROLE, msg.sender); // <= FOUND

[Low-23] Uses of EIP712 does not include a salt

Resolution

It is standard for uses of EIP712 to include a salt, not doing so can cause future incompatibilities and in this instance cause hash collisions do to no salting

Num of instances: 1

Findings

Click to show findings

['105']

105:     
106:     bytes32 public constant DOMAIN_TYPEHASH =
107:         keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); // <= FOUND

[Low-24] Off-by-one timestamp error

Resolution

In Solidity, using >= or <= to compare against block.timestamp (alias now) may introduce off-by-one errors due to the fact that block.timestamp is only updated once per block and its value remains constant throughout the block's execution. If an operation happens at the exact second when block.timestamp changes, it could result in unexpected behavior. To avoid this, it's safer to use strict inequality operators (> or <). For instance, if a condition should only be met after a certain time, use block.timestamp > time rather than block.timestamp >= time. This way, potential off-by-one errors due to the exact timing of block mining are mitigated, leading to safer, more predictable contract behavior.

Num of instances: 3

Findings

Click to show findings

['294']

294:     function state(uint proposalId) public view returns (ProposalState) { // <= FOUND
295:         require(proposalCount >= proposalId && proposalId > 0, "GovernorAlpha::state: invalid proposal id");
296:         Proposal storage proposal = proposals[proposalId];
297:         if (proposal.canceled) {
298:             return ProposalState.Canceled;
299:         } else if (block.number <= proposal.startBlock) {
300:             return ProposalState.Pending;
301:         } else if (block.number <= proposal.endBlock) {
302:             return ProposalState.Active;
303:         } else if (proposal.forVotes <= proposal.againstVotes || proposal.forVotes < quorumVotes()) {
304:             return ProposalState.Defeated;
305:         } else if (proposal.eta == 0) {
306:             return ProposalState.Succeeded;
307:         } else if (proposal.executed) {
308:             return ProposalState.Executed; // <= FOUND
309:         } else if (block.timestamp >= add256(proposal.eta, timelock.GRACE_PERIOD())) { // <= FOUND
310:             return ProposalState.Expired;
311:         } else {
312:             return ProposalState.Queued;
313:         }
314:     }

['294']

294:     function state(uint proposalId) public view returns (ProposalState) { // <= FOUND
295:         require(proposalCount >= proposalId && proposalId > 0, "GovernorAlpha::state: invalid proposal id");
296:         Proposal storage proposal = proposals[proposalId];
297:         if (proposal.canceled) {
298:             return ProposalState.Canceled;
299:         } else if (block.number <= proposal.startBlock) {
300:             return ProposalState.Pending;
301:         } else if (block.number <= proposal.endBlock) {
302:             return ProposalState.Active;
303:         } else if (proposal.forVotes <= proposal.againstVotes || proposal.forVotes < quorumVotes()) {
304:             return ProposalState.Defeated;
305:         } else if (proposal.eta == 0) {
306:             return ProposalState.Succeeded;
307:         } else if (proposal.executed) {
308:             return ProposalState.Executed; // <= FOUND
309:         } else if (block.timestamp >= add256(proposal.eta, timelock.GRACE_PERIOD())) { // <= FOUND
310:             return ProposalState.Expired;
311:         } else {
312:             return ProposalState.Queued;
313:         }
314:     }

['289']

289:     function state(uint proposalId) public view returns (ProposalState) { // <= FOUND
290:         require(
291:             proposalCount >= proposalId && proposalId > initialProposalId,
292:             "GovernorBravo::state: invalid proposal id"
293:         );
294:         Proposal storage proposal = proposals[proposalId];
295:         if (proposal.canceled) {
296:             return ProposalState.Canceled;
297:         } else if (block.number <= proposal.startBlock) {
298:             return ProposalState.Pending;
299:         } else if (block.number <= proposal.endBlock) {
300:             return ProposalState.Active;
301:         } else if (proposal.forVotes <= proposal.againstVotes || proposal.forVotes < quorumVotes) {
302:             return ProposalState.Defeated;
303:         } else if (proposal.eta == 0) {
304:             return ProposalState.Succeeded;
305:         } else if (proposal.executed) {
306:             return ProposalState.Executed; // <= FOUND
307:         } else if (block.timestamp >= add256(proposal.eta, timelock.GRACE_PERIOD())) { // <= FOUND
308:             return ProposalState.Expired;
309:         } else {
310:             return ProposalState.Queued;
311:         }
312:     }

[Low-25] Owner can renounce while system is paused

Resolution

If an owner renounces their role while a system is paused, it could lead to permanent inaccessibility of assets stored in the contract. Such a scenario jeopardizes the trust and functionality of the protocol, as no one would be able to unpause the system or access funds. To mitigate this risk, it's essential to implement a mechanism that either prevents the owner from renouncing while the system is paused or requires multiple signatories to perform such critical actions. By introducing such safeguards, it ensures that user assets are not rendered inaccessible due to a single user's action or oversight.

Num of instances: 1

Findings

Click to show findings

['17']

17: contract BaseOmnichainControllerSrc is Ownable, Pausable  // <= FOUND

[Low-26] Numbers downcast to addresses may result in collisions

Resolution

Downcasting numbers to addresses in blockchain contracts, particularly in Ethereum's Solidity, involves risks such as possible address collisions. A collision occurs when different inputs, when cast or hashed, generate the same output address, potentially compromising contract integrity and asset security. If an uint256, for instance, is downcast to an address (effectively an uint160) without ensuring it’s a legitimate, collision-free conversion, different uint256 inputs might yield the same address, creating vulnerabilities attackers might exploit. Implementing thorough checks and opting for secure practices, like avoiding downcasting in critical logic or utilizing mappings with original uint256 as keys, mitigates risks

Num of instances: 1

Findings

Click to show findings

['252']

252:         ensureNonzeroAddress(address(uint160(bytes20(newRemoteAddress_)))); // <= FOUND

[Low-27] Delegate call can fail

Resolution

When a non payable function contains a payable call, it is important to point up that the delegate call will be limited to only calling other non payable functions. This can potentially cause future issues if this is not accounted for.

Num of instances: 2

Findings

Click to show findings

['67']

66:     function delegateTo(address callee, bytes memory data) internal {
67:         (bool success, bytes memory returnData) = callee.delegatecall(data); // <= FOUND
68:         assembly {
69:             if eq(success, 0) {
70:                 revert(add(returnData, 0x20), returndatasize)
71:             }
72:         }
73:     }

['67']

66:     function delegateTo(address callee, bytes memory data) internal {
67:         (bool success, bytes memory returnData) = callee.delegatecall(data); // <= FOUND
68:         assembly {
69:             if eq(success, 0) {
70:                 revert(add(returnData, 0x20), returndatasize)
71:             }
72:         }
73:     }

[NonCritical-1] Having chainId as a parameter can introduce cross chain replay attacks

Resolution

Accepting chainId as a parameter in a Solidity contract could open up possibilities for cross-chain replay attacks. An attacker could execute a transaction on one chain, then 'replay' it on another chain where the contract has been deployed, leading to unintended consequences. It's crucial to get the chainId within the contract using block.chainId to ensure that the transactions are valid and specific to the chain the contract resides on. The block.chainId value is provided by the EVM and can be trusted, as it's difficult to manipulate and is unique to each Ethereum-based network. This enhances the security of cross-chain operations.

Num of instances: 2

Findings

Click to show findings

['63']

63:     function setMaxDailyLimit(uint16 chainId_, uint256 limit_) external  // <= FOUND

['266']

266:     function setConfig(uint16 version_, uint16 chainId_, uint256 configType_, bytes calldata config_) external  // <= FOUND

[NonCritical-2] Use solidity time variables instead of using literals

Num of instances: 2

Findings

Click to show findings

['30']

29:     function votingPeriod() public pure returns (uint) {
30:         return (60 * 60 * 24 * 3) / 3; // <= FOUND
31:     }

['30']

29:     function votingPeriod() public pure returns (uint) {
30:         return (60 * 60 * 24 * 3) / 3; // <= FOUND
31:     }

[NonCritical-3] Storage Write Removal Bug On Conditional Early Termination

Resolution

In September 2022, a bug in Solidity’s Yul optimizer was identified through differential fuzzing. This bug, introduced in version 0.8.13 and fixed by version 0.8.17, can be activated easier with optimized via-IR code generation but can also potentially occur in optimized legacy code generation. This bug is of medium/high severity, which requires contracts that use large inline assembly blocks containing user-defined assembly functions involving return(...) or stop() instructions to be reviewed.

The bug is associated with the Unused Store Eliminator in Yul optimizer, which removes redundant storage writes, and is triggered when function calls are performed. If the function call conditionally continues after the call to a function and terminates using return(...) or stop(), the optimizer may incorrectly remove storage writes before calls to the function.

To evaluate if a contract is affected, you need to check contracts that include inline assembly block with return(...) or stop() statements. If early termination happens conditionally within a function, the optimizer might remove storage writes before function calls.

To avoid this, contracts should not allow for conditional early termination within a function. It is also recommended to use a version of Solidity where this bug has been fixed (version 0.8.17 or later).

Num of instances: 1

Findings

Click to show findings

['93']

84:         assembly {
85:             let free_mem_ptr := mload(0x40)
86:             returndatacopy(free_mem_ptr, 0, returndatasize)
87: 
88:             switch success
89:             case 0 {
90:                 revert(free_mem_ptr, returndatasize)
91:             }
92:             default {
93:                 return(free_mem_ptr, returndatasize) // <= FOUND
94:             }
95:         }

[NonCritical-4] Some if-statement can be converted to a ternary

Resolution

Improving code readability and compactness is an integral part of optimal programming practices. The use of ternary operators in place of if-else conditions is one such measure. Ternary operators allow us to write conditional statements in a more concise manner, thereby enhancing readability and simplicity. They follow the syntax condition ? exprIfTrue : exprIfFalse, which interprets as "if the condition is true, evaluate to exprIfTrue, else evaluate to exprIfFalse". By adopting this approach, we make our code more streamlined and intuitive, which could potentially aid in better understanding and maintenance of the codebase.

Num of instances: 6

Findings

Click to show findings

['235']

235:         if (bytes(signature).length == 0) {
236:             callData = data; // <= FOUND
237:         }

['51']

51:         if (!isAllowedToCall) { // <= FOUND
52:             revert("Unauthorized"); // <= FOUND
53:         }

['77']

77:         if (!isAllowedToCall) { // <= FOUND
78:             revert Unauthorized(msg.sender, address(this), signature); // <= FOUND
79:         }

['112']

112:         if (hasRole(role, account)) { // <= FOUND
113:             return true;
114:         }

['297']

297:         if (proposal.canceled) { // <= FOUND
298:             return ProposalState.Canceled;
299:         }

['88']

88:         if (a == 0) { // <= FOUND
89:             return 0;
90:         }

[NonCritical-5] Events may be emitted out of order due to code not follow the best practice of check-effects-interaction

Resolution

The "check-effects-interaction" pattern also impacts event ordering. When a contract doesn't adhere to this pattern, events might be emitted in a sequence that doesn't reflect the actual logical flow of operations. This can cause confusion during event tracking, potentially leading to erroneous off-chain interpretations. To rectify this, always ensure that checks are performed first, state modifications come next, and interactions with external contracts or addresses are done last. This will ensure events are emitted in a logical, consistent manner, providing a clear and accurate chronological record of on-chain actions for off-chain systems and observers.

Num of instances: 2

Findings

Click to show findings

['177']

177:     function executeTransaction(
178:         address target,
179:         uint value,
180:         string memory signature,
181:         bytes memory data,
182:         uint eta
183:     ) public payable returns (bytes memory) {
184:         require(msg.sender == admin, "Timelock::executeTransaction: Call must come from admin.");
185: 
186:         bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
187:         require(queuedTransactions[txHash], "Timelock::executeTransaction: Transaction hasn't been queued.");
188:         require(getBlockTimestamp() >= eta, "Timelock::executeTransaction: Transaction hasn't surpassed time lock.");
189:         require(getBlockTimestamp() <= eta.add(GRACE_PERIOD), "Timelock::executeTransaction: Transaction is stale.");
190: 
191:         queuedTransactions[txHash] = false;
192: 
193:         bytes memory callData;
194: 
195:         if (bytes(signature).length == 0) {
196:             callData = data;
197:         } else {
198:             callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data);
199:         }
200: 
201:         
202:         (bool success, bytes memory returnData) = target.call.value(value)(callData); // <= FOUND
203:         require(success, "Timelock::executeTransaction: Transaction execution reverted.");
204: 
205:         emit ExecuteTransaction(txHash, target, value, signature, data, eta); // <= FOUND
206: 
207:         return returnData;
208:     }

['217']

217:     function executeTransaction(
218:         address target,
219:         uint256 value,
220:         string calldata signature,
221:         bytes calldata data,
222:         uint256 eta
223:     ) public payable returns (bytes memory) {
224:         require(msg.sender == admin, "Timelock::executeTransaction: Call must come from admin.");
225: 
226:         bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
227:         require(queuedTransactions[txHash], "Timelock::executeTransaction: Transaction hasn't been queued.");
228:         require(getBlockTimestamp() >= eta, "Timelock::executeTransaction: Transaction hasn't surpassed time lock.");
229:         require(getBlockTimestamp() <= eta + GRACE_PERIOD(), "Timelock::executeTransaction: Transaction is stale.");
230: 
231:         delete (queuedTransactions[txHash]);
232: 
233:         bytes memory callData;
234: 
235:         if (bytes(signature).length == 0) {
236:             callData = data;
237:         } else {
238:             callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data);
239:         }
240: 
241:         
242:         (bool success, bytes memory returnData) = target.call{ value: value }(callData); // <= FOUND
243:         require(success, "Timelock::executeTransaction: Transaction execution reverted.");
244: 
245:         emit ExecuteTransaction(txHash, target, value, signature, data, eta); // <= FOUND
246: 
247:         return returnData;
248:     }

[NonCritical-6] Consider using time variables when defining time related variables

Num of instances: 3

Findings

Click to show findings

['22']

22:     
23:     uint public constant MIN_VOTING_PERIOD = 20 * 60 * 3;  // <= FOUND

['25']

25:     
26:     uint public constant MAX_VOTING_PERIOD = 20 * 60 * 24 * 14;  // <= FOUND

['31']

31:     
32:     uint public constant MAX_VOTING_DELAY = 20 * 60 * 24 * 7;  // <= FOUND

[NonCritical-7] Unnecessary struct attribute prefix

Resolution

In struct definitions, using redundant prefixes for attributes is unnecessary. For instance, in a struct named Employee, attributes like employeeName, employeeID, and employeeEmail can be simplified to name, ID, and email respectively, since they are already inherently associated with Employee. By removing these repetitive prefixes, the code becomes more concise and easier to read, maintaining its contextual clarity.

Num of instances: 3

Findings

Click to show findings

['116']

116:     struct Proposal {
117:         
118:         uint id;
119:         
120:         address proposer;
121:         
122:         uint eta;
123:         
124:         address[] targets;
125:         
126:         uint[] values;
127:         
128:         string[] signatures;
129:         
130:         bytes[] calldatas;
131:         
132:         uint startBlock;
133:         
134:         uint endBlock;
135:         
136:         uint forVotes;
137:         
138:         uint againstVotes;
139:         
140:         uint abstainVotes;
141:         
142:         bool canceled;
143:         
144:         bool executed;
145:         
146:         mapping(address => Receipt) receipts;
147:         
148:         uint8 proposalType; // <= FOUND
149:     }

['193']

193:     struct ProposalConfig {
194:         
195:         uint256 votingDelay;
196:         
197:         uint256 votingPeriod;
198:         
199:         uint256 proposalThreshold; // <= FOUND
200:     }

['29']

29:     struct Proposal {
30:         
31:         uint256 id;
32:         
33:         uint256 eta;
34:         
35:         address[] targets;
36:         
37:         uint256[] values;
38:         
39:         string[] signatures;
40:         
41:         bytes[] calldatas;
42:         
43:         bool canceled;
44:         
45:         bool executed;
46:         
47:         uint8 proposalType; // <= FOUND
48:     }

[NonCritical-8] Contracts should have all public/external functions exposed by interfaces

Resolution

Contracts should expose all public and external functions through interfaces. This practice ensures a clear and consistent definition of how the contract can be interacted with, promoting better transparency and integration.

Num of instances: 104

Findings

Click to show findings

['29']

29:     function accessControlManager() external view returns (IAccessControlManagerV5) 

['93']

93:     function setAccessControlManager(address accessControlManager_) external onlyOwner 

['55']

55:     function accessControlManager() external view returns (IAccessControlManagerV8) 

['48']

48:     function setMaxDailyReceiveLimit(uint256 limit_) external onlyOwner 

['57']

57:     function pause() external onlyOwner 

['65']

65:     function unpause() external onlyOwner 

['63']

63:     function setMaxDailyLimit(uint16 chainId_, uint256 limit_) external 

['57']

57:     function pause() external 

['65']

65:     function unpause() external 

['93']

93:     function setAccessControlManager(address accessControlManager_) external onlyOwner 

['172']

172:     function queue(uint proposalId) external 

['210']

210:     function execute(uint proposalId) external 

['233']

233:     function cancel(uint proposalId) external 

['263']

263:     function getActions(
264:         uint proposalId
265:     )
266:         external
267:         view
268:         returns (address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas)
269:     

['280']

280:     function getReceipt(uint proposalId, address voter) external view returns (Receipt memory) 

['319']

319:     function castVote(uint proposalId, uint8 support) external 

['329']

329:     function castVoteWithReason(uint proposalId, uint8 support, string calldata reason) external 

['337']

337:     function castVoteBySig(uint proposalId, uint8 support, uint8 v, bytes32 r, bytes32 s) external 

['382']

382:     function _setGuardian(address newGuardian) external 

['445']

445:     function _initiate(address governorAlpha) external 

['458']

458:     function _setProposalMaxOperations(uint proposalMaxOperations_) external 

['471']

471:     function _setPendingAdmin(address newPendingAdmin) external 

['489']

489:     function _acceptAdmin() external 

['172']

172:     function queue(uint proposalId) external 

['210']

210:     function execute(uint proposalId) external 

['233']

233:     function cancel(uint proposalId) external 

['319']

319:     function castVote(uint proposalId, uint8 support) external 

['329']

329:     function castVoteWithReason(uint proposalId, uint8 support, string calldata reason) external 

['337']

337:     function castVoteBySig(uint proposalId, uint8 support, uint8 v, bytes32 r, bytes32 s) external 

['395']

395:     function _setVotingDelay(uint newVotingDelay) external 

['411']

411:     function _setVotingPeriod(uint newVotingPeriod) external 

['428']

428:     function _setProposalThreshold(uint newProposalThreshold) external 

['445']

445:     function _initiate(address governorAlpha) external 

['43']

43:     function initialize(address accessControlManager_) external initializer 

['69']

69:     function upsertSignature(string[] calldata signatures_, bool[] calldata active_) external onlyOwner 

['94']

94:     function transferBridgeOwnership(address newOwner_) external 

['146']

146:     function setSrcChainId(uint16 srcChainId_) external onlyOwner 

['157']

157:     function addTimelocks(ITimelock[] memory timelocks_) external onlyOwner 

['178']

178:     function execute(uint256 proposalId_) external nonReentrant 

['212']

212:     function cancel(uint256 proposalId_) external 

['94']

94:     function estimateFees(
95:         uint16 remoteChainId_,
96:         bytes calldata payload_,
97:         bytes calldata adapterParams_
98:     ) external view returns (uint256, uint256) 

['108']

108:     function removeTrustedRemote(uint16 remoteChainId_) external 

['124']

124:     function execute(
125:         uint16 remoteChainId_,
126:         bytes calldata payload_,
127:         bytes calldata adapterParams_
128:     ) external payable whenNotPaused 

['169']

169:     function retryExecute(
170:         uint256 pId_,
171:         uint16 remoteChainId_,
172:         bytes calldata payload_,
173:         bytes calldata adapterParams_,
174:         uint256 originalValue_
175:     ) external payable whenNotPaused nonReentrant 

['214']

214:     function fallbackWithdraw(
215:         address to_,
216:         uint256 pId_,
217:         uint16 remoteChainId_,
218:         bytes calldata payload_,
219:         bytes calldata adapterParams_,
220:         uint256 originalValue_
221:     ) external onlyOwner nonReentrant 

['249']

249:     function setTrustedRemoteAddress(uint16 remoteChainId_, bytes calldata newRemoteAddress_) external 

['266']

266:     function setConfig(uint16 version_, uint16 chainId_, uint256 configType_, bytes calldata config_) external 

['276']

276:     function setSendVersion(uint16 version_) external 

['287']

287:     function getConfig(uint16 version_, uint16 chainId_, uint256 configType_) external view returns (bytes memory) 

['77']

77:     function giveCallPermission(address contractAddress, string calldata functionSig, address accountToPermit) public 

['91']

91:     function revokeCallPermission(
92:         address contractAddress,
93:         string calldata functionSig,
94:         address accountToRevoke
95:     ) public 

['109']

109:     function isAllowedToCall(address account, string calldata functionSig) public view returns (bool) 

['128']

128:     function hasPermission(
129:         address account,
130:         address contractAddress,
131:         string calldata functionSig
132:     ) public view returns (bool) 

['47']

47:     function _setImplementation(address implementation_) public 

['9']

9:     function quorumVotes() public pure returns (uint) 

['14']

14:     function proposalThreshold() public pure returns (uint) 

['19']

19:     function proposalMaxOperations() public pure returns (uint) 

['24']

24:     function votingDelay() public pure returns (uint) 

['29']

29:     function votingPeriod() public pure returns (uint) 

['143']

143:     function propose(
144:         address[] memory targets,
145:         uint[] memory values,
146:         string[] memory signatures,
147:         bytes[] memory calldatas,
148:         string memory description
149:     ) public returns (uint) 

['213']

213:     function queue(uint proposalId) public 

['235']

235:     function execute(uint proposalId) public payable 

['254']

254:     function cancel(uint proposalId) public 

['279']

279:     function getActions(
280:         uint proposalId
281:     )
282:         public
283:         view
284:         returns (address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas)
285:     

['290']

290:     function getReceipt(uint proposalId, address voter) public view returns (Receipt memory) 

['294']

294:     function state(uint proposalId) public view returns (ProposalState) 

['316']

316:     function castVote(uint proposalId, bool support) public 

['320']

320:     function castVoteBySig(uint proposalId, bool support, uint8 v, bytes32 r, bytes32 s) public 

['351']

351:     function __acceptAdmin() public 

['356']

356:     function __abdicate() public 

['361']

361:     function __queueSetTimelockPendingAdmin(address newPendingAdmin, uint eta) public 

['366']

366:     function __executeSetTimelockPendingAdmin(address newPendingAdmin, uint eta) public 

['9']

9:     function quorumVotes() public pure returns (uint) 

['14']

14:     function proposalThreshold() public pure returns (uint) 

['19']

19:     function proposalMaxOperations() public pure returns (uint) 

['24']

24:     function votingDelay() public pure returns (uint) 

['29']

29:     function votingPeriod() public pure returns (uint) 

['143']

143:     function propose(
144:         address[] memory targets,
145:         uint[] memory values,
146:         string[] memory signatures,
147:         bytes[] memory calldatas,
148:         string memory description
149:     ) public returns (uint) 

['213']

213:     function queue(uint proposalId) public 

['235']

235:     function execute(uint proposalId) public payable 

['254']

254:     function cancel(uint proposalId) public 

['294']

294:     function state(uint proposalId) public view returns (ProposalState) 

['316']

316:     function castVote(uint proposalId, bool support) public 

['320']

320:     function castVoteBySig(uint proposalId, bool support, uint8 v, bytes32 r, bytes32 s) public 

['111']

111:     function initialize(
112:         address xvsVault_,
113:         ProposalConfig[] memory proposalConfigs_,
114:         TimelockInterface[] memory timelocks,
115:         address guardian_
116:     ) public 

['181']

181:     function propose(
182:         address[] memory targets,
183:         uint[] memory values,
184:         string[] memory signatures,
185:         bytes[] memory calldatas,
186:         string memory description,
187:         ProposalType proposalType
188:     ) public returns (uint) 

['294']

294:     function state(uint proposalId) public view returns (ProposalState) 

['51']

51:     function initialize(
52:         address timelock_,
53:         address xvsVault_,
54:         uint votingPeriod_,
55:         uint votingDelay_,
56:         uint proposalThreshold_,
57:         address guardian_
58:     ) public 

['143']

143:     function propose(
144:         address[] memory targets,
145:         uint[] memory values,
146:         string[] memory signatures,
147:         bytes[] memory calldatas,
148:         string memory description
149:     ) public returns (uint) 

['294']

294:     function state(uint proposalId) public view returns (ProposalState) 

['47']

47:     function _setImplementation(address implementation_) public 

['246']

246:     function state(uint256 proposalId_) public view returns (ProposalState) 

['86']

86:     function setDelay(uint delay_) public 

['127']

127:     function acceptAdmin() public 

['140']

140:     function setPendingAdmin(address pendingAdmin_) public 

['126']

126:     function queueTransaction(
127:         address target,
128:         uint value,
129:         string memory signature,
130:         bytes memory data,
131:         uint eta
132:     ) public returns (bytes32) 

['154']

154:     function cancelTransaction(
155:         address target,
156:         uint value,
157:         string memory signature,
158:         bytes memory data,
159:         uint eta
160:     ) public 

['177']

177:     function executeTransaction(
178:         address target,
179:         uint value,
180:         string memory signature,
181:         bytes memory data,
182:         uint eta
183:     ) public payable returns (bytes memory) 

['90']

90:     function setDelay(uint256 delay_) public 

['127']

127:     function acceptAdmin() public 

['140']

140:     function setPendingAdmin(address pendingAdmin_) public 

['159']

159:     function queueTransaction(
160:         address target,
161:         uint256 value,
162:         string calldata signature,
163:         bytes calldata data,
164:         uint256 eta
165:     ) public returns (bytes32) 

['190']

190:     function cancelTransaction(
191:         address target,
192:         uint256 value,
193:         string calldata signature,
194:         bytes calldata data,
195:         uint256 eta
196:     ) public 

['217']

217:     function executeTransaction(
218:         address target,
219:         uint256 value,
220:         string calldata signature,
221:         bytes calldata data,
222:         uint256 eta
223:     ) public payable returns (bytes memory) 

[NonCritical-9] Using abi.encodePacked can result in hash collision when used in hashing functions

Resolution

Consider using abi.encode as this pads data to 32 byte segments

Num of instances: 1

Findings

Click to show findings

['325']

325:         bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); // <= FOUND

[NonCritical-10] Consider making private state variables internal to increase flexibility

Resolution

In Solidity, private state variables are strictly confined to the contract they are defined in and can't be accessed or modified by its derived contracts. While this offers strong encapsulation, it can limit contract extensibility and modification in inheritance chains. On the other hand, internal variables can be accessed and potentially overridden by child contracts, granting more flexibility in contract development and upgrades. Therefore, it's recommended to use private only when you explicitly want to prevent child contract access. Otherwise, prefer internal to maintain a balance between encapsulation and the flexibility offered by inheritance patterns in Solidity.

Num of instances: 2

Findings

Click to show findings

['14']

14: IAccessControlManagerV5 private _accessControlManager; // <= FOUND

['17']

17: IAccessControlManagerV8 private _accessControlManager; // <= FOUND

[NonCritical-11] Using zero as a parameter

Resolution

Taking 0 as a valid argument in Solidity without checks can lead to severe security issues. A historical example is the infamous 0x0 address bug where numerous tokens were lost. This happens because '0' can be interpreted as an uninitialized address, leading to transfers to the '0x0' address, effectively burning tokens. Moreover, 0 as a denominator in division operations would cause a runtime exception. It's also often indicative of a logical error in the caller's code. It's important to always validate input and handle edge cases like 0 appropriately. Use require() statements to enforce conditions and provide clear error messages to facilitate debugging and safer code.

Num of instances: 8

Findings

Click to show findings

['109']

109:     function isAllowedToCall(address account, string calldata functionSig) public view returns (bool) { // <= FOUND
110:         bytes32 role = keccak256(abi.encodePacked(msg.sender, functionSig));
111: 
112:         if (hasRole(role, account)) {
113:             return true;
114:         } else {
115:             role = keccak256(abi.encodePacked(address(0), functionSig)); // <= FOUND
116:             return hasRole(role, account);
117:         }
118:     }

['143']

143:     function propose(
144:         address[] memory targets,
145:         uint[] memory values,
146:         string[] memory signatures,
147:         bytes[] memory calldatas,
148:         string memory description
149:     ) public returns (uint) {
150:         require(
151:             xvs.getPriorVotes(msg.sender, sub256(block.number, 1)) > proposalThreshold(),
152:             "GovernorAlpha::propose: proposer votes below proposal threshold"
153:         );
154:         require(
155:             targets.length == values.length &&
156:                 targets.length == signatures.length &&
157:                 targets.length == calldatas.length,
158:             "GovernorAlpha::propose: proposal function information arity mismatch"
159:         );
160:         require(targets.length != 0, "GovernorAlpha::propose: must provide actions");
161:         require(targets.length <= proposalMaxOperations(), "GovernorAlpha::propose: too many actions");
162: 
163:         uint latestProposalId = latestProposalIds[msg.sender];
164:         if (latestProposalId != 0) {
165:             ProposalState proposersLatestProposalState = state(latestProposalId);
166:             require(
167:                 proposersLatestProposalState != ProposalState.Active,
168:                 "GovernorAlpha::propose: found an already active proposal"
169:             );
170:             require(
171:                 proposersLatestProposalState != ProposalState.Pending,
172:                 "GovernorAlpha::propose: found an already pending proposal"
173:             );
174:         }
175: 
176:         uint startBlock = add256(block.number, votingDelay());
177:         uint endBlock = add256(startBlock, votingPeriod());
178: 
179:         proposalCount++;
180:         Proposal memory newProposal = Proposal({
181:             id: proposalCount, // <= FOUND
182:             proposer: msg.sender,
183:             eta: 0,
184:             targets: targets,
185:             values: values,
186:             signatures: signatures,
187:             calldatas: calldatas,
188:             startBlock: startBlock,
189:             endBlock: endBlock,
190:             forVotes: 0,
191:             againstVotes: 0,
192:             canceled: false,
193:             executed: false
194:         });
195: 
196:         proposals[newProposal.id] = newProposal;
197:         latestProposalIds[newProposal.proposer] = newProposal.id;
198: 
199:         emit ProposalCreated(
200:             newProposal.id,
201:             msg.sender,
202:             targets,
203:             values,
204:             signatures,
205:             calldatas,
206:             startBlock,
207:             endBlock,
208:             description
209:         );
210:         return newProposal.id;
211:     }

['143']

143:     function propose(
144:         address[] memory targets,
145:         uint[] memory values,
146:         string[] memory signatures,
147:         bytes[] memory calldatas,
148:         string memory description
149:     ) public returns (uint) {
150:         require(
151:             xvs.getPriorVotes(msg.sender, sub256(block.number, 1)) > proposalThreshold(),
152:             "GovernorAlpha::propose: proposer votes below proposal threshold"
153:         );
154:         require(
155:             targets.length == values.length &&
156:                 targets.length == signatures.length &&
157:                 targets.length == calldatas.length,
158:             "GovernorAlpha::propose: proposal function information arity mismatch"
159:         );
160:         require(targets.length != 0, "GovernorAlpha::propose: must provide actions");
161:         require(targets.length <= proposalMaxOperations(), "GovernorAlpha::propose: too many actions");
162: 
163:         uint latestProposalId = latestProposalIds[msg.sender];
164:         if (latestProposalId != 0) {
165:             ProposalState proposersLatestProposalState = state(latestProposalId);
166:             require(
167:                 proposersLatestProposalState != ProposalState.Active,
168:                 "GovernorAlpha::propose: found an already active proposal"
169:             );
170:             require(
171:                 proposersLatestProposalState != ProposalState.Pending,
172:                 "GovernorAlpha::propose: found an already pending proposal"
173:             );
174:         }
175: 
176:         uint startBlock = add256(block.number, votingDelay());
177:         uint endBlock = add256(startBlock, votingPeriod());
178: 
179:         proposalCount++;
180:         Proposal memory newProposal = Proposal({
181:             id: proposalCount, // <= FOUND
182:             proposer: msg.sender,
183:             eta: 0,
184:             targets: targets,
185:             values: values,
186:             signatures: signatures,
187:             calldatas: calldatas,
188:             startBlock: startBlock,
189:             endBlock: endBlock,
190:             forVotes: 0,
191:             againstVotes: 0,
192:             canceled: false,
193:             executed: false
194:         });
195: 
196:         proposals[newProposal.id] = newProposal;
197:         latestProposalIds[newProposal.proposer] = newProposal.id;
198: 
199:         emit ProposalCreated(
200:             newProposal.id,
201:             msg.sender,
202:             targets,
203:             values,
204:             signatures,
205:             calldatas,
206:             startBlock,
207:             endBlock,
208:             description
209:         );
210:         return newProposal.id;
211:     }

['361']

361:     function __queueSetTimelockPendingAdmin(address newPendingAdmin, uint eta) public { // <= FOUND
362:         require(msg.sender == guardian, "GovernorAlpha::__queueSetTimelockPendingAdmin: sender must be gov guardian");
363:         timelock.queueTransaction(address(timelock), 0, "setPendingAdmin(address)", abi.encode(newPendingAdmin), eta); // <= FOUND
364:     }

['366']

366:     function __executeSetTimelockPendingAdmin(address newPendingAdmin, uint eta) public { // <= FOUND
367:         require(msg.sender == guardian, "GovernorAlpha::__executeSetTimelockPendingAdmin: sender must be gov guardian");
368:         timelock.executeTransaction(address(timelock), 0, "setPendingAdmin(address)", abi.encode(newPendingAdmin), eta); // <= FOUND
369:     }

['181']

181:     function propose(
182:         address[] memory targets,
183:         uint[] memory values,
184:         string[] memory signatures,
185:         bytes[] memory calldatas,
186:         string memory description,
187:         ProposalType proposalType
188:     ) public returns (uint) {
189:         
190:         require(initialProposalId != 0, "GovernorBravo::propose: Governor Bravo not active");
191:         require(
192:             xvsVault.getPriorVotes(msg.sender, sub256(block.number, 1)) >=
193:                 proposalConfigs[uint8(proposalType)].proposalThreshold,
194:             "GovernorBravo::propose: proposer votes below proposal threshold"
195:         );
196:         require(
197:             targets.length == values.length &&
198:                 targets.length == signatures.length &&
199:                 targets.length == calldatas.length,
200:             "GovernorBravo::propose: proposal function information arity mismatch"
201:         );
202:         require(targets.length != 0, "GovernorBravo::propose: must provide actions");
203:         require(targets.length <= proposalMaxOperations, "GovernorBravo::propose: too many actions");
204: 
205:         uint latestProposalId = latestProposalIds[msg.sender];
206:         if (latestProposalId != 0) {
207:             ProposalState proposersLatestProposalState = state(latestProposalId);
208:             require(
209:                 proposersLatestProposalState != ProposalState.Active,
210:                 "GovernorBravo::propose: one live proposal per proposer, found an already active proposal"
211:             );
212:             require(
213:                 proposersLatestProposalState != ProposalState.Pending,
214:                 "GovernorBravo::propose: one live proposal per proposer, found an already pending proposal"
215:             );
216:         }
217: 
218:         uint startBlock = add256(block.number, proposalConfigs[uint8(proposalType)].votingDelay);
219:         uint endBlock = add256(startBlock, proposalConfigs[uint8(proposalType)].votingPeriod);
220: 
221:         proposalCount++;
222:         Proposal memory newProposal = Proposal({
223:             id: proposalCount, // <= FOUND
224:             proposer: msg.sender,
225:             eta: 0,
226:             targets: targets,
227:             values: values,
228:             signatures: signatures,
229:             calldatas: calldatas,
230:             startBlock: startBlock,
231:             endBlock: endBlock,
232:             forVotes: 0,
233:             againstVotes: 0,
234:             abstainVotes: 0,
235:             canceled: false,
236:             executed: false,
237:             proposalType: uint8(proposalType)
238:         });
239: 
240:         proposals[newProposal.id] = newProposal;
241:         latestProposalIds[newProposal.proposer] = newProposal.id;
242: 
243:         emit ProposalCreated(
244:             newProposal.id,
245:             msg.sender,
246:             targets,
247:             values,
248:             signatures,
249:             calldatas,
250:             startBlock,
251:             endBlock,
252:             description,
253:             uint8(proposalType)
254:         );
255:         return newProposal.id;
256:     }

['95']

95:     function propose(
96:         address[] memory targets,
97:         uint[] memory values,
98:         string[] memory signatures,
99:         bytes[] memory calldatas,
100:         string memory description
101:     ) public returns (uint) {
102:         
103:         require(initialProposalId != 0, "GovernorBravo::propose: Governor Bravo not active");
104:         require(
105:             xvsVault.getPriorVotes(msg.sender, sub256(block.number, 1)) > proposalThreshold,
106:             "GovernorBravo::propose: proposer votes below proposal threshold"
107:         );
108:         require(
109:             targets.length == values.length &&
110:                 targets.length == signatures.length &&
111:                 targets.length == calldatas.length,
112:             "GovernorBravo::propose: proposal function information arity mismatch"
113:         );
114:         require(targets.length != 0, "GovernorBravo::propose: must provide actions");
115:         require(targets.length <= proposalMaxOperations, "GovernorBravo::propose: too many actions");
116: 
117:         uint latestProposalId = latestProposalIds[msg.sender];
118:         if (latestProposalId != 0) {
119:             ProposalState proposersLatestProposalState = state(latestProposalId);
120:             require(
121:                 proposersLatestProposalState != ProposalState.Active,
122:                 "GovernorBravo::propose: one live proposal per proposer, found an already active proposal"
123:             );
124:             require(
125:                 proposersLatestProposalState != ProposalState.Pending,
126:                 "GovernorBravo::propose: one live proposal per proposer, found an already pending proposal"
127:             );
128:         }
129: 
130:         uint startBlock = add256(block.number, votingDelay);
131:         uint endBlock = add256(startBlock, votingPeriod);
132: 
133:         proposalCount++;
134:         Proposal memory newProposal = Proposal({
135:             id: proposalCount, // <= FOUND
136:             proposer: msg.sender,
137:             eta: 0,
138:             targets: targets,
139:             values: values,
140:             signatures: signatures,
141:             calldatas: calldatas,
142:             startBlock: startBlock,
143:             endBlock: endBlock,
144:             forVotes: 0,
145:             againstVotes: 0,
146:             abstainVotes: 0,
147:             canceled: false,
148:             executed: false
149:         });
150: 
151:         proposals[newProposal.id] = newProposal;
152:         latestProposalIds[newProposal.proposer] = newProposal.id;
153: 
154:         emit ProposalCreated(
155:             newProposal.id,
156:             msg.sender,
157:             targets,
158:             values,
159:             signatures,
160:             calldatas,
161:             startBlock,
162:             endBlock,
163:             description
164:         );
165:         return newProposal.id;
166:     }

['296']

296:     function _nonblockingLzReceive(uint16, bytes memory, uint64, bytes memory payload_) internal virtual override { // <= FOUND
297:         (bytes memory payload, uint256 pId) = abi.decode(payload_, (bytes, uint256));
298:         (
299:             address[] memory targets,
300:             uint256[] memory values,
301:             string[] memory signatures,
302:             bytes[] memory calldatas,
303:             uint8 pType
304:         ) = abi.decode(payload, (address[], uint256[], string[], bytes[], uint8));
305:         require(proposals[pId].id == 0, "OmnichainGovernanceExecutor::_nonblockingLzReceive: duplicate proposal");
306:         require(
307:             targets.length == values.length &&
308:                 targets.length == signatures.length &&
309:                 targets.length == calldatas.length,
310:             "OmnichainGovernanceExecutor::_nonblockingLzReceive: proposal function information arity mismatch"
311:         );
312:         require(
313:             pType < uint8(type(ProposalType).max) + 1,
314:             "OmnichainGovernanceExecutor::_nonblockingLzReceive: invalid proposal type"
315:         );
316:         _isEligibleToReceive(targets.length);
317: 
318:         Proposal memory newProposal = Proposal({
319:             id: pId, // <= FOUND
320:             eta: 0,
321:             targets: targets,
322:             values: values,
323:             signatures: signatures,
324:             calldatas: calldatas,
325:             canceled: false,
326:             executed: false,
327:             proposalType: pType
328:         });
329: 
330:         proposals[pId] = newProposal;
331:         lastProposalReceived = pId;
332: 
333:         emit ProposalReceived(newProposal.id, targets, values, signatures, calldatas, pType);
334:         _queue(pId);
335:     }

[NonCritical-12] Consider implementing two-step procedure for updating protocol addresses

Resolution

Implementing a two-step procedure for updating protocol addresses adds an extra layer of security. In such a system, the first step initiates the change, and the second step, after a predefined delay, confirms and finalizes it. This delay allows stakeholders or monitoring tools to observe and react to unintended or malicious changes. If an unauthorized change is detected, corrective actions can be taken before the change is finalized. To achieve this, introduce a "proposed address" state variable and a "delay period". Upon an update request, set the "proposed address". After the delay, if not contested, the main protocol address can be updated.

Num of instances: 6

Findings

Click to show findings

['48']

48:     function setAccessControlManager(address accessControlManager_) external onlyOwner { // <= FOUND
49:         _setAccessControlManager(accessControlManager_);
50:     }

['93']

93:     function setAccessControlManager(address accessControlManager_) external onlyOwner { // <= FOUND
94:         ensureNonzeroAddress(accessControlManager_);
95:         emit NewAccessControlManager(accessControlManager, accessControlManager_);
96:         accessControlManager = accessControlManager_;
97:     }

['93']

93:     function setAccessControlManager(address accessControlManager_) external onlyOwner  // <= FOUND

['48']

48:     function setMaxDailyReceiveLimit(uint256 limit_) external onlyOwner  // <= FOUND

['93']

93:     function setAccessControlManager(address accessControlManager_) external onlyOwner  // <= FOUND

['146']

146:     function setSrcChainId(uint16 srcChainId_) external onlyOwner  // <= FOUND

[NonCritical-13] Prefer skip over revert model in iteration

Resolution

It is preferable to skip operations on an array index when a condition is not met rather than reverting the whole transaction as reverting can introduce the possiblity of malicous actors purposefully introducing array objects which fail conditional checks within for/while loops so group operations fail. As such it is recommended to simply skip such array indices over reverting unless there is a valid security or logic reason behind not doing so.

Num of instances: 1

Findings

Click to show findings

['136']

136:        for (uint256 i; i < arrLength; ++i) { // <= FOUND
137:             require( // <= FOUND
138:                 proposalConfigs_[i].votingPeriod >= MIN_VOTING_PERIOD,
139:                 "GovernorBravo::initialize: invalid min voting period"
140:             );
141:             require( // <= FOUND
142:                 proposalConfigs_[i].votingPeriod <= MAX_VOTING_PERIOD,
143:                 "GovernorBravo::initialize: invalid max voting period"
144:             );
145:             require( // <= FOUND
146:                 proposalConfigs_[i].votingDelay >= MIN_VOTING_DELAY,
147:                 "GovernorBravo::initialize: invalid min voting delay"
148:             );
149:             require( // <= FOUND
150:                 proposalConfigs_[i].votingDelay <= MAX_VOTING_DELAY,
151:                 "GovernorBravo::initialize: invalid max voting delay"
152:             );
153:             require( // <= FOUND
154:                 proposalConfigs_[i].proposalThreshold >= MIN_PROPOSAL_THRESHOLD,
155:                 "GovernorBravo::initialize: invalid min proposal threshold"
156:             );
157:             require( // <= FOUND
158:                 proposalConfigs_[i].proposalThreshold <= MAX_PROPOSAL_THRESHOLD,
159:                 "GovernorBravo::initialize: invalid max proposal threshold"
160:             );
161:             require(address(timelocks[i]) != address(0), "GovernorBravo::initialize:invalid timelock address"); // <= FOUND
162: 
163:             proposalConfigs[i] = proposalConfigs_[i];
164:             proposalTimelocks[i] = timelocks[i];
165:         }

[NonCritical-14] Floating pragma should be avoided

Num of instances: 1

Findings

Click to show findings

['1']

1: pragma solidity ^0.5.16; // <= FOUND

[NonCritical-15] Interfaces should be declared in a separate file

Resolution

It is general standard to declare interfaces on files separate from regular contract declarations

Num of instances: 2

Findings

Click to show findings

['391']

391: interface TimelockInterface  // <= FOUND

['391']

391: interface TimelockInterface  // <= FOUND

[NonCritical-16] Events regarding state variable changes should emit the previous state variable value

Resolution

Modify such events to contain the previous value of the state variable as demonstrated in the example below

Num of instances: 4

Findings

Click to show findings

['13']

13: event NewAdmin(address indexed newAdmin);

['17']

17: event NewPendingAdmin(address indexed newPendingAdmin);

['19']

19: event NewDelay(uint indexed newDelay);

['30']

30: event FunctionRegistryChanged(string indexed signature, bool active);

[NonCritical-17] In functions which accept an address as a parameter, there should be a zero address check to prevent bugs

Resolution

In smart contract development, especially with Solidity, it's crucial to validate inputs to functions. When a function accepts an Ethereum address as a parameter, implementing a zero address check (i.e., ensuring the address is not 0x0) is a best practice to prevent potential bugs and vulnerabilities. The zero address (0x0) is a default value and generally indicates an uninitialized or invalid state. Passing the zero address to certain functions can lead to unintended behaviors, like funds getting locked permanently or transactions failing silently. By checking for and rejecting the zero address, developers can ensure that the function operates as intended and interacts only with valid Ethereum addresses. This check enhances the contract's robustness and security.

Num of instances: 39

Findings

Click to show findings

['32']

32:     function __AccessControlled_init(address accessControlManager_) internal onlyInitializing 

['37']

37:     function __AccessControlled_init_unchained(address accessControlManager_) internal onlyInitializing 

['93']

93:     function setAccessControlManager(address accessControlManager_) external onlyOwner 

['77']

77:     function giveCallPermission(address contractAddress, string calldata functionSig, address accountToPermit) public 

['91']

91:     function revokeCallPermission(
92:         address contractAddress,
93:         string calldata functionSig,
94:         address accountToRevoke
95:     ) public 

['109']

109:     function isAllowedToCall(address account, string calldata functionSig) public view returns (bool) 

['128']

128:     function hasPermission(
129:         address account,
130:         address contractAddress,
131:         string calldata functionSig
132:     ) public view returns (bool) 

['93']

93:     function setAccessControlManager(address accessControlManager_) external onlyOwner 

['66']

66:     function delegateTo(address callee, bytes memory data) internal 

['143']

143:     function propose(
144:         address[] memory targets,
145:         uint[] memory values,
146:         string[] memory signatures,
147:         bytes[] memory calldatas,
148:         string memory description
149:     ) public returns (uint) 

['227']

227:     function _queueOrRevert(address target, uint value, string memory signature, bytes memory data, uint eta) internal 

['290']

290:     function getReceipt(uint proposalId, address voter) public view returns (Receipt memory) 

['331']

331:     function _castVote(address voter, uint proposalId, bool support) internal 

['361']

361:     function __queueSetTimelockPendingAdmin(address newPendingAdmin, uint eta) public 

['366']

366:     function __executeSetTimelockPendingAdmin(address newPendingAdmin, uint eta) public 

['143']

143:     function propose(
144:         address[] memory targets,
145:         uint[] memory values,
146:         string[] memory signatures,
147:         bytes[] memory calldatas,
148:         string memory description
149:     ) public returns (uint) 

['227']

227:     function _queueOrRevert(address target, uint value, string memory signature, bytes memory data, uint eta) internal 

['331']

331:     function _castVote(address voter, uint proposalId, bool support) internal 

['181']

181:     function propose(
182:         address[] memory targets,
183:         uint[] memory values,
184:         string[] memory signatures,
185:         bytes[] memory calldatas,
186:         string memory description,
187:         ProposalType proposalType
188:     ) public returns (uint) 

['283']

283:     function queueOrRevertInternal(
284:         address target,
285:         uint value,
286:         string memory signature,
287:         bytes memory data,
288:         uint eta,
289:         uint8 proposalType
290:     ) internal 

['280']

280:     function getReceipt(uint proposalId, address voter) external view returns (Receipt memory) 

['355']

355:     function castVoteInternal(address voter, uint proposalId, uint8 support) internal returns (uint96) 

['445']

445:     function _initiate(address governorAlpha) external 

['471']

471:     function _setPendingAdmin(address newPendingAdmin) external 

['143']

143:     function propose(
144:         address[] memory targets,
145:         uint[] memory values,
146:         string[] memory signatures,
147:         bytes[] memory calldatas,
148:         string memory description
149:     ) public returns (uint) 

['192']

192:     function queueOrRevertInternal(
193:         address target,
194:         uint value,
195:         string memory signature,
196:         bytes memory data,
197:         uint eta
198:     ) internal 

['355']

355:     function castVoteInternal(address voter, uint proposalId, uint8 support) internal returns (uint96) 

['445']

445:     function _initiate(address governorAlpha) external 

['66']

66:     function delegateTo(address callee, bytes memory data) internal 

['376']

376:     function _queueOrRevertInternal(
377:         address target_,
378:         uint256 value_,
379:         string memory signature_,
380:         bytes memory data_,
381:         uint256 eta_,
382:         uint8 proposalType_
383:     ) internal 

['214']

214:     function fallbackWithdraw(
215:         address to_,
216:         uint256 pId_,
217:         uint16 remoteChainId_,
218:         bytes calldata payload_,
219:         bytes calldata adapterParams_,
220:         uint256 originalValue_
221:     ) external onlyOwner nonReentrant 

['140']

140:     function setPendingAdmin(address pendingAdmin_) public 

['126']

126:     function queueTransaction(
127:         address target,
128:         uint value,
129:         string memory signature,
130:         bytes memory data,
131:         uint eta
132:     ) public returns (bytes32) 

['154']

154:     function cancelTransaction(
155:         address target,
156:         uint value,
157:         string memory signature,
158:         bytes memory data,
159:         uint eta
160:     ) public 

['177']

177:     function executeTransaction(
178:         address target,
179:         uint value,
180:         string memory signature,
181:         bytes memory data,
182:         uint eta
183:     ) public payable returns (bytes memory) 

['140']

140:     function setPendingAdmin(address pendingAdmin_) public 

['159']

159:     function queueTransaction(
160:         address target,
161:         uint256 value,
162:         string calldata signature,
163:         bytes calldata data,
164:         uint256 eta
165:     ) public returns (bytes32) 

['190']

190:     function cancelTransaction(
191:         address target,
192:         uint256 value,
193:         string calldata signature,
194:         bytes calldata data,
195:         uint256 eta
196:     ) public 

['217']

217:     function executeTransaction(
218:         address target,
219:         uint256 value,
220:         string calldata signature,
221:         bytes calldata data,
222:         uint256 eta
223:     ) public payable returns (bytes memory) 

[NonCritical-18] Enum values should be used in place of constant array indexes

Resolution

Create a commented enum value to use in place of constant array indexes, this makes the code far easier to understand

Num of instances: 1

Findings

Click to show findings

['117']

117:         require(address(proposalTimelocks[0]) == address(0), "GovernorBravo::initialize: cannot initialize twice"); // <= FOUND

[NonCritical-19] Default address values are manually set

Resolution

In instances where a new variable is defined, there is no need to set it to it's default value.

Num of instances: 3

Findings

Click to show findings

['358']

358:         guardian = address(0); // <= FOUND

['504']

504:         
505:         pendingAdmin = address(0); // <= FOUND

['504']

504:         pendingAdmin = address(0); // <= FOUND

[NonCritical-20] Revert statements within external and public functions can be used to perform DOS attacks

Resolution

In Solidity, 'revert' statements are used to undo changes and throw an exception when certain conditions are not met. However, in public and external functions, improper use of revert can be exploited for Denial of Service (DoS) attacks. An attacker can intentionally trigger these 'revert' conditions, causing legitimate transactions to consistently fail. For example, if a function relies on specific conditions from user input or contract state, an attacker could manipulate these to continually force reverts, blocking the function's execution. Therefore, it's crucial to design contract logic to handle exceptions properly and avoid scenarios where revert can be predictably triggered by malicious actors. This includes careful input validation and considering alternative design patterns that are less susceptible to such abuses.

Num of instances: 1

Findings

Click to show findings

['256']

246:     function state(uint256 proposalId_) public view returns (ProposalState) {
247:         Proposal storage proposal = proposals[proposalId_];
248:         if (proposal.canceled) {
249:             return ProposalState.Canceled;
250:         } else if (proposal.executed) {
251:             return ProposalState.Executed;
252:         } else if (queued[proposalId_]) {
253:             
254:             return ProposalState.Queued;
255:         } else {
256:             revert InvalidProposalId(); // <= FOUND
257:         }
258:     }

[NonCritical-21] Reverts should use customer errors instead of strings

Resolution

Custom error codes should be used in place of strings for revert statements in Solidity contracts to enhance efficiency and reduce gas costs. String-based error messages consume more bytecode and storage, increasing the overall gas consumption during contract deployment and execution

Num of instances: 1

Findings

Click to show findings

['52']

52:             revert("Unauthorized"); // <= FOUND

[NonCritical-22] Functions which are either public or external should not have a preceding _ in their name

Resolution

Remove the _ from the function name, ensure you also refactor where these functions are internally called

Num of instances: 15

Findings

Click to show findings

['382']

382:     function _setGuardian(address newGuardian) external  // <= FOUND

['445']

445:     function _initiate(address governorAlpha) external  // <= FOUND

['458']

458:     function _setProposalMaxOperations(uint proposalMaxOperations_) external  // <= FOUND

['471']

471:     function _setPendingAdmin(address newPendingAdmin) external  // <= FOUND

['489']

489:     function _acceptAdmin() external  // <= FOUND

['395']

395:     function _setVotingDelay(uint newVotingDelay) external  // <= FOUND

['411']

411:     function _setVotingPeriod(uint newVotingPeriod) external  // <= FOUND

['428']

428:     function _setProposalThreshold(uint newProposalThreshold) external  // <= FOUND

['445']

445:     function _initiate(address governorAlpha) external  // <= FOUND

['47']

47:     function _setImplementation(address implementation_) public  // <= FOUND

['351']

351:     function __acceptAdmin() public  // <= FOUND

['356']

356:     function __abdicate() public  // <= FOUND

['361']

361:     function __queueSetTimelockPendingAdmin(address newPendingAdmin, uint eta) public  // <= FOUND

['366']

366:     function __executeSetTimelockPendingAdmin(address newPendingAdmin, uint eta) public  // <= FOUND

['47']

47:     function _setImplementation(address implementation_) public  // <= FOUND

[NonCritical-23] Functions which are either private or internal should have a preceding _ in their name

Resolution

Add a preceding underscore to the function name, take care to refactor where there functions are called

Num of instances: 25

Findings

Click to show findings

['66']

66:     function delegateTo(address callee, bytes memory data) internal 

['371']

371:     function add256(uint256 a, uint256 b) internal pure returns (uint) 

['377']

377:     function sub256(uint256 a, uint256 b) internal pure returns (uint) 

['382']

382:     function getChainId() internal pure returns (uint) 

['371']

371:     function add256(uint256 a, uint256 b) internal pure returns (uint) 

['377']

377:     function sub256(uint256 a, uint256 b) internal pure returns (uint) 

['382']

382:     function getChainId() internal pure returns (uint) 

['283']

283:     function queueOrRevertInternal(
284:         address target,
285:         uint value,
286:         string memory signature,
287:         bytes memory data,
288:         uint eta,
289:         uint8 proposalType
290:     ) internal 

['355']

355:     function castVoteInternal(address voter, uint proposalId, uint8 support) internal returns (uint96) 

['521']

521:     function getChainIdInternal() internal pure returns (uint) 

['192']

192:     function queueOrRevertInternal(
193:         address target,
194:         uint value,
195:         string memory signature,
196:         bytes memory data,
197:         uint eta
198:     ) internal 

['355']

355:     function castVoteInternal(address voter, uint proposalId, uint8 support) internal returns (uint96) 

['521']

521:     function getChainIdInternal() internal pure returns (uint) 

['66']

66:     function delegateTo(address callee, bytes memory data) internal 

['26']

26:     function add(uint256 a, uint256 b) internal pure returns (uint256) 

['39']

39:     function add(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) 

['55']

55:     function sub(uint256 a, uint256 b) internal pure returns (uint256) 

['68']

68:     function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) 

['84']

84:     function mul(uint256 a, uint256 b) internal pure returns (uint256) 

['109']

109:     function div(uint256 a, uint256 b) internal pure returns (uint256) 

['124']

124:     function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) 

['144']

144:     function mod(uint256 a, uint256 b) internal pure returns (uint256) 

['159']

159:     function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) 

['210']

210:     function getBlockTimestamp() internal view returns (uint) 

['254']

254:     function getBlockTimestamp() internal view returns (uint256) 

[NonCritical-24] Contract lines should not be longer than 120 characters for readability

Resolution

Consider spreading these lines over multiple lines to aid in readability and the support of VIM users everywhere.

Num of instances: 51

Findings

Click to show findings

['8']

8:     /// @notice The number of votes in support of a proposal required in order for a quorum to be reached and for a vote to succeed // <= FOUND

['9']

9:  * It is included here for testing purposes because it has a different signature for the ProposalCreated event and Proposal struct // <= FOUND

['468']

468:      * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. // <= FOUND

['13']

13:  * @notice OmnichainProposalSender contract builds upon the functionality of its parent contract , BaseOmnichainControllerSrc // <= FOUND

['37']

37:      * @notice Specifies the allowed path for sending messages (remote chainId => remote app address + local app address) // <= FOUND

['87']

87:      * @dev The estimated fees are the minimum required; it's recommended to increase the fees amount when sending a message. The unused amount will be refunded // <= FOUND

['89']

89:      * @param payload_ The payload to be sent to the remote chain. It's computed as follows: payload = abi.encode(abi.encode(targets, values, signatures, calldatas), pId) // <= FOUND

['90']

90:      * @param adapterParams_ The params used to specify the custom amount of gas required for the execution on the destination // <= FOUND

['118']

118:      * @param payload_ The payload to be sent to the remote chain. It's computed as follows: payload = abi.encode(targets, values, signatures, calldatas, proposalType) // <= FOUND

['121']

121:      * @custom:event Emits StorePayload with last stored payload proposal ID ,remote chain id , payload, adapter params , values and reason for failure // <= FOUND

['131']

131:         // A zero value will result in a failed message; therefore, a positive value is required to send a message across the chain. // <= FOUND

['245']

245:      * @param newRemoteAddress_ The address of the contract on the remote chain to receive messages sent by this contract // <= FOUND

['8']

8:  * @notice Venus Governance latest on chain governance includes several new features including variable proposal routes and fine grained pause control. // <= FOUND

['9']

9:  * Variable routes for proposals allows for governance paramaters such as voting threshold and timelocks to be customized based on the risk level and // <= FOUND

['10']

10:  * impact of the proposal. Added granularity to the pause control mechanism allows governance to pause individual actions on specific markets, // <= FOUND

['13']

13:  * The goal of **Governance** is to increase governance efficiency, while mitigating and eliminating malicious or erroneous proposals. // <= FOUND

['20']

20:  * - XVSVault is the main staking contract for XVS. Users first stake their XVS in the vault and receive voting power proportional to their staked // <= FOUND

['30']

30:  * `GovernanceBravoDelegate` uses the XVSVault to get restrict certain actions based on a user's voting power. The governance rules it inforces are: // <= FOUND

['32']

32:  * - If a user's voting power drops below certain amount, anyone can cancel the the proposal. The governance guardian and proposal creator can also // <= FOUND

['37']

37:  * Venus Governance allows for Venus Improvement Proposals (VIPs) to be categorized based on their impact and risk levels. This allows for optimizing proposals // <= FOUND

['38']

38:  * execution to allow for things such as expediting interest rate changes and quickly updating risk parameters, while moving slower on other types of proposals // <= FOUND

['39']

39:  * that can prevent a larger risk to the protocol and are not urgent. There are three different types of VIPs with different proposal paramters: // <= FOUND

['51']

51:  * There is also a separate timelock executor contract for each route, which is used to dispatch the VIP for execution, giving even more control over the // <= FOUND

['56']

56:  * After a VIP is proposed, voting is opened after the `votingDelay` has passed. For example, if `votingDelay = 0`, then voting will begin in the next block // <= FOUND

['57']

57:  * after the proposal has been submitted. After the delay, the proposal state is `ACTIVE` and users can cast their vote `for`, `against`, or `abstain`, // <= FOUND

['58']

58:  * weighted by their total voting power (tokens + delegated voting power). Abstaining from a voting allows for a vote to be cast and optionally include a // <= FOUND

['59']

59:  * comment, without the incrementing for or against vote count. The total voting power for the user is obtained by calling XVSVault's `getPriorVotes`. // <= FOUND

['61']

61:  * `GovernorBravoDelegate` also accepts [EIP-712](https://eips.ethereum.org/EIPS/eip-712) signatures for voting on proposals via the external function // <= FOUND

['66']

66:  * A users voting power includes the amount of staked XVS the have staked as well as the votes delegate to them. Delegating is the process of a user loaning // <= FOUND

['70']

70:  * The delegation of votes happens through the `XVSVault` contract by calling the `delegate` or `delegateBySig` functions. These same functions can revert // <= FOUND

['9']

9:  * @dev This contract is a wrapper of OpenZeppelin AccessControl extending it in a way to standartize access control within Venus Smart Contract Ecosystem. // <= FOUND

['10']

10:  * @notice Access control plays a crucial role in the Venus governance model. It is used to restrict functions so that they can only be called from one // <= FOUND

['13']

13:  * The implementation of `AccessControlManager`(https://github.com/VenusProtocol/governance-contracts/blob/main/contracts/Governance/AccessControlManager.sol) // <= FOUND

['14']

14:  * inherits the [Open Zeppelin AccessControl](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/AccessControl.sol) // <= FOUND

['19']

19:  * Granular roles are built by hashing the contract address and its function signature. For example, given contract `Foo` with function `Foo.bar()` which // <= FOUND

['28']

28:  * Admin roles allow for an address to call a function signature on any contract guarded by the `AccessControlManager`. This is particularly useful for // <= FOUND

['31']

31:  * For Admin roles a null address is hashed in place of the contract address (`keccak256(0x0000000000000000000000000000000000000000,functionSignatureBar)`. // <= FOUND

['33']

33:  * In the previous example, giving account B the admin role, account B will have permissions to call the `bar()` function on any contract that is guarded by // <= FOUND

['38']

38:  * All restricted functions in Venus Protocol use a hook to ACM in order to check if the caller has the right permission to call the guarded function. // <= FOUND

['39']

39:  * `AccessControlledV5` and `AccessControlledV8` abstract contract makes this integration easier. They call ACM's external method // <= FOUND

['40']

40:  * `isAllowedToCall(address caller, string functionSig)`. Here is an example of how `setCollateralFactor` function in `Comptroller` is integrated with ACM: // <= FOUND

['45']

45:         function setCollateralFactor(VToken vToken, uint256 newCollateralFactorMantissa, uint256 newLiquidationThresholdMantissa) external { // <= FOUND

['103']

103:      * @dev Since restricted contracts using this function as a permission hook, we can get contracts address with msg.sender // <= FOUND

['122']

122:      * @dev This function is used as a view function to check permissions rather than contract hook for access restriction check. // <= FOUND

['10']

10:  * @dev This contract is included for archival and testing purposes as the ProposalCreated event has a different signature than the latest implementation. // <= FOUND

['86']

86:  * @dev This contract is included for archival and testing purposes as the Proposal struct has a different shape than the latest implementation. // <= FOUND

['9']

9:  * @notice This contract is helper between access control manager and actual contract. This contract further inherited by other contract (using solidity 0.5.16) // <= FOUND

['127']

127:         // Revert if the last proposal is already sent in current block i.e multiple proposals cannot be sent within the same block.timestamp // <= FOUND

['14']

14:  * @dev The owner of this contract controls LayerZero configuration. When used in production the owner will be OmnichainExecutor // <= FOUND

['288']

288:             emit ReceivePayloadFailed(srcChainId_, srcAddress_, nonce_, reason); // Retrieve payload from the src side tx if needed to clear // <= FOUND

['12']

12:  * @notice This contract is helper between access control manager and actual contract. This contract further inherited by other contract (using solidity 0.8.13) // <= FOUND

[NonCritical-25] Avoid updating storage when the value hasn't changed

Resolution

In Solidity, manipulating contract storage comes with significant gas costs. One can optimize gas usage by preventing unnecessary storage updates when the new value is the same as the existing one. If an existing value is the same as the new one, not reassigning it to the storage could potentially save substantial amounts of gas, notably 2900 gas for a 'Gsreset'. This saving may come at the expense of a cold storage load operation ('Gcoldsload'), which costs 2100 gas, or a warm storage access operation ('Gwarmaccess'), which costs 100 gas. Therefore, the gas efficiency of your contract can be significantly improved by adding a check that compares the new value with the current one before any storage update operation. If the values are the same, you can bypass the storage operation, thereby saving gas.

Num of instances: 4

Findings

Click to show findings

['48']

48:     function setAccessControlManager(address accessControlManager_) external onlyOwner {
49:         _setAccessControlManager(accessControlManager_);
50:     }

['48']

48:     function setMaxDailyReceiveLimit(uint256 limit_) external onlyOwner {
49:         emit SetMaxDailyReceiveLimit(maxDailyReceiveLimit, limit_);
50:         maxDailyReceiveLimit = limit_;
51:     }

['93']

93:     function setAccessControlManager(address accessControlManager_) external onlyOwner {
94:         ensureNonzeroAddress(accessControlManager_);
95:         emit NewAccessControlManager(accessControlManager, accessControlManager_);
96:         accessControlManager = accessControlManager_;
97:     }

['146']

146:     function setSrcChainId(uint16 srcChainId_) external onlyOwner {
147:         emit SetSrcChainId(srcChainId, srcChainId_);
148:         srcChainId = srcChainId_;
149:     }

[NonCritical-26] Specific imports should be used where possible so only used code is imported

Resolution

In many cases only some functionality is used from an import. In such cases it makes more sense to use {} to specify what to import and thus save gas whilst improving readability

Num of instances: 8

Findings

Click to show findings

['4']

4: import "./IAccessControlManagerV5.sol";

['4']

4: import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

['5']

5: import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";

['4']

4: import "./IAccessControlManagerV8.sol";

['3']

3: import "@openzeppelin/contracts/access/AccessControl.sol";

['4']

4: import "./GovernorBravoInterfaces.sol";

['4']

4: import "@openzeppelin/contracts/access/IAccessControl.sol";

['3']

3: import "../Utils/SafeMath.sol";

[NonCritical-27] Old Solidity version

Resolution

Using outdated Solidity versions can lead to security risks and inefficiencies. It's recommended to adopt newer versions, ideally the latest, which as of now is 0.8.24. This ensures access to the latest bug fixes, features, and optimizations, particularly crucial for Layer 2 deployments. Regular updates to versions like 0.8.19 or later, up to 0.8.24, enhance contract security and performance.

Num of instances: 4

Findings

Click to show findings

[]

2: pragma solidity 0.5.16;

[]

2: pragma solidity 0.8.13;

[]

1: pragma solidity ^0.5.16;

[]

2: pragma experimental ABIEncoderV2;

[NonCritical-28] Not all event definitions are utilizing indexed variables.

Resolution

Try to index as much as three variables in event declarations as this is more gas efficient when done on value type variables (uint, address etc) however not for bytes and string variables

Num of instances: 38

Findings

Click to show findings

['24']

24: event NewAccessControlManager(address oldAccessControlManager, address newAccessControlManager); // <= FOUND

['49']

49: event NewAccessControlManager(address indexed oldAccessControlManager, address indexed newAccessControlManager); // <= FOUND

['56']

56: event PermissionGranted(address account, address contractAddress, string functionSig); // <= FOUND

['59']

59: event PermissionRevoked(address account, address contractAddress, string functionSig); // <= FOUND

['45']

45: event SetMaxDailyLimit(uint16 indexed chainId, uint256 oldMaxLimit, uint256 newMaxLimit); // <= FOUND

['112']

112: event ProposalCreated( // <= FOUND
113:         uint id,
114:         address proposer,
115:         address[] targets,
116:         uint[] values,
117:         string[] signatures,
118:         bytes[] calldatas,
119:         uint startBlock,
120:         uint endBlock,
121:         string description
122:     );

['11']

11: event ProposalCreated( // <= FOUND
12:         uint id,
13:         address proposer,
14:         address[] targets,
15:         uint[] values,
16:         string[] signatures,
17:         bytes[] calldatas,
18:         uint startBlock,
19:         uint endBlock,
20:         string description,
21:         uint8 proposalType
22:     );

['125']

125: event VoteCast(address voter, uint proposalId, bool support, uint votes); // <= FOUND

['32']

32: event VoteCast(address indexed voter, uint proposalId, uint8 support, uint votes, string reason); // <= FOUND

['128']

128: event ProposalCanceled(uint id); // <= FOUND

['117']

117: event ProposalCanceled(uint256 indexed id); // <= FOUND

['131']

131: event ProposalQueued(uint id, uint eta); // <= FOUND

['102']

102: event ProposalQueued(uint256 indexed id, uint256 eta); // <= FOUND

['134']

134: event ProposalExecuted(uint id); // <= FOUND

['107']

107: event ProposalExecuted(uint256 indexed id); // <= FOUND

['44']

44: event VotingDelaySet(uint oldVotingDelay, uint newVotingDelay); // <= FOUND

['47']

47: event VotingPeriodSet(uint oldVotingPeriod, uint newVotingPeriod); // <= FOUND

['50']

50: event NewImplementation(address oldImplementation, address newImplementation); // <= FOUND

['53']

53: event ProposalThresholdSet(uint oldProposalThreshold, uint newProposalThreshold); // <= FOUND

['56']

56: event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin); // <= FOUND

['17']

17: event NewPendingAdmin(address indexed newPendingAdmin); // <= FOUND

['59']

59: event NewAdmin(address oldAdmin, address newAdmin); // <= FOUND

['13']

13: event NewAdmin(address indexed newAdmin); // <= FOUND

['14']

14: event NewAdmin(address indexed oldAdmin, address indexed newAdmin); // <= FOUND

['62']

62: event NewGuardian(address oldGuardian, address newGuardian); // <= FOUND

['65']

65: event ProposalMaxOperationsUpdated(uint oldMaxOperations, uint newMaxOperations); // <= FOUND

['90']

90: event ProposalReceived( // <= FOUND
91:         uint256 indexed proposalId,
92:         address[] targets,
93:         uint256[] values,
94:         string[] signatures,
95:         bytes[] calldatas,
96:         uint8 proposalType
97:     );

['122']

122: event TimelockAdded(uint8 routeType, address indexed oldTimelock, address indexed newTimelock); // <= FOUND

['54']

54: event ExecuteRemoteProposal(uint16 indexed remoteChainId, uint256 proposalId, bytes payload); // <= FOUND

['64']

64: event StorePayload( // <= FOUND
65:         uint256 indexed proposalId,
66:         uint16 indexed remoteChainId,
67:         bytes payload,
68:         bytes adapterParams,
69:         uint256 value,
70:         bytes reason
71:     );

['75']

75: event FallbackWithdraw(address indexed receiver, uint256 value); // <= FOUND

['22']

22: event CancelTransaction( // <= FOUND
23:         bytes32 indexed txHash,
24:         address indexed target,
25:         uint value,
26:         string signature,
27:         bytes data,
28:         uint eta
29:     );

['23']

23: event CancelTransaction( // <= FOUND
24:         bytes32 indexed txHash,
25:         address indexed target,
26:         uint256 value,
27:         string signature,
28:         bytes data,
29:         uint256 eta
30:     );

['32']

32: event ExecuteTransaction( // <= FOUND
33:         bytes32 indexed txHash,
34:         address indexed target,
35:         uint value,
36:         string signature,
37:         bytes data,
38:         uint eta
39:     );

['33']

33: event ExecuteTransaction( // <= FOUND
34:         bytes32 indexed txHash,
35:         address indexed target,
36:         uint256 value,
37:         string signature,
38:         bytes data,
39:         uint256 eta
40:     );

['42']

42: event QueueTransaction( // <= FOUND
43:         bytes32 indexed txHash,
44:         address indexed target,
45:         uint value,
46:         string signature,
47:         bytes data,
48:         uint eta
49:     );

['43']

43: event QueueTransaction( // <= FOUND
44:         bytes32 indexed txHash,
45:         address indexed target,
46:         uint256 value,
47:         string signature,
48:         bytes data,
49:         uint256 eta
50:     );

['36']

36: event SetMaxDailyReceiveLimit(uint256 oldMaxLimit, uint256 newMaxLimit); // <= FOUND

[NonCritical-29] It is convention to make the array size of __gap 50

Resolution

This is the recommendation made by OpenZeppelin and allows plenty of room for updatability

Num of instances: 1

Findings

Click to show findings

['21']

21: uint256[49] private __gap; // <= FOUND

[NonCritical-30] uint/int variables should have the bit size defined explicitly

Resolution

Instead of using uint to declare uint258, explicitly define uint258 to ensure there is no confusion

Num of instances: 70

Findings

Click to show findings

['17']

12:     constructor(
13:         address timelock_,
14:         address xvsVault_,
15:         address admin_,
16:         address implementation_,
17:         uint votingPeriod_, // <= FOUND
18:         uint votingDelay_, // <= FOUND
19:         uint proposalThreshold_, // <= FOUND
20:         address guardian_
21:     ) public {

['44']

43:     
44:     uint public proposalCount; // <= FOUND

['48']

47:         
48:         uint id; // <= FOUND

['52']

51:         
52:         uint eta; // <= FOUND

['62']

61:         
62:         uint startBlock; // <= FOUND

['64']

63:         
64:         uint endBlock; // <= FOUND

['66']

65:         
66:         uint forVotes; // <= FOUND

['68']

67:         
68:         uint againstVotes; // <= FOUND

['114']

112:     
113:     event ProposalCreated(
114:         uint id, // <= FOUND
115:         address proposer,
116:         address[] targets,
117:         uint[] values,
118:         string[] signatures,
119:         bytes[] calldatas,
120:         uint startBlock, // <= FOUND
121:         uint endBlock, // <= FOUND
122:         string description
123:     );

['126']

125:     
126:     event VoteCast(address voter, uint proposalId, bool support, uint votes); // <= FOUND

['132']

131:     
132:     event ProposalQueued(uint id, uint eta); // <= FOUND

['163']

163:         uint latestProposalId = latestProposalIds[msg.sender]; // <= FOUND

['176']

176:         uint startBlock = add256(block.number, votingDelay()); // <= FOUND

['177']

177:         uint endBlock = add256(startBlock, votingPeriod()); // <= FOUND

['219']

219:         uint eta = add256(block.timestamp, timelock.delay()); // <= FOUND

['227']

227:     function _queueOrRevert(address target, uint value, string memory signature, bytes memory data, uint eta) internal { // <= FOUND

['280']

279:     function getActions(
280:         uint proposalId // <= FOUND
281:     )
282:         public
283:         view
284:         returns (address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas)
285:     {

['331']

331:     function _castVote(address voter, uint proposalId, bool support) internal { // <= FOUND

['361']

361:     function __queueSetTimelockPendingAdmin(address newPendingAdmin, uint eta) public { // <= FOUND

['366']

366:     function __executeSetTimelockPendingAdmin(address newPendingAdmin, uint eta) public { // <= FOUND

['372']

372:         uint c = a + b; // <= FOUND

['383']

383:         uint chainId; // <= FOUND

['402']

400:     function queueTransaction(
401:         address target,
402:         uint value, // <= FOUND
403:         string calldata signature,
404:         bytes calldata data,
405:         uint eta // <= FOUND
406:     ) external returns (bytes32);

['410']

408:     function cancelTransaction(
409:         address target,
410:         uint value, // <= FOUND
411:         string calldata signature,
412:         bytes calldata data,
413:         uint eta // <= FOUND
414:     ) external;

['418']

416:     function executeTransaction(
417:         address target,
418:         uint value, // <= FOUND
419:         string calldata signature,
420:         bytes calldata data,
421:         uint eta // <= FOUND
422:     ) external payable returns (bytes memory);

['426']

426:     function getPriorVotes(address account, uint blockNumber) external view returns (uint96); // <= FOUND

['17']

16:     
17:     uint public constant MIN_PROPOSAL_THRESHOLD = 150000e18;  // <= FOUND

['20']

19:     
20:     uint public constant MAX_PROPOSAL_THRESHOLD = 300000e18;  // <= FOUND

['23']

22:     
23:     uint public constant MIN_VOTING_PERIOD = 20 * 60 * 3;  // <= FOUND

['26']

25:     
26:     uint public constant MAX_VOTING_PERIOD = 20 * 60 * 24 * 14;  // <= FOUND

['29']

28:     
29:     uint public constant MIN_VOTING_DELAY = 1; // <= FOUND

['32']

31:     
32:     uint public constant MAX_VOTING_DELAY = 20 * 60 * 24 * 7;  // <= FOUND

['35']

34:     
35:     uint public constant quorumVotes = 600000e18;  // <= FOUND

['218']

218:         uint startBlock = add256(block.number, proposalConfigs[uint8(proposalType)].votingDelay); // <= FOUND

['219']

219:         uint endBlock = add256(startBlock, proposalConfigs[uint8(proposalType)].votingPeriod); // <= FOUND

['268']

268:         uint eta = add256(block.timestamp, proposalTimelocks[uint8(proposal.proposalType)].delay()); // <= FOUND

['285']

283:     function queueOrRevertInternal(
284:         address target,
285:         uint value, // <= FOUND
286:         string memory signature,
287:         bytes memory data,
288:         uint eta, // <= FOUND
289:         uint8 proposalType
290:     ) internal {

['269']

263:     
268:     function getActions(
269:         uint proposalId // <= FOUND
270:     )
271:         external
272:         view
273:         returns (address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas)
274:     {

['362']

355:     
362:     function castVoteInternal(address voter, uint proposalId, uint8 support) internal returns (uint96) { // <= FOUND

['460']

460:         uint oldProposalMaxOperations = proposalMaxOperations; // <= FOUND

['62']

51:     
59:     function initialize(
60:         address timelock_,
61:         address xvsVault_,
62:         uint votingPeriod_, // <= FOUND
63:         uint votingDelay_, // <= FOUND
64:         uint proposalThreshold_, // <= FOUND
65:         address guardian_
66:     ) public {

['130']

130:         uint startBlock = add256(block.number, votingDelay); // <= FOUND

['131']

131:         uint endBlock = add256(startBlock, votingPeriod); // <= FOUND

['194']

192:     function queueOrRevertInternal(
193:         address target,
194:         uint value, // <= FOUND
195:         string memory signature,
196:         bytes memory data,
197:         uint eta // <= FOUND
198:     ) internal {

['401']

401:         uint oldVotingDelay = votingDelay; // <= FOUND

['417']

417:         uint oldVotingPeriod = votingPeriod; // <= FOUND

['434']

434:         uint oldProposalThreshold = proposalThreshold; // <= FOUND

['13']

11:     
12:     event ProposalCreated(
13:         uint id, // <= FOUND
14:         address proposer,
15:         address[] targets,
16:         uint[] values,
17:         string[] signatures,
18:         bytes[] calldatas,
19:         uint startBlock, // <= FOUND
20:         uint endBlock, // <= FOUND
21:         string description,
22:         uint8 proposalType
23:     );

['38']

32:     
38:     event VoteCast(address indexed voter, uint proposalId, uint8 support, uint votes, string reason); // <= FOUND

['45']

44:     
45:     event VotingDelaySet(uint oldVotingDelay, uint newVotingDelay); // <= FOUND

['48']

47:     
48:     event VotingPeriodSet(uint oldVotingPeriod, uint newVotingPeriod); // <= FOUND

['54']

53:     
54:     event ProposalThresholdSet(uint oldProposalThreshold, uint newProposalThreshold); // <= FOUND

['66']

65:     
66:     event ProposalMaxOperationsUpdated(uint oldMaxOperations, uint newMaxOperations); // <= FOUND

['91']

90:     
91:     uint public votingDelay; // <= FOUND

['94']

93:     
94:     uint public votingPeriod; // <= FOUND

['97']

96:     
97:     uint public proposalThreshold; // <= FOUND

['100']

99:     
100:     uint public initialProposalId; // <= FOUND

['141']

140:         
141:         uint abstainVotes; // <= FOUND

['173']

172:     
173:     uint public proposalMaxOperations; // <= FOUND

['26']

22:     
23:     event CancelTransaction(
24:         bytes32 indexed txHash,
25:         address indexed target,
26:         uint value, // <= FOUND
27:         string signature,
28:         bytes data,
29:         uint eta // <= FOUND
30:     );

['36']

32:     
33:     event ExecuteTransaction(
34:         bytes32 indexed txHash,
35:         address indexed target,
36:         uint value, // <= FOUND
37:         string signature,
38:         bytes data,
39:         uint eta // <= FOUND
40:     );

['46']

42:     
43:     event QueueTransaction(
44:         bytes32 indexed txHash,
45:         address indexed target,
46:         uint value, // <= FOUND
47:         string signature,
48:         bytes data,
49:         uint eta // <= FOUND
50:     );

['53']

52:     
53:     uint public constant GRACE_PERIOD = 14 days; // <= FOUND

['56']

55:     
56:     uint public constant MINIMUM_DELAY = 1 hours; // <= FOUND

['59']

58:     
59:     uint public constant MAXIMUM_DELAY = 30 days; // <= FOUND

['68']

67:     
68:     uint public delay; // <= FOUND

['72']

72:     constructor(address admin_, uint delay_) public { // <= FOUND

['137']

126:     
135:     function queueTransaction(
136:         address target,
137:         uint value, // <= FOUND
138:         string memory signature,
139:         bytes memory data,
140:         uint eta // <= FOUND
141:     ) public returns (bytes32) {

['164']

154:     
162:     function cancelTransaction(
163:         address target,
164:         uint value, // <= FOUND
165:         string memory signature,
166:         bytes memory data,
167:         uint eta // <= FOUND
168:     ) public {

['187']

177:     
185:     function executeTransaction(
186:         address target,
187:         uint value, // <= FOUND
188:         string memory signature,
189:         bytes memory data,
190:         uint eta // <= FOUND
191:     ) public payable returns (bytes memory) {

[NonCritical-31] Functions within contracts are not ordered according to the solidity style guide

Resolution

The following order should be used within contracts

constructor

receive function (if exists)

fallback function (if exists)

external

public

internal

private

Rearrange the contract functions and contructors to fit this ordering

Num of instances: 7

Findings

Click to show findings

['11']

11: contract GovernorBravoDelegatorV1 is GovernorBravoDelegatorStorage, GovernorBravoEventsV1  // <= FOUND

['4']

4: contract GovernorAlpha  // <= FOUND

['4']

4: contract GovernorAlpha2  // <= FOUND

['73']

73: contract GovernorBravoDelegate is GovernorBravoDelegateStorageV2, GovernorBravoEvents  // <= FOUND

['11']

11: contract GovernorBravoDelegateV1 is GovernorBravoDelegateStorageV1, GovernorBravoEventsV1  // <= FOUND

['11']

11: contract GovernorBravoDelegator is GovernorBravoDelegatorStorage, GovernorBravoEvents  // <= FOUND

['15']

15: abstract contract AccessControlledV8 is Initializable, Ownable2StepUpgradeable  // <= FOUND

[NonCritical-32] Double type casts create complexity within the code

Resolution

Double type casting should be avoided in Solidity contracts to prevent unintended consequences and ensure accurate data representation. Performing multiple type casts in succession can lead to unexpected truncation, rounding errors, or loss of precision, potentially compromising the contract's functionality and reliability. Furthermore, double type casting can make the code less readable and harder to maintain, increasing the likelihood of errors and misunderstandings during development and debugging. To ensure precise and consistent data handling, developers should use appropriate data types and avoid unnecessary or excessive type casting, promoting a more robust and dependable contract execution.

Num of instances: 1

Findings

Click to show findings

['252']

252:         ensureNonzeroAddress(address(uint160(bytes20(newRemoteAddress_)))); // <= FOUND

[NonCritical-33] Emits without msg.sender parameter

Resolution

In Solidity, when msg.sender plays a crucial role in a function's logic, it's important for transparency and auditability that any events emitted by this function include msg.sender as a parameter. This practice enhances the traceability and accountability of transactions, allowing users and external observers to easily track who initiated a particular action. Including msg.sender in event logs helps in creating a clear and verifiable record of interactions with the contract, thereby increasing user trust and facilitating easier debugging and analysis of contract behavior. It's a key aspect of writing clear, transparent, and user-friendly smart contracts.

Num of instances: 14

Findings

Click to show findings

['48']

47:     function _setImplementation(address implementation_) public {
48:         require(msg.sender == admin, "GovernorBravoDelegator::_setImplementation: admin only"); // <= FOUND
49:         require(
50:             implementation_ != address(0),
51:             "GovernorBravoDelegator::_setImplementation: invalid implementation address"
52:         );
53: 
54:         address oldImplementation = implementation;
55:         implementation = implementation_;
56: 
57:         emit NewImplementation(oldImplementation, implementation);
58:     }

['48']

47:     function _setImplementation(address implementation_) public {
48:         require(msg.sender == admin, "GovernorBravoDelegator::_setImplementation: admin only"); // <= FOUND
49:         require(
50:             implementation_ != address(0),
51:             "GovernorBravoDelegator::_setImplementation: invalid implementation address"
52:         );
53: 
54:         address oldImplementation = implementation;
55:         implementation = implementation_;
56: 
57:         emit NewImplementation(oldImplementation, implementation);
58:     }

['260']

254:     function cancel(uint proposalId) public {
255:         ProposalState state = state(proposalId);
256:         require(state != ProposalState.Executed, "GovernorAlpha::cancel: cannot cancel executed proposal");
257: 
258:         Proposal storage proposal = proposals[proposalId];
259:         require(
260:             msg.sender == guardian || // <= FOUND
261:                 xvs.getPriorVotes(proposal.proposer, sub256(block.number, 1)) < proposalThreshold(),
262:             "GovernorAlpha::cancel: proposer above threshold"
263:         );
264: 
265:         proposal.canceled = true;
266:         for (uint i = 0; i < proposal.targets.length; i++) {
267:             timelock.cancelTransaction(
268:                 proposal.targets[i],
269:                 proposal.values[i],
270:                 proposal.signatures[i],
271:                 proposal.calldatas[i],
272:                 proposal.eta
273:             );
274:         }
275: 
276:         emit ProposalCanceled(proposalId);
277:     }

['260']

254:     function cancel(uint proposalId) public {
255:         ProposalState state = state(proposalId);
256:         require(state != ProposalState.Executed, "GovernorAlpha::cancel: cannot cancel executed proposal");
257: 
258:         Proposal storage proposal = proposals[proposalId];
259:         require(
260:             msg.sender == guardian || // <= FOUND
261:                 xvs.getPriorVotes(proposal.proposer, sub256(block.number, 1)) < proposalThreshold(),
262:             "GovernorAlpha::cancel: proposer above threshold"
263:         );
264: 
265:         proposal.canceled = true;
266:         for (uint i = 0; i < proposal.targets.length; i++) {
267:             timelock.cancelTransaction(
268:                 proposal.targets[i],
269:                 proposal.values[i],
270:                 proposal.signatures[i],
271:                 proposal.calldatas[i],
272:                 proposal.eta
273:             );
274:         }
275: 
276:         emit ProposalCanceled(proposalId);
277:     }

['332']

327:     function cancel(uint proposalId) external {
328:         require(state(proposalId) != ProposalState.Executed, "GovernorBravo::cancel: cannot cancel executed proposal");
329: 
330:         Proposal storage proposal = proposals[proposalId];
331:         require(
332:             msg.sender == guardian || // <= FOUND
333:                 msg.sender == proposal.proposer || // <= FOUND
334:                 xvsVault.getPriorVotes(proposal.proposer, sub256(block.number, 1)) <
335:                 proposalConfigs[proposal.proposalType].proposalThreshold,
336:             "GovernorBravo::cancel: proposer above threshold"
337:         );
338: 
339:         proposal.canceled = true;
340:         for (uint i = 0; i < proposal.targets.length; i++) {
341:             proposalTimelocks[proposal.proposalType].cancelTransaction(
342:                 proposal.targets[i],
343:                 proposal.values[i],
344:                 proposal.signatures[i],
345:                 proposal.calldatas[i],
346:                 proposal.eta
347:             );
348:         }
349: 
350:         emit ProposalCanceled(proposalId);
351:     }

['238']

233:     function cancel(uint proposalId) external {
234:         require(state(proposalId) != ProposalState.Executed, "GovernorBravo::cancel: cannot cancel executed proposal");
235: 
236:         Proposal storage proposal = proposals[proposalId];
237:         require(
238:             msg.sender == guardian || // <= FOUND
239:                 msg.sender == proposal.proposer || // <= FOUND
240:                 xvsVault.getPriorVotes(proposal.proposer, sub256(block.number, 1)) < proposalThreshold,
241:             "GovernorBravo::cancel: proposer above threshold"
242:         );
243: 
244:         proposal.canceled = true;
245:         for (uint i = 0; i < proposal.targets.length; i++) {
246:             timelock.cancelTransaction(
247:                 proposal.targets[i],
248:                 proposal.values[i],
249:                 proposal.signatures[i],
250:                 proposal.calldatas[i],
251:                 proposal.eta
252:             );
253:         }
254: 
255:         emit ProposalCanceled(proposalId);
256:     }

['383']

382:     function _setGuardian(address newGuardian) external {
383:         require(msg.sender == guardian || msg.sender == admin, "GovernorBravo::_setGuardian: admin or guardian only"); // <= FOUND
384:         require(newGuardian != address(0), "GovernorBravo::_setGuardian: cannot live without a guardian");
385:         address oldGuardian = guardian;
386:         guardian = newGuardian;
387: 
388:         emit NewGuardian(oldGuardian, newGuardian);
389:     }

['459']

458:     function _setProposalMaxOperations(uint proposalMaxOperations_) external {
459:         require(msg.sender == admin, "GovernorBravo::_setProposalMaxOperations: admin only"); // <= FOUND
460:         uint oldProposalMaxOperations = proposalMaxOperations;
461:         proposalMaxOperations = proposalMaxOperations_;
462: 
463:         emit ProposalMaxOperationsUpdated(oldProposalMaxOperations, proposalMaxOperations_);
464:     }

['473']

471:     function _setPendingAdmin(address newPendingAdmin) external {
472:         
473:         require(msg.sender == admin, "GovernorBravo:_setPendingAdmin: admin only"); // <= FOUND
474: 
475:         
476:         address oldPendingAdmin = pendingAdmin;
477: 
478:         
479:         pendingAdmin = newPendingAdmin;
480: 
481:         
482:         emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin);
483:     }

['492']

489:     function _acceptAdmin() external {
490:         
491:         require(
492:             msg.sender == pendingAdmin && msg.sender != address(0), // <= FOUND
493:             "GovernorBravo:_acceptAdmin: pending admin only"
494:         );
495: 
496:         
497:         address oldAdmin = admin;
498:         address oldPendingAdmin = pendingAdmin;
499: 
500:         
501:         admin = pendingAdmin;
502: 
503:         
504:         pendingAdmin = address(0);
505: 
506:         emit NewAdmin(oldAdmin, admin);
507:         emit NewPendingAdmin(oldPendingAdmin, pendingAdmin);
508:     }

['396']

395:     function _setVotingDelay(uint newVotingDelay) external {
396:         require(msg.sender == admin, "GovernorBravo::_setVotingDelay: admin only"); // <= FOUND
397:         require(
398:             newVotingDelay >= MIN_VOTING_DELAY && newVotingDelay <= MAX_VOTING_DELAY,
399:             "GovernorBravo::_setVotingDelay: invalid voting delay"
400:         );
401:         uint oldVotingDelay = votingDelay;
402:         votingDelay = newVotingDelay;
403: 
404:         emit VotingDelaySet(oldVotingDelay, votingDelay);
405:     }

['412']

411:     function _setVotingPeriod(uint newVotingPeriod) external {
412:         require(msg.sender == admin, "GovernorBravo::_setVotingPeriod: admin only"); // <= FOUND
413:         require(
414:             newVotingPeriod >= MIN_VOTING_PERIOD && newVotingPeriod <= MAX_VOTING_PERIOD,
415:             "GovernorBravo::_setVotingPeriod: invalid voting period"
416:         );
417:         uint oldVotingPeriod = votingPeriod;
418:         votingPeriod = newVotingPeriod;
419: 
420:         emit VotingPeriodSet(oldVotingPeriod, votingPeriod);
421:     }

['429']

428:     function _setProposalThreshold(uint newProposalThreshold) external {
429:         require(msg.sender == admin, "GovernorBravo::_setProposalThreshold: admin only"); // <= FOUND
430:         require(
431:             newProposalThreshold >= MIN_PROPOSAL_THRESHOLD && newProposalThreshold <= MAX_PROPOSAL_THRESHOLD,
432:             "GovernorBravo::_setProposalThreshold: invalid proposal threshold"
433:         );
434:         uint oldProposalThreshold = proposalThreshold;
435:         proposalThreshold = newProposalThreshold;
436: 
437:         emit ProposalThresholdSet(oldProposalThreshold, proposalThreshold);
438:     }

['218']

212:     function cancel(uint256 proposalId_) external {
213:         require(
214:             state(proposalId_) == ProposalState.Queued,
215:             "OmnichainGovernanceExecutor::cancel: proposal should be queued and not executed"
216:         );
217:         Proposal storage proposal = proposals[proposalId_];
218:         require(msg.sender == GUARDIAN, "OmnichainGovernanceExecutor::cancel: sender must be guardian"); // <= FOUND
219: 
220:         proposal.canceled = true;
221:         ITimelock timelock = proposalTimelocks[proposal.proposalType];
222:         uint256 eta = proposal.eta;
223:         uint256 length = proposal.targets.length;
224: 
225:         emit ProposalCanceled(proposalId_);
226: 
227:         for (uint256 i; i < length; ) {
228:             timelock.cancelTransaction(
229:                 proposal.targets[i],
230:                 proposal.values[i],
231:                 proposal.signatures[i],
232:                 proposal.calldatas[i],
233:                 eta
234:             );
235:             unchecked {
236:                 ++i;
237:             }
238:         }
239:     }

[NonCritical-34] Interface imports should be declared first

Resolution

Amend the ordering of imports to import interfaces first followed by other imports

Num of instances: 6

Findings

Click to show findings

['7']

3: 
5: pragma solidity 0.8.13;
6: 
7: import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol"; // <= FOUND
8: import { ILayerZeroEndpoint } from "@layerzerolabs/solidity-examples/contracts/lzApp/interfaces/ILayerZeroEndpoint.sol"; // <= FOUND
9: import { ensureNonzeroAddress } from "@venusprotocol/solidity-utilities/contracts/validators.sol"; // <= FOUND
10: import { BaseOmnichainControllerSrc } from "./BaseOmnichainControllerSrc.sol"; // <= FOUND
11: 
21: contract OmnichainProposalSender is ReentrancyGuard, BaseOmnichainControllerSrc {
22:     
25:     uint256 public proposalCount;
26: 
31:     mapping(uint256 => bytes32) public storedExecutionHashes;
32: 
36:     ILayerZeroEndpoint public immutable LZ_ENDPOINT;
37: 
41:     mapping(uint16 => bytes) public trustedRemoteLookup;
42: 
46:     event SetTrustedRemoteAddress(uint16 indexed remoteChainId, bytes oldRemoteAddress, bytes newRemoteAddress);
47: 
51:     event TrustedRemoteRemoved(uint16 indexed chainId);
52: 
56:     event ExecuteRemoteProposal(uint16 indexed remoteChainId, uint256 proposalId, bytes payload);
57: 
61:     event ClearPayload(uint256 indexed proposalId, bytes32 executionHash);
62: 
66:     event StorePayload(
67:         uint256 indexed proposalId,
68:         uint16 indexed remoteChainId,
69:         bytes payload,
70:         bytes adapterParams,
71:         uint256 value,
72:         bytes reason
73:     );
74:     
77:     event FallbackWithdraw(address indexed receiver, uint256 value);
78: 
79:     constructor(
80:         ILayerZeroEndpoint lzEndpoint_,
81:         address accessControlManager_
82:     ) BaseOmnichainControllerSrc(accessControlManager_) {
83:         ensureNonzeroAddress(address(lzEndpoint_));
84:         LZ_ENDPOINT = lzEndpoint_;
85:     }
86: 
96:     function estimateFees(
97:         uint16 remoteChainId_,
98:         bytes calldata payload_,
99:         bytes calldata adapterParams_
100:     ) external view returns (uint256, uint256) {
101:         return LZ_ENDPOINT.estimateFees(remoteChainId_, address(this), payload_, false, adapterParams_);
102:     }
103: 
110:     function removeTrustedRemote(uint16 remoteChainId_) external {
111:         _ensureAllowed("removeTrustedRemote(uint16)");
112:         delete trustedRemoteLookup[remoteChainId_];
113:         emit TrustedRemoteRemoved(remoteChainId_);

['4']

2: 
3: pragma solidity 0.8.13;
4: import "@openzeppelin/contracts/access/AccessControl.sol"; // <= FOUND
5: import "./IAccessControlManagerV8.sol"; // <= FOUND
6: 
53: contract AccessControlManager is AccessControl, IAccessControlManagerV8 {
54:     
57:     event PermissionGranted(address account, address contractAddress, string functionSig);
58: 
60:     event PermissionRevoked(address account, address contractAddress, string functionSig);
61: 
62:     constructor() {
63:         
64:         
65:         _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
66:     }
67: 
78:     function giveCallPermission(address contractAddress, string calldata functionSig, address accountToPermit) public {
79:         bytes32 role = keccak256(abi.encodePacked(contractAddress, functionSig));
80:         grantRole(role, accountToPermit);
81:         emit PermissionGranted(accountToPermit, contractAddress, functionSig);
82:     }
83: 
92:     function revokeCallPermission(
93:         address contractAddress,
94:         string calldata functionSig,
95:         address accountToRevoke
96:     ) public {
97:         bytes32 role = keccak256(abi.encodePacked(contractAddress, functionSig));
98:         revokeRole(role, accountToRevoke);
99:         emit PermissionRevoked(accountToRevoke, contractAddress, functionSig);
100:     }
101: 
110:     function isAllowedToCall(address account, string calldata functionSig) public view returns (bool) {
111:         bytes32 role = keccak256(abi.encodePacked(msg.sender, functionSig));
112: 
113:         if (hasRole(role, account)) {
114:             return true;
115:         } else {
116:             role = keccak256(abi.encodePacked(address(0), functionSig));
117:             return hasRole(role, account);
118:         }
119:     }
120: 
129:     function hasPermission(
130:         address account,
131:         address contractAddress,
132:         string calldata functionSig
133:     ) public view returns (bool) {
134:         bytes32 role = keccak256(abi.encodePacked(contractAddress, functionSig));
135:         return hasRole(role, account);
136:     }
137: }

['5']

2: 
3: pragma solidity 0.8.13;
4: 
5: import { AccessControlledV8 } from "../Governance/AccessControlledV8.sol"; // <= FOUND
6: import { IOmnichainGovernanceExecutor } from "./interfaces/IOmnichainGovernanceExecutor.sol"; // <= FOUND
7: 
17: contract OmnichainExecutorOwner is AccessControlledV8 {
18:     
21:     IOmnichainGovernanceExecutor public immutable OMNICHAIN_GOVERNANCE_EXECUTOR;
22: 
26:     mapping(bytes4 => string) public functionRegistry;
27: 
31:     event FunctionRegistryChanged(string indexed signature, bool active);
32: 
34:     constructor(address omnichainGovernanceExecutor_) {
35:         require(omnichainGovernanceExecutor_ != address(0), "Address must not be zero");
36:         OMNICHAIN_GOVERNANCE_EXECUTOR = IOmnichainGovernanceExecutor(omnichainGovernanceExecutor_);
37:         _disableInitializers();
38:     }
39: 
44:     function initialize(address accessControlManager_) external initializer {
45:         require(accessControlManager_ != address(0), "Address must not be zero");
46:         __AccessControlled_init(accessControlManager_);
47:     }
48: 
55:     fallback(bytes calldata data_) external returns (bytes memory) {
56:         string memory fun = functionRegistry[msg.sig];
57:         require(bytes(fun).length != 0, "Function not found");
58:         _checkAccessAllowed(fun);
59:         (bool ok, bytes memory res) = address(OMNICHAIN_GOVERNANCE_EXECUTOR).call(data_);
60:         require(ok, "call failed");
61:         return res;
62:     }
63: 
70:     function upsertSignature(string[] calldata signatures_, bool[] calldata active_) external onlyOwner {
71:         uint256 signatureLength = signatures_.length;
72:         require(signatureLength == active_.length, "Input arrays must have the same length");
73:         for (uint256 i; i < signatureLength; ) {
74:             bytes4 sigHash = bytes4(keccak256(bytes(signatures_[i])));
75:             bytes memory signature = bytes(functionRegistry[sigHash]);
76:             if (active_[i] && signature.length == 0) {
77:                 functionRegistry[sigHash] = signatures_[i];
78:                 emit FunctionRegistryChanged(signatures_[i], true);
79:             } else if (!active_[i] && signature.length != 0) {
80:                 delete functionRegistry[sigHash];
81:                 emit FunctionRegistryChanged(signatures_[i], false);
82:             }
83:             unchecked {
84:                 ++i;
85:             }
86:         }
87:     }
88: 
95:     function transferBridgeOwnership(address newOwner_) external {

['7']

3: 
5: pragma solidity 0.8.13;
6: 
7: import { Pausable } from "@openzeppelin/contracts/security/Pausable.sol"; // <= FOUND
8: import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; // <= FOUND
9: import { ensureNonzeroAddress } from "@venusprotocol/solidity-utilities/contracts/validators.sol"; // <= FOUND
10: import { IAccessControlManagerV8 } from "./../Governance/IAccessControlManagerV8.sol"; // <= FOUND
11: 
19: contract BaseOmnichainControllerSrc is Ownable, Pausable {
20:     
23:     address public accessControlManager;
24: 
28:     mapping(uint16 => uint256) public chainIdToMaxDailyLimit;
29: 
33:     mapping(uint16 => uint256) public chainIdToLast24HourCommandsSent;
34: 
38:     mapping(uint16 => uint256) public chainIdToLast24HourWindowStart;
39:     
42:     mapping(uint16 => uint256) public chainIdToLastProposalSentTimestamp;
43: 
47:     event SetMaxDailyLimit(uint16 indexed chainId, uint256 oldMaxLimit, uint256 newMaxLimit);
48:     
51:     event NewAccessControlManager(address indexed oldAccessControlManager, address indexed newAccessControlManager);
52: 
53:     constructor(address accessControlManager_) {
54:         ensureNonzeroAddress(accessControlManager_);
55:         accessControlManager = accessControlManager_;
56:     }
57: 
65:     function setMaxDailyLimit(uint16 chainId_, uint256 limit_) external {
66:         _ensureAllowed("setMaxDailyLimit(uint16,uint256)");
67:         emit SetMaxDailyLimit(chainId_, chainIdToMaxDailyLimit[chainId_], limit_);
68:         chainIdToMaxDailyLimit[chainId_] = limit_;
69:     }
70: 
75:     function pause() external {
76:         _ensureAllowed("pause()");
77:         _pause();
78:     }
79: 
84:     function unpause() external {
85:         _ensureAllowed("unpause()");
86:         _unpause();
87:     }
88: 
95:     function setAccessControlManager(address accessControlManager_) external onlyOwner {
96:         ensureNonzeroAddress(accessControlManager_);
97:         emit NewAccessControlManager(accessControlManager, accessControlManager_);
98:         accessControlManager = accessControlManager_;
99:     }
100: 
104:     function renounceOwnership() public override {}
105: 
111:     function _isEligibleToSend(uint16 dstChainId_, uint256 noOfCommands_) internal {
112:         
113:         uint256 currentBlockTimestamp = block.timestamp;
114:         uint256 lastDayWindowStart = chainIdToLast24HourWindowStart[dstChainId_];

['5']

2: 
3: pragma solidity 0.8.13;
4: 
5: import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol"; // <= FOUND
6: import { BytesLib } from "@layerzerolabs/solidity-examples/contracts/libraries/BytesLib.sol"; // <= FOUND
7: import { ExcessivelySafeCall } from "@layerzerolabs/solidity-examples/contracts/libraries/ExcessivelySafeCall.sol"; // <= FOUND
8: import { ensureNonzeroAddress } from "@venusprotocol/solidity-utilities/contracts/validators.sol"; // <= FOUND
9: import { BaseOmnichainControllerDest } from "./BaseOmnichainControllerDest.sol"; // <= FOUND
10: import { ITimelock } from "./interfaces/ITimelock.sol"; // <= FOUND
11: 
20: contract OmnichainGovernanceExecutor is ReentrancyGuard, BaseOmnichainControllerDest {
21:     using BytesLib for bytes;
22:     using ExcessivelySafeCall for address;
23: 
24:     enum ProposalType {
25:         NORMAL,
26:         FASTTRACK,
27:         CRITICAL
28:     }
29: 
30:     struct Proposal {
31:         
32:         uint256 id;
33:         
34:         uint256 eta;
35:         
36:         address[] targets;
37:         
38:         uint256[] values;
39:         
40:         string[] signatures;
41:         
42:         bytes[] calldatas;
43:         
44:         bool canceled;
45:         
46:         bool executed;
47:         
48:         uint8 proposalType;
49:     }
50:     
52:     enum ProposalState {
53:         Canceled,
54:         Queued,
55:         Executed
56:     }
57: 
61:     address public immutable GUARDIAN;
62: 
66:     uint16 public srcChainId;
67: 
71:     uint256 public lastProposalReceived;
72: 
76:     mapping(uint256 => Proposal) public proposals;
77: 
81:     mapping(uint256 => ITimelock) public proposalTimelocks;
82: 
86:     mapping(uint256 => bool) public queued;

['5']

2: 
3: pragma solidity 0.8.13;
4: 
5: import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; // <= FOUND
6: import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; // <= FOUND
7: 
8: import "./IAccessControlManagerV8.sol"; // <= FOUND
9: 
16: abstract contract AccessControlledV8 is Initializable, Ownable2StepUpgradeable {
17:     
18:     IAccessControlManagerV8 private _accessControlManager;
19: 
25:     uint256[49] private __gap;
26: 
28:     event NewAccessControlManager(address oldAccessControlManager, address newAccessControlManager);
29: 
31:     error Unauthorized(address sender, address calledContract, string methodSignature);
32: 
33:     function __AccessControlled_init(address accessControlManager_) internal onlyInitializing {
34:         __Ownable2Step_init();
35:         __AccessControlled_init_unchained(accessControlManager_);
36:     }
37: 
38:     function __AccessControlled_init_unchained(address accessControlManager_) internal onlyInitializing {
39:         _setAccessControlManager(accessControlManager_);
40:     }
41: 
49:     function setAccessControlManager(address accessControlManager_) external onlyOwner {
50:         _setAccessControlManager(accessControlManager_);
51:     }
52: 
56:     function accessControlManager() external view returns (IAccessControlManagerV8) {
57:         return _accessControlManager;
58:     }
59: 
64:     function _setAccessControlManager(address accessControlManager_) internal {
65:         require(address(accessControlManager_) != address(0), "invalid acess control manager address");
66:         address oldAccessControlManager = address(_accessControlManager);
67:         _accessControlManager = IAccessControlManagerV8(accessControlManager_);
68:         emit NewAccessControlManager(oldAccessControlManager, accessControlManager_);
69:     }
70: 
75:     function _checkAccessAllowed(string memory signature) internal view {
76:         bool isAllowedToCall = _accessControlManager.isAllowedToCall(msg.sender, signature);
77: 
78:         if (!isAllowedToCall) {
79:             revert Unauthorized(msg.sender, address(this), signature);
80:         }
81:     }
82: }
83: 

[NonCritical-35] Multiple mappings can be replaced with a single struct mapping

Resolution

Using a single struct mapping in place of multiple defined mappings in a Solidity contract can lead to improved code organization, better readability, and easier maintainability. By consolidating related data into a single struct, developers can create a more cohesive data structure that logically groups together relevant pieces of information, thus reducing redundancy and clutter. This approach simplifies the codebase, making it easier to understand, navigate, and modify. Additionally, it can result in more efficient gas usage when accessing or updating multiple related data points simultaneously.

Num of instances: 8

Findings

Click to show findings

['26']

17: contract BaseOmnichainControllerSrc is Ownable, Pausable {
18:     
21:     address public accessControlManager;
22: 
26:     mapping(uint16 => uint256) public chainIdToMaxDailyLimit; // <= FOUND
27: 
31:     mapping(uint16 => uint256) public chainIdToLast24HourCommandsSent; // <= FOUND
32: 
36:     mapping(uint16 => uint256) public chainIdToLast24HourWindowStart; // <= FOUND
37:     
40:     mapping(uint16 => uint256) public chainIdToLastProposalSentTimestamp; // <= FOUND
41: 
99: }

['53']

4: contract GovernorAlpha {
5:     
6:     string public constant name = "Venus Governor Alpha";
7: 
24:     TimelockInterface public timelock;
25: 
27:     XVSInterface public xvs;
28: 
30:     address public guardian;
31: 
33:     uint public proposalCount;
34: 
41:     enum ProposalState {
42:         Pending,
43:         Active,
44:         Canceled,
45:         Defeated,
46:         Succeeded,
47:         Queued,
48:         Expired,
49:         Executed
50:     }
51: 
53:     mapping(uint => Proposal) public proposals; // <= FOUND
54: 
56:     mapping(address => uint) public latestProposalIds; // <= FOUND
57: 
59:     bytes32 public constant DOMAIN_TYPEHASH =
60:         keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)");
61: 
63:     bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,bool support)");
64: 
117: }

['53']

4: contract GovernorAlpha2 {
5:     
6:     string public constant name = "Venus Governor Alpha";
7: 
24:     TimelockInterface public timelock;
25: 
27:     XVSInterface public xvs;
28: 
30:     address public guardian;
31: 
33:     uint public proposalCount;
34: 
41:     enum ProposalState {
42:         Pending,
43:         Active,
44:         Canceled,
45:         Defeated,
46:         Succeeded,
47:         Queued,
48:         Expired,
49:         Executed
50:     }
51: 
53:     mapping(uint => Proposal) public proposals; // <= FOUND
54: 
56:     mapping(address => uint) public latestProposalIds; // <= FOUND
57: 
59:     bytes32 public constant DOMAIN_TYPEHASH =
60:         keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)");
61: 
63:     bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,bool support)");
64: 
117: }

['111']

88: contract GovernorBravoDelegateStorageV1 is GovernorBravoDelegatorStorage {
89:     
90:     uint public votingDelay;
91: 
93:     uint public votingPeriod;
94: 
96:     uint public proposalThreshold;
97: 
99:     uint public initialProposalId;
100: 
102:     uint public proposalCount;
103: 
105:     TimelockInterface public timelock;
106: 
108:     XvsVaultInterface public xvsVault;
109: 
111:     mapping(uint => Proposal) public proposals; // <= FOUND
112: 
114:     mapping(address => uint) public latestProposalIds; // <= FOUND
115: 
122:     enum ProposalState {
123:         Pending,
124:         Active,
125:         Canceled,
126:         Defeated,
127:         Succeeded,
128:         Queued,
129:         Expired,
130:         Executed
131:     }
132: 
134:     uint public proposalMaxOperations;
135: 
137:     address public guardian;
138: }

['111']

88: contract GovernorBravoDelegateStorageV1 is GovernorBravoDelegatorStorage {
89:     
90:     uint public votingDelay;
91: 
93:     uint public votingPeriod;
94: 
96:     uint public proposalThreshold;
97: 
99:     uint public initialProposalId;
100: 
102:     uint public proposalCount;
103: 
105:     TimelockInterface public timelock;
106: 
108:     XvsVaultInterface public xvsVault;
109: 
111:     mapping(uint => Proposal) public proposals; // <= FOUND
112: 
114:     mapping(address => uint) public latestProposalIds; // <= FOUND
115: 
122:     enum ProposalState {
123:         Pending,
124:         Active,
125:         Canceled,
126:         Defeated,
127:         Succeeded,
128:         Queued,
129:         Expired,
130:         Executed
131:     }
132: 
134:     uint public proposalMaxOperations;
135: 
137:     address public guardian;
138: }

['196']

186: contract GovernorBravoDelegateStorageV2 is GovernorBravoDelegateStorageV1 {
187:     enum ProposalType {
188:         NORMAL,
189:         FASTTRACK,
190:         CRITICAL
191:     }
192: 
196:     mapping(uint => ProposalConfig) public proposalConfigs; // <= FOUND
197: 
199:     mapping(uint => TimelockInterface) public proposalTimelocks; // <= FOUND
200: }

['56']

19: contract OmnichainGovernanceExecutor is ReentrancyGuard, BaseOmnichainControllerDest {
20:     using BytesLib for bytes;
21:     using ExcessivelySafeCall for address;
22: 
23:     enum ProposalType {
24:         NORMAL,
25:         FASTTRACK,
26:         CRITICAL
27:     }
28: 
32:     enum ProposalState {
33:         Canceled,
34:         Queued,
35:         Executed
36:     }
37: 
41:     address public immutable GUARDIAN;
42: 
46:     uint16 public srcChainId;
47: 
51:     uint256 public lastProposalReceived;
52: 
56:     mapping(uint256 => Proposal) public proposals; // <= FOUND
57: 
61:     mapping(uint256 => ITimelock) public proposalTimelocks; // <= FOUND
62: 
66:     mapping(uint256 => bool) public queued; // <= FOUND
67: 
182: }

['29']

19: contract OmnichainProposalSender is ReentrancyGuard, BaseOmnichainControllerSrc {
20:     
23:     uint256 public proposalCount;
24: 
29:     mapping(uint256 => bytes32) public storedExecutionHashes; // <= FOUND
30: 
34:     ILayerZeroEndpoint public immutable LZ_ENDPOINT;
35: 
39:     mapping(uint16 => bytes) public trustedRemoteLookup; // <= FOUND
40: 
165: }

[NonCritical-36] Constants should be on the left side of the comparison

Resolution

Putting constants on the left side of a comparison operator like == or < is a best practice known as "Yoda conditions", which can help prevent accidental assignment instead of comparison. In some programming languages, if a variable is mistakenly put on the left with a single = instead of ==, it assigns the constant's value to the variable without any compiler error. However, doing this with the constant on the left would generate an error, as constants cannot be assigned values. Although Solidity's static typing system prevents accidental assignments within conditionals, adopting this practice enhances code readability and consistency, especially when developers are working across multiple languages that support this convention.

Num of instances: 5

Findings

Click to show findings

['164']

164:         if (latestProposalId != 0)  // <= FOUND

['164']

164:         if (latestProposalId != 0)  // <= FOUND

['363']

363:         if (support == 0)  // <= FOUND

['88']

88:         if (a == 0)  // <= FOUND

['235']

235:         if (bytes(signature).length == 0)  // <= FOUND

[NonCritical-37] Interface names should have an I as the first character

Resolution

Modify such instances to include a capital I as the first character in the name to signify it is an interface. This improved readability during in

Num of instances: 5

Findings

Click to show findings

['391']

391: interface TimelockInterface 

['425']

425: interface XVSInterface 

['391']

391: interface TimelockInterface 

['248']

248: interface XvsVaultInterface 

['252']

252: interface GovernorAlphaInterface 

[NonCritical-38] Initialize functions do not emit an event

Resolution

Emitting an event within initializer functions in Solidity is a best practice for providing transparency and traceability. Initializer functions set the initial state and values of an upgradeable contract. Emitting an event during initialization allows anyone to verify and audit the initial state of the contract via the transaction logs. This can be particularly useful for verifying the parameters set during initialization, tracking the contract's deployment, and troubleshooting or debugging. Therefore, developers should include an event emission in their initializer functions, providing a clear record of the contract's initialization and enhancing the contract's transparency and security.

Num of instances: 3

Findings

Click to show findings

['111']

111:     function initialize( // <= FOUND
112:         address xvsVault_,
113:         ProposalConfig[] memory proposalConfigs_,
114:         TimelockInterface[] memory timelocks,
115:         address guardian_
116:     ) public {
117:         require(address(proposalTimelocks[0]) == address(0), "GovernorBravo::initialize: cannot initialize twice");
118:         require(msg.sender == admin, "GovernorBravo::initialize: admin only");
119:         require(xvsVault_ != address(0), "GovernorBravo::initialize: invalid xvs address");
120:         require(guardian_ != address(0), "GovernorBravo::initialize: invalid guardian");
121:         require(
122:             timelocks.length == uint8(ProposalType.CRITICAL) + 1,
123:             "GovernorBravo::initialize:number of timelocks should match number of governance routes"
124:         );
125:         require(
126:             proposalConfigs_.length == uint8(ProposalType.CRITICAL) + 1,
127:             "GovernorBravo::initialize:number of proposal configs should match number of governance routes"
128:         );
129: 
130:         xvsVault = XvsVaultInterface(xvsVault_);
131:         proposalMaxOperations = 10;
132:         guardian = guardian_;
133: 
134:         
135:         uint256 arrLength = proposalConfigs_.length;
136:         for (uint256 i; i < arrLength; ++i) {
137:             require(
138:                 proposalConfigs_[i].votingPeriod >= MIN_VOTING_PERIOD,
139:                 "GovernorBravo::initialize: invalid min voting period"
140:             );
141:             require(
142:                 proposalConfigs_[i].votingPeriod <= MAX_VOTING_PERIOD,
143:                 "GovernorBravo::initialize: invalid max voting period"
144:             );
145:             require(
146:                 proposalConfigs_[i].votingDelay >= MIN_VOTING_DELAY,
147:                 "GovernorBravo::initialize: invalid min voting delay"
148:             );
149:             require(
150:                 proposalConfigs_[i].votingDelay <= MAX_VOTING_DELAY,
151:                 "GovernorBravo::initialize: invalid max voting delay"
152:             );
153:             require(
154:                 proposalConfigs_[i].proposalThreshold >= MIN_PROPOSAL_THRESHOLD,
155:                 "GovernorBravo::initialize: invalid min proposal threshold"
156:             );
157:             require(
158:                 proposalConfigs_[i].proposalThreshold <= MAX_PROPOSAL_THRESHOLD,
159:                 "GovernorBravo::initialize: invalid max proposal threshold"
160:             );

['51']

51:     function initialize( // <= FOUND
52:         address timelock_,
53:         address xvsVault_,
54:         uint votingPeriod_,
55:         uint votingDelay_,
56:         uint proposalThreshold_,
57:         address guardian_
58:     ) public {
59:         require(address(timelock) == address(0), "GovernorBravo::initialize: can only initialize once");
60:         require(msg.sender == admin, "GovernorBravo::initialize: admin only");
61:         require(timelock_ != address(0), "GovernorBravo::initialize: invalid timelock address");
62:         require(xvsVault_ != address(0), "GovernorBravo::initialize: invalid xvs address");
63:         require(
64:             votingPeriod_ >= MIN_VOTING_PERIOD && votingPeriod_ <= MAX_VOTING_PERIOD,
65:             "GovernorBravo::initialize: invalid voting period"
66:         );
67:         require(
68:             votingDelay_ >= MIN_VOTING_DELAY && votingDelay_ <= MAX_VOTING_DELAY,
69:             "GovernorBravo::initialize: invalid voting delay"
70:         );
71:         require(
72:             proposalThreshold_ >= MIN_PROPOSAL_THRESHOLD && proposalThreshold_ <= MAX_PROPOSAL_THRESHOLD,
73:             "GovernorBravo::initialize: invalid proposal threshold"
74:         );
75:         require(guardian_ != address(0), "GovernorBravo::initialize: invalid guardian");
76: 
77:         timelock = TimelockInterface(timelock_);
78:         xvsVault = XvsVaultInterface(xvsVault_);
79:         votingPeriod = votingPeriod_;
80:         votingDelay = votingDelay_;
81:         proposalThreshold = proposalThreshold_;
82:         proposalMaxOperations = 10;
83:         guardian = guardian_;
84:     }

['43']

43:     function initialize(address accessControlManager_) external initializer { // <= FOUND
44:         require(accessControlManager_ != address(0), "Address must not be zero");
45:         __AccessControlled_init(accessControlManager_);
46:     }

[NonCritical-39] Both immutable and constant state variables should be CONSTANT_CASE

Resolution

Make found instants CAPITAL_CASE

Num of instances: 3

Findings

Click to show findings

['6']

6: string public constant name = "Venus Governor Alpha"; // <= FOUND

['13']

13: string public constant name = "Venus Governor Bravo"; // <= FOUND

['34']

34: uint public constant quorumVotes = 600000e18;  // <= FOUND

[NonCritical-40] Consider using named mappings

Resolution

In Solidity version 0.8.18 and beyond mapping parameters can be named. This makes the purpose and function of a given mapping far clearer which in turn improves readability.

Num of instances: 10

Findings

Click to show findings

['85']

85:     mapping(uint256 => bool) public queued; // <= FOUND

['71']

71:     mapping(bytes32 => bool) public queuedTransactions; // <= FOUND

['25']

25:     mapping(bytes4 => string) public functionRegistry; // <= FOUND

['39']

39:     mapping(uint16 => bytes) public trustedRemoteLookup; // <= FOUND

['102']

102:     mapping(address => uint) public latestProposalIds; // <= FOUND

['29']

29:     mapping(uint256 => bytes32) public storedExecutionHashes; // <= FOUND

['26']

26:     mapping(uint16 => uint256) public chainIdToMaxDailyLimit; // <= FOUND

['31']

31:     mapping(uint16 => uint256) public chainIdToLast24HourCommandsSent; // <= FOUND

['36']

36:     mapping(uint16 => uint256) public chainIdToLast24HourWindowStart; // <= FOUND

['40']

40:     mapping(uint16 => uint256) public chainIdToLastProposalSentTimestamp; // <= FOUND

[NonCritical-41] Use a single file for system wide constants

Num of instances: 6

Findings

Click to show findings

['6']

4: contract GovernorAlpha {
5:     
6:     string public constant name = "Venus Governor Alpha"; // <= FOUND
59:     bytes32 public constant DOMAIN_TYPEHASH = // <= FOUND

63:     bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,bool support)"); // <= FOUND

['6']

4: contract GovernorAlpha2 {
5:     
6:     string public constant name = "Venus Governor Alpha"; // <= FOUND
59:     bytes32 public constant DOMAIN_TYPEHASH = // <= FOUND

63:     bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,bool support)"); // <= FOUND

['75']

73: contract GovernorBravoDelegate is GovernorBravoDelegateStorageV2, GovernorBravoEvents {
74:     
75:     string public constant name = "Venus Governor Bravo"; // <= FOUND
78:     uint public constant MIN_PROPOSAL_THRESHOLD = 150000e18;  // <= FOUND

81:     uint public constant MAX_PROPOSAL_THRESHOLD = 300000e18;  // <= FOUND

84:     uint public constant MIN_VOTING_PERIOD = 20 * 60 * 3;  // <= FOUND

87:     uint public constant MAX_VOTING_PERIOD = 20 * 60 * 24 * 14;  // <= FOUND

90:     uint public constant MIN_VOTING_DELAY = 1; // <= FOUND

93:     uint public constant MAX_VOTING_DELAY = 20 * 60 * 24 * 7;  // <= FOUND

96:     uint public constant quorumVotes = 600000e18;  // <= FOUND

99:     bytes32 public constant DOMAIN_TYPEHASH = // <= FOUND

103:     bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,uint8 support)"); // <= FOUND

['13']

11: contract GovernorBravoDelegateV1 is GovernorBravoDelegateStorageV1, GovernorBravoEventsV1 {
12:     
13:     string public constant name = "Venus Governor Bravo"; // <= FOUND
16:     uint public constant MIN_PROPOSAL_THRESHOLD = 150000e18;  // <= FOUND

19:     uint public constant MAX_PROPOSAL_THRESHOLD = 300000e18;  // <= FOUND

22:     uint public constant MIN_VOTING_PERIOD = 20 * 60 * 3;  // <= FOUND

25:     uint public constant MAX_VOTING_PERIOD = 20 * 60 * 24 * 14;  // <= FOUND

28:     uint public constant MIN_VOTING_DELAY = 1; // <= FOUND

31:     uint public constant MAX_VOTING_DELAY = 20 * 60 * 24 * 7;  // <= FOUND

34:     uint public constant quorumVotes = 600000e18;  // <= FOUND

37:     bytes32 public constant DOMAIN_TYPEHASH = // <= FOUND

41:     bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,uint8 support)"); // <= FOUND

['31']

10: contract Timelock {
11:     using SafeMath for uint;
12:     
31:     uint public constant GRACE_PERIOD = 14 days; // <= FOUND

34:     uint public constant MINIMUM_DELAY = 1 hours; // <= FOUND

37:     uint public constant MAXIMUM_DELAY = 30 days; // <= FOUND

['32']

12: contract TimelockV8 {
13:     
32:     uint256 private constant DEFAULT_GRACE_PERIOD = 14 days; // <= FOUND
35:     uint256 private constant DEFAULT_MINIMUM_DELAY = 1 hours; // <= FOUND

38:     uint256 private constant DEFAULT_MAXIMUM_DELAY = 30 days; // <= FOUND

[NonCritical-42] Use of non-named numeric constants

Resolution

Magic numbers should be avoided in Solidity code to enhance readability, maintainability, and reduce the likelihood of errors. Magic numbers are hard-coded values with no clear meaning or context, which can create confusion and make the code harder to understand for developers. Using well-defined constants or variables with descriptive names instead of magic numbers not only clarifies the purpose and significance of the value but also simplifies code updates and modifications.

Num of instances: 7

Findings

Click to show findings

['20']

20:         return 10; // <= FOUND

['278']

278:         (bool success, bytes memory reason) = address(this).excessivelySafeCall(
279:             gasleft() - gasToStoreAndEmit,
280:             150, // <= FOUND
281:             abi.encodeCall(this.nonblockingLzReceive, (srcChainId_, srcAddress_, nonce_, payload_))
282:         );

['30']

30:         return (60 * 60 * 24 * 3) / 3; // <= FOUND

['367']

367:         } else if (support == 2) { // <= FOUND

['15']

15:         return 300000e18; // <= FOUND

['20']

20:         return 30; // <= FOUND

['10']

10:         return 600000e18; // <= FOUND

[NonCritical-43] Redundant else statement

Num of instances: 6

Findings

Click to show findings

['109']

109:     function isAllowedToCall(address account, string calldata functionSig) public view returns (bool) { // <= FOUND
110:         bytes32 role = keccak256(abi.encodePacked(msg.sender, functionSig));
111: 
112:         if (hasRole(role, account)) {
113:             return true;
114:         } else {
115:             role = keccak256(abi.encodePacked(address(0), functionSig));
116:             return hasRole(role, account);
117:         }
118:     }

['294']

294:     function state(uint proposalId) public view returns (ProposalState) { // <= FOUND
295:         require(proposalCount >= proposalId && proposalId > 0, "GovernorAlpha::state: invalid proposal id");
296:         Proposal storage proposal = proposals[proposalId];
297:         if (proposal.canceled) {
298:             return ProposalState.Canceled;
299:         } else if (block.number <= proposal.startBlock) {
300:             return ProposalState.Pending;
301:         } else if (block.number <= proposal.endBlock) {
302:             return ProposalState.Active;
303:         } else if (proposal.forVotes <= proposal.againstVotes || proposal.forVotes < quorumVotes()) {
304:             return ProposalState.Defeated;
305:         } else if (proposal.eta == 0) {
306:             return ProposalState.Succeeded;
307:         } else if (proposal.executed) {
308:             return ProposalState.Executed;
309:         } else if (block.timestamp >= add256(proposal.eta, timelock.GRACE_PERIOD())) {
310:             return ProposalState.Expired;
311:         } else {
312:             return ProposalState.Queued;
313:         }
314:     }

['294']

294:     function state(uint proposalId) public view returns (ProposalState) { // <= FOUND
295:         require(proposalCount >= proposalId && proposalId > 0, "GovernorAlpha::state: invalid proposal id");
296:         Proposal storage proposal = proposals[proposalId];
297:         if (proposal.canceled) {
298:             return ProposalState.Canceled;
299:         } else if (block.number <= proposal.startBlock) {
300:             return ProposalState.Pending;
301:         } else if (block.number <= proposal.endBlock) {
302:             return ProposalState.Active;
303:         } else if (proposal.forVotes <= proposal.againstVotes || proposal.forVotes < quorumVotes()) {
304:             return ProposalState.Defeated;
305:         } else if (proposal.eta == 0) {
306:             return ProposalState.Succeeded;
307:         } else if (proposal.executed) {
308:             return ProposalState.Executed;
309:         } else if (block.timestamp >= add256(proposal.eta, timelock.GRACE_PERIOD())) {
310:             return ProposalState.Expired;
311:         } else {
312:             return ProposalState.Queued;
313:         }
314:     }

['384']

384:     function state(uint proposalId) public view returns (ProposalState) { // <= FOUND
385:         require(
386:             proposalCount >= proposalId && proposalId > initialProposalId,
387:             "GovernorBravo::state: invalid proposal id"
388:         );
389:         Proposal storage proposal = proposals[proposalId];
390:         if (proposal.canceled) {
391:             return ProposalState.Canceled;
392:         } else if (block.number <= proposal.startBlock) {
393:             return ProposalState.Pending;
394:         } else if (block.number <= proposal.endBlock) {
395:             return ProposalState.Active;
396:         } else if (proposal.forVotes <= proposal.againstVotes || proposal.forVotes < quorumVotes) {
397:             return ProposalState.Defeated;
398:         } else if (proposal.eta == 0) {
399:             return ProposalState.Succeeded;
400:         } else if (proposal.executed) {
401:             return ProposalState.Executed;
402:         } else if (
403:             block.timestamp >= add256(proposal.eta, proposalTimelocks[uint8(proposal.proposalType)].GRACE_PERIOD())
404:         ) {
405:             return ProposalState.Expired;
406:         } else {
407:             return ProposalState.Queued;
408:         }
409:     }

['289']

289:     function state(uint proposalId) public view returns (ProposalState) { // <= FOUND
290:         require(
291:             proposalCount >= proposalId && proposalId > initialProposalId,
292:             "GovernorBravo::state: invalid proposal id"
293:         );
294:         Proposal storage proposal = proposals[proposalId];
295:         if (proposal.canceled) {
296:             return ProposalState.Canceled;
297:         } else if (block.number <= proposal.startBlock) {
298:             return ProposalState.Pending;
299:         } else if (block.number <= proposal.endBlock) {
300:             return ProposalState.Active;
301:         } else if (proposal.forVotes <= proposal.againstVotes || proposal.forVotes < quorumVotes) {
302:             return ProposalState.Defeated;
303:         } else if (proposal.eta == 0) {
304:             return ProposalState.Succeeded;
305:         } else if (proposal.executed) {
306:             return ProposalState.Executed;
307:         } else if (block.timestamp >= add256(proposal.eta, timelock.GRACE_PERIOD())) {
308:             return ProposalState.Expired;
309:         } else {
310:             return ProposalState.Queued;
311:         }
312:     }

['246']

246:     function state(uint256 proposalId_) public view returns (ProposalState) { // <= FOUND
247:         Proposal storage proposal = proposals[proposalId_];
248:         if (proposal.canceled) {
249:             return ProposalState.Canceled;
250:         } else if (proposal.executed) {
251:             return ProposalState.Executed;
252:         } else if (queued[proposalId_]) {
253:             
254:             return ProposalState.Queued;
255:         } else {
256:             revert InvalidProposalId();
257:         }
258:     }

[NonCritical-44] Inconsistent usage of int/uint with int256/uint256 in contract/abstract/library/interface

Resolution

Use uint256/int256 consistently in place of uint/int to prevent ambiguity rather than using both within the same contract/abstract/library/interface

Num of instances: 7

Findings

Click to show findings

['11']

11: contract GovernorBravoDelegatorV1 is GovernorBravoDelegatorStorage, GovernorBravoEventsV1 {
12:     constructor(
17:         uint votingPeriod_, // <= FOUND

18:         uint votingDelay_, // <= FOUND

19:         uint proposalThreshold_, // <= FOUND

28:                 "initialize(address,address,uint256,uint256,uint256,address)", // <= FOUND

['4']

4: contract GovernorAlpha {
5:     
43:     uint public proposalCount; // <= FOUND

47:         uint id; // <= FOUND

51:         uint eta; // <= FOUND

61:         uint startBlock; // <= FOUND

63:         uint endBlock; // <= FOUND

65:         uint forVotes; // <= FOUND

67:         uint againstVotes; // <= FOUND

99:     mapping(uint => Proposal) public proposals; // <= FOUND

106:         keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); // <= FOUND

109:     bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,bool support)"); // <= FOUND

113:         uint id, // <= FOUND

119:         uint startBlock, // <= FOUND

120:         uint endBlock, // <= FOUND

125:     event VoteCast(address voter, uint proposalId, bool support, uint votes); // <= FOUND

128:     event ProposalCanceled(uint id); // <= FOUND

131:     event ProposalQueued(uint id, uint eta); // <= FOUND

134:     event ProposalExecuted(uint id); // <= FOUND

162:         uint latestProposalId = latestProposalIds[msg.sender]; // <= FOUND

175:         uint startBlock = add256(block.number, votingDelay()); // <= FOUND

176:         uint endBlock = add256(startBlock, votingPeriod()); // <= FOUND

212:     function queue(uint proposalId) public { // <= FOUND

218:         uint eta = add256(block.timestamp, timelock.delay()); // <= FOUND

219:         for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND

226:     function _queueOrRevert(address target, uint value, string memory signature, bytes memory data, uint eta) internal { // <= FOUND

234:     function execute(uint proposalId) public payable { // <= FOUND

241:         for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND

253:     function cancel(uint proposalId) public { // <= FOUND

265:         for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND

279:         uint proposalId // <= FOUND

289:     function getReceipt(uint proposalId, address voter) public view returns (Receipt memory) { // <= FOUND

293:     function state(uint proposalId) public view returns (ProposalState) { // <= FOUND

315:     function castVote(uint proposalId, bool support) public { // <= FOUND

319:     function castVoteBySig(uint proposalId, bool support, uint8 v, bytes32 r, bytes32 s) public { // <= FOUND

330:     function _castVote(address voter, uint proposalId, bool support) internal { // <= FOUND

360:     function __queueSetTimelockPendingAdmin(address newPendingAdmin, uint eta) public { // <= FOUND

365:     function __executeSetTimelockPendingAdmin(address newPendingAdmin, uint eta) public { // <= FOUND

370:     function add256(uint256 a, uint256 b) internal pure returns (uint) { // <= FOUND

371:         uint c = a + b; // <= FOUND

376:     function sub256(uint256 a, uint256 b) internal pure returns (uint) { // <= FOUND

382:         uint chainId; // <= FOUND

['4']

4: contract GovernorAlpha2 {
5:     
43:     uint public proposalCount; // <= FOUND

47:         uint id; // <= FOUND

51:         uint eta; // <= FOUND

61:         uint startBlock; // <= FOUND

63:         uint endBlock; // <= FOUND

65:         uint forVotes; // <= FOUND

67:         uint againstVotes; // <= FOUND

99:     mapping(uint => Proposal) public proposals; // <= FOUND

106:         keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); // <= FOUND

109:     bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,bool support)"); // <= FOUND

113:         uint id, // <= FOUND

119:         uint startBlock, // <= FOUND

120:         uint endBlock, // <= FOUND

125:     event VoteCast(address voter, uint proposalId, bool support, uint votes); // <= FOUND

128:     event ProposalCanceled(uint id); // <= FOUND

131:     event ProposalQueued(uint id, uint eta); // <= FOUND

134:     event ProposalExecuted(uint id); // <= FOUND

136:     constructor(address timelock_, address xvs_, address guardian_, uint256 lastProposalId_) public { // <= FOUND

163:         uint latestProposalId = latestProposalIds[msg.sender]; // <= FOUND

176:         uint startBlock = add256(block.number, votingDelay()); // <= FOUND

177:         uint endBlock = add256(startBlock, votingPeriod()); // <= FOUND

213:     function queue(uint proposalId) public { // <= FOUND

219:         uint eta = add256(block.timestamp, timelock.delay()); // <= FOUND

220:         for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND

227:     function _queueOrRevert(address target, uint value, string memory signature, bytes memory data, uint eta) internal { // <= FOUND

235:     function execute(uint proposalId) public payable { // <= FOUND

242:         for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND

254:     function cancel(uint proposalId) public { // <= FOUND

266:         for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND

280:         uint proposalId // <= FOUND

290:     function getReceipt(uint proposalId, address voter) public view returns (Receipt memory) { // <= FOUND

294:     function state(uint proposalId) public view returns (ProposalState) { // <= FOUND

316:     function castVote(uint proposalId, bool support) public { // <= FOUND

320:     function castVoteBySig(uint proposalId, bool support, uint8 v, bytes32 r, bytes32 s) public { // <= FOUND

331:     function _castVote(address voter, uint proposalId, bool support) internal { // <= FOUND

361:     function __queueSetTimelockPendingAdmin(address newPendingAdmin, uint eta) public { // <= FOUND

366:     function __executeSetTimelockPendingAdmin(address newPendingAdmin, uint eta) public { // <= FOUND

371:     function add256(uint256 a, uint256 b) internal pure returns (uint) { // <= FOUND

372:         uint c = a + b; // <= FOUND

377:     function sub256(uint256 a, uint256 b) internal pure returns (uint) { // <= FOUND

383:         uint chainId; // <= FOUND

['73']

73: contract GovernorBravoDelegate is GovernorBravoDelegateStorageV2, GovernorBravoEvents {
74:     
78:     uint public constant MIN_PROPOSAL_THRESHOLD = 150000e18;  // <= FOUND

81:     uint public constant MAX_PROPOSAL_THRESHOLD = 300000e18;  // <= FOUND

84:     uint public constant MIN_VOTING_PERIOD = 20 * 60 * 3;  // <= FOUND

87:     uint public constant MAX_VOTING_PERIOD = 20 * 60 * 24 * 14;  // <= FOUND

90:     uint public constant MIN_VOTING_DELAY = 1; // <= FOUND

93:     uint public constant MAX_VOTING_DELAY = 20 * 60 * 24 * 7;  // <= FOUND

96:     uint public constant quorumVotes = 600000e18;  // <= FOUND

100:         keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); // <= FOUND

103:     bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,uint8 support)"); // <= FOUND

135:         uint256 arrLength = proposalConfigs_.length; // <= FOUND

136:         for (uint256 i; i < arrLength; ++i) { // <= FOUND

205:         uint latestProposalId = latestProposalIds[msg.sender]; // <= FOUND

218:         uint startBlock = add256(block.number, proposalConfigs[uint8(proposalType)].votingDelay); // <= FOUND

219:         uint endBlock = add256(startBlock, proposalConfigs[uint8(proposalType)].votingPeriod); // <= FOUND

262:     function queue(uint proposalId) external { // <= FOUND

268:         uint eta = add256(block.timestamp, proposalTimelocks[uint8(proposal.proposalType)].delay()); // <= FOUND

269:         for (uint i; i < proposal.targets.length; ++i) { // <= FOUND

285:         uint value, // <= FOUND

288:         uint eta, // <= FOUND

304:     function execute(uint proposalId) external { // <= FOUND

311:         for (uint i; i < proposal.targets.length; ++i) { // <= FOUND

327:     function cancel(uint proposalId) external { // <= FOUND

340:         for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND

359:         uint proposalId // <= FOUND

375:     function getReceipt(uint proposalId, address voter) external view returns (Receipt memory) { // <= FOUND

384:     function state(uint proposalId) public view returns (ProposalState) { // <= FOUND

416:     function castVote(uint proposalId, uint8 support) external { // <= FOUND

426:     function castVoteWithReason(uint proposalId, uint8 support, string calldata reason) external { // <= FOUND

439:     function castVoteBySig(uint proposalId, uint8 support, uint8 v, bytes32 r, bytes32 s) external { // <= FOUND

457:     function castVoteInternal(address voter, uint proposalId, uint8 support) internal returns (uint96) { // <= FOUND

503:         for (uint256 i; i < uint8(ProposalType.CRITICAL) + 1; ++i) { // <= FOUND

513:     function _setProposalMaxOperations(uint proposalMaxOperations_) external { // <= FOUND

515:         uint oldProposalMaxOperations = proposalMaxOperations; // <= FOUND

565:     function add256(uint256 a, uint256 b) internal pure returns (uint) { // <= FOUND

566:         uint c = a + b; // <= FOUND

571:     function sub256(uint256 a, uint256 b) internal pure returns (uint) { // <= FOUND

577:         uint chainId; // <= FOUND

['11']

11: contract GovernorBravoDelegateV1 is GovernorBravoDelegateStorageV1, GovernorBravoEventsV1 {
12:     
16:     uint public constant MIN_PROPOSAL_THRESHOLD = 150000e18;  // <= FOUND

19:     uint public constant MAX_PROPOSAL_THRESHOLD = 300000e18;  // <= FOUND

22:     uint public constant MIN_VOTING_PERIOD = 20 * 60 * 3;  // <= FOUND

25:     uint public constant MAX_VOTING_PERIOD = 20 * 60 * 24 * 14;  // <= FOUND

28:     uint public constant MIN_VOTING_DELAY = 1; // <= FOUND

31:     uint public constant MAX_VOTING_DELAY = 20 * 60 * 24 * 7;  // <= FOUND

34:     uint public constant quorumVotes = 600000e18;  // <= FOUND

38:         keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); // <= FOUND

41:     bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,uint8 support)"); // <= FOUND

54:         uint votingPeriod_, // <= FOUND

55:         uint votingDelay_, // <= FOUND

56:         uint proposalThreshold_, // <= FOUND

117:         uint latestProposalId = latestProposalIds[msg.sender]; // <= FOUND

130:         uint startBlock = add256(block.number, votingDelay); // <= FOUND

131:         uint endBlock = add256(startBlock, votingPeriod); // <= FOUND

172:     function queue(uint proposalId) external { // <= FOUND

178:         uint eta = add256(block.timestamp, timelock.delay()); // <= FOUND

179:         for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND

194:         uint value, // <= FOUND

197:         uint eta // <= FOUND

210:     function execute(uint proposalId) external { // <= FOUND

217:         for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND

233:     function cancel(uint proposalId) external { // <= FOUND

245:         for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND

264:         uint proposalId // <= FOUND

280:     function getReceipt(uint proposalId, address voter) external view returns (Receipt memory) { // <= FOUND

289:     function state(uint proposalId) public view returns (ProposalState) { // <= FOUND

319:     function castVote(uint proposalId, uint8 support) external { // <= FOUND

329:     function castVoteWithReason(uint proposalId, uint8 support, string calldata reason) external { // <= FOUND

337:     function castVoteBySig(uint proposalId, uint8 support, uint8 v, bytes32 r, bytes32 s) external { // <= FOUND

355:     function castVoteInternal(address voter, uint proposalId, uint8 support) internal returns (uint96) { // <= FOUND

395:     function _setVotingDelay(uint newVotingDelay) external { // <= FOUND

401:         uint oldVotingDelay = votingDelay; // <= FOUND

411:     function _setVotingPeriod(uint newVotingPeriod) external { // <= FOUND

417:         uint oldVotingPeriod = votingPeriod; // <= FOUND

428:     function _setProposalThreshold(uint newProposalThreshold) external { // <= FOUND

434:         uint oldProposalThreshold = proposalThreshold; // <= FOUND

458:     function _setProposalMaxOperations(uint proposalMaxOperations_) external { // <= FOUND

460:         uint oldProposalMaxOperations = proposalMaxOperations; // <= FOUND

510:     function add256(uint256 a, uint256 b) internal pure returns (uint) { // <= FOUND

511:         uint c = a + b; // <= FOUND

516:     function sub256(uint256 a, uint256 b) internal pure returns (uint) { // <= FOUND

522:         uint chainId; // <= FOUND

['11']

11: contract GovernorBravoDelegator is GovernorBravoDelegatorStorage, GovernorBravoEvents {
12:     constructor(
17:         uint votingPeriod_, // <= FOUND

18:         uint votingDelay_, // <= FOUND

19:         uint proposalThreshold_, // <= FOUND

28:                 "initialize(address,address,uint256,uint256,uint256,address)", // <= FOUND

['186']

186: contract GovernorBravoDelegateStorageV2 is GovernorBravoDelegateStorageV1 {
187:     enum ProposalType {
195:         uint256 votingDelay; // <= FOUND

197:         uint256 votingPeriod; // <= FOUND

199:         uint256 proposalThreshold; // <= FOUND

203:     mapping(uint => ProposalConfig) public proposalConfigs; // <= FOUND

206:     mapping(uint => TimelockInterface) public proposalTimelocks; // <= FOUND

[NonCritical-45] Use immutable not constant for keccak state variables

Resolution

It's crucial to leverage the right features for the appropriate contexts in Solidity, despite the compiler's ability to correct common developer mistakes. Both constant and immutable variables have distinct uses. Constant variables are best suited for hard-coded, literal values within your contract, where the value doesn't need to be computed or modified.

On the other hand, immutable variables are ideal for situations where the value might be the result of an expression or received from a constructor. While both serve to define unchanging variables, they function differently and their proper utilization contributes to clearer, more efficient code. Remember, even if the compiler can correct certain mistakes, best practices dictate using the correct feature for the task at hand.

Num of instances: 2

Findings

Click to show findings

['109']

109: bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,bool support)"); // <= FOUND

['41']

41: bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,uint8 support)"); // <= FOUND

[NonCritical-46] Consider adding emergency-stop functionality

Resolution

In the event of a security breach or any unforeseen emergency, swiftly suspending all protocol operations becomes crucial. Having a mechanism in place to halt all functions collectively, instead of pausing individual contracts separately, substantially enhances the efficiency of mitigating ongoing attacks or vulnerabilities. This not only quickens the response time to potential threats but also reduces operational stress during these critical periods. Therefore, consider integrating a 'circuit breaker' or 'emergency stop' function into the smart contract system architecture. Such a feature would provide the capability to suspend the entire protocol instantly, which could prove invaluable during a time-sensitive crisis management situation.

Num of instances: 19

Findings

Click to show findings

['12']

12: contract AccessControlledV5 

['52']

52: contract AccessControlManager is AccessControl, IAccessControlManagerV8 

['11']

11: contract GovernorBravoDelegatorV1 is GovernorBravoDelegatorStorage, GovernorBravoEventsV1 

['4']

4: contract GovernorAlpha 

['4']

4: contract GovernorAlpha2 

['73']

73: contract GovernorBravoDelegate is GovernorBravoDelegateStorageV2, GovernorBravoEvents 

['11']

11: contract GovernorBravoDelegateV1 is GovernorBravoDelegateStorageV1, GovernorBravoEventsV1 

['11']

11: contract GovernorBravoDelegator is GovernorBravoDelegatorStorage, GovernorBravoEvents 

['9']

9: contract GovernorBravoEvents 

['73']

73: contract GovernorBravoDelegatorStorage 

['88']

88: contract GovernorBravoDelegateStorageV1 is GovernorBravoDelegatorStorage 

['186']

186: contract GovernorBravoDelegateStorageV2 is GovernorBravoDelegateStorageV1 

['12']

12: contract GovernorBravoEventsV1 

['88']

88: contract GovernorBravoDelegateStorageV1 is GovernorBravoDelegatorStorage 

['16']

16: contract OmnichainExecutorOwner is AccessControlledV8 

['19']

19: contract OmnichainGovernanceExecutor is ReentrancyGuard, BaseOmnichainControllerDest 

['19']

19: contract OmnichainProposalSender is ReentrancyGuard, BaseOmnichainControllerSrc 

['15']

15: abstract contract AccessControlledV8 is Initializable, Ownable2StepUpgradeable 

['16']

16: library SafeMath 

[NonCritical-47] Employ Explicit Casting to Bytes or Bytes32 for Enhanced Code Clarity and Meaning

Resolution

Smart contracts are complex entities, and clarity in their operations is fundamental to ensure that they function as intended. Casting a single argument instead of utilizing 'abi.encodePacked()' improves the transparency of the operation. It elucidates the intent of the code, reducing ambiguity and making it easier for auditors and developers to understand the code’s purpose. Such practices promote readability and maintainability, thus reducing the likelihood of errors and misunderstandings. Therefore, it's recommended to employ explicit casts for single arguments where possible, to increase the contract's comprehensibility and ensure a smoother review process.

Num of instances: 8

Findings

Click to show findings

['78']

77:     function giveCallPermission(address contractAddress, string calldata functionSig, address accountToPermit) public {
78:         bytes32 role = keccak256(abi.encodePacked(contractAddress, functionSig)); // <= FOUND
79:         grantRole(role, accountToPermit);
80:         emit PermissionGranted(accountToPermit, contractAddress, functionSig);
81:     }

['96']

91:     function revokeCallPermission(
92:         address contractAddress,
93:         string calldata functionSig,
94:         address accountToRevoke
95:     ) public {
96:         bytes32 role = keccak256(abi.encodePacked(contractAddress, functionSig)); // <= FOUND
97:         revokeRole(role, accountToRevoke);
98:         emit PermissionRevoked(accountToRevoke, contractAddress, functionSig);
99:     }

['110']

109:     function isAllowedToCall(address account, string calldata functionSig) public view returns (bool) {
110:         bytes32 role = keccak256(abi.encodePacked(msg.sender, functionSig)); // <= FOUND
111: 
112:         if (hasRole(role, account)) {
113:             return true;
114:         } else {
115:             role = keccak256(abi.encodePacked(address(0), functionSig)); // <= FOUND
116:             return hasRole(role, account);
117:         }
118:     }

['133']

128:     function hasPermission(
129:         address account,
130:         address contractAddress,
131:         string calldata functionSig
132:     ) public view returns (bool) {
133:         bytes32 role = keccak256(abi.encodePacked(contractAddress, functionSig)); // <= FOUND
134:         return hasRole(role, account);
135:     }

['325']

320:     function castVoteBySig(uint proposalId, bool support, uint8 v, bytes32 r, bytes32 s) public {
321:         bytes32 domainSeparator = keccak256(
322:             abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainId(), address(this))
323:         );
324:         bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));
325:         bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); // <= FOUND
326:         address signatory = ecrecover(digest, v, r, s);
327:         require(signatory != address(0), "GovernorAlpha::castVoteBySig: invalid signature");
328:         return _castVote(signatory, proposalId, support);
329:     }

['325']

320:     function castVoteBySig(uint proposalId, bool support, uint8 v, bytes32 r, bytes32 s) public {
321:         bytes32 domainSeparator = keccak256(
322:             abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainId(), address(this))
323:         );
324:         bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));
325:         bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); // <= FOUND
326:         address signatory = ecrecover(digest, v, r, s);
327:         require(signatory != address(0), "GovernorAlpha::castVoteBySig: invalid signature");
328:         return _castVote(signatory, proposalId, support);
329:     }

['342']

337:     function castVoteBySig(uint proposalId, uint8 support, uint8 v, bytes32 r, bytes32 s) external {
338:         bytes32 domainSeparator = keccak256(
339:             abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainIdInternal(), address(this))
340:         );
341:         bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));
342:         bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); // <= FOUND
343:         address signatory = ecrecover(digest, v, r, s);
344:         require(signatory != address(0), "GovernorBravo::castVoteBySig: invalid signature");
345:         emit VoteCast(signatory, proposalId, support, castVoteInternal(signatory, proposalId, support), "");
346:     }

['342']

337:     function castVoteBySig(uint proposalId, uint8 support, uint8 v, bytes32 r, bytes32 s) external {
338:         bytes32 domainSeparator = keccak256(
339:             abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainIdInternal(), address(this))
340:         );
341:         bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));
342:         bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); // <= FOUND
343:         address signatory = ecrecover(digest, v, r, s);
344:         require(signatory != address(0), "GovernorBravo::castVoteBySig: invalid signature");
345:         emit VoteCast(signatory, proposalId, support, castVoteInternal(signatory, proposalId, support), "");
346:     }

[NonCritical-48] Non-assembly method available

Resolution

The codebase features instances where low-level assembly is used to access properties like chain ID, external contract size, and external contract hash. However, since Solidity provides native high-level constructs to access these properties, it is recommended to use those for better readability, maintainability, and reduced complexity. For example, assembly{ id := chainid() } can be replaced with uint256 id = block.chainid, assembly { size := extcodesize() } can be substituted with uint256 size = address().code.length, and assembly { hash := extcodehash() } can be transformed into bytes32 hash = address().codehash. The usage of inline assembly can often flag the project as more complex by automated analysis tools and therefore, should be avoided where high-level constructs can achieve the same functionality.

Num of instances: 1

Findings

Click to show findings

['385']

384:         assembly {
385:             chainId := chainid() // <= FOUND
386:         }

[NonCritical-49] Empty bytes check is missing

Resolution

When developing smart contracts in Solidity, it's crucial to validate the inputs of your functions. This includes ensuring that the bytes parameters are not empty, especially when they represent crucial data such as addresses, identifiers, or raw data that the contract needs to process.

Missing empty bytes checks can lead to unexpected behaviour in your contract. For instance, certain operations might fail, produce incorrect results, or consume unnecessary gas when performed with empty bytes. Moreover, missing input validation can potentially expose your contract to malicious activity, including exploitation of unhandled edge cases.

To mitigate these issues, always validate that bytes parameters are not empty when the logic of your contract requires it.

Num of instances: 19

Findings

Click to show findings

['66']

66:     function delegateTo(address callee, bytes memory data) internal {
67:         (bool success, bytes memory returnData) = callee.delegatecall(data);
68:         assembly {
69:             if eq(success, 0) {
70:                 revert(add(returnData, 0x20), returndatasize)
71:             }
72:         }
73:     }

['227']

227:     function _queueOrRevert(address target, uint value, string memory signature, bytes memory data, uint eta) internal {
228:         require(
229:             !timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))),
230:             "GovernorAlpha::_queueOrRevert: proposal action already queued at eta"
231:         );
232:         timelock.queueTransaction(target, value, signature, data, eta);
233:     }

['320']

320:     function castVoteBySig(uint proposalId, bool support, uint8 v, bytes32 r, bytes32 s) public {
321:         bytes32 domainSeparator = keccak256(
322:             abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainId(), address(this))
323:         );
324:         bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));
325:         bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
326:         address signatory = ecrecover(digest, v, r, s);
327:         require(signatory != address(0), "GovernorAlpha::castVoteBySig: invalid signature");
328:         return _castVote(signatory, proposalId, support);
329:     }

['227']

227:     function _queueOrRevert(address target, uint value, string memory signature, bytes memory data, uint eta) internal {
228:         require(
229:             !timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))),
230:             "GovernorAlpha::_queueOrRevert: proposal action already queued at eta"
231:         );
232:         timelock.queueTransaction(target, value, signature, data, eta);
233:     }

['320']

320:     function castVoteBySig(uint proposalId, bool support, uint8 v, bytes32 r, bytes32 s) public {
321:         bytes32 domainSeparator = keccak256(
322:             abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainId(), address(this))
323:         );
324:         bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));
325:         bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
326:         address signatory = ecrecover(digest, v, r, s);
327:         require(signatory != address(0), "GovernorAlpha::castVoteBySig: invalid signature");
328:         return _castVote(signatory, proposalId, support);
329:     }

['283']

283:     function queueOrRevertInternal(
284:         address target,
285:         uint value,
286:         string memory signature,
287:         bytes memory data,
288:         uint eta,
289:         uint8 proposalType
290:     ) internal {
291:         require(
292:             !proposalTimelocks[proposalType].queuedTransactions(
293:                 keccak256(abi.encode(target, value, signature, data, eta))
294:             ),
295:             "GovernorBravo::queueOrRevertInternal: identical proposal action already queued at eta"
296:         );
297:         proposalTimelocks[proposalType].queueTransaction(target, value, signature, data, eta);
298:     }

['337']

337:     function castVoteBySig(uint proposalId, uint8 support, uint8 v, bytes32 r, bytes32 s) external {
338:         bytes32 domainSeparator = keccak256(
339:             abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainIdInternal(), address(this))
340:         );
341:         bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));
342:         bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
343:         address signatory = ecrecover(digest, v, r, s);
344:         require(signatory != address(0), "GovernorBravo::castVoteBySig: invalid signature");
345:         emit VoteCast(signatory, proposalId, support, castVoteInternal(signatory, proposalId, support), "");
346:     }

['192']

192:     function queueOrRevertInternal(
193:         address target,
194:         uint value,
195:         string memory signature,
196:         bytes memory data,
197:         uint eta
198:     ) internal {
199:         require(
200:             !timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))),
201:             "GovernorBravo::queueOrRevertInternal: identical proposal action already queued at eta"
202:         );
203:         timelock.queueTransaction(target, value, signature, data, eta);
204:     }

['337']

337:     function castVoteBySig(uint proposalId, uint8 support, uint8 v, bytes32 r, bytes32 s) external {
338:         bytes32 domainSeparator = keccak256(
339:             abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainIdInternal(), address(this))
340:         );
341:         bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));
342:         bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
343:         address signatory = ecrecover(digest, v, r, s);
344:         require(signatory != address(0), "GovernorBravo::castVoteBySig: invalid signature");
345:         emit VoteCast(signatory, proposalId, support, castVoteInternal(signatory, proposalId, support), "");
346:     }

['66']

66:     function delegateTo(address callee, bytes memory data) internal {
67:         (bool success, bytes memory returnData) = callee.delegatecall(data);
68:         assembly {
69:             if eq(success, 0) {
70:                 revert(add(returnData, 0x20), returndatasize)
71:             }
72:         }
73:     }

['268']

268:     function _blockingLzReceive(
269:         uint16 srcChainId_,
270:         bytes memory srcAddress_,
271:         uint64 nonce_,
272:         bytes memory payload_
273:     ) internal virtual override whenNotPaused {
274:         uint256 gasToStoreAndEmit = 30000; 
275: 
276:         require(srcChainId_ == srcChainId, "OmnichainGovernanceExecutor::_blockingLzReceive: invalid source chain id");
277: 
278:         (bool success, bytes memory reason) = address(this).excessivelySafeCall(
279:             gasleft() - gasToStoreAndEmit,
280:             150,
281:             abi.encodeCall(this.nonblockingLzReceive, (srcChainId_, srcAddress_, nonce_, payload_))
282:         );
283:         
284:         if (!success) {
285:             bytes32 hashedPayload = keccak256(payload_);
286:             failedMessages[srcChainId_][srcAddress_][nonce_] = hashedPayload;
287:             emit ReceivePayloadFailed(srcChainId_, srcAddress_, nonce_, reason); 
288:         }
289:     }

['376']

376:     function _queueOrRevertInternal(
377:         address target_,
378:         uint256 value_,
379:         string memory signature_,
380:         bytes memory data_,
381:         uint256 eta_,
382:         uint8 proposalType_
383:     ) internal {
384:         require(
385:             !proposalTimelocks[proposalType_].queuedTransactions(
386:                 keccak256(abi.encode(target_, value_, signature_, data_, eta_))
387:             ),
388:             "OmnichainGovernanceExecutor::queueOrRevertInternal: identical proposal action already queued at eta"
389:         );
390: 
391:         proposalTimelocks[proposalType_].queueTransaction(target_, value_, signature_, data_, eta_);
392:     }

['94']

94:     function estimateFees(
95:         uint16 remoteChainId_,
96:         bytes calldata payload_,
97:         bytes calldata adapterParams_
98:     ) external view returns (uint256, uint256) {
99:         return LZ_ENDPOINT.estimateFees(remoteChainId_, address(this), payload_, false, adapterParams_);
100:     }

['249']

249:     function setTrustedRemoteAddress(uint16 remoteChainId_, bytes calldata newRemoteAddress_) external {
250:         _ensureAllowed("setTrustedRemoteAddress(uint16,bytes)");
251:         require(remoteChainId_ != 0, "ChainId must not be zero");
252:         ensureNonzeroAddress(address(uint160(bytes20(newRemoteAddress_))));
253:         bytes memory oldRemoteAddress = trustedRemoteLookup[remoteChainId_];
254:         trustedRemoteLookup[remoteChainId_] = abi.encodePacked(newRemoteAddress_, address(this));
255:         emit SetTrustedRemoteAddress(remoteChainId_, oldRemoteAddress, trustedRemoteLookup[remoteChainId_]);
256:     }

['266']

266:     function setConfig(uint16 version_, uint16 chainId_, uint256 configType_, bytes calldata config_) external {
267:         _ensureAllowed("setConfig(uint16,uint16,uint256,bytes)");
268:         LZ_ENDPOINT.setConfig(version_, chainId_, configType_, config_);
269:     }

['126']

126:     function queueTransaction(
127:         address target,
128:         uint value,
129:         string memory signature,
130:         bytes memory data,
131:         uint eta
132:     ) public returns (bytes32) {
133:         require(msg.sender == admin, "Timelock::queueTransaction: Call must come from admin.");
134:         require(
135:             eta >= getBlockTimestamp().add(delay),
136:             "Timelock::queueTransaction: Estimated execution block must satisfy delay."
137:         );
138: 
139:         bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
140:         queuedTransactions[txHash] = true;
141: 
142:         emit QueueTransaction(txHash, target, value, signature, data, eta);
143:         return txHash;
144:     }

['154']

154:     function cancelTransaction(
155:         address target,
156:         uint value,
157:         string memory signature,
158:         bytes memory data,
159:         uint eta
160:     ) public {
161:         require(msg.sender == admin, "Timelock::cancelTransaction: Call must come from admin.");
162: 
163:         bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
164:         queuedTransactions[txHash] = false;
165: 
166:         emit CancelTransaction(txHash, target, value, signature, data, eta);
167:     }

['159']

159:     function queueTransaction(
160:         address target,
161:         uint256 value,
162:         string calldata signature,
163:         bytes calldata data,
164:         uint256 eta
165:     ) public returns (bytes32) {
166:         require(msg.sender == admin, "Timelock::queueTransaction: Call must come from admin.");
167:         require(
168:             eta >= getBlockTimestamp() + delay,
169:             "Timelock::queueTransaction: Estimated execution block must satisfy delay."
170:         );
171: 
172:         bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
173:         require(!queuedTransactions[txHash], "Timelock::queueTransaction: transaction already queued.");
174:         queuedTransactions[txHash] = true;
175: 
176:         emit QueueTransaction(txHash, target, value, signature, data, eta);
177:         return txHash;
178:     }

['190']

190:     function cancelTransaction(
191:         address target,
192:         uint256 value,
193:         string calldata signature,
194:         bytes calldata data,
195:         uint256 eta
196:     ) public {
197:         require(msg.sender == admin, "Timelock::cancelTransaction: Call must come from admin.");
198: 
199:         bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
200:         require(queuedTransactions[txHash], "Timelock::cancelTransaction: transaction is not queued yet.");
201:         delete (queuedTransactions[txHash]);
202: 
203:         emit CancelTransaction(txHash, target, value, signature, data, eta);
204:     }

[NonCritical-50] Top level declarations should be separated by two blank lines

Num of instances: 13

Findings

Click to show findings

['211']

211:     } // <= FOUND
212: 
213:     function queue(uint proposalId) public { // <= FOUND

['277']

277:     } // <= FOUND
278: 
279:     function getActions( // <= FOUND

['292']

292:     } // <= FOUND
293: 
294:     function state(uint proposalId) public view returns (ProposalState) { // <= FOUND

['349']

349:     } // <= FOUND
350: 
351:     function __acceptAdmin() public { // <= FOUND

['364']

364:     } // <= FOUND
365: 
366:     function __executeSetTimelockPendingAdmin(address newPendingAdmin, uint eta) public { // <= FOUND

['141']

141:     } // <= FOUND
142: 
143:     function propose( // <= FOUND

['225']

225:     } // <= FOUND
226: 
227:     function _queueOrRevert(address target, uint value, string memory signature, bytes memory data, uint eta) internal { // <= FOUND

['252']

252:     } // <= FOUND
253: 
254:     function cancel(uint proposalId) public { // <= FOUND

['288']

288:     } // <= FOUND
289: 
290:     function getReceipt(uint proposalId, address voter) public view returns (Receipt memory) { // <= FOUND

['318']

318:     } // <= FOUND
319: 
320:     function castVoteBySig(uint proposalId, bool support, uint8 v, bytes32 r, bytes32 s) public { // <= FOUND

['354']

354:     } // <= FOUND
355: 
356:     function __abdicate() public { // <= FOUND

['369']

369:     } // <= FOUND
370: 
371:     function add256(uint256 a, uint256 b) internal pure returns (uint) { // <= FOUND

['375']

375:     } // <= FOUND
376: 
377:     function sub256(uint256 a, uint256 b) internal pure returns (uint) { // <= FOUND

[NonCritical-51] Assembly block creates dirty bits

Resolution

Manipulating data directly at the free memory pointer location without subsequently adjusting the pointer can lead to unwanted data remnants, or "dirty bits", in that memory spot. This can cause challenges for the Solidity optimizer, making it difficult to determine if memory cleaning is required before reuse, potentially resulting in less efficient optimization. To mitigate this issue, it's advised to always update the free memory pointer following any data write operation. Furthermore, using the assembly ("memory-safe") { ... } annotation will clearly indicate to the optimizer the sections of your code that are memory-safe, improving code efficiency and reducing the potential for errors.

Num of instances: 1

Findings

Click to show findings

['85']

84:         assembly {
85:             let free_mem_ptr := mload(0x40) // <= FOUND
86:             returndatacopy(free_mem_ptr, 0, returndatasize) // <= FOUND
87: 
88:             switch success
89:             case 0 {
90:                 revert(free_mem_ptr, returndatasize) // <= FOUND
91:             }
92:             default {
93:                 return(free_mem_ptr, returndatasize) // <= FOUND
94:             }
95:         }

[NonCritical-52] Whitespace in expressions

Resolution

Avoid unnecessary whitespace in contract lines such as ' ;' and ', )'

Num of instances: 3

Findings

Click to show findings

['83']

82:         
83:         (bool success, ) = implementation.delegatecall(msg.data); // <= FOUND

['182']

182:         (bytes memory payload, ) = abi.decode(payload_, (bytes, uint256)); // <= FOUND

['239']

238:         
239:         (bool sent, ) = to_.call{ value: originalValue_ }(""); // <= FOUND

[NonCritical-53] Use of override is unnecessary

Resolution

Starting with Solidity version 0.8.8, the use of the override keyword is simplified. If a function solely overrides an interface function and does not exist in multiple base contracts, specifying override becomes unnecessary. This change streamlines the code and makes it less verbose. Removing unnecessary use of override in these situations can make the code cleaner and more maintainable, aligning with the newer Solidity guidelines. It's a good practice to adapt to this updated behavior to stay consistent with the language's evolution and current best practices.

Num of instances: 2

Findings

Click to show findings

['103']

103:     function renounceOwnership() public virtual override  // <= FOUND

['296']

296:     function _nonblockingLzReceive(uint16, bytes memory, uint64, bytes memory payload_) internal virtual override  // <= FOUND

[NonCritical-54] No access control on receive/payable fallback

Resolution

Without access control on receive/payable fallback functions in a contract, anyone can send Ether (ETH) to the contract's address. If there's no way to withdraw those funds defined within the contract, any Ether sent, whether intentionally or by mistake, will be permanently stuck. This could lead to unintended loss of funds. Implementing proper access control ensures that only authorized addresses can interact with these functions. Resolution could involve adding access control mechanisms, like Ownable or specific permission requirements, or creating a withdrawal function accessible only to the contract's owner, thus preventing unintentional loss of funds.

Num of instances: 1

Findings

Click to show findings

['82']

82:     fallback() external payable {} // <= FOUND

[NonCritical-55] Cyclomatic complexity in functions

Resolution

Cyclomatic complexity is a software metric used to measure the complexity of a program. It quantifies the number of linearly independent paths through a program's source code, giving an idea of how complex the control flow is. High cyclomatic complexity may indicate a higher risk of defects and can make the code harder to understand, test, and maintain. It often suggests that a function or method is trying to do too much, and a refactor might be needed. By breaking down complex functions into smaller, more focused pieces, you can improve readability, ease of testing, and overall maintainability.

Num of instances: 7

Findings

Click to show findings

['294']

294:     function state(uint proposalId) public view returns (ProposalState) { // <= FOUND
295:         require(proposalCount >= proposalId && proposalId > 0, "GovernorAlpha::state: invalid proposal id");
296:         Proposal storage proposal = proposals[proposalId];
297:         if (proposal.canceled) {
298:             return ProposalState.Canceled;
299:         } else if (block.number <= proposal.startBlock) {
300:             return ProposalState.Pending;
301:         } else if (block.number <= proposal.endBlock) {
302:             return ProposalState.Active;
303:         } else if (proposal.forVotes <= proposal.againstVotes || proposal.forVotes < quorumVotes()) {
304:             return ProposalState.Defeated;
305:         } else if (proposal.eta == 0) {
306:             return ProposalState.Succeeded;
307:         } else if (proposal.executed) {
308:             return ProposalState.Executed;
309:         } else if (block.timestamp >= add256(proposal.eta, timelock.GRACE_PERIOD())) {
310:             return ProposalState.Expired;
311:         } else {
312:             return ProposalState.Queued;
313:         }
314:     }

['294']

294:     function state(uint proposalId) public view returns (ProposalState) { // <= FOUND
295:         require(proposalCount >= proposalId && proposalId > 0, "GovernorAlpha::state: invalid proposal id");
296:         Proposal storage proposal = proposals[proposalId];
297:         if (proposal.canceled) {
298:             return ProposalState.Canceled;
299:         } else if (block.number <= proposal.startBlock) {
300:             return ProposalState.Pending;
301:         } else if (block.number <= proposal.endBlock) {
302:             return ProposalState.Active;
303:         } else if (proposal.forVotes <= proposal.againstVotes || proposal.forVotes < quorumVotes()) {
304:             return ProposalState.Defeated;
305:         } else if (proposal.eta == 0) {
306:             return ProposalState.Succeeded;
307:         } else if (proposal.executed) {
308:             return ProposalState.Executed;
309:         } else if (block.timestamp >= add256(proposal.eta, timelock.GRACE_PERIOD())) {
310:             return ProposalState.Expired;
311:         } else {
312:             return ProposalState.Queued;
313:         }
314:     }

['384']

384:     function state(uint proposalId) public view returns (ProposalState) { // <= FOUND
385:         require(
386:             proposalCount >= proposalId && proposalId > initialProposalId,
387:             "GovernorBravo::state: invalid proposal id"
388:         );
389:         Proposal storage proposal = proposals[proposalId];
390:         if (proposal.canceled) {
391:             return ProposalState.Canceled;
392:         } else if (block.number <= proposal.startBlock) {
393:             return ProposalState.Pending;
394:         } else if (block.number <= proposal.endBlock) {
395:             return ProposalState.Active;
396:         } else if (proposal.forVotes <= proposal.againstVotes || proposal.forVotes < quorumVotes) {
397:             return ProposalState.Defeated;
398:         } else if (proposal.eta == 0) {
399:             return ProposalState.Succeeded;
400:         } else if (proposal.executed) {
401:             return ProposalState.Executed;
402:         } else if (
403:             block.timestamp >= add256(proposal.eta, proposalTimelocks[uint8(proposal.proposalType)].GRACE_PERIOD())
404:         ) {
405:             return ProposalState.Expired;
406:         } else {
407:             return ProposalState.Queued;
408:         }
409:     }

['289']

289:     function state(uint proposalId) public view returns (ProposalState) { // <= FOUND
290:         require(
291:             proposalCount >= proposalId && proposalId > initialProposalId,
292:             "GovernorBravo::state: invalid proposal id"
293:         );
294:         Proposal storage proposal = proposals[proposalId];
295:         if (proposal.canceled) {
296:             return ProposalState.Canceled;
297:         } else if (block.number <= proposal.startBlock) {
298:             return ProposalState.Pending;
299:         } else if (block.number <= proposal.endBlock) {
300:             return ProposalState.Active;
301:         } else if (proposal.forVotes <= proposal.againstVotes || proposal.forVotes < quorumVotes) {
302:             return ProposalState.Defeated;
303:         } else if (proposal.eta == 0) {
304:             return ProposalState.Succeeded;
305:         } else if (proposal.executed) {
306:             return ProposalState.Executed;
307:         } else if (block.timestamp >= add256(proposal.eta, timelock.GRACE_PERIOD())) {
308:             return ProposalState.Expired;
309:         } else {
310:             return ProposalState.Queued;
311:         }
312:     }

['355']

355:     function castVoteInternal(address voter, uint proposalId, uint8 support) internal returns (uint96) { // <= FOUND
356:         require(state(proposalId) == ProposalState.Active, "GovernorBravo::castVoteInternal: voting is closed");
357:         require(support <= 2, "GovernorBravo::castVoteInternal: invalid vote type");
358:         Proposal storage proposal = proposals[proposalId];
359:         Receipt storage receipt = proposal.receipts[voter];
360:         require(receipt.hasVoted == false, "GovernorBravo::castVoteInternal: voter already voted");
361:         uint96 votes = xvsVault.getPriorVotes(voter, proposal.startBlock);
362: 
363:         if (support == 0) {
364:             proposal.againstVotes = add256(proposal.againstVotes, votes);
365:         } else if (support == 1) {
366:             proposal.forVotes = add256(proposal.forVotes, votes);
367:         } else if (support == 2) {
368:             proposal.abstainVotes = add256(proposal.abstainVotes, votes);
369:         }
370: 
371:         receipt.hasVoted = true;
372:         receipt.support = support;
373:         receipt.votes = votes;
374: 
375:         return votes;
376:     }

['355']

355:     function castVoteInternal(address voter, uint proposalId, uint8 support) internal returns (uint96) { // <= FOUND
356:         require(state(proposalId) == ProposalState.Active, "GovernorBravo::castVoteInternal: voting is closed");
357:         require(support <= 2, "GovernorBravo::castVoteInternal: invalid vote type");
358:         Proposal storage proposal = proposals[proposalId];
359:         Receipt storage receipt = proposal.receipts[voter];
360:         require(receipt.hasVoted == false, "GovernorBravo::castVoteInternal: voter already voted");
361:         uint96 votes = xvsVault.getPriorVotes(voter, proposal.startBlock);
362: 
363:         if (support == 0) {
364:             proposal.againstVotes = add256(proposal.againstVotes, votes);
365:         } else if (support == 1) {
366:             proposal.forVotes = add256(proposal.forVotes, votes);
367:         } else if (support == 2) {
368:             proposal.abstainVotes = add256(proposal.abstainVotes, votes);
369:         }
370: 
371:         receipt.hasVoted = true;
372:         receipt.support = support;
373:         receipt.votes = votes;
374: 
375:         return votes;
376:     }

['246']

246:     function state(uint256 proposalId_) public view returns (ProposalState) { // <= FOUND
247:         Proposal storage proposal = proposals[proposalId_];
248:         if (proposal.canceled) {
249:             return ProposalState.Canceled;
250:         } else if (proposal.executed) {
251:             return ProposalState.Executed;
252:         } else if (queued[proposalId_]) {
253:             
254:             return ProposalState.Queued;
255:         } else {
256:             revert InvalidProposalId();
257:         }
258:     }

[NonCritical-56] Contract uses both require()/revert() as well as custom errors

Resolution

Consider using one and sticking to it for consistency

Num of instances: 2

Findings

Click to show findings

['19']

19: contract OmnichainGovernanceExecutor is ReentrancyGuard, BaseOmnichainControllerDest 

['15']

15: abstract contract AccessControlledV8 is Initializable, Ownable2StepUpgradeable 

[NonCritical-57] function names should be lowerCamelCase

Num of instances: 3

Findings

Click to show findings

['102']

102:     function GRACE_PERIOD() public view virtual returns (uint256) 

['110']

110:     function MINIMUM_DELAY() public view virtual returns (uint256) 

['118']

118:     function MAXIMUM_DELAY() public view virtual returns (uint256) 

[NonCritical-58] Consider bounding input array length

Resolution

Unbounded array inputs in functions can lead to unintentional excessive gas consumption, potentially causing a transaction to revert after expending substantial gas. To enhance user experience and prevent such scenarios, consider implementing a require() statement that limits the array length to a defined maximum. This constraint ensures that transactions won't proceed if they're likely to hit gas limits due to array size, saving users from unnecessary gas costs and offering a more predictable interaction with the contract.

Num of instances: 9

Findings

Click to show findings

['220']

220:        for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
221:             _queueOrRevert(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], eta);
222:         }

['242']

242:        for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
243:             timelock.executeTransaction.value(proposal.values[i])(
244:                 proposal.targets[i],
245:                 proposal.values[i],
246:                 proposal.signatures[i],
247:                 proposal.calldatas[i],
248:                 proposal.eta
249:             );
250:         }

['266']

266:        for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
267:             timelock.cancelTransaction(
268:                 proposal.targets[i],
269:                 proposal.values[i],
270:                 proposal.signatures[i],
271:                 proposal.calldatas[i],
272:                 proposal.eta
273:             );
274:         }

['269']

269:        for (uint i; i < proposal.targets.length; ++i) { // <= FOUND
270:             queueOrRevertInternal(
271:                 proposal.targets[i],
272:                 proposal.values[i],
273:                 proposal.signatures[i],
274:                 proposal.calldatas[i],
275:                 eta,
276:                 uint8(proposal.proposalType)
277:             );
278:         }

['311']

311:        for (uint i; i < proposal.targets.length; ++i) { // <= FOUND
312:             proposalTimelocks[uint8(proposal.proposalType)].executeTransaction(
313:                 proposal.targets[i],
314:                 proposal.values[i],
315:                 proposal.signatures[i],
316:                 proposal.calldatas[i],
317:                 proposal.eta
318:             );
319:         }

['340']

340:        for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
341:             proposalTimelocks[proposal.proposalType].cancelTransaction(
342:                 proposal.targets[i],
343:                 proposal.values[i],
344:                 proposal.signatures[i],
345:                 proposal.calldatas[i],
346:                 proposal.eta
347:             );
348:         }

['179']

179:        for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
180:             queueOrRevertInternal(
181:                 proposal.targets[i],
182:                 proposal.values[i],
183:                 proposal.signatures[i],
184:                 proposal.calldatas[i],
185:                 eta
186:             );
187:         }

['217']

217:        for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
218:             timelock.executeTransaction(
219:                 proposal.targets[i],
220:                 proposal.values[i],
221:                 proposal.signatures[i],
222:                 proposal.calldatas[i],
223:                 proposal.eta
224:             );
225:         }

['75']

72:        for (uint256 i; i < signatureLength; ) {
73:             bytes4 sigHash = bytes4(keccak256(bytes(signatures_[i])));
74:             bytes memory signature = bytes(functionRegistry[sigHash]);
75:             if (active_[i] && signature.length == 0) { // <= FOUND
76:                 functionRegistry[sigHash] = signatures_[i];
77:                 emit FunctionRegistryChanged(signatures_[i], true);
78:             } else if (!active_[i] && signature.length != 0) { // <= FOUND
79:                 delete functionRegistry[sigHash];
80:                 emit FunctionRegistryChanged(signatures_[i], false);
81:             }
82:             unchecked {
83:                 ++i;
84:             }
85:         }

[NonCritical-59] Missing events in sensitive functions

Resolution

Sensitive setter functions in smart contracts often alter critical state variables. Without events emitted in these functions, external observers or dApps cannot easily track or react to these state changes. Missing events can obscure contract activity, hampering transparency and making integration more challenging. To resolve this, incorporate appropriate event emissions within these functions. Events offer an efficient way to log crucial changes, aiding in real-time tracking and post-transaction verification.

Num of instances: 3

Findings

Click to show findings

['48']

48:     function setAccessControlManager(address accessControlManager_) external onlyOwner { // <= FOUND
49:         _setAccessControlManager(accessControlManager_);
50:     }

['266']

266:     function setConfig(uint16 version_, uint16 chainId_, uint256 configType_, bytes calldata config_) external { // <= FOUND
267:         _ensureAllowed("setConfig(uint16,uint16,uint256,bytes)");
268:         LZ_ENDPOINT.setConfig(version_, chainId_, configType_, config_);
269:     }

['276']

276:     function setSendVersion(uint16 version_) external { // <= FOUND
277:         _ensureAllowed("setSendVersion(uint16)");
278:         LZ_ENDPOINT.setSendVersion(version_);
279:     }

[NonCritical-60] Consider implementing EIP-5267 to securely describe EIP-712 domains being used

Resolution

EIP-5267 aims to enhance EIP-712, which standardizes Ethereum's structured data signing. While EIP-712 defines how structured data should be signed, EIP-5267 focuses on standardizing the representation of domains. By doing so, it allows applications to fetch domain descriptions and dynamically construct domain separators, aiding in secure EIP-712 signature integration. To implement EIP-5267, contracts should expose a consistent method (e.g., EIP712Domain()) returning the domain's description. Adopting this ensures that applications can universally and securely handle EIP-712 signatures across various contracts, enhancing both security and scalability in Ethereum applications.

Num of instances: 1

Findings

Click to show findings

['105']

105:     
106:     bytes32 public constant DOMAIN_TYPEHASH =
107:         keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); // <= FOUND

[NonCritical-61] Ensure block.timestamp is only used in long time intervals

Resolution

block.timestamp represents the current block's timestamp and can be influenced, within limits, by miners. For short time intervals, this malleability can be exploited, potentially allowing miners to manipulate contract behavior. For instance, they might fast-forward an expiration or delay an event. When designing smart contracts, if precise time checks are needed for short intervals, alternatives like block numbers can be considered. However, for longer durations where a few seconds of deviation is inconsequential, block.timestamp is generally safe and efficient. Always assess the implications of time manipulations for the specific use-case before utilizing block.timestamp. In practice, if you're using block.timestamp to measure intervals that are a matter of days, weeks, or longer, the potential manipulation by miners becomes less significant. Always prioritize the security and integrity of your smart contract operations when making these decisions.

Num of instances: 1

Findings

Click to show findings

['344']

344:         uint256 eta = block.timestamp + proposalTimelocks[proposal.proposalType].delay(); // <= FOUND

[NonCritical-62] Contracts with only unimplemented functions can be labeled as abstract

Resolution

In Solidity, a contract that's not meant to be deployed on its own but is intended to be inherited by other contracts should be marked abstract. This ensures that developers recognize the contract's incomplete or intended-to-be-extended nature. If a contract has unimplemented functions or is designed with the intention that another contract should extend its functionality, it should be explicitly labeled as abstract. This helps prevent inadvertent deployments and clearly communicates the contract's purpose to other developers. Resolution: Review the contract, and if it's not supposed to function standalone, mark it as abstract to make the intention clear.

Num of instances: 9

Findings

Click to show findings

['12']

12: contract AccessControlledV5 

['73']

73: contract GovernorBravoDelegate is GovernorBravoDelegateStorageV2, GovernorBravoEvents 

['11']

11: contract GovernorBravoDelegateV1 is GovernorBravoDelegateStorageV1, GovernorBravoEventsV1 

['9']

9: contract GovernorBravoEvents 

['73']

73: contract GovernorBravoDelegatorStorage 

['88']

88: contract GovernorBravoDelegateStorageV1 is GovernorBravoDelegatorStorage 

['186']

186: contract GovernorBravoDelegateStorageV2 is GovernorBravoDelegateStorageV1 

['12']

12: contract GovernorBravoEventsV1 

['88']

88: contract GovernorBravoDelegateStorageV1 is GovernorBravoDelegatorStorage 

[NonCritical-63] A event should be emitted if a non immutable state variable is set in a constructor

Num of instances: 8

Findings

Click to show findings

['51']

51:     constructor(address accessControlManager_) {
52:         ensureNonzeroAddress(accessControlManager_);
53:         accessControlManager = accessControlManager_; // <= FOUND
54:     }

['136']

136:     constructor(address timelock_, address xvs_, address guardian_) public {
137:         timelock = TimelockInterface(timelock_); // <= FOUND
138:         xvs = XVSInterface(xvs_);
139:         guardian = guardian_;
140:     }

['136']

136:     constructor(address timelock_, address xvs_, address guardian_, uint256 lastProposalId_) public {
137:         timelock = TimelockInterface(timelock_); // <= FOUND
138:         xvs = XVSInterface(xvs_);
139:         guardian = guardian_;
140:         proposalCount = lastProposalId_;
141:     }

['12']

12:     constructor(
13:         address timelock_,
14:         address xvsVault_,
15:         address admin_,
16:         address implementation_,
17:         uint votingPeriod_,
18:         uint votingDelay_,
19:         uint proposalThreshold_,
20:         address guardian_
21:     ) public {
22:         
23:         admin = msg.sender; // <= FOUND
24: 
25:         delegateTo(
26:             implementation_,
27:             abi.encodeWithSignature(
28:                 "initialize(address,address,uint256,uint256,uint256,address)",
29:                 timelock_,
30:                 xvsVault_,
31:                 votingPeriod_,
32:                 votingDelay_,
33:                 proposalThreshold_,
34:                 guardian_
35:             )
36:         );
37: 
38:         _setImplementation(implementation_);
39: 
40:         admin = admin_; // <= FOUND
41:     }

['12']

12:     constructor(
13:         address timelock_,
14:         address xvsVault_,
15:         address admin_,
16:         address implementation_,
17:         uint votingPeriod_,
18:         uint votingDelay_,
19:         uint proposalThreshold_,
20:         address guardian_
21:     ) public {
22:         
23:         admin = msg.sender; // <= FOUND
24: 
25:         delegateTo(
26:             implementation_,
27:             abi.encodeWithSignature(
28:                 "initialize(address,address,uint256,uint256,uint256,address)",
29:                 timelock_,
30:                 xvsVault_,
31:                 votingPeriod_,
32:                 votingDelay_,
33:                 proposalThreshold_,
34:                 guardian_
35:             )
36:         );
37: 
38:         _setImplementation(implementation_);
39: 
40:         admin = admin_; // <= FOUND
41:     }

['72']

72:     constructor(address admin_, uint delay_) public {
73:         require(delay_ >= MINIMUM_DELAY, "Timelock::constructor: Delay must exceed minimum delay.");
74:         require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay.");
75: 
76:         admin = admin_; // <= FOUND
77:         delay = delay_;
78:     }

['73']

73:     constructor(address admin_, uint256 delay_) {
74:         require(delay_ >= MINIMUM_DELAY(), "Timelock::constructor: Delay must exceed minimum delay.");
75:         require(delay_ <= MAXIMUM_DELAY(), "Timelock::setDelay: Delay must not exceed maximum delay.");
76:         ensureNonzeroAddress(admin_);
77: 
78:         admin = admin_; // <= FOUND
79:         delay = delay_;
80:     }

['134']

134:     constructor(address endpoint_, address guardian_, uint16 srcChainId_) BaseOmnichainControllerDest(endpoint_) {
135:         ensureNonzeroAddress(guardian_);
136:         GUARDIAN = guardian_;
137:         srcChainId = srcChainId_; // <= FOUND
138:     }

[NonCritical-64] Superfluous parameter can only be one value

Resolution

Using redundant parameters in smart contracts can lead to unnecessary complexity and potential vulnerabilities. When a function parameter is always constrained to a specific value due to an if or require statement, it renders the parameter superfluous. Including such parameters can be misleading to developers or users, suggesting a flexibility that doesn't exist in reality. Additionally, unnecessary parameters increase the gas cost for transactions. Resolution: Analyze the contract to identify parameters that are rendered static by conditional checks. Remove these parameters from the function signature and update the function logic accordingly. This simplifies the code, reduces gas costs, and enhances clarity and security.

Num of instances: 1

Findings

Click to show findings

['268']

268:     function _blockingLzReceive(
269:         uint16 srcChainId_,
270:         bytes memory srcAddress_,
271:         uint64 nonce_,
272:         bytes memory payload_
273:     ) internal virtual override whenNotPaused {
274:         uint256 gasToStoreAndEmit = 30000; 
275: 
276:         require(srcChainId_ == srcChainId, "OmnichainGovernanceExecutor::_blockingLzReceive: invalid source chain id"); // <= FOUND
277: 
278:         (bool success, bytes memory reason) = address(this).excessivelySafeCall(
279:             gasleft() - gasToStoreAndEmit,
280:             150,
281:             abi.encodeCall(this.nonblockingLzReceive, (srcChainId_, srcAddress_, nonce_, payload_))
282:         );
283:         
284:         if (!success) {
285:             bytes32 hashedPayload = keccak256(payload_);
286:             failedMessages[srcChainId_][srcAddress_][nonce_] = hashedPayload;
287:             emit ReceivePayloadFailed(srcChainId_, srcAddress_, nonce_, reason); 
288:         }
289:     }

[NonCritical-65] Consider only defining one library/interface/contract per sol file

Resolution

Combining multiple libraries, interfaces, or contracts in a single .sol file can lead to clutter, reduced readability, and versioning issues. Resolution: Adopt the best practice of defining only one library, interface, or contract per Solidity file. This modular approach enhances clarity, simplifies unit testing, and streamlines code review. Furthermore, segregating components makes version management easier, as updates to one component won't necessitate changes to a file housing multiple unrelated components. Structured file management can further assist in avoiding naming collisions and ensure smoother integration into larger systems or DApps.

Num of instances: 4

Findings

Click to show findings

['1']

1: pragma solidity ^0.5.16;
2: pragma experimental ABIEncoderV2;
3: 
4: contract GovernorAlpha2 { // <= FOUND
5:     
6:     string public constant name = "Venus Governor Alpha";
7: 
9:     function quorumVotes() public pure returns (uint) {
10:         return 600000e18;
11:     } 
12: 
14:     function proposalThreshold() public pure returns (uint) {
15:         return 300000e18;
16:     } 
17: 
19:     function proposalMaxOperations() public pure returns (uint) {
20:         return 30;
21:     } 
22: 
24:     function votingDelay() public pure returns (uint) {
25:         return 1;
26:     } 
27: 
29:     function votingPeriod() public pure returns (uint) {
30:         return (60 * 60 * 24 * 3) / 3;
31:     } 
32: 
34:     TimelockInterface public timelock;
35: 
37:     XVSInterface public xvs;
38: 
40:     address public guardian;
41: 
43:     uint public proposalCount;
44: 
45:     struct Proposal {
46:         
47:         uint id;
48:         
49:         address proposer;
50:         
51:         uint eta;
52:         
53:         address[] targets;
54:         
55:         uint[] values;
56:         
57:         string[] signatures;
58:         
59:         bytes[] calldatas;
60:         
61:         uint startBlock;
62:         
63:         uint endBlock;
64:         
65:         uint forVotes;
66:         
67:         uint againstVotes;
68:         
69:         bool canceled;
70:         
71:         bool executed;
72:         
73:         mapping(address => Receipt) receipts;
74:     }
75: 
77:     struct Receipt {
78:         
79:         bool hasVoted;
80:         
81:         bool support;
82:         
83:         uint96 votes;
84:     }
85: 
87:     enum ProposalState {
88:         Pending,
89:         Active,
90:         Canceled,
91:         Defeated,
92:         Succeeded,
93:         Queued,
94:         Expired,
95:         Executed
96:     }
97: 
99:     mapping(uint => Proposal) public proposals;
100: 
102:     mapping(address => uint) public latestProposalIds;
103: 
105:     bytes32 public constant DOMAIN_TYPEHASH =
106:         keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)");
107: 
109:     bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,bool support)");
110: 
112:     event ProposalCreated(
113:         uint id,
114:         address proposer,
115:         address[] targets,
116:         uint[] values,
117:         string[] signatures,
118:         bytes[] calldatas,
119:         uint startBlock,
120:         uint endBlock,
121:         string description
122:     );
123: 
125:     event VoteCast(address voter, uint proposalId, bool support, uint votes);
126: 
128:     event ProposalCanceled(uint id);
129: 
131:     event ProposalQueued(uint id, uint eta);
132: 
134:     event ProposalExecuted(uint id);
135: 
136:     constructor(address timelock_, address xvs_, address guardian_, uint256 lastProposalId_) public {
137:         timelock = TimelockInterface(timelock_);
138:         xvs = XVSInterface(xvs_);
139:         guardian = guardian_;
140:         proposalCount = lastProposalId_;
141:     }
142: 
143:     function propose(
144:         address[] memory targets,
145:         uint[] memory values,
146:         string[] memory signatures,
147:         bytes[] memory calldatas,
148:         string memory description
149:     ) public returns (uint) {
150:         require(
151:             xvs.getPriorVotes(msg.sender, sub256(block.number, 1)) > proposalThreshold(),
152:             "GovernorAlpha::propose: proposer votes below proposal threshold"
153:         );
154:         require(
155:             targets.length == values.length &&
156:                 targets.length == signatures.length &&
157:                 targets.length == calldatas.length,
158:             "GovernorAlpha::propose: proposal function information arity mismatch"
159:         );
160:         require(targets.length != 0, "GovernorAlpha::propose: must provide actions");
161:         require(targets.length <= proposalMaxOperations(), "GovernorAlpha::propose: too many actions");
162: 
163:         uint latestProposalId = latestProposalIds[msg.sender];
164:         if (latestProposalId != 0) {
165:             ProposalState proposersLatestProposalState = state(latestProposalId);
166:             require(
167:                 proposersLatestProposalState != ProposalState.Active,
168:                 "GovernorAlpha::propose: found an already active proposal"
169:             );
170:             require(
171:                 proposersLatestProposalState != ProposalState.Pending,
172:                 "GovernorAlpha::propose: found an already pending proposal"
173:             );
174:         }
175: 
176:         uint startBlock = add256(block.number, votingDelay());
177:         uint endBlock = add256(startBlock, votingPeriod());
178: 
179:         proposalCount++;
180:         Proposal memory newProposal = Proposal({
181:             id: proposalCount,
182:             proposer: msg.sender,
183:             eta: 0,
184:             targets: targets,
185:             values: values,
186:             signatures: signatures,
187:             calldatas: calldatas,
188:             startBlock: startBlock,
189:             endBlock: endBlock,
190:             forVotes: 0,
191:             againstVotes: 0,
192:             canceled: false,
193:             executed: false
194:         });
195: 
196:         proposals[newProposal.id] = newProposal;
197:         latestProposalIds[newProposal.proposer] = newProposal.id;
198: 
199:         emit ProposalCreated(
200:             newProposal.id,
201:             msg.sender,
202:             targets,
203:             values,
204:             signatures,
205:             calldatas,
206:             startBlock,
207:             endBlock,
208:             description
209:         );
210:         return newProposal.id;
211:     }
212: 
213:     function queue(uint proposalId) public {
214:         require(
215:             state(proposalId) == ProposalState.Succeeded,
216:             "GovernorAlpha::queue: proposal can only be queued if it is succeeded"
217:         );
218:         Proposal storage proposal = proposals[proposalId];
219:         uint eta = add256(block.timestamp, timelock.delay());
220:         for (uint i = 0; i < proposal.targets.length; i++) {
221:             _queueOrRevert(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], eta);
222:         }
223:         proposal.eta = eta;
224:         emit ProposalQueued(proposalId, eta);
225:     }
226: 
227:     function _queueOrRevert(address target, uint value, string memory signature, bytes memory data, uint eta) internal {
228:         require(
229:             !timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))),
230:             "GovernorAlpha::_queueOrRevert: proposal action already queued at eta"
231:         );
232:         timelock.queueTransaction(target, value, signature, data, eta);
233:     }
234: 
235:     function execute(uint proposalId) public payable {
236:         require(
237:             state(proposalId) == ProposalState.Queued,
238:             "GovernorAlpha::execute: proposal can only be executed if it is queued"
239:         );
240:         Proposal storage proposal = proposals[proposalId];
241:         proposal.executed = true;
242:         for (uint i = 0; i < proposal.targets.length; i++) {
243:             timelock.executeTransaction.value(proposal.values[i])(
244:                 proposal.targets[i],
245:                 proposal.values[i],
246:                 proposal.signatures[i],
247:                 proposal.calldatas[i],
248:                 proposal.eta
249:             );
250:         }
251:         emit ProposalExecuted(proposalId);
252:     }
253: 
254:     function cancel(uint proposalId) public {
255:         ProposalState state = state(proposalId);
256:         require(state != ProposalState.Executed, "GovernorAlpha::cancel: cannot cancel executed proposal");
257: 
258:         Proposal storage proposal = proposals[proposalId];
259:         require(
260:             msg.sender == guardian ||
261:                 xvs.getPriorVotes(proposal.proposer, sub256(block.number, 1)) < proposalThreshold(),
262:             "GovernorAlpha::cancel: proposer above threshold"
263:         );
264: 
265:         proposal.canceled = true;
266:         for (uint i = 0; i < proposal.targets.length; i++) {
267:             timelock.cancelTransaction(
268:                 proposal.targets[i],
269:                 proposal.values[i],
270:                 proposal.signatures[i],
271:                 proposal.calldatas[i],
272:                 proposal.eta
273:             );
274:         }
275: 
276:         emit ProposalCanceled(proposalId);
277:     }
278: 
279:     function getActions(
280:         uint proposalId
281:     )
282:         public
283:         view
284:         returns (address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas)
285:     {
286:         Proposal storage p = proposals[proposalId];
287:         return (p.targets, p.values, p.signatures, p.calldatas);
288:     }
289: 
290:     function getReceipt(uint proposalId, address voter) public view returns (Receipt memory) {
291:         return proposals[proposalId].receipts[voter];
292:     }
293: 
294:     function state(uint proposalId) public view returns (ProposalState) {
295:         require(proposalCount >= proposalId && proposalId > 0, "GovernorAlpha::state: invalid proposal id");
296:         Proposal storage proposal = proposals[proposalId];
297:         if (proposal.canceled) {
298:             return ProposalState.Canceled;
299:         } else if (block.number <= proposal.startBlock) {
300:             return ProposalState.Pending;
301:         } else if (block.number <= proposal.endBlock) {
302:             return ProposalState.Active;
303:         } else if (proposal.forVotes <= proposal.againstVotes || proposal.forVotes < quorumVotes()) {
304:             return ProposalState.Defeated;
305:         } else if (proposal.eta == 0) {
306:             return ProposalState.Succeeded;
307:         } else if (proposal.executed) {
308:             return ProposalState.Executed;
309:         } else if (block.timestamp >= add256(proposal.eta, timelock.GRACE_PERIOD())) {
310:             return ProposalState.Expired;
311:         } else {
312:             return ProposalState.Queued;
313:         }
314:     }
315: 
316:     function castVote(uint proposalId, bool support) public {
317:         return _castVote(msg.sender, proposalId, support);
318:     }
319: 
320:     function castVoteBySig(uint proposalId, bool support, uint8 v, bytes32 r, bytes32 s) public {
321:         bytes32 domainSeparator = keccak256(
322:             abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainId(), address(this))
323:         );
324:         bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));
325:         bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
326:         address signatory = ecrecover(digest, v, r, s);
327:         require(signatory != address(0), "GovernorAlpha::castVoteBySig: invalid signature");
328:         return _castVote(signatory, proposalId, support);
329:     }
330: 
331:     function _castVote(address voter, uint proposalId, bool support) internal {
332:         require(state(proposalId) == ProposalState.Active, "GovernorAlpha::_castVote: voting is closed");
333:         Proposal storage proposal = proposals[proposalId];
334:         Receipt storage receipt = proposal.receipts[voter];
335:         require(receipt.hasVoted == false, "GovernorAlpha::_castVote: voter already voted");
336:         uint96 votes = xvs.getPriorVotes(voter, proposal.startBlock);
337: 
338:         if (support) {
339:             proposal.forVotes = add256(proposal.forVotes, votes);
340:         } else {
341:             proposal.againstVotes = add256(proposal.againstVotes, votes);
342:         }
343: 
344:         receipt.hasVoted = true;
345:         receipt.support = support;
346:         receipt.votes = votes;
347: 
348:         emit VoteCast(voter, proposalId, support, votes);
349:     }
350: 
351:     function __acceptAdmin() public {
352:         require(msg.sender == guardian, "GovernorAlpha::__acceptAdmin: sender must be gov guardian");
353:         timelock.acceptAdmin();
354:     }
355: 
356:     function __abdicate() public {
357:         require(msg.sender == guardian, "GovernorAlpha::__abdicate: sender must be gov guardian");
358:         guardian = address(0);
359:     }
360: 
361:     function __queueSetTimelockPendingAdmin(address newPendingAdmin, uint eta) public {
362:         require(msg.sender == guardian, "GovernorAlpha::__queueSetTimelockPendingAdmin: sender must be gov guardian");
363:         timelock.queueTransaction(address(timelock), 0, "setPendingAdmin(address)", abi.encode(newPendingAdmin), eta);
364:     }
365: 
366:     function __executeSetTimelockPendingAdmin(address newPendingAdmin, uint eta) public {
367:         require(msg.sender == guardian, "GovernorAlpha::__executeSetTimelockPendingAdmin: sender must be gov guardian");
368:         timelock.executeTransaction(address(timelock), 0, "setPendingAdmin(address)", abi.encode(newPendingAdmin), eta);
369:     }
370: 
371:     function add256(uint256 a, uint256 b) internal pure returns (uint) {
372:         uint c = a + b;
373:         require(c >= a, "addition overflow");
374:         return c;
375:     }
376: 
377:     function sub256(uint256 a, uint256 b) internal pure returns (uint) {
378:         require(b <= a, "subtraction underflow");
379:         return a - b;
380:     }
381: 
382:     function getChainId() internal pure returns (uint) {
383:         uint chainId;
384:         assembly {
385:             chainId := chainid()
386:         }
387:         return chainId;
388:     }
389: }
390: 
391: interface TimelockInterface { // <= FOUND
392:     function delay() external view returns (uint);
393: 
394:     function GRACE_PERIOD() external view returns (uint);
395: 
396:     function acceptAdmin() external;
397: 
398:     function queuedTransactions(bytes32 hash) external view returns (bool);
399: 
400:     function queueTransaction(
401:         address target,
402:         uint value,
403:         string calldata signature,
404:         bytes calldata data,
405:         uint eta
406:     ) external returns (bytes32);
407: 
408:     function cancelTransaction(
409:         address target,
410:         uint value,
411:         string calldata signature,
412:         bytes calldata data,
413:         uint eta
414:     ) external;
415: 
416:     function executeTransaction(
417:         address target,
418:         uint value,
419:         string calldata signature,
420:         bytes calldata data,
421:         uint eta
422:     ) external payable returns (bytes memory);
423: }
424: 
425: interface XVSInterface { // <= FOUND
426:     function getPriorVotes(address account, uint blockNumber) external view returns (uint96);
427: }
428: 

['1']

1: pragma solidity ^0.5.16;
2: pragma experimental ABIEncoderV2;
3: 
4: import { XvsVaultInterface, TimelockInterface, GovernorAlphaInterface } from "../Governance/GovernorBravoInterfaces.sol";
5: 
12: contract GovernorBravoEventsV1 { // <= FOUND
13:     
14:     event ProposalCreated(
15:         uint id,
16:         address proposer,
17:         address[] targets,
18:         uint[] values,
19:         string[] signatures,
20:         bytes[] calldatas,
21:         uint startBlock,
22:         uint endBlock,
23:         string description
24:     );
25: 
32:     event VoteCast(address indexed voter, uint proposalId, uint8 support, uint votes, string reason);
33: 
35:     event ProposalCanceled(uint id);
36: 
38:     event ProposalQueued(uint id, uint eta);
39: 
41:     event ProposalExecuted(uint id);
42: 
44:     event VotingDelaySet(uint oldVotingDelay, uint newVotingDelay);
45: 
47:     event VotingPeriodSet(uint oldVotingPeriod, uint newVotingPeriod);
48: 
50:     event NewImplementation(address oldImplementation, address newImplementation);
51: 
53:     event ProposalThresholdSet(uint oldProposalThreshold, uint newProposalThreshold);
54: 
56:     event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin);
57: 
59:     event NewAdmin(address oldAdmin, address newAdmin);
60: 
62:     event NewGuardian(address oldGuardian, address newGuardian);
63: 
65:     event ProposalMaxOperationsUpdated(uint oldMaxOperations, uint newMaxOperations);
66: }
67: 
73: contract GovernorBravoDelegatorStorage { // <= FOUND
74:     
75:     address public admin;
76: 
78:     address public pendingAdmin;
79: 
81:     address public implementation;
82: }
83: 
88: contract GovernorBravoDelegateStorageV1 is GovernorBravoDelegatorStorage { // <= FOUND
89:     
90:     uint public votingDelay;
91: 
93:     uint public votingPeriod;
94: 
96:     uint public proposalThreshold;
97: 
99:     uint public initialProposalId;
100: 
102:     uint public proposalCount;
103: 
105:     TimelockInterface public timelock;
106: 
108:     XvsVaultInterface public xvsVault;
109: 
111:     mapping(uint => Proposal) public proposals;
112: 
114:     mapping(address => uint) public latestProposalIds;
115: 
116:     struct Proposal {
117:         
118:         uint id;
119:         
120:         address proposer;
121:         
122:         uint eta;
123:         
124:         address[] targets;
125:         
126:         uint[] values;
127:         
128:         string[] signatures;
129:         
130:         bytes[] calldatas;
131:         
132:         uint startBlock;
133:         
134:         uint endBlock;
135:         
136:         uint forVotes;
137:         
138:         uint againstVotes;
139:         
140:         uint abstainVotes;
141:         
142:         bool canceled;
143:         
144:         bool executed;
145:         
146:         mapping(address => Receipt) receipts;
147:     }
148: 
150:     struct Receipt {
151:         
152:         bool hasVoted;
153:         
154:         uint8 support;
155:         
156:         uint96 votes;
157:     }
158: 
160:     enum ProposalState {
161:         Pending,
162:         Active,
163:         Canceled,
164:         Defeated,
165:         Succeeded,
166:         Queued,
167:         Expired,
168:         Executed
169:     }
170: 
172:     uint public proposalMaxOperations;
173: 
175:     address public guardian;
176: }
177: 

['1']

1: pragma solidity ^0.5.16;
2: pragma experimental ABIEncoderV2;
3: 
4: contract GovernorAlpha { // <= FOUND
5:     
6:     string public constant name = "Venus Governor Alpha";
7: 
9:     function quorumVotes() public pure returns (uint) {
10:         return 600000e18;
11:     } 
12: 
14:     function proposalThreshold() public pure returns (uint) {
15:         return 300000e18;
16:     } 
17: 
19:     function proposalMaxOperations() public pure returns (uint) {
20:         return 10;
21:     } 
22: 
24:     function votingDelay() public pure returns (uint) {
25:         return 1;
26:     } 
27: 
29:     function votingPeriod() public pure returns (uint) {
30:         return (60 * 60 * 24 * 3) / 3;
31:     } 
32: 
34:     TimelockInterface public timelock;
35: 
37:     XVSInterface public xvs;
38: 
40:     address public guardian;
41: 
43:     uint public proposalCount;
44: 
45:     struct Proposal {
46:         
47:         uint id;
48:         
49:         address proposer;
50:         
51:         uint eta;
52:         
53:         address[] targets;
54:         
55:         uint[] values;
56:         
57:         string[] signatures;
58:         
59:         bytes[] calldatas;
60:         
61:         uint startBlock;
62:         
63:         uint endBlock;
64:         
65:         uint forVotes;
66:         
67:         uint againstVotes;
68:         
69:         bool canceled;
70:         
71:         bool executed;
72:         
73:         mapping(address => Receipt) receipts;
74:     }
75: 
77:     struct Receipt {
78:         
79:         bool hasVoted;
80:         
81:         bool support;
82:         
83:         uint96 votes;
84:     }
85: 
87:     enum ProposalState {
88:         Pending,
89:         Active,
90:         Canceled,
91:         Defeated,
92:         Succeeded,
93:         Queued,
94:         Expired,
95:         Executed
96:     }
97: 
99:     mapping(uint => Proposal) public proposals;
100: 
102:     mapping(address => uint) public latestProposalIds;
103: 
105:     bytes32 public constant DOMAIN_TYPEHASH =
106:         keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)");
107: 
109:     bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,bool support)");
110: 
112:     event ProposalCreated(
113:         uint id,
114:         address proposer,
115:         address[] targets,
116:         uint[] values,
117:         string[] signatures,
118:         bytes[] calldatas,
119:         uint startBlock,
120:         uint endBlock,
121:         string description
122:     );
123: 
125:     event VoteCast(address voter, uint proposalId, bool support, uint votes);
126: 
128:     event ProposalCanceled(uint id);
129: 
131:     event ProposalQueued(uint id, uint eta);
132: 
134:     event ProposalExecuted(uint id);
135: 
136:     constructor(address timelock_, address xvs_, address guardian_) public {
137:         timelock = TimelockInterface(timelock_);
138:         xvs = XVSInterface(xvs_);
139:         guardian = guardian_;
140:     }
141: 
142:     function propose(
143:         address[] memory targets,
144:         uint[] memory values,
145:         string[] memory signatures,
146:         bytes[] memory calldatas,
147:         string memory description
148:     ) public returns (uint) {
149:         require(
150:             xvs.getPriorVotes(msg.sender, sub256(block.number, 1)) > proposalThreshold(),
151:             "GovernorAlpha::propose: proposer votes below proposal threshold"
152:         );
153:         require(
154:             targets.length == values.length &&
155:                 targets.length == signatures.length &&
156:                 targets.length == calldatas.length,
157:             "GovernorAlpha::propose: proposal function information arity mismatch"
158:         );
159:         require(targets.length != 0, "GovernorAlpha::propose: must provide actions");
160:         require(targets.length <= proposalMaxOperations(), "GovernorAlpha::propose: too many actions");
161: 
162:         uint latestProposalId = latestProposalIds[msg.sender];
163:         if (latestProposalId != 0) {
164:             ProposalState proposersLatestProposalState = state(latestProposalId);
165:             require(
166:                 proposersLatestProposalState != ProposalState.Active,
167:                 "GovernorAlpha::propose: found an already active proposal"
168:             );
169:             require(
170:                 proposersLatestProposalState != ProposalState.Pending,
171:                 "GovernorAlpha::propose: found an already pending proposal"
172:             );
173:         }
174: 
175:         uint startBlock = add256(block.number, votingDelay());
176:         uint endBlock = add256(startBlock, votingPeriod());
177: 
178:         proposalCount++;
179:         Proposal memory newProposal = Proposal({
180:             id: proposalCount,
181:             proposer: msg.sender,
182:             eta: 0,
183:             targets: targets,
184:             values: values,
185:             signatures: signatures,
186:             calldatas: calldatas,
187:             startBlock: startBlock,
188:             endBlock: endBlock,
189:             forVotes: 0,
190:             againstVotes: 0,
191:             canceled: false,
192:             executed: false
193:         });
194: 
195:         proposals[newProposal.id] = newProposal;
196:         latestProposalIds[newProposal.proposer] = newProposal.id;
197: 
198:         emit ProposalCreated(
199:             newProposal.id,
200:             msg.sender,
201:             targets,
202:             values,
203:             signatures,
204:             calldatas,
205:             startBlock,
206:             endBlock,
207:             description
208:         );
209:         return newProposal.id;
210:     }
211: 
212:     function queue(uint proposalId) public {
213:         require(
214:             state(proposalId) == ProposalState.Succeeded,
215:             "GovernorAlpha::queue: proposal can only be queued if it is succeeded"
216:         );
217:         Proposal storage proposal = proposals[proposalId];
218:         uint eta = add256(block.timestamp, timelock.delay());
219:         for (uint i = 0; i < proposal.targets.length; i++) {
220:             _queueOrRevert(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], eta);
221:         }
222:         proposal.eta = eta;
223:         emit ProposalQueued(proposalId, eta);
224:     }
225: 
226:     function _queueOrRevert(address target, uint value, string memory signature, bytes memory data, uint eta) internal {
227:         require(
228:             !timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))),
229:             "GovernorAlpha::_queueOrRevert: proposal action already queued at eta"
230:         );
231:         timelock.queueTransaction(target, value, signature, data, eta);
232:     }
233: 
234:     function execute(uint proposalId) public payable {
235:         require(
236:             state(proposalId) == ProposalState.Queued,
237:             "GovernorAlpha::execute: proposal can only be executed if it is queued"
238:         );
239:         Proposal storage proposal = proposals[proposalId];
240:         proposal.executed = true;
241:         for (uint i = 0; i < proposal.targets.length; i++) {
242:             timelock.executeTransaction.value(proposal.values[i])(
243:                 proposal.targets[i],
244:                 proposal.values[i],
245:                 proposal.signatures[i],
246:                 proposal.calldatas[i],
247:                 proposal.eta
248:             );
249:         }
250:         emit ProposalExecuted(proposalId);
251:     }
252: 
253:     function cancel(uint proposalId) public {
254:         ProposalState state = state(proposalId);
255:         require(state != ProposalState.Executed, "GovernorAlpha::cancel: cannot cancel executed proposal");
256: 
257:         Proposal storage proposal = proposals[proposalId];
258:         require(
259:             msg.sender == guardian ||
260:                 xvs.getPriorVotes(proposal.proposer, sub256(block.number, 1)) < proposalThreshold(),
261:             "GovernorAlpha::cancel: proposer above threshold"
262:         );
263: 
264:         proposal.canceled = true;
265:         for (uint i = 0; i < proposal.targets.length; i++) {
266:             timelock.cancelTransaction(
267:                 proposal.targets[i],
268:                 proposal.values[i],
269:                 proposal.signatures[i],
270:                 proposal.calldatas[i],
271:                 proposal.eta
272:             );
273:         }
274: 
275:         emit ProposalCanceled(proposalId);
276:     }
277: 
278:     function getActions(
279:         uint proposalId
280:     )
281:         public
282:         view
283:         returns (address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas)
284:     {
285:         Proposal storage p = proposals[proposalId];
286:         return (p.targets, p.values, p.signatures, p.calldatas);
287:     }
288: 
289:     function getReceipt(uint proposalId, address voter) public view returns (Receipt memory) {
290:         return proposals[proposalId].receipts[voter];
291:     }
292: 
293:     function state(uint proposalId) public view returns (ProposalState) {
294:         require(proposalCount >= proposalId && proposalId > 0, "GovernorAlpha::state: invalid proposal id");
295:         Proposal storage proposal = proposals[proposalId];
296:         if (proposal.canceled) {
297:             return ProposalState.Canceled;
298:         } else if (block.number <= proposal.startBlock) {
299:             return ProposalState.Pending;
300:         } else if (block.number <= proposal.endBlock) {
301:             return ProposalState.Active;
302:         } else if (proposal.forVotes <= proposal.againstVotes || proposal.forVotes < quorumVotes()) {
303:             return ProposalState.Defeated;
304:         } else if (proposal.eta == 0) {
305:             return ProposalState.Succeeded;
306:         } else if (proposal.executed) {
307:             return ProposalState.Executed;
308:         } else if (block.timestamp >= add256(proposal.eta, timelock.GRACE_PERIOD())) {
309:             return ProposalState.Expired;
310:         } else {
311:             return ProposalState.Queued;
312:         }
313:     }
314: 
315:     function castVote(uint proposalId, bool support) public {
316:         return _castVote(msg.sender, proposalId, support);
317:     }
318: 
319:     function castVoteBySig(uint proposalId, bool support, uint8 v, bytes32 r, bytes32 s) public {
320:         bytes32 domainSeparator = keccak256(
321:             abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainId(), address(this))
322:         );
323:         bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));
324:         bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
325:         address signatory = ecrecover(digest, v, r, s);
326:         require(signatory != address(0), "GovernorAlpha::castVoteBySig: invalid signature");
327:         return _castVote(signatory, proposalId, support);
328:     }
329: 
330:     function _castVote(address voter, uint proposalId, bool support) internal {
331:         require(state(proposalId) == ProposalState.Active, "GovernorAlpha::_castVote: voting is closed");
332:         Proposal storage proposal = proposals[proposalId];
333:         Receipt storage receipt = proposal.receipts[voter];
334:         require(receipt.hasVoted == false, "GovernorAlpha::_castVote: voter already voted");
335:         uint96 votes = xvs.getPriorVotes(voter, proposal.startBlock);
336: 
337:         if (support) {
338:             proposal.forVotes = add256(proposal.forVotes, votes);
339:         } else {
340:             proposal.againstVotes = add256(proposal.againstVotes, votes);
341:         }
342: 
343:         receipt.hasVoted = true;
344:         receipt.support = support;
345:         receipt.votes = votes;
346: 
347:         emit VoteCast(voter, proposalId, support, votes);
348:     }
349: 
350:     function __acceptAdmin() public {
351:         require(msg.sender == guardian, "GovernorAlpha::__acceptAdmin: sender must be gov guardian");
352:         timelock.acceptAdmin();
353:     }
354: 
355:     function __abdicate() public {
356:         require(msg.sender == guardian, "GovernorAlpha::__abdicate: sender must be gov guardian");
357:         guardian = address(0);
358:     }
359: 
360:     function __queueSetTimelockPendingAdmin(address newPendingAdmin, uint eta) public {
361:         require(msg.sender == guardian, "GovernorAlpha::__queueSetTimelockPendingAdmin: sender must be gov guardian");
362:         timelock.queueTransaction(address(timelock), 0, "setPendingAdmin(address)", abi.encode(newPendingAdmin), eta);
363:     }
364: 
365:     function __executeSetTimelockPendingAdmin(address newPendingAdmin, uint eta) public {
366:         require(msg.sender == guardian, "GovernorAlpha::__executeSetTimelockPendingAdmin: sender must be gov guardian");
367:         timelock.executeTransaction(address(timelock), 0, "setPendingAdmin(address)", abi.encode(newPendingAdmin), eta);
368:     }
369: 
370:     function add256(uint256 a, uint256 b) internal pure returns (uint) {
371:         uint c = a + b;
372:         require(c >= a, "addition overflow");
373:         return c;
374:     }
375: 
376:     function sub256(uint256 a, uint256 b) internal pure returns (uint) {
377:         require(b <= a, "subtraction underflow");
378:         return a - b;
379:     }
380: 
381:     function getChainId() internal pure returns (uint) {
382:         uint chainId;
383:         assembly {
384:             chainId := chainid()
385:         }
386:         return chainId;
387:     }
388: }
389: 
390: interface TimelockInterface { // <= FOUND
391:     function delay() external view returns (uint);
392: 
393:     function GRACE_PERIOD() external view returns (uint);
394: 
395:     function acceptAdmin() external;
396: 
397:     function queuedTransactions(bytes32 hash) external view returns (bool);
398: 
399:     function queueTransaction(
400:         address target,
401:         uint value,
402:         string calldata signature,
403:         bytes calldata data,
404:         uint eta
405:     ) external returns (bytes32);
406: 
407:     function cancelTransaction(
408:         address target,
409:         uint value,
410:         string calldata signature,
411:         bytes calldata data,
412:         uint eta
413:     ) external;
414: 
415:     function executeTransaction(
416:         address target,
417:         uint value,
418:         string calldata signature,
419:         bytes calldata data,
420:         uint eta
421:     ) external payable returns (bytes memory);
422: }
423: 
424: interface XVSInterface { // <= FOUND
425:     function getPriorVotes(address account, uint blockNumber) external view returns (uint96);
426: }
427: 

['1']

1: pragma solidity ^0.5.16;
2: pragma experimental ABIEncoderV2;
3: 
9: contract GovernorBravoEvents { // <= FOUND
10:     
11:     event ProposalCreated(
12:         uint id,
13:         address proposer,
14:         address[] targets,
15:         uint[] values,
16:         string[] signatures,
17:         bytes[] calldatas,
18:         uint startBlock,
19:         uint endBlock,
20:         string description,
21:         uint8 proposalType
22:     );
23: 
30:     event VoteCast(address indexed voter, uint proposalId, uint8 support, uint votes, string reason);
31: 
33:     event ProposalCanceled(uint id);
34: 
36:     event ProposalQueued(uint id, uint eta);
37: 
39:     event ProposalExecuted(uint id);
40: 
42:     event VotingDelaySet(uint oldVotingDelay, uint newVotingDelay);
43: 
45:     event VotingPeriodSet(uint oldVotingPeriod, uint newVotingPeriod);
46: 
48:     event NewImplementation(address oldImplementation, address newImplementation);
49: 
51:     event ProposalThresholdSet(uint oldProposalThreshold, uint newProposalThreshold);
52: 
54:     event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin);
55: 
57:     event NewAdmin(address oldAdmin, address newAdmin);
58: 
60:     event NewGuardian(address oldGuardian, address newGuardian);
61: 
63:     event ProposalMaxOperationsUpdated(uint oldMaxOperations, uint newMaxOperations);
64: }
65: 
71: contract GovernorBravoDelegatorStorage { // <= FOUND
72:     
73:     address public admin;
74: 
76:     address public pendingAdmin;
77: 
79:     address public implementation;
80: }
81: 
88: contract GovernorBravoDelegateStorageV1 is GovernorBravoDelegatorStorage { // <= FOUND
89:     
90:     uint public votingDelay;
91: 
93:     uint public votingPeriod;
94: 
96:     uint public proposalThreshold;
97: 
99:     uint public initialProposalId;
100: 
102:     uint public proposalCount;
103: 
105:     TimelockInterface public timelock;
106: 
108:     XvsVaultInterface public xvsVault;
109: 
111:     mapping(uint => Proposal) public proposals;
112: 
114:     mapping(address => uint) public latestProposalIds;
115: 
116:     struct Proposal {
117:         
118:         uint id;
119:         
120:         address proposer;
121:         
122:         uint eta;
123:         
124:         address[] targets;
125:         
126:         uint[] values;
127:         
128:         string[] signatures;
129:         
130:         bytes[] calldatas;
131:         
132:         uint startBlock;
133:         
134:         uint endBlock;
135:         
136:         uint forVotes;
137:         
138:         uint againstVotes;
139:         
140:         uint abstainVotes;
141:         
142:         bool canceled;
143:         
144:         bool executed;
145:         
146:         mapping(address => Receipt) receipts;
147:         
148:         uint8 proposalType;
149:     }
150: 
152:     struct Receipt {
153:         
154:         bool hasVoted;
155:         
156:         uint8 support;
157:         
158:         uint96 votes;
159:     }
160: 
162:     enum ProposalState {
163:         Pending,
164:         Active,
165:         Canceled,
166:         Defeated,
167:         Succeeded,
168:         Queued,
169:         Expired,
170:         Executed
171:     }
172: 
174:     uint public proposalMaxOperations;
175: 
177:     address public guardian;
178: }
179: 
186: contract GovernorBravoDelegateStorageV2 is GovernorBravoDelegateStorageV1 { // <= FOUND
187:     enum ProposalType {
188:         NORMAL,
189:         FASTTRACK,
190:         CRITICAL
191:     }
192: 
193:     struct ProposalConfig {
194:         
195:         uint256 votingDelay;
196:         
197:         uint256 votingPeriod;
198:         
199:         uint256 proposalThreshold;
200:     }
201: 
203:     mapping(uint => ProposalConfig) public proposalConfigs;
204: 
206:     mapping(uint => TimelockInterface) public proposalTimelocks;
207: }
208: 
214: interface TimelockInterface { // <= FOUND
215:     function delay() external view returns (uint);
216: 
217:     function GRACE_PERIOD() external view returns (uint);
218: 
219:     function acceptAdmin() external;
220: 
221:     function queuedTransactions(bytes32 hash) external view returns (bool);
222: 
223:     function queueTransaction(
224:         address target,
225:         uint value,
226:         string calldata signature,
227:         bytes calldata data,
228:         uint eta
229:     ) external returns (bytes32);
230: 
231:     function cancelTransaction(
232:         address target,
233:         uint value,
234:         string calldata signature,
235:         bytes calldata data,
236:         uint eta
237:     ) external;
238: 
239:     function executeTransaction(
240:         address target,
241:         uint value,
242:         string calldata signature,
243:         bytes calldata data,
244:         uint eta
245:     ) external payable returns (bytes memory);
246: }
247: 
248: interface XvsVaultInterface { // <= FOUND
249:     function getPriorVotes(address account, uint blockNumber) external view returns (uint96);
250: }
251: 
252: interface GovernorAlphaInterface { // <= FOUND
253:     
254:     function proposalCount() external returns (uint);
255: }
256: 

[NonCritical-66] Using Low-Level Call for Transfers

Resolution

Utilizing low-level calls like .call{value: value} for Ether transfers in Ethereum can be risky, as it can inadvertently allow malicious contract executions through fallback functions. To mitigate these risks and ensure safer Ether transfers, it is recommended to adopt more secure and explicit methods provided by reputable libraries such as OpenZeppelin. Functions like Address.sendValue() from OpenZeppelin provide a clearer and safer alternative for sending Ether, as they encapsulate necessary checks and error handling, ensuring that Ether is transferred securely and any errors are appropriately dealt with. This not only enhances the security of your smart contract but also improves code readability and maintainability, aligning with modern Solidity development practices.

Num of instances: 1

Findings

Click to show findings

['238']

238:         
239:         (bool sent, ) = to_.call{ value: originalValue_ }(""); // <= FOUND

[NonCritical-67] Long numbers should include underscores to improve readability and prevent typos

Resolution

A large number such as 2000000 is far more readable as 2_000_000, this will help prevent unintended bugs in the code

Num of instances: 1

Findings

Click to show findings

['274']

274:         uint256 gasToStoreAndEmit = 30000;  // <= FOUND

[NonCritical-68] package.json name variable should only consist of lowercase letters and underscores

Num of instances: 1

Findings

Click to show findings

['[1](project_package.txt: 1-2)']

1: {
2:   "name": "@venusprotocol/governance-contracts", // <= FOUND
3:   "description": "",
4:   "version": "1.4.0",
5:   "author": "",
6:   "files": [
7:     "artifacts",
8:     "dist",
9:     "deploy",
10:     "deployments",
11:     "contracts"
12:   ],
13:   "keywords": [
14:     "blockchain",
15:     "ethers",
16:     "ethereum",
17:     "hardhat",
18:     "smart-contracts",
19:     "solidity",
20:     "template",
21:     "typescript",
22:     "typechain"
23:   ],
24:   "packageManager": "yarn@3.2.0",
25:   "publishConfig": {
26:     "access": "public"
27:   },
28:   "scripts": {
29:     "compile": "hardhat compile",
30:     "test": "hardhat test",
31:     "build": "rm -rf dist && tsc --declaration && hardhat compile",
32:     "publish:dist": "yarn build && cd dist && yarn publish --access public",
33:     "lint": "yarn lint:ts && yarn lint:sol && yarn prettier:check",
34:     "lint:ts": "eslint --ignore-path ./.eslintignore --ext .js,.ts .",
35:     "lint:sol": "solhint \"contracts/**/*.sol\"",
36:     "lint:sol:fix": "prettier --write \"contracts/**/*.sol\"",
37:     "prettier": "prettier  --write \"**/*.{js,json,md,ts,yaml,yml,sol}\"",
38:     "prettier:check": "prettier --check \"**/*.{js,json,md,ts,yaml,yml,sol}\"",
39:     "docgen": "hardhat docgen",
40:     "prepare": "husky install"
41:   },
42:   "dependencies": {
43:     "@venusprotocol/solidity-utilities": "1.3.0",
44:     "hardhat-deploy-ethers": "^0.3.0-beta.13",
45:     "module-alias": "^2.2.2"
46:   },
47:   "devDependencies": {
48:     "@commitlint/cli": "^17.4.4",
49:     "@commitlint/config-conventional": "^17.4.4",
50:     "@defi-wonderland/smock": "^2.3.4",
51:     "@ethersproject/abi": "^5.7.0",

[NonCritical-69] package.json missing/empty description

Num of instances: 1

Findings

Click to show findings

['[1](project_package.txt: 1-3)']

1: {
2:   "name": "@venusprotocol/governance-contracts",
3:   "description": "", // <= FOUND
4:   "version": "1.4.0",
5:   "author": "",
6:   "files": [
7:     "artifacts",
8:     "dist",
9:     "deploy",
10:     "deployments",
11:     "contracts"
12:   ],
13:   "keywords": [
14:     "blockchain",
15:     "ethers",
16:     "ethereum",
17:     "hardhat",
18:     "smart-contracts",
19:     "solidity",
20:     "template",
21:     "typescript",
22:     "typechain"
23:   ],
24:   "packageManager": "yarn@3.2.0",
25:   "publishConfig": {
26:     "access": "public"
27:   },
28:   "scripts": {
29:     "compile": "hardhat compile",
30:     "test": "hardhat test",
31:     "build": "rm -rf dist && tsc --declaration && hardhat compile",
32:     "publish:dist": "yarn build && cd dist && yarn publish --access public",
33:     "lint": "yarn lint:ts && yarn lint:sol && yarn prettier:check",
34:     "lint:ts": "eslint --ignore-path ./.eslintignore --ext .js,.ts .",
35:     "lint:sol": "solhint \"contracts/**/*.sol\"",
36:     "lint:sol:fix": "prettier --write \"contracts/**/*.sol\"",
37:     "prettier": "prettier  --write \"**/*.{js,json,md,ts,yaml,yml,sol}\"",
38:     "prettier:check": "prettier --check \"**/*.{js,json,md,ts,yaml,yml,sol}\"",
39:     "docgen": "hardhat docgen",
40:     "prepare": "husky install"
41:   },
42:   "dependencies": {
43:     "@venusprotocol/solidity-utilities": "1.3.0",
44:     "hardhat-deploy-ethers": "^0.3.0-beta.13",
45:     "module-alias": "^2.2.2"
46:   },
47:   "devDependencies": {
48:     "@commitlint/cli": "^17.4.4",
49:     "@commitlint/config-conventional": "^17.4.4",
50:     "@defi-wonderland/smock": "^2.3.4",
51:     "@ethersproject/abi": "^5.7.0",
52:     "@ethersproject/abstract-signer": "^5.7.0",

[NonCritical-70] Avoid revertible function calls in a constructor

Resolution

It is advisable to to perform validation within the constructor itself rather than in function calls it makes. This is because contract deployement may be performed through a frontend or manually so by having all of the validation conditions viewable in a single place allows for greater transparency during deployment for both the team and project users.

Num of instances: 2

Findings

Click to show findings

['25']

12:     constructor(
13:         address timelock_,
14:         address xvsVault_,
15:         address admin_,
16:         address implementation_,
17:         uint votingPeriod_,
18:         uint votingDelay_,
19:         uint proposalThreshold_,
20:         address guardian_
21:     ) public {
22:         
23:         admin = msg.sender;
24: 
25:         delegateTo( // <= FOUND
26:             implementation_,
27:             abi.encodeWithSignature(
28:                 "initialize(address,address,uint256,uint256,uint256,address)",
29:                 timelock_,
30:                 xvsVault_,
31:                 votingPeriod_,
32:                 votingDelay_,
33:                 proposalThreshold_,
34:                 guardian_
35:             )
36:         );
37: 
38:         _setImplementation(implementation_);
39: 
40:         admin = admin_;
41:     }

['25']

12:     constructor(
13:         address timelock_,
14:         address xvsVault_,
15:         address admin_,
16:         address implementation_,
17:         uint votingPeriod_,
18:         uint votingDelay_,
19:         uint proposalThreshold_,
20:         address guardian_
21:     ) public {
22:         
23:         admin = msg.sender;
24: 
25:         delegateTo( // <= FOUND
26:             implementation_,
27:             abi.encodeWithSignature(
28:                 "initialize(address,address,uint256,uint256,uint256,address)",
29:                 timelock_,
30:                 xvsVault_,
31:                 votingPeriod_,
32:                 votingDelay_,
33:                 proposalThreshold_,
34:                 guardian_
35:             )
36:         );
37: 
38:         _setImplementation(implementation_);
39: 
40:         admin = admin_;
41:     }

[NonCritical-71] Avoid declaring variables with the names of defined functions within the project

Resolution

Having such variables can create confusion in both developers and in users of the project. Consider renaming these variables to improve code clarity.

Num of instances: 4

Findings

Click to show findings

['49']

49:         bool isAllowedToCall = _accessControlManager.isAllowedToCall(msg.sender, signature); // <= FOUND

['200']

199:         
200:         uint256 proposalThreshold; // <= FOUND

['196']

195:         
196:         uint256 votingDelay; // <= FOUND

['198']

197:         
198:         uint256 votingPeriod; // <= FOUND

[NonCritical-72] Consider using the Upgradeable version of the OpenZeppelin libraries/contracts

Resolution

Using the upgradeable counterpart of the OpenZeppelin (OZ) library in Solidity is beneficial for creating contracts that can be updated in the future. OpenZeppelin's upgradeable contracts library is designed with proxy patterns in mind, which allow the logic of contracts to be upgraded while preserving the contract's state and address. This can be crucial for long-lived contracts where future requirements or improvements may not be fully known at the time of deployment. The upgradeable OZ contracts also include protection against a class of vulnerabilities related to initialization of storage variables in upgradeable contracts. Hence, it's a good idea to use them when developing contracts that may need to be upgraded in the future, as they provide a solid foundation for secure and upgradeable smart contracts.

Num of instances: 6

Findings

Click to show findings

['3']

3: import "@openzeppelin/contracts/access/AccessControl.sol"; // <= FOUND

['4']

4: import "@openzeppelin/contracts/access/IAccessControl.sol"; // <= FOUND

['6']

6: import { Pausable } from "@openzeppelin/contracts/security/Pausable.sol"; // <= FOUND

['5']

5: import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; // <= FOUND

['6']

6: import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; // <= FOUND

['5']

5: import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol"; // <= FOUND

[NonCritical-73] Simplify complex require statements

Resolution

Simplifying complex require statements with multiple logical OR (||) operators in Solidity can be achieved by using revert statements. This involves converting the conditional logic of require into separate if statements, each followed by a revert for specific failing conditions. This approach enhances readability and maintains clarity, especially when dealing with multiple conditions. It allows for more descriptive error messages for each specific case, improving the debugging process and making the code more maintainable.

Num of instances: 2

Findings

Click to show findings

['331']

331: require(
332:             msg.sender == guardian || // <= FOUND
333:                 msg.sender == proposal.proposer || // <= FOUND
334:                 xvsVault.getPriorVotes(proposal.proposer, sub256(block.number, 1)) <
335:                 proposalConfigs[proposal.proposalType].proposalThreshold,
336:             "GovernorBravo::cancel: proposer above threshold"
337:         );

['237']

237: require(
238:             msg.sender == guardian || // <= FOUND
239:                 msg.sender == proposal.proposer || // <= FOUND
240:                 xvsVault.getPriorVotes(proposal.proposer, sub256(block.number, 1)) < proposalThreshold,
241:             "GovernorBravo::cancel: proposer above threshold"
242:         );

[NonCritical-74] Constructors should emit an event

Resolution

Emitting an event in a constructor of a smart contract provides transparency and traceability in blockchain applications. This event logs the contract’s creation, aiding in monitoring and verifying contract deployment. Although constructors are executed only once, the emitted event ensures the contract's initialization is recorded on the blockchain.

Num of instances: 12

Findings

Click to show findings

['61']

61:     constructor() { // <= FOUND
62:         
63:         
64:         _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
65:     }

['38']

38:     constructor(address endpoint_) NonblockingLzApp(endpoint_) { // <= FOUND
39:         ensureNonzeroAddress(endpoint_);
40:     }

['51']

51:     constructor(address accessControlManager_) { // <= FOUND
52:         ensureNonzeroAddress(accessControlManager_);
53:         accessControlManager = accessControlManager_;
54:     }

['12']

12:     constructor( // <= FOUND
13:         address timelock_,
14:         address xvsVault_,
15:         address admin_,
16:         address implementation_,
17:         uint votingPeriod_,
18:         uint votingDelay_,
19:         uint proposalThreshold_,
20:         address guardian_
21:     ) public {
22:         
23:         admin = msg.sender;
24: 
25:         delegateTo(
26:             implementation_,
27:             abi.encodeWithSignature(
28:                 "initialize(address,address,uint256,uint256,uint256,address)",
29:                 timelock_,
30:                 xvsVault_,
31:                 votingPeriod_,
32:                 votingDelay_,
33:                 proposalThreshold_,
34:                 guardian_
35:             )
36:         );
37: 
38:         _setImplementation(implementation_);
39: 
40:         admin = admin_;
41:     }

['136']

136:     constructor(address timelock_, address xvs_, address guardian_) public { // <= FOUND
137:         timelock = TimelockInterface(timelock_);
138:         xvs = XVSInterface(xvs_);
139:         guardian = guardian_;
140:     }

['136']

136:     constructor(address timelock_, address xvs_, address guardian_, uint256 lastProposalId_) public { // <= FOUND
137:         timelock = TimelockInterface(timelock_);
138:         xvs = XVSInterface(xvs_);
139:         guardian = guardian_;
140:         proposalCount = lastProposalId_;
141:     }

['12']

12:     constructor( // <= FOUND
13:         address timelock_,
14:         address xvsVault_,
15:         address admin_,
16:         address implementation_,
17:         uint votingPeriod_,
18:         uint votingDelay_,
19:         uint proposalThreshold_,
20:         address guardian_
21:     ) public {
22:         
23:         admin = msg.sender;
24: 
25:         delegateTo(
26:             implementation_,
27:             abi.encodeWithSignature(
28:                 "initialize(address,address,uint256,uint256,uint256,address)",
29:                 timelock_,
30:                 xvsVault_,
31:                 votingPeriod_,
32:                 votingDelay_,
33:                 proposalThreshold_,
34:                 guardian_
35:             )
36:         );
37: 
38:         _setImplementation(implementation_);
39: 
40:         admin = admin_;
41:     }

['33']

33:     constructor(address omnichainGovernanceExecutor_) { // <= FOUND
34:         require(omnichainGovernanceExecutor_ != address(0), "Address must not be zero");
35:         OMNICHAIN_GOVERNANCE_EXECUTOR = IOmnichainGovernanceExecutor(omnichainGovernanceExecutor_);
36:         _disableInitializers();
37:     }

['134']

134:     constructor(address endpoint_, address guardian_, uint16 srcChainId_) BaseOmnichainControllerDest(endpoint_) { // <= FOUND
135:         ensureNonzeroAddress(guardian_);
136:         GUARDIAN = guardian_;
137:         srcChainId = srcChainId_;
138:     }

['77']

77:     constructor( // <= FOUND
78:         ILayerZeroEndpoint lzEndpoint_,
79:         address accessControlManager_
80:     ) BaseOmnichainControllerSrc(accessControlManager_) {
81:         ensureNonzeroAddress(address(lzEndpoint_));
82:         LZ_ENDPOINT = lzEndpoint_;
83:     }

['72']

72:     constructor(address admin_, uint delay_) public { // <= FOUND
73:         require(delay_ >= MINIMUM_DELAY, "Timelock::constructor: Delay must exceed minimum delay."); // <= FOUND
74:         require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay.");
75: 
76:         admin = admin_;
77:         delay = delay_;
78:     }

['73']

73:     constructor(address admin_, uint256 delay_) { // <= FOUND
74:         require(delay_ >= MINIMUM_DELAY(), "Timelock::constructor: Delay must exceed minimum delay."); // <= FOUND
75:         require(delay_ <= MAXIMUM_DELAY(), "Timelock::setDelay: Delay must not exceed maximum delay.");
76:         ensureNonzeroAddress(admin_);
77: 
78:         admin = admin_;
79:         delay = delay_;
80:     }

[NonCritical-75] Contract and Abstract files should have a fixed compiler version

Resolution

Using a fixed compiler version in Solidity contract and abstract files ensures consistency and predictability in smart contract behavior. A fixed version prevents unforeseen issues arising from compiler updates, which might introduce breaking changes or new bugs. It guarantees that the contract's behavior remains stable and consistent with the version used during its development and testing.

Num of instances: 14

Findings

Click to show findings

[]

11: contract GovernorBravoDelegatorV1 is GovernorBravoDelegatorStorage, GovernorBravoEventsV1 

[]

4: contract GovernorAlpha 

[]

4: contract GovernorAlpha2 

[]

73: contract GovernorBravoDelegate is GovernorBravoDelegateStorageV2, GovernorBravoEvents 

[]

11: contract GovernorBravoDelegateV1 is GovernorBravoDelegateStorageV1, GovernorBravoEventsV1 

[]

11: contract GovernorBravoDelegator is GovernorBravoDelegatorStorage, GovernorBravoEvents 

[]

9: contract GovernorBravoEvents 

[]

73: contract GovernorBravoDelegatorStorage 

[]

88: contract GovernorBravoDelegateStorageV1 is GovernorBravoDelegatorStorage 

[]

186: contract GovernorBravoDelegateStorageV2 is GovernorBravoDelegateStorageV1 

[]

12: contract GovernorBravoEventsV1 

[]

88: contract GovernorBravoDelegateStorageV1 is GovernorBravoDelegatorStorage 

[]

10: contract Timelock 

[]

16: library SafeMath 

[NonCritical-76] Function call in event emit

Resolution

Emits are designed to make users aware of state variable changes. As such the event declaration should be clear on what it will output, by passing in function calls this can affect the readability of a emit declaration. As such it is advisable to make function calls outside of the event emit and pass the return value into the emit instead.

Num of instances: 6

Findings

Click to show findings

['319']

319:     function castVote(uint proposalId, uint8 support) external { // <= FOUND
320:         emit VoteCast(msg.sender, proposalId, support, castVoteInternal(msg.sender, proposalId, support), ""); // <= FOUND
321:     }

['319']

319:     function castVote(uint proposalId, uint8 support) external { // <= FOUND
320:         emit VoteCast(msg.sender, proposalId, support, castVoteInternal(msg.sender, proposalId, support), ""); // <= FOUND
321:     }

['329']

329:     function castVoteWithReason(uint proposalId, uint8 support, string calldata reason) external { // <= FOUND
330:         emit VoteCast(msg.sender, proposalId, support, castVoteInternal(msg.sender, proposalId, support), reason); // <= FOUND
331:     }

['329']

329:     function castVoteWithReason(uint proposalId, uint8 support, string calldata reason) external { // <= FOUND
330:         emit VoteCast(msg.sender, proposalId, support, castVoteInternal(msg.sender, proposalId, support), reason); // <= FOUND
331:     }

['337']

337:     function castVoteBySig(uint proposalId, uint8 support, uint8 v, bytes32 r, bytes32 s) external { // <= FOUND
338:         bytes32 domainSeparator = keccak256(
339:             abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainIdInternal(), address(this))
340:         );
341:         bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));
342:         bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
343:         address signatory = ecrecover(digest, v, r, s);
344:         require(signatory != address(0), "GovernorBravo::castVoteBySig: invalid signature");
345:         emit VoteCast(signatory, proposalId, support, castVoteInternal(signatory, proposalId, support), ""); // <= FOUND
346:     }

['337']

337:     function castVoteBySig(uint proposalId, uint8 support, uint8 v, bytes32 r, bytes32 s) external { // <= FOUND
338:         bytes32 domainSeparator = keccak256(
339:             abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainIdInternal(), address(this))
340:         );
341:         bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));
342:         bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
343:         address signatory = ecrecover(digest, v, r, s);
344:         require(signatory != address(0), "GovernorBravo::castVoteBySig: invalid signature");
345:         emit VoteCast(signatory, proposalId, support, castVoteInternal(signatory, proposalId, support), ""); // <= FOUND
346:     }

[NonCritical-77] Consider using AccessControlDefaultAdminRules rather than AccessControl

Resolution

Using AccessControlDefaultAdminRules over AccessControl in Solidity contracts provides a predefined set of rules for administrative privileges, simplifying the management of roles and permissions. It offers a structured approach to defining who can grant or revoke roles, enhancing security and governance within contracts by standardizing access controls and reducing the risk of misconfiguration.

Num of instances: 1

Findings

Click to show findings

['52']

52: contract AccessControlManager is AccessControl, IAccessControlManagerV8  // <= FOUND

[NonCritical-78] Errors should have parameters

Resolution

In Solidity, custom errors with parameters offer a gas-efficient way to convey detailed information about issues encountered during contract execution. Unlike revert messages, which are strings consuming more gas, custom errors defined with parameters allow developers to specify types and details of errors succinctly. This method enhances debugging, provides clearer insights into contract failures, and improves the developer's and end-user's understanding of what went wrong, all while optimizing for gas usage and maintaining contract efficiency.

Num of instances: 2

Findings

Click to show findings

['132']

132:     
135:     error InvalidProposalId(); // <= FOUND

['132']

132: error InvalidProposalId(); // <= FOUND

[NonCritical-79] Consider using OpenZeppelins SafeCall library when making calls to arbitrary contracts

Resolution

Using OpenZeppelin's SafeCall library for interactions with arbitrary contracts is a best practice in smart contract development. This library provides functions that ensure safer external calls by validating that calls are successfully completed, helping to prevent common pitfalls such as reentrancy attacks or unexpected failures. It encapsulates low-level call operations with safety checks, reducing the risk of vulnerabilities associated with direct interactions with unknown code. Incorporating SafeCall enhances contract security and robustness, making it a wise choice for developers aiming to safeguard their applications.

Num of instances: 3

Findings

Click to show findings

['214']

214:     function fallbackWithdraw(
215:         address to_,
216:         uint256 pId_,
217:         uint16 remoteChainId_,
218:         bytes calldata payload_,
219:         bytes calldata adapterParams_,
220:         uint256 originalValue_
221:     ) external onlyOwner nonReentrant {
222:         ensureNonzeroAddress(to_);
223:         require(originalValue_ > 0, "OmnichainProposalSender: invalid native amount");
224:         require(payload_.length != 0, "OmnichainProposalSender: Empty payload");
225: 
226:         bytes32 hash = storedExecutionHashes[pId_];
227:         require(hash != bytes32(0), "OmnichainProposalSender: no stored payload");
228: 
229:         bytes memory execution = abi.encode(remoteChainId_, payload_, adapterParams_, originalValue_);
230:         require(keccak256(execution) == hash, "OmnichainProposalSender: invalid execution params");
231: 
232:         delete storedExecutionHashes[pId_];
233: 
234:         emit FallbackWithdraw(to_, originalValue_);
235:         emit ClearPayload(pId_, hash);
236: 
237:         
238:         (bool sent, ) = to_.call{ value: originalValue_ }(""); // <= FOUND
239:         require(sent, "Call failed");
240:     }

['177']

177:     function executeTransaction(
178:         address target,
179:         uint value,
180:         string memory signature,
181:         bytes memory data,
182:         uint eta
183:     ) public payable returns (bytes memory) {
184:         require(msg.sender == admin, "Timelock::executeTransaction: Call must come from admin.");
185: 
186:         bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
187:         require(queuedTransactions[txHash], "Timelock::executeTransaction: Transaction hasn't been queued.");
188:         require(getBlockTimestamp() >= eta, "Timelock::executeTransaction: Transaction hasn't surpassed time lock.");
189:         require(getBlockTimestamp() <= eta.add(GRACE_PERIOD), "Timelock::executeTransaction: Transaction is stale.");
190: 
191:         queuedTransactions[txHash] = false;
192: 
193:         bytes memory callData;
194: 
195:         if (bytes(signature).length == 0) {
196:             callData = data;
197:         } else {
198:             callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data);
199:         }
200: 
201:         
202:         (bool success, bytes memory returnData) = target.call.value(value)(callData); // <= FOUND
203:         require(success, "Timelock::executeTransaction: Transaction execution reverted.");
204: 
205:         emit ExecuteTransaction(txHash, target, value, signature, data, eta);
206: 
207:         return returnData;
208:     }

['217']

217:     function executeTransaction(
218:         address target,
219:         uint256 value,
220:         string calldata signature,
221:         bytes calldata data,
222:         uint256 eta
223:     ) public payable returns (bytes memory) {
224:         require(msg.sender == admin, "Timelock::executeTransaction: Call must come from admin.");
225: 
226:         bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
227:         require(queuedTransactions[txHash], "Timelock::executeTransaction: Transaction hasn't been queued.");
228:         require(getBlockTimestamp() >= eta, "Timelock::executeTransaction: Transaction hasn't surpassed time lock.");
229:         require(getBlockTimestamp() <= eta + GRACE_PERIOD(), "Timelock::executeTransaction: Transaction is stale.");
230: 
231:         delete (queuedTransactions[txHash]);
232: 
233:         bytes memory callData;
234: 
235:         if (bytes(signature).length == 0) {
236:             callData = data;
237:         } else {
238:             callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data);
239:         }
240: 
241:         
242:         (bool success, bytes memory returnData) = target.call{ value: value }(callData); // <= FOUND
243:         require(success, "Timelock::executeTransaction: Transaction execution reverted.");
244: 
245:         emit ExecuteTransaction(txHash, target, value, signature, data, eta);
246: 
247:         return returnData;
248:     }

[NonCritical-80] Memory-safe annotation missing

Resolution

Tagging assembly blocks as "memory safe" in Solidity helps maintainers and auditors quickly identify areas of the code less likely to introduce vulnerabilities due to improper memory access. This practice promotes safer contract development by clearly communicating assumptions about memory handling, aiding in the review process, and reducing the risk of introducing memory-related bugs. It's a part of best coding practices, enhancing code clarity and security, especially in complex, low-level operations where Solidity's safety checks are bypassed.

Num of instances: 1

Findings

Click to show findings

['384']

384:         assembly {
385:             chainId := chainid()
386:         }

[NonCritical-81] Constant state variables defined more than once

Resolution

Rather than redefining state variable constant, consider utilising a library to store all constants as this will prevent data redundancy

Num of instances: 12

Findings

Click to show findings

['6']

6: string public constant name = "Venus Governor Alpha"; // <= FOUND

['105']

105: bytes32 public constant DOMAIN_TYPEHASH = // <= FOUND

['109']

109: bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,bool support)"); // <= FOUND

['13']

13: string public constant name = "Venus Governor Bravo"; // <= FOUND

['16']

16: uint public constant MIN_PROPOSAL_THRESHOLD = 150000e18;  // <= FOUND

['19']

19: uint public constant MAX_PROPOSAL_THRESHOLD = 300000e18;  // <= FOUND

['22']

22: uint public constant MIN_VOTING_PERIOD = 20 * 60 * 3;  // <= FOUND

['25']

25: uint public constant MAX_VOTING_PERIOD = 20 * 60 * 24 * 14;  // <= FOUND

['28']

28: uint public constant MIN_VOTING_DELAY = 1; // <= FOUND

['31']

31: uint public constant MAX_VOTING_DELAY = 20 * 60 * 24 * 7;  // <= FOUND

['34']

34: uint public constant quorumVotes = 600000e18;  // <= FOUND

['41']

41: bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,uint8 support)"); // <= FOUND

[NonCritical-82] Function control structures do not follow the Solidity Style Guide

Resolution

When defining function declarations, the opening brace should be on the same line as the end of the declaration and preceded by only a single space.

Num of instances: 2

Findings

Click to show findings

['285']

279:     function getActions(
280:         uint proposalId
281:     )
282:         public
283:         view
284:         returns (address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas)
285:     { // <= FOUND

['269']

263:     function getActions(
264:         uint proposalId
265:     )
266:         external
267:         view
268:         returns (address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas)
269:     { // <= FOUND

[Gas-1] State variables used within a function more than once should be cached to save gas

Resolution

Cache such variables and perform operations on them, if operations include modifications to the state variable(s) then remember to equate the state variable to it's cached counterpart at the end

Num of instances: 1

Findings

Click to show findings

['78']

78:     function _isEligibleToReceive(uint256 noOfCommands_) internal { // <= FOUND 'function _isEligibleToReceive'
79:         uint256 currentBlockTimestamp = block.timestamp;
80: 
81:         
82:         uint256 receivedInWindow = last24HourCommandsReceived;
83: 
84:         
85:         if (currentBlockTimestamp - last24HourReceiveWindowStart > 1 days) { // <= FOUND 'last24HourReceiveWindowStart'
86:             receivedInWindow = noOfCommands_;
87:             last24HourReceiveWindowStart = currentBlockTimestamp; // <= FOUND 'last24HourReceiveWindowStart'
88:         } else {
89:             receivedInWindow += noOfCommands_;
90:         }
91: 
92:         
93:         require(receivedInWindow <= maxDailyReceiveLimit, "Daily Transaction Limit Exceeded");
94: 
95:         
96:         last24HourCommandsReceived = receivedInWindow;
97:     }

[Gas-2] The result of a function call should be cached rather than re-calling the function

Resolution

External calls in Solidity are costly in terms of gas usage. This can significantly impact contract efficiency and cost. Functions that make repetitive calls to fetch the same data from other contracts can cause unnecessary gas expenditure. To optimize this, it's advisable to store the returned value of these function calls in a state variable, essentially caching the data. This data can be updated at regular intervals or under specific conditions instead of fetching it from the external contract on every invocation. Be sure to analyze the frequency of data change in the external contract to balance data freshness with gas efficiency when implementing caching.

Num of instances: 2

Findings

Click to show findings

['177']

177:     function executeTransaction(
178:         address target,
179:         uint value,
180:         string memory signature,
181:         bytes memory data,
182:         uint eta
183:     ) public payable returns (bytes memory) {
184:         require(msg.sender == admin, "Timelock::executeTransaction: Call must come from admin.");
185: 
186:         bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
187:         require(queuedTransactions[txHash], "Timelock::executeTransaction: Transaction hasn't been queued.");
188:         require(getBlockTimestamp() >= eta, "Timelock::executeTransaction: Transaction hasn't surpassed time lock."); // <= FOUND
189:         require(getBlockTimestamp() <= eta.add(GRACE_PERIOD), "Timelock::executeTransaction: Transaction is stale."); // <= FOUND
190: 
191:         queuedTransactions[txHash] = false;
192: 
193:         bytes memory callData;
194: 
195:         if (bytes(signature).length == 0) {
196:             callData = data;
197:         } else {
198:             callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data);
199:         }
200: 
201:         
202:         (bool success, bytes memory returnData) = target.call.value(value)(callData);
203:         require(success, "Timelock::executeTransaction: Transaction execution reverted.");
204: 
205:         emit ExecuteTransaction(txHash, target, value, signature, data, eta);
206: 
207:         return returnData;
208:     }

['217']

217:     function executeTransaction(
218:         address target,
219:         uint256 value,
220:         string calldata signature,
221:         bytes calldata data,
222:         uint256 eta
223:     ) public payable returns (bytes memory) {
224:         require(msg.sender == admin, "Timelock::executeTransaction: Call must come from admin.");
225: 
226:         bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
227:         require(queuedTransactions[txHash], "Timelock::executeTransaction: Transaction hasn't been queued.");
228:         require(getBlockTimestamp() >= eta, "Timelock::executeTransaction: Transaction hasn't surpassed time lock."); // <= FOUND
229:         require(getBlockTimestamp() <= eta + GRACE_PERIOD(), "Timelock::executeTransaction: Transaction is stale."); // <= FOUND
230: 
231:         delete (queuedTransactions[txHash]);
232: 
233:         bytes memory callData;
234: 
235:         if (bytes(signature).length == 0) {
236:             callData = data;
237:         } else {
238:             callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data);
239:         }
240: 
241:         
242:         (bool success, bytes memory returnData) = target.call{ value: value }(callData);
243:         require(success, "Timelock::executeTransaction: Transaction execution reverted.");
244: 
245:         emit ExecuteTransaction(txHash, target, value, signature, data, eta);
246: 
247:         return returnData;
248:     }

[Gas-3] Multiple accesses of the same mapping/array key/index should be cached

Resolution

Caching repeated accesses to the same mapping or array key/index in smart contracts can lead to significant gas savings. In Solidity, each read operation from storage (like accessing a value in a mapping or array using a key or index) costs gas. By storing the accessed value in a local variable and reusing it within the function, you avoid multiple expensive storage read operations. This practice is particularly beneficial in loops or functions with multiple reads of the same data. Implementing this caching approach enhances efficiency and reduces transaction costs, which is crucial for optimizing smart contract performance and user experience on the blockchain.

Num of instances: 3

Findings

Click to show findings

['63']

63:     function setMaxDailyLimit(uint16 chainId_, uint256 limit_) external {
64:         _ensureAllowed("setMaxDailyLimit(uint16,uint256)");
65:         emit SetMaxDailyLimit(chainId_, chainIdToMaxDailyLimit[chainId_], limit_); // <= FOUND
66:         chainIdToMaxDailyLimit[chainId_] = limit_; // <= FOUND
67:     }

['109']

109:     function _isEligibleToSend(uint16 dstChainId_, uint256 noOfCommands_) internal {
110:         
111:         uint256 currentBlockTimestamp = block.timestamp;
112:         uint256 lastDayWindowStart = chainIdToLast24HourWindowStart[dstChainId_]; // <= FOUND
113:         uint256 commandsSentInWindow = chainIdToLast24HourCommandsSent[dstChainId_]; // <= FOUND
114:         uint256 maxDailyLimit = chainIdToMaxDailyLimit[dstChainId_]; // <= FOUND
115:         uint256 lastProposalSentTimestamp = chainIdToLastProposalSentTimestamp[dstChainId_]; // <= FOUND
116: 
117:         
118:         if (currentBlockTimestamp - lastDayWindowStart > 1 days) {
119:             commandsSentInWindow = noOfCommands_;
120:             chainIdToLast24HourWindowStart[dstChainId_] = currentBlockTimestamp; // <= FOUND
121:         } else {
122:             commandsSentInWindow += noOfCommands_;
123:         }
124: 
125:         
126:         require(commandsSentInWindow <= maxDailyLimit, "Daily Transaction Limit Exceeded");
127:         
128:         require(lastProposalSentTimestamp != currentBlockTimestamp, "Multiple bridging in a proposal");
129: 
130:         
131:         chainIdToLast24HourCommandsSent[dstChainId_] = commandsSentInWindow; // <= FOUND
132:         
133:         chainIdToLastProposalSentTimestamp[dstChainId_] = currentBlockTimestamp; // <= FOUND
134:     }

['249']

249:     function setTrustedRemoteAddress(uint16 remoteChainId_, bytes calldata newRemoteAddress_) external {
250:         _ensureAllowed("setTrustedRemoteAddress(uint16,bytes)");
251:         require(remoteChainId_ != 0, "ChainId must not be zero");
252:         ensureNonzeroAddress(address(uint160(bytes20(newRemoteAddress_))));
253:         bytes memory oldRemoteAddress = trustedRemoteLookup[remoteChainId_]; // <= FOUND
254:         trustedRemoteLookup[remoteChainId_] = abi.encodePacked(newRemoteAddress_, address(this)); // <= FOUND
255:         emit SetTrustedRemoteAddress(remoteChainId_, oldRemoteAddress, trustedRemoteLookup[remoteChainId_]); // <= FOUND
256:     }

[Gas-4] bytes.concat() can be used in place of abi.encodePacked

Resolution

Given concatenation is not going to be used for hashing bytes.concat is the preferred method to use as its more gas efficient when used with bytes variables

Num of instances: 1

Findings

Click to show findings

['238']

238:             callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data); // <= FOUND

[Gas-5] Shortcircuit rules can be be used to optimize some gas usage

Resolution

Reading state variables takes alot of gas to do, so if you have a conditional statement regarding a state varaible alongide others which are not related to state vars, it is advisable to have conditions which do not involve state vars be declared first in the overall conditional so unnecessary state variable lookups can be avoided.

Num of instances: 2

Findings

Click to show findings

['295']

295:         require(proposalCount >= proposalId && proposalId > 0, "GovernorAlpha::state: invalid proposal id"); // <= FOUND

['491']

491:         
492:         require(
493:             msg.sender == pendingAdmin && msg.sender != address(0), // <= FOUND
494:             "GovernorBravo:_acceptAdmin: pending admin only"
495:         );

[Gas-6] Function calls within for loops

Resolution

Making function calls or external calls within loops in Solidity can lead to inefficient gas usage, potential bottlenecks, and increased vulnerability to attacks. Each function call or external call consumes gas, and when executed within a loop, the gas cost multiplies, potentially causing the transaction to run out of gas or exceed block gas limits. This can result in transaction failure or unpredictable behavior.

Num of instances: 11

Findings

Click to show findings

['220']

220:        for (uint i = 0; i < proposal.targets.length; i++) {
221:             _queueOrRevert(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], eta); // <= FOUND
222:         }

['269']

269:        for (uint i; i < proposal.targets.length; ++i) {
270:             queueOrRevertInternal( // <= FOUND
271:                 proposal.targets[i],
272:                 proposal.values[i],
273:                 proposal.signatures[i],
274:                 proposal.calldatas[i],
275:                 eta,
276:                 uint8(proposal.proposalType)
277:             );
278:         }

['179']

179:        for (uint i = 0; i < proposal.targets.length; i++) {
180:             queueOrRevertInternal( // <= FOUND
181:                 proposal.targets[i],
182:                 proposal.values[i],
183:                 proposal.signatures[i],
184:                 proposal.calldatas[i],
185:                 eta
186:             );
187:         }

['352']

352:        for (uint256 i; i < length; ) {
353:             _queueOrRevertInternal( // <= FOUND
354:                 proposal.targets[i],
355:                 proposal.values[i],
356:                 proposal.signatures[i],
357:                 proposal.calldatas[i],
358:                 eta,
359:                 proposalType
360:             );
361:             unchecked {
362:                 ++i;
363:             }
364:         }

['503']

503:        for (uint256 i; i < uint8(ProposalType.CRITICAL) + 1; ++i) {
504:             proposalTimelocks[i].acceptAdmin(); // <= FOUND
505:         }

['266']

266:        for (uint i = 0; i < proposal.targets.length; i++) {
267:             timelock.cancelTransaction( // <= FOUND
268:                 proposal.targets[i],
269:                 proposal.values[i],
270:                 proposal.signatures[i],
271:                 proposal.calldatas[i],
272:                 proposal.eta
273:             );
274:         }

['340']

340:        for (uint i = 0; i < proposal.targets.length; i++) {
341:             proposalTimelocks[proposal.proposalType].cancelTransaction( // <= FOUND
342:                 proposal.targets[i],
343:                 proposal.values[i],
344:                 proposal.signatures[i],
345:                 proposal.calldatas[i],
346:                 proposal.eta
347:             );
348:         }

['227']

227:        for (uint256 i; i < length; ) {
228:             timelock.cancelTransaction( // <= FOUND
229:                 proposal.targets[i],
230:                 proposal.values[i],
231:                 proposal.signatures[i],
232:                 proposal.calldatas[i],
233:                 eta
234:             );
235:             unchecked {
236:                 ++i;
237:             }
238:         }

['311']

311:        for (uint i; i < proposal.targets.length; ++i) {
312:             proposalTimelocks[uint8(proposal.proposalType)].executeTransaction( // <= FOUND
313:                 proposal.targets[i],
314:                 proposal.values[i],
315:                 proposal.signatures[i],
316:                 proposal.calldatas[i],
317:                 proposal.eta
318:             );
319:         }

['217']

217:        for (uint i = 0; i < proposal.targets.length; i++) {
218:             timelock.executeTransaction( // <= FOUND
219:                 proposal.targets[i],
220:                 proposal.values[i],
221:                 proposal.signatures[i],
222:                 proposal.calldatas[i],
223:                 proposal.eta
224:             );
225:         }

['192']

192:        for (uint256 i; i < length; ) {
193:             timelock.executeTransaction( // <= FOUND
194:                 proposal.targets[i],
195:                 proposal.values[i],
196:                 proposal.signatures[i],
197:                 proposal.calldatas[i],
198:                 eta
199:             );
200:             unchecked {
201:                 ++i;
202:             }
203:         }

[Gas-7] For loops in public or external functions should be avoided due to high gas costs and possible DOS

Resolution

In Solidity, for loops can potentially cause Denial of Service (DoS) attacks if not handled carefully. DoS attacks can occur when an attacker intentionally exploits the gas cost of a function, causing it to run out of gas or making it too expensive for other users to call. Below are some scenarios where for loops can lead to DoS attacks: Nested for loops can become exceptionally gas expensive and should be used sparingly

Num of instances: 18

Findings

Click to show findings

['269']

262:     function queue(uint proposalId) external {
263:         require(
264:             state(proposalId) == ProposalState.Succeeded,
265:             "GovernorBravo::queue: proposal can only be queued if it is succeeded"
266:         );
267:         Proposal storage proposal = proposals[proposalId];
268:         uint eta = add256(block.timestamp, proposalTimelocks[uint8(proposal.proposalType)].delay());
269:         for (uint i; i < proposal.targets.length; ++i) { // <= FOUND
270:             queueOrRevertInternal(
271:                 proposal.targets[i],
272:                 proposal.values[i],
273:                 proposal.signatures[i],
274:                 proposal.calldatas[i],
275:                 eta,
276:                 uint8(proposal.proposalType)
277:             );
278:         }
279:         proposal.eta = eta;
280:         emit ProposalQueued(proposalId, eta);
281:     }

['311']

304:     function execute(uint proposalId) external {
305:         require(
306:             state(proposalId) == ProposalState.Queued,
307:             "GovernorBravo::execute: proposal can only be executed if it is queued"
308:         );
309:         Proposal storage proposal = proposals[proposalId];
310:         proposal.executed = true;
311:         for (uint i; i < proposal.targets.length; ++i) { // <= FOUND
312:             proposalTimelocks[uint8(proposal.proposalType)].executeTransaction(
313:                 proposal.targets[i],
314:                 proposal.values[i],
315:                 proposal.signatures[i],
316:                 proposal.calldatas[i],
317:                 proposal.eta
318:             );
319:         }
320:         emit ProposalExecuted(proposalId);
321:     }

['340']

327:     function cancel(uint proposalId) external {
328:         require(state(proposalId) != ProposalState.Executed, "GovernorBravo::cancel: cannot cancel executed proposal");
329: 
330:         Proposal storage proposal = proposals[proposalId];
331:         require(
332:             msg.sender == guardian ||
333:                 msg.sender == proposal.proposer ||
334:                 xvsVault.getPriorVotes(proposal.proposer, sub256(block.number, 1)) <
335:                 proposalConfigs[proposal.proposalType].proposalThreshold,
336:             "GovernorBravo::cancel: proposer above threshold"
337:         );
338: 
339:         proposal.canceled = true;
340:         for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
341:             proposalTimelocks[proposal.proposalType].cancelTransaction(
342:                 proposal.targets[i],
343:                 proposal.values[i],
344:                 proposal.signatures[i],
345:                 proposal.calldatas[i],
346:                 proposal.eta
347:             );
348:         }
349: 
350:         emit ProposalCanceled(proposalId);
351:     }

['503']

498:     function _initiate(address governorAlpha) external {
499:         require(msg.sender == admin, "GovernorBravo::_initiate: admin only");
500:         require(initialProposalId == 0, "GovernorBravo::_initiate: can only initiate once");
501:         proposalCount = GovernorAlphaInterface(governorAlpha).proposalCount();
502:         initialProposalId = proposalCount;
503:         for (uint256 i; i < uint8(ProposalType.CRITICAL) + 1; ++i) { // <= FOUND
504:             proposalTimelocks[i].acceptAdmin();
505:         }
506:     }

['179']

172:     function queue(uint proposalId) external {
173:         require(
174:             state(proposalId) == ProposalState.Succeeded,
175:             "GovernorBravo::queue: proposal can only be queued if it is succeeded"
176:         );
177:         Proposal storage proposal = proposals[proposalId];
178:         uint eta = add256(block.timestamp, timelock.delay());
179:         for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
180:             queueOrRevertInternal(
181:                 proposal.targets[i],
182:                 proposal.values[i],
183:                 proposal.signatures[i],
184:                 proposal.calldatas[i],
185:                 eta
186:             );
187:         }
188:         proposal.eta = eta;
189:         emit ProposalQueued(proposalId, eta);
190:     }

['217']

210:     function execute(uint proposalId) external {
211:         require(
212:             state(proposalId) == ProposalState.Queued,
213:             "GovernorBravo::execute: proposal can only be executed if it is queued"
214:         );
215:         Proposal storage proposal = proposals[proposalId];
216:         proposal.executed = true;
217:         for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
218:             timelock.executeTransaction(
219:                 proposal.targets[i],
220:                 proposal.values[i],
221:                 proposal.signatures[i],
222:                 proposal.calldatas[i],
223:                 proposal.eta
224:             );
225:         }
226:         emit ProposalExecuted(proposalId);
227:     }

['245']

233:     function cancel(uint proposalId) external {
234:         require(state(proposalId) != ProposalState.Executed, "GovernorBravo::cancel: cannot cancel executed proposal");
235: 
236:         Proposal storage proposal = proposals[proposalId];
237:         require(
238:             msg.sender == guardian ||
239:                 msg.sender == proposal.proposer ||
240:                 xvsVault.getPriorVotes(proposal.proposer, sub256(block.number, 1)) < proposalThreshold,
241:             "GovernorBravo::cancel: proposer above threshold"
242:         );
243: 
244:         proposal.canceled = true;
245:         for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
246:             timelock.cancelTransaction(
247:                 proposal.targets[i],
248:                 proposal.values[i],
249:                 proposal.signatures[i],
250:                 proposal.calldatas[i],
251:                 proposal.eta
252:             );
253:         }
254: 
255:         emit ProposalCanceled(proposalId);
256:     }

['72']

69:     function upsertSignature(string[] calldata signatures_, bool[] calldata active_) external onlyOwner {
70:         uint256 signatureLength = signatures_.length;
71:         require(signatureLength == active_.length, "Input arrays must have the same length");
72:         for (uint256 i; i < signatureLength; ) { // <= FOUND
73:             bytes4 sigHash = bytes4(keccak256(bytes(signatures_[i])));
74:             bytes memory signature = bytes(functionRegistry[sigHash]);
75:             if (active_[i] && signature.length == 0) {
76:                 functionRegistry[sigHash] = signatures_[i];
77:                 emit FunctionRegistryChanged(signatures_[i], true);
78:             } else if (!active_[i] && signature.length != 0) {
79:                 delete functionRegistry[sigHash];
80:                 emit FunctionRegistryChanged(signatures_[i], false);
81:             }
82:             unchecked {
83:                 ++i;
84:             }
85:         }
86:     }

['163']

157:     function addTimelocks(ITimelock[] memory timelocks_) external onlyOwner {
158:         uint8 length = uint8(type(ProposalType).max) + 1;
159:         require(
160:             timelocks_.length == length,
161:             "OmnichainGovernanceExecutor::initialize:number of timelocks _should match the number of governance routes"
162:         );
163:         for (uint8 i; i < length; ) { // <= FOUND
164:             ensureNonzeroAddress(address(timelocks_[i]));
165:             emit TimelockAdded(i, address(proposalTimelocks[i]), address(timelocks_[i]));
166:             proposalTimelocks[i] = timelocks_[i];
167:             unchecked {
168:                 ++i;
169:             }
170:         }
171:     }

['192']

178:     function execute(uint256 proposalId_) external nonReentrant {
179:         require(
180:             state(proposalId_) == ProposalState.Queued,
181:             "OmnichainGovernanceExecutor::execute: proposal can only be executed if it is queued"
182:         );
183: 
184:         Proposal storage proposal = proposals[proposalId_];
185:         proposal.executed = true;
186:         ITimelock timelock = proposalTimelocks[proposal.proposalType];
187:         uint256 eta = proposal.eta;
188:         uint256 length = proposal.targets.length;
189: 
190:         emit ProposalExecuted(proposalId_);
191: 
192:         for (uint256 i; i < length; ) { // <= FOUND
193:             timelock.executeTransaction(
194:                 proposal.targets[i],
195:                 proposal.values[i],
196:                 proposal.signatures[i],
197:                 proposal.calldatas[i],
198:                 eta
199:             );
200:             unchecked {
201:                 ++i;
202:             }
203:         }
204:     }

['227']

212:     function cancel(uint256 proposalId_) external {
213:         require(
214:             state(proposalId_) == ProposalState.Queued,
215:             "OmnichainGovernanceExecutor::cancel: proposal should be queued and not executed"
216:         );
217:         Proposal storage proposal = proposals[proposalId_];
218:         require(msg.sender == GUARDIAN, "OmnichainGovernanceExecutor::cancel: sender must be guardian");
219: 
220:         proposal.canceled = true;
221:         ITimelock timelock = proposalTimelocks[proposal.proposalType];
222:         uint256 eta = proposal.eta;
223:         uint256 length = proposal.targets.length;
224: 
225:         emit ProposalCanceled(proposalId_);
226: 
227:         for (uint256 i; i < length; ) { // <= FOUND
228:             timelock.cancelTransaction(
229:                 proposal.targets[i],
230:                 proposal.values[i],
231:                 proposal.signatures[i],
232:                 proposal.calldatas[i],
233:                 eta
234:             );
235:             unchecked {
236:                 ++i;
237:             }
238:         }
239:     }

['220']

213:     function queue(uint proposalId) public {
214:         require(
215:             state(proposalId) == ProposalState.Succeeded,
216:             "GovernorAlpha::queue: proposal can only be queued if it is succeeded"
217:         );
218:         Proposal storage proposal = proposals[proposalId];
219:         uint eta = add256(block.timestamp, timelock.delay());
220:         for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
221:             _queueOrRevert(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], eta);
222:         }
223:         proposal.eta = eta;
224:         emit ProposalQueued(proposalId, eta);
225:     }

['242']

235:     function execute(uint proposalId) public payable {
236:         require(
237:             state(proposalId) == ProposalState.Queued,
238:             "GovernorAlpha::execute: proposal can only be executed if it is queued"
239:         );
240:         Proposal storage proposal = proposals[proposalId];
241:         proposal.executed = true;
242:         for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
243:             timelock.executeTransaction.value(proposal.values[i])(
244:                 proposal.targets[i],
245:                 proposal.values[i],
246:                 proposal.signatures[i],
247:                 proposal.calldatas[i],
248:                 proposal.eta
249:             );
250:         }
251:         emit ProposalExecuted(proposalId);
252:     }

['266']

254:     function cancel(uint proposalId) public {
255:         ProposalState state = state(proposalId);
256:         require(state != ProposalState.Executed, "GovernorAlpha::cancel: cannot cancel executed proposal");
257: 
258:         Proposal storage proposal = proposals[proposalId];
259:         require(
260:             msg.sender == guardian ||
261:                 xvs.getPriorVotes(proposal.proposer, sub256(block.number, 1)) < proposalThreshold(),
262:             "GovernorAlpha::cancel: proposer above threshold"
263:         );
264: 
265:         proposal.canceled = true;
266:         for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
267:             timelock.cancelTransaction(
268:                 proposal.targets[i],
269:                 proposal.values[i],
270:                 proposal.signatures[i],
271:                 proposal.calldatas[i],
272:                 proposal.eta
273:             );
274:         }
275: 
276:         emit ProposalCanceled(proposalId);
277:     }

['220']

213:     function queue(uint proposalId) public {
214:         require(
215:             state(proposalId) == ProposalState.Succeeded,
216:             "GovernorAlpha::queue: proposal can only be queued if it is succeeded"
217:         );
218:         Proposal storage proposal = proposals[proposalId];
219:         uint eta = add256(block.timestamp, timelock.delay());
220:         for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
221:             _queueOrRevert(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], eta);
222:         }
223:         proposal.eta = eta;
224:         emit ProposalQueued(proposalId, eta);
225:     }

['242']

235:     function execute(uint proposalId) public payable {
236:         require(
237:             state(proposalId) == ProposalState.Queued,
238:             "GovernorAlpha::execute: proposal can only be executed if it is queued"
239:         );
240:         Proposal storage proposal = proposals[proposalId];
241:         proposal.executed = true;
242:         for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
243:             timelock.executeTransaction.value(proposal.values[i])(
244:                 proposal.targets[i],
245:                 proposal.values[i],
246:                 proposal.signatures[i],
247:                 proposal.calldatas[i],
248:                 proposal.eta
249:             );
250:         }
251:         emit ProposalExecuted(proposalId);
252:     }

['266']

254:     function cancel(uint proposalId) public {
255:         ProposalState state = state(proposalId);
256:         require(state != ProposalState.Executed, "GovernorAlpha::cancel: cannot cancel executed proposal");
257: 
258:         Proposal storage proposal = proposals[proposalId];
259:         require(
260:             msg.sender == guardian ||
261:                 xvs.getPriorVotes(proposal.proposer, sub256(block.number, 1)) < proposalThreshold(),
262:             "GovernorAlpha::cancel: proposer above threshold"
263:         );
264: 
265:         proposal.canceled = true;
266:         for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
267:             timelock.cancelTransaction(
268:                 proposal.targets[i],
269:                 proposal.values[i],
270:                 proposal.signatures[i],
271:                 proposal.calldatas[i],
272:                 proposal.eta
273:             );
274:         }
275: 
276:         emit ProposalCanceled(proposalId);
277:     }

['136']

111:     function initialize(
112:         address xvsVault_,
113:         ProposalConfig[] memory proposalConfigs_,
114:         TimelockInterface[] memory timelocks,
115:         address guardian_
116:     ) public {
117:         require(address(proposalTimelocks[0]) == address(0), "GovernorBravo::initialize: cannot initialize twice");
118:         require(msg.sender == admin, "GovernorBravo::initialize: admin only");
119:         require(xvsVault_ != address(0), "GovernorBravo::initialize: invalid xvs address");
120:         require(guardian_ != address(0), "GovernorBravo::initialize: invalid guardian");
121:         require(
122:             timelocks.length == uint8(ProposalType.CRITICAL) + 1,
123:             "GovernorBravo::initialize:number of timelocks should match number of governance routes"
124:         );
125:         require(
126:             proposalConfigs_.length == uint8(ProposalType.CRITICAL) + 1,
127:             "GovernorBravo::initialize:number of proposal configs should match number of governance routes"
128:         );
129: 
130:         xvsVault = XvsVaultInterface(xvsVault_);
131:         proposalMaxOperations = 10;
132:         guardian = guardian_;
133: 
134:         
135:         uint256 arrLength = proposalConfigs_.length;
136:         for (uint256 i; i < arrLength; ++i) { // <= FOUND
137:             require(
138:                 proposalConfigs_[i].votingPeriod >= MIN_VOTING_PERIOD,
139:                 "GovernorBravo::initialize: invalid min voting period"
140:             );
141:             require(
142:                 proposalConfigs_[i].votingPeriod <= MAX_VOTING_PERIOD,
143:                 "GovernorBravo::initialize: invalid max voting period"
144:             );
145:             require(
146:                 proposalConfigs_[i].votingDelay >= MIN_VOTING_DELAY,
147:                 "GovernorBravo::initialize: invalid min voting delay"
148:             );
149:             require(
150:                 proposalConfigs_[i].votingDelay <= MAX_VOTING_DELAY,
151:                 "GovernorBravo::initialize: invalid max voting delay"
152:             );
153:             require(
154:                 proposalConfigs_[i].proposalThreshold >= MIN_PROPOSAL_THRESHOLD,
155:                 "GovernorBravo::initialize: invalid min proposal threshold"
156:             );
157:             require(
158:                 proposalConfigs_[i].proposalThreshold <= MAX_PROPOSAL_THRESHOLD,
159:                 "GovernorBravo::initialize: invalid max proposal threshold"
160:             );
161:             require(address(timelocks[i]) != address(0), "GovernorBravo::initialize:invalid timelock address");
162: 
163:             proposalConfigs[i] = proposalConfigs_[i];
164:             proposalTimelocks[i] = timelocks[i];
165:         }
166:     }

[Gas-8] Stack variable cost less than structs while used in emiting event

Resolution

When emitting events in Solidity, using stack variables (local variables within a function) instead of structs can lead to significant gas savings. Stack variables reside in memory only for the duration of the function execution and are less costly to access compared to structs, which are stored on the blockchain. When an event is emitted, accessing these stack variables requires less gas than fetching data from structs, which involves reading from the contract's storage - a more expensive operation. Thus, for efficiency, prefer using local variables within functions for event emission, especially in functions that are called frequently.

Num of instances: 3

Findings

Click to show findings

['199']

199:         emit ProposalCreated( // <= FOUND
200:             newProposal.id, // <= FOUND
201:             msg.sender,
202:             targets,
203:             values,
204:             signatures,
205:             calldatas,
206:             startBlock,
207:             endBlock,
208:             description
209:         );

['243']

243:         emit ProposalCreated( // <= FOUND
244:             newProposal.id, // <= FOUND
245:             msg.sender,
246:             targets,
247:             values,
248:             signatures,
249:             calldatas,
250:             startBlock,
251:             endBlock,
252:             description,
253:             uint8(proposalType)
254:         );

['333']

333:         emit ProposalReceived(newProposal.id, targets, values, signatures, calldatas, pType); // <= FOUND

[Gas-9] Use assembly to write address/contract storage values

Num of instances: 36

Findings

Click to show findings

['40']

40:         _accessControlManager = IAccessControlManagerV5(accessControlManager_); // <= FOUND

['66']

66:         _accessControlManager = IAccessControlManagerV8(accessControlManager_); // <= FOUND

['50']

50:         maxDailyReceiveLimit = limit_; // <= FOUND

['87']

87:             last24HourReceiveWindowStart = currentBlockTimestamp; // <= FOUND

['96']

96:         last24HourCommandsReceived = receivedInWindow; // <= FOUND

['53']

53:         accessControlManager = accessControlManager_; // <= FOUND

['130']

130:         admin = msg.sender; // <= FOUND

['78']

78:         admin = admin_; // <= FOUND

['55']

55:         implementation = implementation_; // <= FOUND

['82']

82:         (bool success, ) = implementation.delegatecall(msg.data); // <= FOUND

['137']

137:         timelock = TimelockInterface(timelock_); // <= FOUND

['138']

138:         xvs = XVSInterface(xvs_); // <= FOUND

['139']

139:         guardian = guardian_; // <= FOUND

['140']

140:         proposalCount = lastProposalId_; // <= FOUND

['78']

78:         xvsVault = XvsVaultInterface(xvsVault_); // <= FOUND

['82']

82:         proposalMaxOperations = 10; // <= FOUND

['386']

386:         guardian = newGuardian; // <= FOUND

['448']

448:         proposalCount = GovernorAlphaInterface(governorAlpha).proposalCount(); // <= FOUND

['449']

449:         initialProposalId = proposalCount; // <= FOUND

['461']

461:         proposalMaxOperations = proposalMaxOperations_; // <= FOUND

['479']

479:         pendingAdmin = newPendingAdmin; // <= FOUND

['501']

501:         admin = pendingAdmin; // <= FOUND

['79']

79:         votingPeriod = votingPeriod_; // <= FOUND

['80']

80:         votingDelay = votingDelay_; // <= FOUND

['81']

81:         proposalThreshold = proposalThreshold_; // <= FOUND

['402']

402:         votingDelay = newVotingDelay; // <= FOUND

['418']

418:         votingPeriod = newVotingPeriod; // <= FOUND

['435']

435:         proposalThreshold = newProposalThreshold; // <= FOUND

['35']

35:         OMNICHAIN_GOVERNANCE_EXECUTOR = IOmnichainGovernanceExecutor(omnichainGovernanceExecutor_); // <= FOUND

['136']

136:         GUARDIAN = guardian_; // <= FOUND

['137']

137:         srcChainId = srcChainId_; // <= FOUND

['186']

186:         ITimelock timelock = proposalTimelocks[proposal.proposalType]; // <= FOUND

['331']

331:         lastProposalReceived = pId; // <= FOUND

['238']

238:         (bool sent, ) = to_.call{ value: originalValue_ }(""); // <= FOUND

['79']

79:         delay = delay_; // <= FOUND

['143']

143:         pendingAdmin = pendingAdmin_; // <= FOUND

[Gas-10] Avoid caching global vars used once within the function

Resolution

If a cached variable is not used many times, it can be cheaper to call the global var directly.

Num of instances: 2

Findings

Click to show findings

['79']

79:         uint256 currentBlockTimestamp = block.timestamp; // <= FOUND

['79']

79:         
80:         uint256 currentBlockTimestamp = block.timestamp; // <= FOUND

[Gas-11] There is a 32 byte length threshold for error strings, strings longer than this consume more gas

Resolution

In require statements found which are longer than 32 characters, shorten these to 32 character or less

Num of instances: 77

Findings

Click to show findings

['38']

37:     function _setAccessControlManager(address accessControlManager_) internal {
38:         require(address(accessControlManager_) != address(0), "invalid acess control manager address"); // <= FOUND
39:         address oldAccessControlManager = address(_accessControlManager);
40:         _accessControlManager = IAccessControlManagerV5(accessControlManager_);
41:         emit NewAccessControlManager(oldAccessControlManager, accessControlManager_);
42:     }

['64']

63:     function _setAccessControlManager(address accessControlManager_) internal {
64:         require(address(accessControlManager_) != address(0), "invalid acess control manager address"); // <= FOUND
65:         address oldAccessControlManager = address(_accessControlManager);
66:         _accessControlManager = IAccessControlManagerV8(accessControlManager_);
67:         emit NewAccessControlManager(oldAccessControlManager, accessControlManager_);
68:     }

['93']

78:     function _isEligibleToReceive(uint256 noOfCommands_) internal {
79:         uint256 currentBlockTimestamp = block.timestamp;
80: 
81:         
82:         uint256 receivedInWindow = last24HourCommandsReceived;
83: 
84:         
85:         if (currentBlockTimestamp - last24HourReceiveWindowStart > 1 days) {
86:             receivedInWindow = noOfCommands_;
87:             last24HourReceiveWindowStart = currentBlockTimestamp;
88:         } else {
89:             receivedInWindow += noOfCommands_;
90:         }
91: 
92:         
93:         require(receivedInWindow <= maxDailyReceiveLimit, "Daily Transaction Limit Exceeded"); // <= FOUND
94: 
95:         
96:         last24HourCommandsReceived = receivedInWindow;
97:     }

['126']

109:     function _isEligibleToSend(uint16 dstChainId_, uint256 noOfCommands_) internal {
110:         
111:         uint256 currentBlockTimestamp = block.timestamp;
112:         uint256 lastDayWindowStart = chainIdToLast24HourWindowStart[dstChainId_];
113:         uint256 commandsSentInWindow = chainIdToLast24HourCommandsSent[dstChainId_];
114:         uint256 maxDailyLimit = chainIdToMaxDailyLimit[dstChainId_];
115:         uint256 lastProposalSentTimestamp = chainIdToLastProposalSentTimestamp[dstChainId_];
116: 
117:         
118:         if (currentBlockTimestamp - lastDayWindowStart > 1 days) {
119:             commandsSentInWindow = noOfCommands_;
120:             chainIdToLast24HourWindowStart[dstChainId_] = currentBlockTimestamp;
121:         } else {
122:             commandsSentInWindow += noOfCommands_;
123:         }
124: 
125:         
126:         require(commandsSentInWindow <= maxDailyLimit, "Daily Transaction Limit Exceeded"); // <= FOUND
127:         
128:         require(lastProposalSentTimestamp != currentBlockTimestamp, "Multiple bridging in a proposal");
129: 
130:         
131:         chainIdToLast24HourCommandsSent[dstChainId_] = commandsSentInWindow;
132:         
133:         chainIdToLastProposalSentTimestamp[dstChainId_] = currentBlockTimestamp;
134:     }

['48']

47:     function _setImplementation(address implementation_) public {
48:         require(msg.sender == admin, "GovernorBravoDelegator::_setImplementation: admin only"); // <= FOUND
49:         require(
50:             implementation_ != address(0),
51:             "GovernorBravoDelegator::_setImplementation: invalid implementation address"
52:         );
53: 
54:         address oldImplementation = implementation;
55:         implementation = implementation_;
56: 
57:         emit NewImplementation(oldImplementation, implementation);
58:     }

['48']

47:     function _setImplementation(address implementation_) public {
48:         require(msg.sender == admin, "GovernorBravoDelegator::_setImplementation: admin only"); // <= FOUND
49:         require(
50:             implementation_ != address(0),
51:             "GovernorBravoDelegator::_setImplementation: invalid implementation address"
52:         );
53: 
54:         address oldImplementation = implementation;
55:         implementation = implementation_;
56: 
57:         emit NewImplementation(oldImplementation, implementation);
58:     }

[]

143:     function propose(
144:         address[] memory targets,
145:         uint[] memory values,
146:         string[] memory signatures,
147:         bytes[] memory calldatas,
148:         string memory description
149:     ) public returns (uint) {
150:         require(
151:             xvs.getPriorVotes(msg.sender, sub256(block.number, 1)) > proposalThreshold(),
152:             "GovernorAlpha::propose: proposer votes below proposal threshold"
153:         );
154:         require(
155:             targets.length == values.length &&
156:                 targets.length == signatures.length &&
157:                 targets.length == calldatas.length,
158:             "GovernorAlpha::propose: proposal function information arity mismatch"
159:         );
160:         require(targets.length != 0, "GovernorAlpha::propose: must provide actions");
161:         require(targets.length <= proposalMaxOperations(), "GovernorAlpha::propose: too many actions");
162: 
163:         uint latestProposalId = latestProposalIds[msg.sender];
164:         if (latestProposalId != 0) {
165:             ProposalState proposersLatestProposalState = state(latestProposalId);
166:             require(
167:                 proposersLatestProposalState != ProposalState.Active,
168:                 "GovernorAlpha::propose: found an already active proposal"
169:             );
170:             require(
171:                 proposersLatestProposalState != ProposalState.Pending,
172:                 "GovernorAlpha::propose: found an already pending proposal"
173:             );
174:         }
175: 
176:         uint startBlock = add256(block.number, votingDelay());
177:         uint endBlock = add256(startBlock, votingPeriod());
178: 
179:         proposalCount++;
180:         Proposal memory newProposal = Proposal({
181:             id: proposalCount,
182:             proposer: msg.sender,
183:             eta: 0,
184:             targets: targets,
185:             values: values,
186:             signatures: signatures,
187:             calldatas: calldatas,
188:             startBlock: startBlock,
189:             endBlock: endBlock,
190:             forVotes: 0,
191:             againstVotes: 0,
192:             canceled: false,
193:             executed: false
194:         });
195: 
196:         proposals[newProposal.id] = newProposal;
197:         latestProposalIds[newProposal.proposer] = newProposal.id;
198: 
199:         emit ProposalCreated(
200:             newProposal.id,
201:             msg.sender,
202:             targets,
203:             values,
204:             signatures,
205:             calldatas,
206:             startBlock,
207:             endBlock,
208:             description
209:         );
210:         return newProposal.id;
211:     }

[]

143:     function propose(
144:         address[] memory targets,
145:         uint[] memory values,
146:         string[] memory signatures,
147:         bytes[] memory calldatas,
148:         string memory description
149:     ) public returns (uint) {
150:         require(
151:             xvs.getPriorVotes(msg.sender, sub256(block.number, 1)) > proposalThreshold(),
152:             "GovernorAlpha::propose: proposer votes below proposal threshold"
153:         );
154:         require(
155:             targets.length == values.length &&
156:                 targets.length == signatures.length &&
157:                 targets.length == calldatas.length,
158:             "GovernorAlpha::propose: proposal function information arity mismatch"
159:         );
160:         require(targets.length != 0, "GovernorAlpha::propose: must provide actions");
161:         require(targets.length <= proposalMaxOperations(), "GovernorAlpha::propose: too many actions");
162: 
163:         uint latestProposalId = latestProposalIds[msg.sender];
164:         if (latestProposalId != 0) {
165:             ProposalState proposersLatestProposalState = state(latestProposalId);
166:             require(
167:                 proposersLatestProposalState != ProposalState.Active,
168:                 "GovernorAlpha::propose: found an already active proposal"
169:             );
170:             require(
171:                 proposersLatestProposalState != ProposalState.Pending,
172:                 "GovernorAlpha::propose: found an already pending proposal"
173:             );
174:         }
175: 
176:         uint startBlock = add256(block.number, votingDelay());
177:         uint endBlock = add256(startBlock, votingPeriod());
178: 
179:         proposalCount++;
180:         Proposal memory newProposal = Proposal({
181:             id: proposalCount,
182:             proposer: msg.sender,
183:             eta: 0,
184:             targets: targets,
185:             values: values,
186:             signatures: signatures,
187:             calldatas: calldatas,
188:             startBlock: startBlock,
189:             endBlock: endBlock,
190:             forVotes: 0,
191:             againstVotes: 0,
192:             canceled: false,
193:             executed: false
194:         });
195: 
196:         proposals[newProposal.id] = newProposal;
197:         latestProposalIds[newProposal.proposer] = newProposal.id;
198: 
199:         emit ProposalCreated(
200:             newProposal.id,
201:             msg.sender,
202:             targets,
203:             values,
204:             signatures,
205:             calldatas,
206:             startBlock,
207:             endBlock,
208:             description
209:         );
210:         return newProposal.id;
211:     }

[]

213:     function queue(uint proposalId) public {
214:         require(
215:             state(proposalId) == ProposalState.Succeeded,
216:             "GovernorAlpha::queue: proposal can only be queued if it is succeeded"
217:         );
218:         Proposal storage proposal = proposals[proposalId];
219:         uint eta = add256(block.timestamp, timelock.delay());
220:         for (uint i = 0; i < proposal.targets.length; i++) {
221:             _queueOrRevert(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], eta);
222:         }
223:         proposal.eta = eta;
224:         emit ProposalQueued(proposalId, eta);
225:     }

[]

213:     function queue(uint proposalId) public {
214:         require(
215:             state(proposalId) == ProposalState.Succeeded,
216:             "GovernorAlpha::queue: proposal can only be queued if it is succeeded"
217:         );
218:         Proposal storage proposal = proposals[proposalId];
219:         uint eta = add256(block.timestamp, timelock.delay());
220:         for (uint i = 0; i < proposal.targets.length; i++) {
221:             _queueOrRevert(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], eta);
222:         }
223:         proposal.eta = eta;
224:         emit ProposalQueued(proposalId, eta);
225:     }

[]

227:     function _queueOrRevert(address target, uint value, string memory signature, bytes memory data, uint eta) internal {
228:         require(
229:             !timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))),
230:             "GovernorAlpha::_queueOrRevert: proposal action already queued at eta"
231:         );
232:         timelock.queueTransaction(target, value, signature, data, eta);
233:     }

[]

227:     function _queueOrRevert(address target, uint value, string memory signature, bytes memory data, uint eta) internal {
228:         require(
229:             !timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))),
230:             "GovernorAlpha::_queueOrRevert: proposal action already queued at eta"
231:         );
232:         timelock.queueTransaction(target, value, signature, data, eta);
233:     }

[]

235:     function execute(uint proposalId) public payable {
236:         require(
237:             state(proposalId) == ProposalState.Queued,
238:             "GovernorAlpha::execute: proposal can only be executed if it is queued"
239:         );
240:         Proposal storage proposal = proposals[proposalId];
241:         proposal.executed = true;
242:         for (uint i = 0; i < proposal.targets.length; i++) {
243:             timelock.executeTransaction.value(proposal.values[i])(
244:                 proposal.targets[i],
245:                 proposal.values[i],
246:                 proposal.signatures[i],
247:                 proposal.calldatas[i],
248:                 proposal.eta
249:             );
250:         }
251:         emit ProposalExecuted(proposalId);
252:     }

[]

235:     function execute(uint proposalId) public payable {
236:         require(
237:             state(proposalId) == ProposalState.Queued,
238:             "GovernorAlpha::execute: proposal can only be executed if it is queued"
239:         );
240:         Proposal storage proposal = proposals[proposalId];
241:         proposal.executed = true;
242:         for (uint i = 0; i < proposal.targets.length; i++) {
243:             timelock.executeTransaction.value(proposal.values[i])(
244:                 proposal.targets[i],
245:                 proposal.values[i],
246:                 proposal.signatures[i],
247:                 proposal.calldatas[i],
248:                 proposal.eta
249:             );
250:         }
251:         emit ProposalExecuted(proposalId);
252:     }

['256']

254:     function cancel(uint proposalId) public {
255:         ProposalState state = state(proposalId);
256:         require(state != ProposalState.Executed, "GovernorAlpha::cancel: cannot cancel executed proposal"); // <= FOUND
257: 
258:         Proposal storage proposal = proposals[proposalId];
259:         require(
260:             msg.sender == guardian ||
261:                 xvs.getPriorVotes(proposal.proposer, sub256(block.number, 1)) < proposalThreshold(),
262:             "GovernorAlpha::cancel: proposer above threshold"
263:         );
264: 
265:         proposal.canceled = true;
266:         for (uint i = 0; i < proposal.targets.length; i++) {
267:             timelock.cancelTransaction(
268:                 proposal.targets[i],
269:                 proposal.values[i],
270:                 proposal.signatures[i],
271:                 proposal.calldatas[i],
272:                 proposal.eta
273:             );
274:         }
275: 
276:         emit ProposalCanceled(proposalId);
277:     }

['256']

254:     function cancel(uint proposalId) public {
255:         ProposalState state = state(proposalId);
256:         require(state != ProposalState.Executed, "GovernorAlpha::cancel: cannot cancel executed proposal"); // <= FOUND
257: 
258:         Proposal storage proposal = proposals[proposalId];
259:         require(
260:             msg.sender == guardian ||
261:                 xvs.getPriorVotes(proposal.proposer, sub256(block.number, 1)) < proposalThreshold(),
262:             "GovernorAlpha::cancel: proposer above threshold"
263:         );
264: 
265:         proposal.canceled = true;
266:         for (uint i = 0; i < proposal.targets.length; i++) {
267:             timelock.cancelTransaction(
268:                 proposal.targets[i],
269:                 proposal.values[i],
270:                 proposal.signatures[i],
271:                 proposal.calldatas[i],
272:                 proposal.eta
273:             );
274:         }
275: 
276:         emit ProposalCanceled(proposalId);
277:     }

['295']

294:     function state(uint proposalId) public view returns (ProposalState) {
295:         require(proposalCount >= proposalId && proposalId > 0, "GovernorAlpha::state: invalid proposal id"); // <= FOUND
296:         Proposal storage proposal = proposals[proposalId];
297:         if (proposal.canceled) {
298:             return ProposalState.Canceled;
299:         } else if (block.number <= proposal.startBlock) {
300:             return ProposalState.Pending;
301:         } else if (block.number <= proposal.endBlock) {
302:             return ProposalState.Active;
303:         } else if (proposal.forVotes <= proposal.againstVotes || proposal.forVotes < quorumVotes()) {
304:             return ProposalState.Defeated;
305:         } else if (proposal.eta == 0) {
306:             return ProposalState.Succeeded;
307:         } else if (proposal.executed) {
308:             return ProposalState.Executed;
309:         } else if (block.timestamp >= add256(proposal.eta, timelock.GRACE_PERIOD())) {
310:             return ProposalState.Expired;
311:         } else {
312:             return ProposalState.Queued;
313:         }
314:     }

['295']

294:     function state(uint proposalId) public view returns (ProposalState) {
295:         require(proposalCount >= proposalId && proposalId > 0, "GovernorAlpha::state: invalid proposal id"); // <= FOUND
296:         Proposal storage proposal = proposals[proposalId];
297:         if (proposal.canceled) {
298:             return ProposalState.Canceled;
299:         } else if (block.number <= proposal.startBlock) {
300:             return ProposalState.Pending;
301:         } else if (block.number <= proposal.endBlock) {
302:             return ProposalState.Active;
303:         } else if (proposal.forVotes <= proposal.againstVotes || proposal.forVotes < quorumVotes()) {
304:             return ProposalState.Defeated;
305:         } else if (proposal.eta == 0) {
306:             return ProposalState.Succeeded;
307:         } else if (proposal.executed) {
308:             return ProposalState.Executed;
309:         } else if (block.timestamp >= add256(proposal.eta, timelock.GRACE_PERIOD())) {
310:             return ProposalState.Expired;
311:         } else {
312:             return ProposalState.Queued;
313:         }
314:     }

['327']

320:     function castVoteBySig(uint proposalId, bool support, uint8 v, bytes32 r, bytes32 s) public {
321:         bytes32 domainSeparator = keccak256(
322:             abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainId(), address(this))
323:         );
324:         bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));
325:         bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
326:         address signatory = ecrecover(digest, v, r, s);
327:         require(signatory != address(0), "GovernorAlpha::castVoteBySig: invalid signature"); // <= FOUND
328:         return _castVote(signatory, proposalId, support);
329:     }

['327']

320:     function castVoteBySig(uint proposalId, bool support, uint8 v, bytes32 r, bytes32 s) public {
321:         bytes32 domainSeparator = keccak256(
322:             abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainId(), address(this))
323:         );
324:         bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));
325:         bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
326:         address signatory = ecrecover(digest, v, r, s);
327:         require(signatory != address(0), "GovernorAlpha::castVoteBySig: invalid signature"); // <= FOUND
328:         return _castVote(signatory, proposalId, support);
329:     }

['332']

331:     function _castVote(address voter, uint proposalId, bool support) internal {
332:         require(state(proposalId) == ProposalState.Active, "GovernorAlpha::_castVote: voting is closed"); // <= FOUND
333:         Proposal storage proposal = proposals[proposalId];
334:         Receipt storage receipt = proposal.receipts[voter];
335:         require(receipt.hasVoted == false, "GovernorAlpha::_castVote: voter already voted");
336:         uint96 votes = xvs.getPriorVotes(voter, proposal.startBlock);
337: 
338:         if (support) {
339:             proposal.forVotes = add256(proposal.forVotes, votes);
340:         } else {
341:             proposal.againstVotes = add256(proposal.againstVotes, votes);
342:         }
343: 
344:         receipt.hasVoted = true;
345:         receipt.support = support;
346:         receipt.votes = votes;
347: 
348:         emit VoteCast(voter, proposalId, support, votes);
349:     }

['332']

331:     function _castVote(address voter, uint proposalId, bool support) internal {
332:         require(state(proposalId) == ProposalState.Active, "GovernorAlpha::_castVote: voting is closed"); // <= FOUND
333:         Proposal storage proposal = proposals[proposalId];
334:         Receipt storage receipt = proposal.receipts[voter];
335:         require(receipt.hasVoted == false, "GovernorAlpha::_castVote: voter already voted");
336:         uint96 votes = xvs.getPriorVotes(voter, proposal.startBlock);
337: 
338:         if (support) {
339:             proposal.forVotes = add256(proposal.forVotes, votes);
340:         } else {
341:             proposal.againstVotes = add256(proposal.againstVotes, votes);
342:         }
343: 
344:         receipt.hasVoted = true;
345:         receipt.support = support;
346:         receipt.votes = votes;
347: 
348:         emit VoteCast(voter, proposalId, support, votes);
349:     }

['352']

351:     function __acceptAdmin() public {
352:         require(msg.sender == guardian, "GovernorAlpha::__acceptAdmin: sender must be gov guardian"); // <= FOUND
353:         timelock.acceptAdmin();
354:     }

['357']

356:     function __abdicate() public {
357:         require(msg.sender == guardian, "GovernorAlpha::__abdicate: sender must be gov guardian"); // <= FOUND
358:         guardian = address(0);
359:     }

['362']

361:     function __queueSetTimelockPendingAdmin(address newPendingAdmin, uint eta) public {
362:         require(msg.sender == guardian, "GovernorAlpha::__queueSetTimelockPendingAdmin: sender must be gov guardian"); // <= FOUND
363:         timelock.queueTransaction(address(timelock), 0, "setPendingAdmin(address)", abi.encode(newPendingAdmin), eta);
364:     }

['367']

366:     function __executeSetTimelockPendingAdmin(address newPendingAdmin, uint eta) public {
367:         require(msg.sender == guardian, "GovernorAlpha::__executeSetTimelockPendingAdmin: sender must be gov guardian"); // <= FOUND
368:         timelock.executeTransaction(address(timelock), 0, "setPendingAdmin(address)", abi.encode(newPendingAdmin), eta);
369:     }

['117']

111:     function initialize(
112:         address xvsVault_,
113:         ProposalConfig[] memory proposalConfigs_,
114:         TimelockInterface[] memory timelocks,
115:         address guardian_
116:     ) public {
117:         require(address(proposalTimelocks[0]) == address(0), "GovernorBravo::initialize: cannot initialize twice"); // <= FOUND
118:         require(msg.sender == admin, "GovernorBravo::initialize: admin only"); // <= FOUND
119:         require(xvsVault_ != address(0), "GovernorBravo::initialize: invalid xvs address");
120:         require(guardian_ != address(0), "GovernorBravo::initialize: invalid guardian");
121:         require(
122:             timelocks.length == uint8(ProposalType.CRITICAL) + 1,
123:             "GovernorBravo::initialize:number of timelocks should match number of governance routes"
124:         );
125:         require(
126:             proposalConfigs_.length == uint8(ProposalType.CRITICAL) + 1,
127:             "GovernorBravo::initialize:number of proposal configs should match number of governance routes"
128:         );
129: 
130:         xvsVault = XvsVaultInterface(xvsVault_);
131:         proposalMaxOperations = 10;
132:         guardian = guardian_;
133: 
134:         
135:         uint256 arrLength = proposalConfigs_.length;
136:         for (uint256 i; i < arrLength; ++i) {
137:             require(
138:                 proposalConfigs_[i].votingPeriod >= MIN_VOTING_PERIOD,
139:                 "GovernorBravo::initialize: invalid min voting period"
140:             );
141:             require(
142:                 proposalConfigs_[i].votingPeriod <= MAX_VOTING_PERIOD,
143:                 "GovernorBravo::initialize: invalid max voting period"
144:             );
145:             require(
146:                 proposalConfigs_[i].votingDelay >= MIN_VOTING_DELAY,
147:                 "GovernorBravo::initialize: invalid min voting delay"
148:             );
149:             require(
150:                 proposalConfigs_[i].votingDelay <= MAX_VOTING_DELAY,
151:                 "GovernorBravo::initialize: invalid max voting delay"
152:             );
153:             require(
154:                 proposalConfigs_[i].proposalThreshold >= MIN_PROPOSAL_THRESHOLD,
155:                 "GovernorBravo::initialize: invalid min proposal threshold"
156:             );
157:             require(
158:                 proposalConfigs_[i].proposalThreshold <= MAX_PROPOSAL_THRESHOLD,
159:                 "GovernorBravo::initialize: invalid max proposal threshold"
160:             );
161:             require(address(timelocks[i]) != address(0), "GovernorBravo::initialize:invalid timelock address");
162: 
163:             proposalConfigs[i] = proposalConfigs_[i];
164:             proposalTimelocks[i] = timelocks[i];
165:         }
166:     }

['60']

51:     function initialize(
52:         address timelock_,
53:         address xvsVault_,
54:         uint votingPeriod_,
55:         uint votingDelay_,
56:         uint proposalThreshold_,
57:         address guardian_
58:     ) public {
59:         require(address(timelock) == address(0), "GovernorBravo::initialize: can only initialize once");
60:         require(msg.sender == admin, "GovernorBravo::initialize: admin only"); // <= FOUND
61:         require(timelock_ != address(0), "GovernorBravo::initialize: invalid timelock address");
62:         require(xvsVault_ != address(0), "GovernorBravo::initialize: invalid xvs address");
63:         require(
64:             votingPeriod_ >= MIN_VOTING_PERIOD && votingPeriod_ <= MAX_VOTING_PERIOD,
65:             "GovernorBravo::initialize: invalid voting period"
66:         );
67:         require(
68:             votingDelay_ >= MIN_VOTING_DELAY && votingDelay_ <= MAX_VOTING_DELAY,
69:             "GovernorBravo::initialize: invalid voting delay"
70:         );
71:         require(
72:             proposalThreshold_ >= MIN_PROPOSAL_THRESHOLD && proposalThreshold_ <= MAX_PROPOSAL_THRESHOLD,
73:             "GovernorBravo::initialize: invalid proposal threshold"
74:         );
75:         require(guardian_ != address(0), "GovernorBravo::initialize: invalid guardian");
76: 
77:         timelock = TimelockInterface(timelock_);
78:         xvsVault = XvsVaultInterface(xvsVault_);
79:         votingPeriod = votingPeriod_;
80:         votingDelay = votingDelay_;
81:         proposalThreshold = proposalThreshold_;
82:         proposalMaxOperations = 10;
83:         guardian = guardian_;
84:     }

['190']

181:     function propose(
182:         address[] memory targets,
183:         uint[] memory values,
184:         string[] memory signatures,
185:         bytes[] memory calldatas,
186:         string memory description,
187:         ProposalType proposalType
188:     ) public returns (uint) {
189:         
190:         require(initialProposalId != 0, "GovernorBravo::propose: Governor Bravo not active"); // <= FOUND
191:         require(
192:             xvsVault.getPriorVotes(msg.sender, sub256(block.number, 1)) >=
193:                 proposalConfigs[uint8(proposalType)].proposalThreshold,
194:             "GovernorBravo::propose: proposer votes below proposal threshold"
195:         );
196:         require(
197:             targets.length == values.length &&
198:                 targets.length == signatures.length &&
199:                 targets.length == calldatas.length,
200:             "GovernorBravo::propose: proposal function information arity mismatch"
201:         );
202:         require(targets.length != 0, "GovernorBravo::propose: must provide actions");
203:         require(targets.length <= proposalMaxOperations, "GovernorBravo::propose: too many actions");
204: 
205:         uint latestProposalId = latestProposalIds[msg.sender];
206:         if (latestProposalId != 0) {
207:             ProposalState proposersLatestProposalState = state(latestProposalId);
208:             require(
209:                 proposersLatestProposalState != ProposalState.Active,
210:                 "GovernorBravo::propose: one live proposal per proposer, found an already active proposal"
211:             );
212:             require(
213:                 proposersLatestProposalState != ProposalState.Pending,
214:                 "GovernorBravo::propose: one live proposal per proposer, found an already pending proposal"
215:             );
216:         }
217: 
218:         uint startBlock = add256(block.number, proposalConfigs[uint8(proposalType)].votingDelay);
219:         uint endBlock = add256(startBlock, proposalConfigs[uint8(proposalType)].votingPeriod);
220: 
221:         proposalCount++;
222:         Proposal memory newProposal = Proposal({
223:             id: proposalCount,
224:             proposer: msg.sender,
225:             eta: 0,
226:             targets: targets,
227:             values: values,
228:             signatures: signatures,
229:             calldatas: calldatas,
230:             startBlock: startBlock,
231:             endBlock: endBlock,
232:             forVotes: 0,
233:             againstVotes: 0,
234:             abstainVotes: 0,
235:             canceled: false,
236:             executed: false,
237:             proposalType: uint8(proposalType)
238:         });
239: 

['103']

95:     function propose(
96:         address[] memory targets,
97:         uint[] memory values,
98:         string[] memory signatures,
99:         bytes[] memory calldatas,
100:         string memory description
101:     ) public returns (uint) {
102:         
103:         require(initialProposalId != 0, "GovernorBravo::propose: Governor Bravo not active"); // <= FOUND
104:         require(
105:             xvsVault.getPriorVotes(msg.sender, sub256(block.number, 1)) > proposalThreshold,
106:             "GovernorBravo::propose: proposer votes below proposal threshold"
107:         );
108:         require(
109:             targets.length == values.length &&
110:                 targets.length == signatures.length &&
111:                 targets.length == calldatas.length,
112:             "GovernorBravo::propose: proposal function information arity mismatch"
113:         );
114:         require(targets.length != 0, "GovernorBravo::propose: must provide actions");
115:         require(targets.length <= proposalMaxOperations, "GovernorBravo::propose: too many actions");
116: 
117:         uint latestProposalId = latestProposalIds[msg.sender];
118:         if (latestProposalId != 0) {
119:             ProposalState proposersLatestProposalState = state(latestProposalId);
120:             require(
121:                 proposersLatestProposalState != ProposalState.Active,
122:                 "GovernorBravo::propose: one live proposal per proposer, found an already active proposal"
123:             );
124:             require(
125:                 proposersLatestProposalState != ProposalState.Pending,
126:                 "GovernorBravo::propose: one live proposal per proposer, found an already pending proposal"
127:             );
128:         }
129: 
130:         uint startBlock = add256(block.number, votingDelay);
131:         uint endBlock = add256(startBlock, votingPeriod);
132: 
133:         proposalCount++;
134:         Proposal memory newProposal = Proposal({
135:             id: proposalCount,
136:             proposer: msg.sender,
137:             eta: 0,
138:             targets: targets,
139:             values: values,
140:             signatures: signatures,
141:             calldatas: calldatas,
142:             startBlock: startBlock,
143:             endBlock: endBlock,
144:             forVotes: 0,
145:             againstVotes: 0,
146:             abstainVotes: 0,
147:             canceled: false,
148:             executed: false
149:         });
150: 
151:         proposals[newProposal.id] = newProposal;
152:         latestProposalIds[newProposal.proposer] = newProposal.id;

[]

262:     function queue(uint proposalId) external {
263:         require(
264:             state(proposalId) == ProposalState.Succeeded,
265:             "GovernorBravo::queue: proposal can only be queued if it is succeeded"
266:         );
267:         Proposal storage proposal = proposals[proposalId];
268:         uint eta = add256(block.timestamp, proposalTimelocks[uint8(proposal.proposalType)].delay());
269:         for (uint i; i < proposal.targets.length; ++i) {
270:             queueOrRevertInternal(
271:                 proposal.targets[i],
272:                 proposal.values[i],
273:                 proposal.signatures[i],
274:                 proposal.calldatas[i],
275:                 eta,
276:                 uint8(proposal.proposalType)
277:             );
278:         }
279:         proposal.eta = eta;
280:         emit ProposalQueued(proposalId, eta);
281:     }

[]

172:     function queue(uint proposalId) external {
173:         require(
174:             state(proposalId) == ProposalState.Succeeded,
175:             "GovernorBravo::queue: proposal can only be queued if it is succeeded"
176:         );
177:         Proposal storage proposal = proposals[proposalId];
178:         uint eta = add256(block.timestamp, timelock.delay());
179:         for (uint i = 0; i < proposal.targets.length; i++) {
180:             queueOrRevertInternal(
181:                 proposal.targets[i],
182:                 proposal.values[i],
183:                 proposal.signatures[i],
184:                 proposal.calldatas[i],
185:                 eta
186:             );
187:         }
188:         proposal.eta = eta;
189:         emit ProposalQueued(proposalId, eta);
190:     }

[]

283:     function queueOrRevertInternal(
284:         address target,
285:         uint value,
286:         string memory signature,
287:         bytes memory data,
288:         uint eta,
289:         uint8 proposalType
290:     ) internal {
291:         require(
292:             !proposalTimelocks[proposalType].queuedTransactions(
293:                 keccak256(abi.encode(target, value, signature, data, eta))
294:             ),
295:             "GovernorBravo::queueOrRevertInternal: identical proposal action already queued at eta"
296:         );
297:         proposalTimelocks[proposalType].queueTransaction(target, value, signature, data, eta);
298:     }

[]

304:     function execute(uint proposalId) external {
305:         require(
306:             state(proposalId) == ProposalState.Queued,
307:             "GovernorBravo::execute: proposal can only be executed if it is queued"
308:         );
309:         Proposal storage proposal = proposals[proposalId];
310:         proposal.executed = true;
311:         for (uint i; i < proposal.targets.length; ++i) {
312:             proposalTimelocks[uint8(proposal.proposalType)].executeTransaction(
313:                 proposal.targets[i],
314:                 proposal.values[i],
315:                 proposal.signatures[i],
316:                 proposal.calldatas[i],
317:                 proposal.eta
318:             );
319:         }
320:         emit ProposalExecuted(proposalId);
321:     }

[]

210:     function execute(uint proposalId) external {
211:         require(
212:             state(proposalId) == ProposalState.Queued,
213:             "GovernorBravo::execute: proposal can only be executed if it is queued"
214:         );
215:         Proposal storage proposal = proposals[proposalId];
216:         proposal.executed = true;
217:         for (uint i = 0; i < proposal.targets.length; i++) {
218:             timelock.executeTransaction(
219:                 proposal.targets[i],
220:                 proposal.values[i],
221:                 proposal.signatures[i],
222:                 proposal.calldatas[i],
223:                 proposal.eta
224:             );
225:         }
226:         emit ProposalExecuted(proposalId);
227:     }

['328']

327:     function cancel(uint proposalId) external {
328:         require(state(proposalId) != ProposalState.Executed, "GovernorBravo::cancel: cannot cancel executed proposal"); // <= FOUND
329: 
330:         Proposal storage proposal = proposals[proposalId];
331:         require(
332:             msg.sender == guardian ||
333:                 msg.sender == proposal.proposer ||
334:                 xvsVault.getPriorVotes(proposal.proposer, sub256(block.number, 1)) <
335:                 proposalConfigs[proposal.proposalType].proposalThreshold,
336:             "GovernorBravo::cancel: proposer above threshold"
337:         );
338: 
339:         proposal.canceled = true;
340:         for (uint i = 0; i < proposal.targets.length; i++) {
341:             proposalTimelocks[proposal.proposalType].cancelTransaction(
342:                 proposal.targets[i],
343:                 proposal.values[i],
344:                 proposal.signatures[i],
345:                 proposal.calldatas[i],
346:                 proposal.eta
347:             );
348:         }
349: 
350:         emit ProposalCanceled(proposalId);
351:     }

['234']

233:     function cancel(uint proposalId) external {
234:         require(state(proposalId) != ProposalState.Executed, "GovernorBravo::cancel: cannot cancel executed proposal"); // <= FOUND
235: 
236:         Proposal storage proposal = proposals[proposalId];
237:         require(
238:             msg.sender == guardian ||
239:                 msg.sender == proposal.proposer ||
240:                 xvsVault.getPriorVotes(proposal.proposer, sub256(block.number, 1)) < proposalThreshold,
241:             "GovernorBravo::cancel: proposer above threshold"
242:         );
243: 
244:         proposal.canceled = true;
245:         for (uint i = 0; i < proposal.targets.length; i++) {
246:             timelock.cancelTransaction(
247:                 proposal.targets[i],
248:                 proposal.values[i],
249:                 proposal.signatures[i],
250:                 proposal.calldatas[i],
251:                 proposal.eta
252:             );
253:         }
254: 
255:         emit ProposalCanceled(proposalId);
256:     }

[]

384:     function state(uint proposalId) public view returns (ProposalState) {
385:         require(
386:             proposalCount >= proposalId && proposalId > initialProposalId,
387:             "GovernorBravo::state: invalid proposal id"
388:         );
389:         Proposal storage proposal = proposals[proposalId];
390:         if (proposal.canceled) {
391:             return ProposalState.Canceled;
392:         } else if (block.number <= proposal.startBlock) {
393:             return ProposalState.Pending;
394:         } else if (block.number <= proposal.endBlock) {
395:             return ProposalState.Active;
396:         } else if (proposal.forVotes <= proposal.againstVotes || proposal.forVotes < quorumVotes) {
397:             return ProposalState.Defeated;
398:         } else if (proposal.eta == 0) {
399:             return ProposalState.Succeeded;
400:         } else if (proposal.executed) {
401:             return ProposalState.Executed;
402:         } else if (
403:             block.timestamp >= add256(proposal.eta, proposalTimelocks[uint8(proposal.proposalType)].GRACE_PERIOD())
404:         ) {
405:             return ProposalState.Expired;
406:         } else {
407:             return ProposalState.Queued;
408:         }
409:     }

[]

289:     function state(uint proposalId) public view returns (ProposalState) {
290:         require(
291:             proposalCount >= proposalId && proposalId > initialProposalId,
292:             "GovernorBravo::state: invalid proposal id"
293:         );
294:         Proposal storage proposal = proposals[proposalId];
295:         if (proposal.canceled) {
296:             return ProposalState.Canceled;
297:         } else if (block.number <= proposal.startBlock) {
298:             return ProposalState.Pending;
299:         } else if (block.number <= proposal.endBlock) {
300:             return ProposalState.Active;
301:         } else if (proposal.forVotes <= proposal.againstVotes || proposal.forVotes < quorumVotes) {
302:             return ProposalState.Defeated;
303:         } else if (proposal.eta == 0) {
304:             return ProposalState.Succeeded;
305:         } else if (proposal.executed) {
306:             return ProposalState.Executed;
307:         } else if (block.timestamp >= add256(proposal.eta, timelock.GRACE_PERIOD())) {
308:             return ProposalState.Expired;
309:         } else {
310:             return ProposalState.Queued;
311:         }
312:     }

['344']

337:     function castVoteBySig(uint proposalId, uint8 support, uint8 v, bytes32 r, bytes32 s) external {
338:         bytes32 domainSeparator = keccak256(
339:             abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainIdInternal(), address(this))
340:         );
341:         bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));
342:         bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
343:         address signatory = ecrecover(digest, v, r, s);
344:         require(signatory != address(0), "GovernorBravo::castVoteBySig: invalid signature"); // <= FOUND
345:         emit VoteCast(signatory, proposalId, support, castVoteInternal(signatory, proposalId, support), "");
346:     }

['344']

337:     function castVoteBySig(uint proposalId, uint8 support, uint8 v, bytes32 r, bytes32 s) external {
338:         bytes32 domainSeparator = keccak256(
339:             abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainIdInternal(), address(this))
340:         );
341:         bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));
342:         bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
343:         address signatory = ecrecover(digest, v, r, s);
344:         require(signatory != address(0), "GovernorBravo::castVoteBySig: invalid signature"); // <= FOUND
345:         emit VoteCast(signatory, proposalId, support, castVoteInternal(signatory, proposalId, support), "");
346:     }

['356']

355:     function castVoteInternal(address voter, uint proposalId, uint8 support) internal returns (uint96) {
356:         require(state(proposalId) == ProposalState.Active, "GovernorBravo::castVoteInternal: voting is closed"); // <= FOUND
357:         require(support <= 2, "GovernorBravo::castVoteInternal: invalid vote type");
358:         Proposal storage proposal = proposals[proposalId];
359:         Receipt storage receipt = proposal.receipts[voter];
360:         require(receipt.hasVoted == false, "GovernorBravo::castVoteInternal: voter already voted");
361:         uint96 votes = xvsVault.getPriorVotes(voter, proposal.startBlock);
362: 
363:         if (support == 0) {
364:             proposal.againstVotes = add256(proposal.againstVotes, votes);
365:         } else if (support == 1) {
366:             proposal.forVotes = add256(proposal.forVotes, votes);
367:         } else if (support == 2) {
368:             proposal.abstainVotes = add256(proposal.abstainVotes, votes);
369:         }
370: 
371:         receipt.hasVoted = true;
372:         receipt.support = support;
373:         receipt.votes = votes;
374: 
375:         return votes;
376:     }

['356']

355:     function castVoteInternal(address voter, uint proposalId, uint8 support) internal returns (uint96) {
356:         require(state(proposalId) == ProposalState.Active, "GovernorBravo::castVoteInternal: voting is closed"); // <= FOUND
357:         require(support <= 2, "GovernorBravo::castVoteInternal: invalid vote type");
358:         Proposal storage proposal = proposals[proposalId];
359:         Receipt storage receipt = proposal.receipts[voter];
360:         require(receipt.hasVoted == false, "GovernorBravo::castVoteInternal: voter already voted");
361:         uint96 votes = xvsVault.getPriorVotes(voter, proposal.startBlock);
362: 
363:         if (support == 0) {
364:             proposal.againstVotes = add256(proposal.againstVotes, votes);
365:         } else if (support == 1) {
366:             proposal.forVotes = add256(proposal.forVotes, votes);
367:         } else if (support == 2) {
368:             proposal.abstainVotes = add256(proposal.abstainVotes, votes);
369:         }
370: 
371:         receipt.hasVoted = true;
372:         receipt.support = support;
373:         receipt.votes = votes;
374: 
375:         return votes;
376:     }

['383']

382:     function _setGuardian(address newGuardian) external {
383:         require(msg.sender == guardian || msg.sender == admin, "GovernorBravo::_setGuardian: admin or guardian only"); // <= FOUND
384:         require(newGuardian != address(0), "GovernorBravo::_setGuardian: cannot live without a guardian");
385:         address oldGuardian = guardian;
386:         guardian = newGuardian;
387: 
388:         emit NewGuardian(oldGuardian, newGuardian);
389:     }

['499']

498:     function _initiate(address governorAlpha) external {
499:         require(msg.sender == admin, "GovernorBravo::_initiate: admin only"); // <= FOUND
500:         require(initialProposalId == 0, "GovernorBravo::_initiate: can only initiate once");
501:         proposalCount = GovernorAlphaInterface(governorAlpha).proposalCount();
502:         initialProposalId = proposalCount;
503:         for (uint256 i; i < uint8(ProposalType.CRITICAL) + 1; ++i) {
504:             proposalTimelocks[i].acceptAdmin();
505:         }
506:     }

['446']

445:     function _initiate(address governorAlpha) external {
446:         require(msg.sender == admin, "GovernorBravo::_initiate: admin only"); // <= FOUND
447:         require(initialProposalId == 0, "GovernorBravo::_initiate: can only initiate once");
448:         proposalCount = GovernorAlphaInterface(governorAlpha).proposalCount();
449:         initialProposalId = proposalCount;
450:         timelock.acceptAdmin();
451:     }

['459']

458:     function _setProposalMaxOperations(uint proposalMaxOperations_) external {
459:         require(msg.sender == admin, "GovernorBravo::_setProposalMaxOperations: admin only"); // <= FOUND
460:         uint oldProposalMaxOperations = proposalMaxOperations;
461:         proposalMaxOperations = proposalMaxOperations_;
462: 
463:         emit ProposalMaxOperationsUpdated(oldProposalMaxOperations, proposalMaxOperations_);
464:     }

['473']

471:     function _setPendingAdmin(address newPendingAdmin) external {
472:         
473:         require(msg.sender == admin, "GovernorBravo:_setPendingAdmin: admin only"); // <= FOUND
474: 
475:         
476:         address oldPendingAdmin = pendingAdmin;
477: 
478:         
479:         pendingAdmin = newPendingAdmin;
480: 
481:         
482:         emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin);
483:     }

[]

489:     function _acceptAdmin() external {
490:         
491:         require(
492:             msg.sender == pendingAdmin && msg.sender != address(0),
493:             "GovernorBravo:_acceptAdmin: pending admin only"
494:         );
495: 
496:         
497:         address oldAdmin = admin;
498:         address oldPendingAdmin = pendingAdmin;
499: 
500:         
501:         admin = pendingAdmin;
502: 
503:         
504:         pendingAdmin = address(0);
505: 
506:         emit NewAdmin(oldAdmin, admin);
507:         emit NewPendingAdmin(oldPendingAdmin, pendingAdmin);
508:     }

[]

192:     function queueOrRevertInternal(
193:         address target,
194:         uint value,
195:         string memory signature,
196:         bytes memory data,
197:         uint eta
198:     ) internal {
199:         require(
200:             !timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))),
201:             "GovernorBravo::queueOrRevertInternal: identical proposal action already queued at eta"
202:         );
203:         timelock.queueTransaction(target, value, signature, data, eta);
204:     }

['396']

395:     function _setVotingDelay(uint newVotingDelay) external {
396:         require(msg.sender == admin, "GovernorBravo::_setVotingDelay: admin only"); // <= FOUND
397:         require(
398:             newVotingDelay >= MIN_VOTING_DELAY && newVotingDelay <= MAX_VOTING_DELAY,
399:             "GovernorBravo::_setVotingDelay: invalid voting delay"
400:         );
401:         uint oldVotingDelay = votingDelay;
402:         votingDelay = newVotingDelay;
403: 
404:         emit VotingDelaySet(oldVotingDelay, votingDelay);
405:     }

['412']

411:     function _setVotingPeriod(uint newVotingPeriod) external {
412:         require(msg.sender == admin, "GovernorBravo::_setVotingPeriod: admin only"); // <= FOUND
413:         require(
414:             newVotingPeriod >= MIN_VOTING_PERIOD && newVotingPeriod <= MAX_VOTING_PERIOD,
415:             "GovernorBravo::_setVotingPeriod: invalid voting period"
416:         );
417:         uint oldVotingPeriod = votingPeriod;
418:         votingPeriod = newVotingPeriod;
419: 
420:         emit VotingPeriodSet(oldVotingPeriod, votingPeriod);
421:     }

['429']

428:     function _setProposalThreshold(uint newProposalThreshold) external {
429:         require(msg.sender == admin, "GovernorBravo::_setProposalThreshold: admin only"); // <= FOUND
430:         require(
431:             newProposalThreshold >= MIN_PROPOSAL_THRESHOLD && newProposalThreshold <= MAX_PROPOSAL_THRESHOLD,
432:             "GovernorBravo::_setProposalThreshold: invalid proposal threshold"
433:         );
434:         uint oldProposalThreshold = proposalThreshold;
435:         proposalThreshold = newProposalThreshold;
436: 
437:         emit ProposalThresholdSet(oldProposalThreshold, proposalThreshold);
438:     }

['71']

69:     function upsertSignature(string[] calldata signatures_, bool[] calldata active_) external onlyOwner {
70:         uint256 signatureLength = signatures_.length;
71:         require(signatureLength == active_.length, "Input arrays must have the same length"); // <= FOUND
72:         for (uint256 i; i < signatureLength; ) {
73:             bytes4 sigHash = bytes4(keccak256(bytes(signatures_[i])));
74:             bytes memory signature = bytes(functionRegistry[sigHash]);
75:             if (active_[i] && signature.length == 0) {
76:                 functionRegistry[sigHash] = signatures_[i];
77:                 emit FunctionRegistryChanged(signatures_[i], true);
78:             } else if (!active_[i] && signature.length != 0) {
79:                 delete functionRegistry[sigHash];
80:                 emit FunctionRegistryChanged(signatures_[i], false);
81:             }
82:             unchecked {
83:                 ++i;
84:             }
85:         }
86:     }

[]

157:     function addTimelocks(ITimelock[] memory timelocks_) external onlyOwner {
158:         uint8 length = uint8(type(ProposalType).max) + 1;
159:         require(
160:             timelocks_.length == length,
161:             "OmnichainGovernanceExecutor::initialize:number of timelocks _should match the number of governance routes"
162:         );
163:         for (uint8 i; i < length; ) {
164:             ensureNonzeroAddress(address(timelocks_[i]));
165:             emit TimelockAdded(i, address(proposalTimelocks[i]), address(timelocks_[i]));
166:             proposalTimelocks[i] = timelocks_[i];
167:             unchecked {
168:                 ++i;
169:             }
170:         }
171:     }

[]

178:     function execute(uint256 proposalId_) external nonReentrant {
179:         require(
180:             state(proposalId_) == ProposalState.Queued,
181:             "OmnichainGovernanceExecutor::execute: proposal can only be executed if it is queued"
182:         );
183: 
184:         Proposal storage proposal = proposals[proposalId_];
185:         proposal.executed = true;
186:         ITimelock timelock = proposalTimelocks[proposal.proposalType];
187:         uint256 eta = proposal.eta;
188:         uint256 length = proposal.targets.length;
189: 
190:         emit ProposalExecuted(proposalId_);
191: 
192:         for (uint256 i; i < length; ) {
193:             timelock.executeTransaction(
194:                 proposal.targets[i],
195:                 proposal.values[i],
196:                 proposal.signatures[i],
197:                 proposal.calldatas[i],
198:                 eta
199:             );
200:             unchecked {
201:                 ++i;
202:             }
203:         }
204:     }

[]

212:     function cancel(uint256 proposalId_) external {
213:         require(
214:             state(proposalId_) == ProposalState.Queued,
215:             "OmnichainGovernanceExecutor::cancel: proposal should be queued and not executed"
216:         );
217:         Proposal storage proposal = proposals[proposalId_];
218:         require(msg.sender == GUARDIAN, "OmnichainGovernanceExecutor::cancel: sender must be guardian");
219: 
220:         proposal.canceled = true;
221:         ITimelock timelock = proposalTimelocks[proposal.proposalType];
222:         uint256 eta = proposal.eta;
223:         uint256 length = proposal.targets.length;
224: 
225:         emit ProposalCanceled(proposalId_);
226: 
227:         for (uint256 i; i < length; ) {
228:             timelock.cancelTransaction(
229:                 proposal.targets[i],
230:                 proposal.values[i],
231:                 proposal.signatures[i],
232:                 proposal.calldatas[i],
233:                 eta
234:             );
235:             unchecked {
236:                 ++i;
237:             }
238:         }
239:     }

['276']

268:     function _blockingLzReceive(
269:         uint16 srcChainId_,
270:         bytes memory srcAddress_,
271:         uint64 nonce_,
272:         bytes memory payload_
273:     ) internal virtual override whenNotPaused {
274:         uint256 gasToStoreAndEmit = 30000; 
275: 
276:         require(srcChainId_ == srcChainId, "OmnichainGovernanceExecutor::_blockingLzReceive: invalid source chain id"); // <= FOUND
277: 
278:         (bool success, bytes memory reason) = address(this).excessivelySafeCall(
279:             gasleft() - gasToStoreAndEmit,
280:             150,
281:             abi.encodeCall(this.nonblockingLzReceive, (srcChainId_, srcAddress_, nonce_, payload_))
282:         );
283:         
284:         if (!success) {
285:             bytes32 hashedPayload = keccak256(payload_);
286:             failedMessages[srcChainId_][srcAddress_][nonce_] = hashedPayload;
287:             emit ReceivePayloadFailed(srcChainId_, srcAddress_, nonce_, reason); 
288:         }
289:     }

['305']

296:     function _nonblockingLzReceive(uint16, bytes memory, uint64, bytes memory payload_) internal virtual override {
297:         (bytes memory payload, uint256 pId) = abi.decode(payload_, (bytes, uint256));
298:         (
299:             address[] memory targets,
300:             uint256[] memory values,
301:             string[] memory signatures,
302:             bytes[] memory calldatas,
303:             uint8 pType
304:         ) = abi.decode(payload, (address[], uint256[], string[], bytes[], uint8));
305:         require(proposals[pId].id == 0, "OmnichainGovernanceExecutor::_nonblockingLzReceive: duplicate proposal"); // <= FOUND
306:         require(
307:             targets.length == values.length &&
308:                 targets.length == signatures.length &&
309:                 targets.length == calldatas.length,
310:             "OmnichainGovernanceExecutor::_nonblockingLzReceive: proposal function information arity mismatch"
311:         );
312:         require(
313:             pType < uint8(type(ProposalType).max) + 1,
314:             "OmnichainGovernanceExecutor::_nonblockingLzReceive: invalid proposal type"
315:         );
316:         _isEligibleToReceive(targets.length);
317: 
318:         Proposal memory newProposal = Proposal({
319:             id: pId,
320:             eta: 0,
321:             targets: targets,
322:             values: values,
323:             signatures: signatures,
324:             calldatas: calldatas,
325:             canceled: false,
326:             executed: false,
327:             proposalType: pType
328:         });
329: 
330:         proposals[pId] = newProposal;
331:         lastProposalReceived = pId;
332: 
333:         emit ProposalReceived(newProposal.id, targets, values, signatures, calldatas, pType);
334:         _queue(pId);
335:     }

[]

376:     function _queueOrRevertInternal(
377:         address target_,
378:         uint256 value_,
379:         string memory signature_,
380:         bytes memory data_,
381:         uint256 eta_,
382:         uint8 proposalType_
383:     ) internal {
384:         require(
385:             !proposalTimelocks[proposalType_].queuedTransactions(
386:                 keccak256(abi.encode(target_, value_, signature_, data_, eta_))
387:             ),
388:             "OmnichainGovernanceExecutor::queueOrRevertInternal: identical proposal action already queued at eta"
389:         );
390: 
391:         proposalTimelocks[proposalType_].queueTransaction(target_, value_, signature_, data_, eta_);
392:     }

['132']

124:     function execute(
125:         uint16 remoteChainId_,
126:         bytes calldata payload_,
127:         bytes calldata adapterParams_
128:     ) external payable whenNotPaused {
129:         _ensureAllowed("execute(uint16,bytes,bytes)");
130: 
131:         
132:         require(msg.value > 0, "OmnichainProposalSender: value cannot be zero"); // <= FOUND
133:         require(payload_.length != 0, "OmnichainProposalSender: Empty payload"); // <= FOUND
134: 
135:         bytes memory trustedRemote = trustedRemoteLookup[remoteChainId_];
136:         require(trustedRemote.length != 0, "OmnichainProposalSender: destination chain is not a trusted source");
137:         _validateProposal(remoteChainId_, payload_);
138:         uint256 _pId = ++proposalCount;
139:         bytes memory payload = abi.encode(payload_, _pId);
140: 
141:         try
142:             LZ_ENDPOINT.send{ value: msg.value }(
143:                 remoteChainId_,
144:                 trustedRemote,
145:                 payload,
146:                 payable(msg.sender),
147:                 address(0),
148:                 adapterParams_
149:             )
150:         {
151:             emit ExecuteRemoteProposal(remoteChainId_, _pId, payload);
152:         } catch (bytes memory reason) {
153:             storedExecutionHashes[_pId] = keccak256(abi.encode(remoteChainId_, payload, adapterParams_, msg.value));
154:             emit StorePayload(_pId, remoteChainId_, payload, adapterParams_, msg.value, reason);
155:         }
156:     }

['181']

169:     function retryExecute(
170:         uint256 pId_,
171:         uint16 remoteChainId_,
172:         bytes calldata payload_,
173:         bytes calldata adapterParams_,
174:         uint256 originalValue_
175:     ) external payable whenNotPaused nonReentrant {
176:         _ensureAllowed("retryExecute(uint256,uint16,bytes,bytes,uint256)");
177:         bytes memory trustedRemote = trustedRemoteLookup[remoteChainId_];
178:         require(trustedRemote.length != 0, "OmnichainProposalSender: destination chain is not a trusted source");
179:         bytes32 hash = storedExecutionHashes[pId_];
180:         require(hash != bytes32(0), "OmnichainProposalSender: no stored payload");
181:         require(payload_.length != 0, "OmnichainProposalSender: Empty payload"); // <= FOUND
182:         (bytes memory payload, ) = abi.decode(payload_, (bytes, uint256));
183:         _validateProposal(remoteChainId_, payload);
184: 
185:         bytes memory execution = abi.encode(remoteChainId_, payload_, adapterParams_, originalValue_);
186:         require(keccak256(execution) == hash, "OmnichainProposalSender: invalid execution params");
187: 
188:         delete storedExecutionHashes[pId_];
189: 
190:         emit ClearPayload(pId_, hash);
191: 
192:         LZ_ENDPOINT.send{ value: originalValue_ + msg.value }(
193:             remoteChainId_,
194:             trustedRemoteLookup[remoteChainId_],
195:             payload_,
196:             payable(msg.sender),
197:             address(0),
198:             adapterParams_
199:         );
200:     }

['224']

214:     function fallbackWithdraw(
215:         address to_,
216:         uint256 pId_,
217:         uint16 remoteChainId_,
218:         bytes calldata payload_,
219:         bytes calldata adapterParams_,
220:         uint256 originalValue_
221:     ) external onlyOwner nonReentrant {
222:         ensureNonzeroAddress(to_);
223:         require(originalValue_ > 0, "OmnichainProposalSender: invalid native amount");
224:         require(payload_.length != 0, "OmnichainProposalSender: Empty payload"); // <= FOUND
225: 
226:         bytes32 hash = storedExecutionHashes[pId_];
227:         require(hash != bytes32(0), "OmnichainProposalSender: no stored payload");
228: 
229:         bytes memory execution = abi.encode(remoteChainId_, payload_, adapterParams_, originalValue_);
230:         require(keccak256(execution) == hash, "OmnichainProposalSender: invalid execution params");
231: 
232:         delete storedExecutionHashes[pId_];
233: 
234:         emit FallbackWithdraw(to_, originalValue_);
235:         emit ClearPayload(pId_, hash);
236: 
237:         
238:         (bool sent, ) = to_.call{ value: originalValue_ }("");
239:         require(sent, "Call failed");
240:     }

[]

291:     function _validateProposal(uint16 remoteChainId_, bytes memory payload_) internal {
292:         (
293:             address[] memory targets,
294:             uint256[] memory values,
295:             string[] memory signatures,
296:             bytes[] memory calldatas,
297: 
298:         ) = abi.decode(payload_, (address[], uint[], string[], bytes[], uint8));
299:         require(
300:             targets.length == values.length &&
301:                 targets.length == signatures.length &&
302:                 targets.length == calldatas.length,
303:             "OmnichainProposalSender: proposal function information arity mismatch"
304:         );
305:         _isEligibleToSend(remoteChainId_, targets.length);
306:     }

['93']

84:     function mul(uint256 a, uint256 b) internal pure returns (uint256) {
85:         
86:         
87:         
88:         if (a == 0) {
89:             return 0;
90:         }
91: 
92:         uint256 c = a * b;
93:         require(c / a == b, "SafeMath: multiplication overflow"); // <= FOUND
94: 
95:         return c;
96:     }

['87']

86:     function setDelay(uint delay_) public {
87:         require(msg.sender == address(this), "Timelock::setDelay: Call must come from Timelock."); // <= FOUND
88:         require(delay_ >= MINIMUM_DELAY, "Timelock::setDelay: Delay must exceed minimum delay.");
89:         require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay."); // <= FOUND
90:         delay = delay_;
91: 
92:         emit NewDelay(delay);
93:     }

['91']

90:     function setDelay(uint256 delay_) public {
91:         require(msg.sender == address(this), "Timelock::setDelay: Call must come from Timelock."); // <= FOUND
92:         require(delay_ >= MINIMUM_DELAY(), "Timelock::setDelay: Delay must exceed minimum delay.");
93:         require(delay_ <= MAXIMUM_DELAY(), "Timelock::setDelay: Delay must not exceed maximum delay.");
94:         emit NewDelay(delay, delay_);
95:         delay = delay_;
96:     }

['99']

98:     function acceptAdmin() public {
99:         require(msg.sender == pendingAdmin, "Timelock::acceptAdmin: Call must come from pendingAdmin."); // <= FOUND
100:         admin = msg.sender;
101:         pendingAdmin = address(0);
102: 
103:         emit NewAdmin(admin);
104:     }

['128']

127:     function acceptAdmin() public {
128:         require(msg.sender == pendingAdmin, "Timelock::acceptAdmin: Call must come from pendingAdmin."); // <= FOUND
129:         emit NewAdmin(admin, msg.sender);
130:         admin = msg.sender;
131:         pendingAdmin = address(0);
132:     }

['111']

110:     function setPendingAdmin(address pendingAdmin_) public {
111:         require(msg.sender == address(this), "Timelock::setPendingAdmin: Call must come from Timelock."); // <= FOUND
112:         pendingAdmin = pendingAdmin_;
113: 
114:         emit NewPendingAdmin(pendingAdmin);
115:     }

['141']

140:     function setPendingAdmin(address pendingAdmin_) public {
141:         require(msg.sender == address(this), "Timelock::setPendingAdmin: Call must come from Timelock."); // <= FOUND
142:         ensureNonzeroAddress(pendingAdmin_);
143:         pendingAdmin = pendingAdmin_;
144: 
145:         emit NewPendingAdmin(pendingAdmin);
146:     }

['133']

126:     function queueTransaction(
127:         address target,
128:         uint value,
129:         string memory signature,
130:         bytes memory data,
131:         uint eta
132:     ) public returns (bytes32) {
133:         require(msg.sender == admin, "Timelock::queueTransaction: Call must come from admin."); // <= FOUND
134:         require(
135:             eta >= getBlockTimestamp().add(delay),
136:             "Timelock::queueTransaction: Estimated execution block must satisfy delay."
137:         );
138: 
139:         bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
140:         queuedTransactions[txHash] = true;
141: 
142:         emit QueueTransaction(txHash, target, value, signature, data, eta);
143:         return txHash;
144:     }

['166']

159:     function queueTransaction(
160:         address target,
161:         uint256 value,
162:         string calldata signature,
163:         bytes calldata data,
164:         uint256 eta
165:     ) public returns (bytes32) {
166:         require(msg.sender == admin, "Timelock::queueTransaction: Call must come from admin."); // <= FOUND
167:         require(
168:             eta >= getBlockTimestamp() + delay,
169:             "Timelock::queueTransaction: Estimated execution block must satisfy delay."
170:         );
171: 
172:         bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
173:         require(!queuedTransactions[txHash], "Timelock::queueTransaction: transaction already queued.");
174:         queuedTransactions[txHash] = true;
175: 
176:         emit QueueTransaction(txHash, target, value, signature, data, eta);
177:         return txHash;
178:     }

['161']

154:     function cancelTransaction(
155:         address target,
156:         uint value,
157:         string memory signature,
158:         bytes memory data,
159:         uint eta
160:     ) public {
161:         require(msg.sender == admin, "Timelock::cancelTransaction: Call must come from admin."); // <= FOUND
162: 
163:         bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
164:         queuedTransactions[txHash] = false;
165: 
166:         emit CancelTransaction(txHash, target, value, signature, data, eta);
167:     }

['197']

190:     function cancelTransaction(
191:         address target,
192:         uint256 value,
193:         string calldata signature,
194:         bytes calldata data,
195:         uint256 eta
196:     ) public {
197:         require(msg.sender == admin, "Timelock::cancelTransaction: Call must come from admin."); // <= FOUND
198: 
199:         bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
200:         require(queuedTransactions[txHash], "Timelock::cancelTransaction: transaction is not queued yet.");
201:         delete (queuedTransactions[txHash]);
202: 
203:         emit CancelTransaction(txHash, target, value, signature, data, eta);
204:     }

['184']

177:     function executeTransaction(
178:         address target,
179:         uint value,
180:         string memory signature,
181:         bytes memory data,
182:         uint eta
183:     ) public payable returns (bytes memory) {
184:         require(msg.sender == admin, "Timelock::executeTransaction: Call must come from admin."); // <= FOUND
185: 
186:         bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
187:         require(queuedTransactions[txHash], "Timelock::executeTransaction: Transaction hasn't been queued.");
188:         require(getBlockTimestamp() >= eta, "Timelock::executeTransaction: Transaction hasn't surpassed time lock.");
189:         require(getBlockTimestamp() <= eta.add(GRACE_PERIOD), "Timelock::executeTransaction: Transaction is stale.");
190: 
191:         queuedTransactions[txHash] = false;
192: 
193:         bytes memory callData;
194: 
195:         if (bytes(signature).length == 0) {
196:             callData = data;
197:         } else {
198:             callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data);
199:         }
200: 
201:         
202:         (bool success, bytes memory returnData) = target.call.value(value)(callData);
203:         require(success, "Timelock::executeTransaction: Transaction execution reverted.");
204: 
205:         emit ExecuteTransaction(txHash, target, value, signature, data, eta);
206: 
207:         return returnData;
208:     }

['224']

217:     function executeTransaction(
218:         address target,
219:         uint256 value,
220:         string calldata signature,
221:         bytes calldata data,
222:         uint256 eta
223:     ) public payable returns (bytes memory) {
224:         require(msg.sender == admin, "Timelock::executeTransaction: Call must come from admin."); // <= FOUND
225: 
226:         bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
227:         require(queuedTransactions[txHash], "Timelock::executeTransaction: Transaction hasn't been queued.");
228:         require(getBlockTimestamp() >= eta, "Timelock::executeTransaction: Transaction hasn't surpassed time lock.");
229:         require(getBlockTimestamp() <= eta + GRACE_PERIOD(), "Timelock::executeTransaction: Transaction is stale.");
230: 
231:         delete (queuedTransactions[txHash]);
232: 
233:         bytes memory callData;
234: 
235:         if (bytes(signature).length == 0) {
236:             callData = data;
237:         } else {
238:             callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data);
239:         }
240: 
241:         
242:         (bool success, bytes memory returnData) = target.call{ value: value }(callData);
243:         require(success, "Timelock::executeTransaction: Transaction execution reverted.");
244: 
245:         emit ExecuteTransaction(txHash, target, value, signature, data, eta);
246: 
247:         return returnData;
248:     }

[Gas-12] Public functions not used internally can be marked as external to save gas

Resolution

Public functions that aren't used internally in Solidity contracts should be made external to optimize gas usage and improve contract efficiency. External functions can only be called from outside the contract, and their arguments are directly read from the calldata, which is more gas-efficient than loading them into memory, as is the case for public functions. By using external visibility, developers can reduce gas consumption for external calls and ensure that the contract operates more cost-effectively for users. Moreover, setting the appropriate visibility level for functions also enhances code readability and maintainability, promoting a more secure and well-structured contract design.

Num of instances: 28

Findings

Click to show findings

['77']

77:     function giveCallPermission(address contractAddress, string calldata functionSig, address accountToPermit) public 

['91']

91:     function revokeCallPermission(
92:         address contractAddress,
93:         string calldata functionSig,
94:         address accountToRevoke
95:     ) public 

['128']

128:     function hasPermission(
129:         address account,
130:         address contractAddress,
131:         string calldata functionSig
132:     ) public view returns (bool) 

['72']

72:     function renounceOwnership() public override 

['72']

72:     function renounceOwnership() public override 

['143']

143:     function propose(
144:         address[] memory targets,
145:         uint[] memory values,
146:         string[] memory signatures,
147:         bytes[] memory calldatas,
148:         string memory description
149:     ) public returns (uint) 

['143']

143:     function propose(
144:         address[] memory targets,
145:         uint[] memory values,
146:         string[] memory signatures,
147:         bytes[] memory calldatas,
148:         string memory description
149:     ) public returns (uint) 

['181']

181:     function propose(
182:         address[] memory targets,
183:         uint[] memory values,
184:         string[] memory signatures,
185:         bytes[] memory calldatas,
186:         string memory description,
187:         ProposalType proposalType
188:     ) public returns (uint) 

['143']

143:     function propose(
144:         address[] memory targets,
145:         uint[] memory values,
146:         string[] memory signatures,
147:         bytes[] memory calldatas,
148:         string memory description
149:     ) public returns (uint) 

['254']

254:     function cancel(uint proposalId) public 

['254']

254:     function cancel(uint proposalId) public 

['233']

233:     function cancel(uint proposalId) external 

['233']

233:     function cancel(uint proposalId) external 

['212']

212:     function cancel(uint256 proposalId_) external 

['279']

279:     function getActions(
280:         uint proposalId
281:     )
282:         public
283:         view
284:         returns (address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas)
285:     

['263']

263:     function getActions(
264:         uint proposalId
265:     )
266:         external
267:         view
268:         returns (address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas)
269:     

['290']

290:     function getReceipt(uint proposalId, address voter) public view returns (Receipt memory) 

['280']

280:     function getReceipt(uint proposalId, address voter) external view returns (Receipt memory) 

['320']

320:     function castVoteBySig(uint proposalId, bool support, uint8 v, bytes32 r, bytes32 s) public 

['320']

320:     function castVoteBySig(uint proposalId, bool support, uint8 v, bytes32 r, bytes32 s) public 

['337']

337:     function castVoteBySig(uint proposalId, uint8 support, uint8 v, bytes32 r, bytes32 s) external 

['337']

337:     function castVoteBySig(uint proposalId, uint8 support, uint8 v, bytes32 r, bytes32 s) external 

['351']

351:     function __acceptAdmin() public 

['356']

356:     function __abdicate() public 

['361']

361:     function __queueSetTimelockPendingAdmin(address newPendingAdmin, uint eta) public 

['366']

366:     function __executeSetTimelockPendingAdmin(address newPendingAdmin, uint eta) public 

['86']

86:     function setDelay(uint delay_) public 

['90']

90:     function setDelay(uint256 delay_) public 

[Gas-13] Calldata should be used in place of memory function parameters when not mutated

Resolution

In Solidity, calldata should be used in place of memory for function parameters when the function is external. This is because calldata is a non-modifiable, non-persistent area where function arguments are stored, and it's cheaper in terms of gas than memory. It's especially efficient for arrays and complex data types. calldata provides a gas-efficient way to pass data in external function calls, whereas memory is a temporary space that's erased between external function calls. This distinction is crucial for optimizing smart contracts for gas usage and performance.

Num of instances: 5

Findings

Click to show findings

['143']

143:     function propose(
144:         address[] memory targets,
145:         uint[] memory values, // <= FOUND
146:         string[] memory signatures,
147:         bytes[] memory calldatas,
148:         string memory description
149:     ) public returns (uint) {
150:         require(
151:             xvs.getPriorVotes(msg.sender, sub256(block.number, 1)) > proposalThreshold(),
152:             "GovernorAlpha::propose: proposer votes below proposal threshold"
153:         );
154:         require(
155:             targets.length == values.length && // <= FOUND
156:                 targets.length == signatures.length &&
157:                 targets.length == calldatas.length,
158:             "GovernorAlpha::propose: proposal function information arity mismatch"
159:         );
160:         require(targets.length != 0, "GovernorAlpha::propose: must provide actions");
161:         require(targets.length <= proposalMaxOperations(), "GovernorAlpha::propose: too many actions");
162: 
163:         uint latestProposalId = latestProposalIds[msg.sender];
164:         if (latestProposalId != 0) {
165:             ProposalState proposersLatestProposalState = state(latestProposalId);
166:             require(
167:                 proposersLatestProposalState != ProposalState.Active,
168:                 "GovernorAlpha::propose: found an already active proposal"
169:             );
170:             require(
171:                 proposersLatestProposalState != ProposalState.Pending,
172:                 "GovernorAlpha::propose: found an already pending proposal"
173:             );
174:         }
175: 
176:         uint startBlock = add256(block.number, votingDelay());
177:         uint endBlock = add256(startBlock, votingPeriod());
178: 
179:         proposalCount++;
180:         Proposal memory newProposal = Proposal({
181:             id: proposalCount,
182:             proposer: msg.sender,
183:             eta: 0,
184:             targets: targets,
185:             values: values, // <= FOUND
186:             signatures: signatures,
187:             calldatas: calldatas,
188:             startBlock: startBlock,
189:             endBlock: endBlock,
190:             forVotes: 0,
191:             againstVotes: 0,
192:             canceled: false,
193:             executed: false
194:         });
195: 
196:         proposals[newProposal.id] = newProposal;
197:         latestProposalIds[newProposal.proposer] = newProposal.id;
198: 
199:         emit ProposalCreated(
200:             newProposal.id,
201:             msg.sender,
202:             targets,
203:             values, // <= FOUND
204:             signatures,
205:             calldatas,
206:             startBlock,
207:             endBlock,
208:             description
209:         );
210:         return newProposal.id;
211:     }

['143']

143:     function propose(
144:         address[] memory targets,
145:         uint[] memory values, // <= FOUND
146:         string[] memory signatures,
147:         bytes[] memory calldatas,
148:         string memory description
149:     ) public returns (uint) {
150:         require(
151:             xvs.getPriorVotes(msg.sender, sub256(block.number, 1)) > proposalThreshold(),
152:             "GovernorAlpha::propose: proposer votes below proposal threshold"
153:         );
154:         require(
155:             targets.length == values.length && // <= FOUND
156:                 targets.length == signatures.length &&
157:                 targets.length == calldatas.length,
158:             "GovernorAlpha::propose: proposal function information arity mismatch"
159:         );
160:         require(targets.length != 0, "GovernorAlpha::propose: must provide actions");
161:         require(targets.length <= proposalMaxOperations(), "GovernorAlpha::propose: too many actions");
162: 
163:         uint latestProposalId = latestProposalIds[msg.sender];
164:         if (latestProposalId != 0) {
165:             ProposalState proposersLatestProposalState = state(latestProposalId);
166:             require(
167:                 proposersLatestProposalState != ProposalState.Active,
168:                 "GovernorAlpha::propose: found an already active proposal"
169:             );
170:             require(
171:                 proposersLatestProposalState != ProposalState.Pending,
172:                 "GovernorAlpha::propose: found an already pending proposal"
173:             );
174:         }
175: 
176:         uint startBlock = add256(block.number, votingDelay());
177:         uint endBlock = add256(startBlock, votingPeriod());
178: 
179:         proposalCount++;
180:         Proposal memory newProposal = Proposal({
181:             id: proposalCount,
182:             proposer: msg.sender,
183:             eta: 0,
184:             targets: targets,
185:             values: values, // <= FOUND
186:             signatures: signatures,
187:             calldatas: calldatas,
188:             startBlock: startBlock,
189:             endBlock: endBlock,
190:             forVotes: 0,
191:             againstVotes: 0,
192:             canceled: false,
193:             executed: false
194:         });
195: 
196:         proposals[newProposal.id] = newProposal;
197:         latestProposalIds[newProposal.proposer] = newProposal.id;
198: 
199:         emit ProposalCreated(
200:             newProposal.id,
201:             msg.sender,
202:             targets,
203:             values, // <= FOUND
204:             signatures,
205:             calldatas,
206:             startBlock,
207:             endBlock,
208:             description
209:         );
210:         return newProposal.id;
211:     }

['111']

111:     function initialize(
112:         address xvsVault_,
113:         ProposalConfig[] memory proposalConfigs_, // <= FOUND
114:         TimelockInterface[] memory timelocks,
115:         address guardian_
116:     ) public {
117:         require(address(proposalTimelocks[0]) == address(0), "GovernorBravo::initialize: cannot initialize twice");
118:         require(msg.sender == admin, "GovernorBravo::initialize: admin only");
119:         require(xvsVault_ != address(0), "GovernorBravo::initialize: invalid xvs address");
120:         require(guardian_ != address(0), "GovernorBravo::initialize: invalid guardian");
121:         require(
122:             timelocks.length == uint8(ProposalType.CRITICAL) + 1,
123:             "GovernorBravo::initialize:number of timelocks should match number of governance routes"
124:         );
125:         require(
126:             proposalConfigs_.length == uint8(ProposalType.CRITICAL) + 1, // <= FOUND
127:             "GovernorBravo::initialize:number of proposal configs should match number of governance routes"
128:         );
129: 
130:         xvsVault = XvsVaultInterface(xvsVault_);
131:         proposalMaxOperations = 10;
132:         guardian = guardian_;
133: 
134:         
135:         uint256 arrLength = proposalConfigs_.length; // <= FOUND
136:         for (uint256 i; i < arrLength; ++i) {
137:             require(
138:                 proposalConfigs_[i].votingPeriod >= MIN_VOTING_PERIOD, // <= FOUND
139:                 "GovernorBravo::initialize: invalid min voting period"
140:             );
141:             require(
142:                 proposalConfigs_[i].votingPeriod <= MAX_VOTING_PERIOD, // <= FOUND
143:                 "GovernorBravo::initialize: invalid max voting period"
144:             );
145:             require(
146:                 proposalConfigs_[i].votingDelay >= MIN_VOTING_DELAY, // <= FOUND
147:                 "GovernorBravo::initialize: invalid min voting delay"
148:             );
149:             require(
150:                 proposalConfigs_[i].votingDelay <= MAX_VOTING_DELAY, // <= FOUND
151:                 "GovernorBravo::initialize: invalid max voting delay"
152:             );
153:             require(
154:                 proposalConfigs_[i].proposalThreshold >= MIN_PROPOSAL_THRESHOLD, // <= FOUND
155:                 "GovernorBravo::initialize: invalid min proposal threshold"
156:             );
157:             require(
158:                 proposalConfigs_[i].proposalThreshold <= MAX_PROPOSAL_THRESHOLD, // <= FOUND
159:                 "GovernorBravo::initialize: invalid max proposal threshold"
160:             );
161:             require(address(timelocks[i]) != address(0), "GovernorBravo::initialize:invalid timelock address");
162: 
163:             proposalConfigs[i] = proposalConfigs_[i]; // <= FOUND
164:             proposalTimelocks[i] = timelocks[i];
165:         }
166:     }

['181']

181:     function propose(
182:         address[] memory targets,
183:         uint[] memory values, // <= FOUND
184:         string[] memory signatures,
185:         bytes[] memory calldatas,
186:         string memory description,
187:         ProposalType proposalType
188:     ) public returns (uint) {
189:         
190:         require(initialProposalId != 0, "GovernorBravo::propose: Governor Bravo not active");
191:         require(
192:             xvsVault.getPriorVotes(msg.sender, sub256(block.number, 1)) >=
193:                 proposalConfigs[uint8(proposalType)].proposalThreshold,
194:             "GovernorBravo::propose: proposer votes below proposal threshold"
195:         );
196:         require(
197:             targets.length == values.length && // <= FOUND
198:                 targets.length == signatures.length &&
199:                 targets.length == calldatas.length,
200:             "GovernorBravo::propose: proposal function information arity mismatch"
201:         );
202:         require(targets.length != 0, "GovernorBravo::propose: must provide actions");
203:         require(targets.length <= proposalMaxOperations, "GovernorBravo::propose: too many actions");
204: 
205:         uint latestProposalId = latestProposalIds[msg.sender];
206:         if (latestProposalId != 0) {
207:             ProposalState proposersLatestProposalState = state(latestProposalId);
208:             require(
209:                 proposersLatestProposalState != ProposalState.Active,
210:                 "GovernorBravo::propose: one live proposal per proposer, found an already active proposal"
211:             );
212:             require(
213:                 proposersLatestProposalState != ProposalState.Pending,
214:                 "GovernorBravo::propose: one live proposal per proposer, found an already pending proposal"
215:             );
216:         }
217: 
218:         uint startBlock = add256(block.number, proposalConfigs[uint8(proposalType)].votingDelay);
219:         uint endBlock = add256(startBlock, proposalConfigs[uint8(proposalType)].votingPeriod);
220: 
221:         proposalCount++;
222:         Proposal memory newProposal = Proposal({
223:             id: proposalCount,
224:             proposer: msg.sender,
225:             eta: 0,
226:             targets: targets,
227:             values: values, // <= FOUND
228:             signatures: signatures,
229:             calldatas: calldatas,
230:             startBlock: startBlock,
231:             endBlock: endBlock,
232:             forVotes: 0,
233:             againstVotes: 0,
234:             abstainVotes: 0,
235:             canceled: false,
236:             executed: false,
237:             proposalType: uint8(proposalType)
238:         });
239: 
240:         proposals[newProposal.id] = newProposal;
241:         latestProposalIds[newProposal.proposer] = newProposal.id;
242: 
243:         emit ProposalCreated(
244:             newProposal.id,
245:             msg.sender,
246:             targets,
247:             values, // <= FOUND
248:             signatures,
249:             calldatas,
250:             startBlock,
251:             endBlock,
252:             description,
253:             uint8(proposalType)
254:         );
255:         return newProposal.id;
256:     }

['95']

95:     function propose(
96:         address[] memory targets,
97:         uint[] memory values, // <= FOUND
98:         string[] memory signatures,
99:         bytes[] memory calldatas,
100:         string memory description
101:     ) public returns (uint) {
102:         
103:         require(initialProposalId != 0, "GovernorBravo::propose: Governor Bravo not active");
104:         require(
105:             xvsVault.getPriorVotes(msg.sender, sub256(block.number, 1)) > proposalThreshold,
106:             "GovernorBravo::propose: proposer votes below proposal threshold"
107:         );
108:         require(
109:             targets.length == values.length && // <= FOUND
110:                 targets.length == signatures.length &&
111:                 targets.length == calldatas.length,
112:             "GovernorBravo::propose: proposal function information arity mismatch"
113:         );
114:         require(targets.length != 0, "GovernorBravo::propose: must provide actions");
115:         require(targets.length <= proposalMaxOperations, "GovernorBravo::propose: too many actions");
116: 
117:         uint latestProposalId = latestProposalIds[msg.sender];
118:         if (latestProposalId != 0) {
119:             ProposalState proposersLatestProposalState = state(latestProposalId);
120:             require(
121:                 proposersLatestProposalState != ProposalState.Active,
122:                 "GovernorBravo::propose: one live proposal per proposer, found an already active proposal"
123:             );
124:             require(
125:                 proposersLatestProposalState != ProposalState.Pending,
126:                 "GovernorBravo::propose: one live proposal per proposer, found an already pending proposal"
127:             );
128:         }
129: 
130:         uint startBlock = add256(block.number, votingDelay);
131:         uint endBlock = add256(startBlock, votingPeriod);
132: 
133:         proposalCount++;
134:         Proposal memory newProposal = Proposal({
135:             id: proposalCount,
136:             proposer: msg.sender,
137:             eta: 0,
138:             targets: targets,
139:             values: values, // <= FOUND
140:             signatures: signatures,
141:             calldatas: calldatas,
142:             startBlock: startBlock,
143:             endBlock: endBlock,
144:             forVotes: 0,
145:             againstVotes: 0,
146:             abstainVotes: 0,
147:             canceled: false,
148:             executed: false
149:         });
150: 
151:         proposals[newProposal.id] = newProposal;
152:         latestProposalIds[newProposal.proposer] = newProposal.id;
153: 
154:         emit ProposalCreated(
155:             newProposal.id,
156:             msg.sender,
157:             targets,
158:             values, // <= FOUND
159:             signatures,
160:             calldatas,
161:             startBlock,
162:             endBlock,
163:             description
164:         );
165:         return newProposal.id;
166:     }

[Gas-14] Usage of smaller uint/int types causes overhead

Resolution

When using a smaller int/uint type it first needs to be converted to it's 258 bit counterpart to be operated, this increases the gass cost and thus should be avoided. However it does make sense to use smaller int/uint values within structs provided you pack the struct properly.

Num of instances: 33

Findings

Click to show findings

['320']

320:     function castVoteBySig(uint proposalId, bool support, uint8 v, bytes32 r, bytes32 s) public { // <= FOUND
321:         bytes32 domainSeparator = keccak256(
322:             abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainId(), address(this))
323:         );
324:         bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));
325:         bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
326:         address signatory = ecrecover(digest, v, r, s);
327:         require(signatory != address(0), "GovernorAlpha::castVoteBySig: invalid signature");
328:         return _castVote(signatory, proposalId, support);
329:     }

['320']

320:     function castVoteBySig(uint proposalId, bool support, uint8 v, bytes32 r, bytes32 s) public { // <= FOUND
321:         bytes32 domainSeparator = keccak256(
322:             abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainId(), address(this))
323:         );
324:         bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));
325:         bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
326:         address signatory = ecrecover(digest, v, r, s);
327:         require(signatory != address(0), "GovernorAlpha::castVoteBySig: invalid signature");
328:         return _castVote(signatory, proposalId, support);
329:     }

['289']

283:     function queueOrRevertInternal(
284:         address target,
285:         uint value,
286:         string memory signature,
287:         bytes memory data,
288:         uint eta,
289:         uint8 proposalType // <= FOUND
290:     ) internal {
291:         require(
292:             !proposalTimelocks[proposalType].queuedTransactions(
293:                 keccak256(abi.encode(target, value, signature, data, eta))
294:             ),
295:             "GovernorBravo::queueOrRevertInternal: identical proposal action already queued at eta"
296:         );
297:         proposalTimelocks[proposalType].queueTransaction(target, value, signature, data, eta);
298:     }

['319']

319:     function castVote(uint proposalId, uint8 support) external { // <= FOUND
320:         emit VoteCast(msg.sender, proposalId, support, castVoteInternal(msg.sender, proposalId, support), "");
321:     }

['329']

329:     function castVoteWithReason(uint proposalId, uint8 support, string calldata reason) external { // <= FOUND
330:         emit VoteCast(msg.sender, proposalId, support, castVoteInternal(msg.sender, proposalId, support), reason);
331:     }

['337']

337:     function castVoteBySig(uint proposalId, uint8 support, uint8 v, bytes32 r, bytes32 s) external { // <= FOUND
338:         bytes32 domainSeparator = keccak256(
339:             abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainIdInternal(), address(this))
340:         );
341:         bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));
342:         bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
343:         address signatory = ecrecover(digest, v, r, s);
344:         require(signatory != address(0), "GovernorBravo::castVoteBySig: invalid signature");
345:         emit VoteCast(signatory, proposalId, support, castVoteInternal(signatory, proposalId, support), "");
346:     }

['355']

355:     function castVoteInternal(address voter, uint proposalId, uint8 support) internal returns (uint96) { // <= FOUND
356:         require(state(proposalId) == ProposalState.Active, "GovernorBravo::castVoteInternal: voting is closed");
357:         require(support <= 2, "GovernorBravo::castVoteInternal: invalid vote type");
358:         Proposal storage proposal = proposals[proposalId];
359:         Receipt storage receipt = proposal.receipts[voter];
360:         require(receipt.hasVoted == false, "GovernorBravo::castVoteInternal: voter already voted");
361:         uint96 votes = xvsVault.getPriorVotes(voter, proposal.startBlock); // <= FOUND
362: 
363:         if (support == 0) {
364:             proposal.againstVotes = add256(proposal.againstVotes, votes);
365:         } else if (support == 1) {
366:             proposal.forVotes = add256(proposal.forVotes, votes);
367:         } else if (support == 2) {
368:             proposal.abstainVotes = add256(proposal.abstainVotes, votes);
369:         }
370: 
371:         receipt.hasVoted = true;
372:         receipt.support = support;
373:         receipt.votes = votes;
374: 
375:         return votes;
376:     }

['319']

319:     function castVote(uint proposalId, uint8 support) external { // <= FOUND
320:         emit VoteCast(msg.sender, proposalId, support, castVoteInternal(msg.sender, proposalId, support), "");
321:     }

['329']

329:     function castVoteWithReason(uint proposalId, uint8 support, string calldata reason) external { // <= FOUND
330:         emit VoteCast(msg.sender, proposalId, support, castVoteInternal(msg.sender, proposalId, support), reason);
331:     }

['337']

337:     function castVoteBySig(uint proposalId, uint8 support, uint8 v, bytes32 r, bytes32 s) external { // <= FOUND
338:         bytes32 domainSeparator = keccak256(
339:             abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainIdInternal(), address(this))
340:         );
341:         bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));
342:         bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
343:         address signatory = ecrecover(digest, v, r, s);
344:         require(signatory != address(0), "GovernorBravo::castVoteBySig: invalid signature");
345:         emit VoteCast(signatory, proposalId, support, castVoteInternal(signatory, proposalId, support), "");
346:     }

['355']

355:     function castVoteInternal(address voter, uint proposalId, uint8 support) internal returns (uint96) { // <= FOUND
356:         require(state(proposalId) == ProposalState.Active, "GovernorBravo::castVoteInternal: voting is closed");
357:         require(support <= 2, "GovernorBravo::castVoteInternal: invalid vote type");
358:         Proposal storage proposal = proposals[proposalId];
359:         Receipt storage receipt = proposal.receipts[voter];
360:         require(receipt.hasVoted == false, "GovernorBravo::castVoteInternal: voter already voted");
361:         uint96 votes = xvsVault.getPriorVotes(voter, proposal.startBlock); // <= FOUND
362: 
363:         if (support == 0) {
364:             proposal.againstVotes = add256(proposal.againstVotes, votes);
365:         } else if (support == 1) {
366:             proposal.forVotes = add256(proposal.forVotes, votes);
367:         } else if (support == 2) {
368:             proposal.abstainVotes = add256(proposal.abstainVotes, votes);
369:         }
370: 
371:         receipt.hasVoted = true;
372:         receipt.support = support;
373:         receipt.votes = votes;
374: 
375:         return votes;
376:     }

['158']

157:     function addTimelocks(ITimelock[] memory timelocks_) external onlyOwner {
158:         uint8 length = uint8(type(ProposalType).max) + 1; // <= FOUND
159:         require(
160:             timelocks_.length == length,
161:             "OmnichainGovernanceExecutor::initialize:number of timelocks _should match the number of governance routes"
162:         );
163:         for (uint8 i; i < length; ) { // <= FOUND
164:             ensureNonzeroAddress(address(timelocks_[i]));
165:             emit TimelockAdded(i, address(proposalTimelocks[i]), address(timelocks_[i]));
166:             proposalTimelocks[i] = timelocks_[i];
167:             unchecked {
168:                 ++i;
169:             }
170:         }
171:     }

['303']

296:     function _nonblockingLzReceive(uint16, bytes memory, uint64, bytes memory payload_) internal virtual override {
297:         (bytes memory payload, uint256 pId) = abi.decode(payload_, (bytes, uint256));
298:         (
299:             address[] memory targets,
300:             uint256[] memory values,
301:             string[] memory signatures,
302:             bytes[] memory calldatas,
303:             uint8 pType // <= FOUND
304:         ) = abi.decode(payload, (address[], uint256[], string[], bytes[], uint8));
305:         require(proposals[pId].id == 0, "OmnichainGovernanceExecutor::_nonblockingLzReceive: duplicate proposal");
306:         require(
307:             targets.length == values.length &&
308:                 targets.length == signatures.length &&
309:                 targets.length == calldatas.length,
310:             "OmnichainGovernanceExecutor::_nonblockingLzReceive: proposal function information arity mismatch"
311:         );
312:         require(
313:             pType < uint8(type(ProposalType).max) + 1,
314:             "OmnichainGovernanceExecutor::_nonblockingLzReceive: invalid proposal type"
315:         );
316:         _isEligibleToReceive(targets.length);
317: 
318:         Proposal memory newProposal = Proposal({
319:             id: pId,
320:             eta: 0,
321:             targets: targets,
322:             values: values,
323:             signatures: signatures,
324:             calldatas: calldatas,
325:             canceled: false,
326:             executed: false,
327:             proposalType: pType
328:         });
329: 
330:         proposals[pId] = newProposal;
331:         lastProposalReceived = pId;
332: 
333:         emit ProposalReceived(newProposal.id, targets, values, signatures, calldatas, pType);
334:         _queue(pId);
335:     }

['348']

342:     function _queue(uint256 proposalId_) internal {
343:         Proposal storage proposal = proposals[proposalId_];
344:         uint256 eta = block.timestamp + proposalTimelocks[proposal.proposalType].delay();
345: 
346:         proposal.eta = eta;
347:         queued[proposalId_] = true;
348:         uint8 proposalType = proposal.proposalType; // <= FOUND
349:         uint256 length = proposal.targets.length;
350:         emit ProposalQueued(proposalId_, eta);
351: 
352:         for (uint256 i; i < length; ) {
353:             _queueOrRevertInternal(
354:                 proposal.targets[i],
355:                 proposal.values[i],
356:                 proposal.signatures[i],
357:                 proposal.calldatas[i],
358:                 eta,
359:                 proposalType
360:             );
361:             unchecked {
362:                 ++i;
363:             }
364:         }
365:     }

['382']

376:     function _queueOrRevertInternal(
377:         address target_,
378:         uint256 value_,
379:         string memory signature_,
380:         bytes memory data_,
381:         uint256 eta_,
382:         uint8 proposalType_ // <= FOUND
383:     ) internal {
384:         require(
385:             !proposalTimelocks[proposalType_].queuedTransactions(
386:                 keccak256(abi.encode(target_, value_, signature_, data_, eta_))
387:             ),
388:             "OmnichainGovernanceExecutor::queueOrRevertInternal: identical proposal action already queued at eta"
389:         );
390: 
391:         proposalTimelocks[proposalType_].queueTransaction(target_, value_, signature_, data_, eta_);
392:     }

['41']

41: bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,uint8 support)"); // <= FOUND

['63']

63:     function setMaxDailyLimit(uint16 chainId_, uint256 limit_) external { // <= FOUND
64:         _ensureAllowed("setMaxDailyLimit(uint16,uint256)");
65:         emit SetMaxDailyLimit(chainId_, chainIdToMaxDailyLimit[chainId_], limit_);
66:         chainIdToMaxDailyLimit[chainId_] = limit_;
67:     }

['109']

109:     function _isEligibleToSend(uint16 dstChainId_, uint256 noOfCommands_) internal { // <= FOUND
110:         
111:         uint256 currentBlockTimestamp = block.timestamp;
112:         uint256 lastDayWindowStart = chainIdToLast24HourWindowStart[dstChainId_];
113:         uint256 commandsSentInWindow = chainIdToLast24HourCommandsSent[dstChainId_];
114:         uint256 maxDailyLimit = chainIdToMaxDailyLimit[dstChainId_];
115:         uint256 lastProposalSentTimestamp = chainIdToLastProposalSentTimestamp[dstChainId_];
116: 
117:         
118:         if (currentBlockTimestamp - lastDayWindowStart > 1 days) {
119:             commandsSentInWindow = noOfCommands_;
120:             chainIdToLast24HourWindowStart[dstChainId_] = currentBlockTimestamp;
121:         } else {
122:             commandsSentInWindow += noOfCommands_;
123:         }
124: 
125:         
126:         require(commandsSentInWindow <= maxDailyLimit, "Daily Transaction Limit Exceeded");
127:         
128:         require(lastProposalSentTimestamp != currentBlockTimestamp, "Multiple bridging in a proposal");
129: 
130:         
131:         chainIdToLast24HourCommandsSent[dstChainId_] = commandsSentInWindow;
132:         
133:         chainIdToLastProposalSentTimestamp[dstChainId_] = currentBlockTimestamp;
134:     }

['146']

146:     function setSrcChainId(uint16 srcChainId_) external onlyOwner { // <= FOUND
147:         emit SetSrcChainId(srcChainId, srcChainId_);
148:         srcChainId = srcChainId_;
149:     }

['269']

268:     function _blockingLzReceive(
269:         uint16 srcChainId_, // <= FOUND
270:         bytes memory srcAddress_,
271:         uint64 nonce_,
272:         bytes memory payload_
273:     ) internal virtual override whenNotPaused {
274:         uint256 gasToStoreAndEmit = 30000; 
275: 
276:         require(srcChainId_ == srcChainId, "OmnichainGovernanceExecutor::_blockingLzReceive: invalid source chain id");
277: 
278:         (bool success, bytes memory reason) = address(this).excessivelySafeCall(
279:             gasleft() - gasToStoreAndEmit,
280:             150,
281:             abi.encodeCall(this.nonblockingLzReceive, (srcChainId_, srcAddress_, nonce_, payload_))
282:         );
283:         
284:         if (!success) {
285:             bytes32 hashedPayload = keccak256(payload_);
286:             failedMessages[srcChainId_][srcAddress_][nonce_] = hashedPayload;
287:             emit ReceivePayloadFailed(srcChainId_, srcAddress_, nonce_, reason); 
288:         }
289:     }

['95']

94:     function estimateFees(
95:         uint16 remoteChainId_, // <= FOUND
96:         bytes calldata payload_,
97:         bytes calldata adapterParams_
98:     ) external view returns (uint256, uint256) {
99:         return LZ_ENDPOINT.estimateFees(remoteChainId_, address(this), payload_, false, adapterParams_);
100:     }

['108']

108:     function removeTrustedRemote(uint16 remoteChainId_) external { // <= FOUND
109:         _ensureAllowed("removeTrustedRemote(uint16)");
110:         delete trustedRemoteLookup[remoteChainId_];
111:         emit TrustedRemoteRemoved(remoteChainId_);
112:     }

['125']

124:     function execute(
125:         uint16 remoteChainId_, // <= FOUND
126:         bytes calldata payload_,
127:         bytes calldata adapterParams_
128:     ) external payable whenNotPaused {
129:         _ensureAllowed("execute(uint16,bytes,bytes)");
130: 
131:         
132:         require(msg.value > 0, "OmnichainProposalSender: value cannot be zero");
133:         require(payload_.length != 0, "OmnichainProposalSender: Empty payload");
134: 
135:         bytes memory trustedRemote = trustedRemoteLookup[remoteChainId_];
136:         require(trustedRemote.length != 0, "OmnichainProposalSender: destination chain is not a trusted source");
137:         _validateProposal(remoteChainId_, payload_);
138:         uint256 _pId = ++proposalCount;
139:         bytes memory payload = abi.encode(payload_, _pId);
140: 
141:         try
142:             LZ_ENDPOINT.send{ value: msg.value }(
143:                 remoteChainId_,
144:                 trustedRemote,
145:                 payload,
146:                 payable(msg.sender),
147:                 address(0),
148:                 adapterParams_
149:             )
150:         {
151:             emit ExecuteRemoteProposal(remoteChainId_, _pId, payload);
152:         } catch (bytes memory reason) {
153:             storedExecutionHashes[_pId] = keccak256(abi.encode(remoteChainId_, payload, adapterParams_, msg.value));
154:             emit StorePayload(_pId, remoteChainId_, payload, adapterParams_, msg.value, reason);
155:         }
156:     }

['171']

169:     function retryExecute(
170:         uint256 pId_,
171:         uint16 remoteChainId_, // <= FOUND
172:         bytes calldata payload_,
173:         bytes calldata adapterParams_,
174:         uint256 originalValue_
175:     ) external payable whenNotPaused nonReentrant {
176:         _ensureAllowed("retryExecute(uint256,uint16,bytes,bytes,uint256)");
177:         bytes memory trustedRemote = trustedRemoteLookup[remoteChainId_];
178:         require(trustedRemote.length != 0, "OmnichainProposalSender: destination chain is not a trusted source");
179:         bytes32 hash = storedExecutionHashes[pId_];
180:         require(hash != bytes32(0), "OmnichainProposalSender: no stored payload");
181:         require(payload_.length != 0, "OmnichainProposalSender: Empty payload");
182:         (bytes memory payload, ) = abi.decode(payload_, (bytes, uint256));
183:         _validateProposal(remoteChainId_, payload);
184: 
185:         bytes memory execution = abi.encode(remoteChainId_, payload_, adapterParams_, originalValue_);
186:         require(keccak256(execution) == hash, "OmnichainProposalSender: invalid execution params");
187: 
188:         delete storedExecutionHashes[pId_];
189: 
190:         emit ClearPayload(pId_, hash);
191: 
192:         LZ_ENDPOINT.send{ value: originalValue_ + msg.value }(
193:             remoteChainId_,
194:             trustedRemoteLookup[remoteChainId_],
195:             payload_,
196:             payable(msg.sender),
197:             address(0),
198:             adapterParams_
199:         );
200:     }

['217']

214:     function fallbackWithdraw(
215:         address to_,
216:         uint256 pId_,
217:         uint16 remoteChainId_, // <= FOUND
218:         bytes calldata payload_,
219:         bytes calldata adapterParams_,
220:         uint256 originalValue_
221:     ) external onlyOwner nonReentrant {
222:         ensureNonzeroAddress(to_);
223:         require(originalValue_ > 0, "OmnichainProposalSender: invalid native amount");
224:         require(payload_.length != 0, "OmnichainProposalSender: Empty payload");
225: 
226:         bytes32 hash = storedExecutionHashes[pId_];
227:         require(hash != bytes32(0), "OmnichainProposalSender: no stored payload");
228: 
229:         bytes memory execution = abi.encode(remoteChainId_, payload_, adapterParams_, originalValue_);
230:         require(keccak256(execution) == hash, "OmnichainProposalSender: invalid execution params");
231: 
232:         delete storedExecutionHashes[pId_];
233: 
234:         emit FallbackWithdraw(to_, originalValue_);
235:         emit ClearPayload(pId_, hash);
236: 
237:         
238:         (bool sent, ) = to_.call{ value: originalValue_ }("");
239:         require(sent, "Call failed");
240:     }

['249']

249:     function setTrustedRemoteAddress(uint16 remoteChainId_, bytes calldata newRemoteAddress_) external { // <= FOUND
250:         _ensureAllowed("setTrustedRemoteAddress(uint16,bytes)");
251:         require(remoteChainId_ != 0, "ChainId must not be zero");
252:         ensureNonzeroAddress(address(uint160(bytes20(newRemoteAddress_))));
253:         bytes memory oldRemoteAddress = trustedRemoteLookup[remoteChainId_];
254:         trustedRemoteLookup[remoteChainId_] = abi.encodePacked(newRemoteAddress_, address(this));
255:         emit SetTrustedRemoteAddress(remoteChainId_, oldRemoteAddress, trustedRemoteLookup[remoteChainId_]);
256:     }

['266']

266:     function setConfig(uint16 version_, uint16 chainId_, uint256 configType_, bytes calldata config_) external { // <= FOUND
267:         _ensureAllowed("setConfig(uint16,uint16,uint256,bytes)");
268:         LZ_ENDPOINT.setConfig(version_, chainId_, configType_, config_);
269:     }

['276']

276:     function setSendVersion(uint16 version_) external { // <= FOUND
277:         _ensureAllowed("setSendVersion(uint16)");
278:         LZ_ENDPOINT.setSendVersion(version_);
279:     }

['287']

287:     function getConfig(uint16 version_, uint16 chainId_, uint256 configType_) external view returns (bytes memory) { // <= FOUND
288:         return LZ_ENDPOINT.getConfig(version_, chainId_, address(this), configType_);
289:     }

['291']

291:     function _validateProposal(uint16 remoteChainId_, bytes memory payload_) internal { // <= FOUND
292:         (
293:             address[] memory targets,
294:             uint256[] memory values,
295:             string[] memory signatures,
296:             bytes[] memory calldatas,
297: 
298:         ) = abi.decode(payload_, (address[], uint[], string[], bytes[], uint8));
299:         require(
300:             targets.length == values.length &&
301:                 targets.length == signatures.length &&
302:                 targets.length == calldatas.length,
303:             "OmnichainProposalSender: proposal function information arity mismatch"
304:         );
305:         _isEligibleToSend(remoteChainId_, targets.length);
306:     }

['65']

65: uint16 public srcChainId; // <= FOUND

['336']

331:     function _castVote(address voter, uint proposalId, bool support) internal {
332:         require(state(proposalId) == ProposalState.Active, "GovernorAlpha::_castVote: voting is closed");
333:         Proposal storage proposal = proposals[proposalId];
334:         Receipt storage receipt = proposal.receipts[voter];
335:         require(receipt.hasVoted == false, "GovernorAlpha::_castVote: voter already voted");
336:         uint96 votes = xvs.getPriorVotes(voter, proposal.startBlock); // <= FOUND
337: 
338:         if (support) {
339:             proposal.forVotes = add256(proposal.forVotes, votes);
340:         } else {
341:             proposal.againstVotes = add256(proposal.againstVotes, votes);
342:         }
343: 
344:         receipt.hasVoted = true;
345:         receipt.support = support;
346:         receipt.votes = votes;
347: 
348:         emit VoteCast(voter, proposalId, support, votes);
349:     }

['336']

331:     function _castVote(address voter, uint proposalId, bool support) internal {
332:         require(state(proposalId) == ProposalState.Active, "GovernorAlpha::_castVote: voting is closed");
333:         Proposal storage proposal = proposals[proposalId];
334:         Receipt storage receipt = proposal.receipts[voter];
335:         require(receipt.hasVoted == false, "GovernorAlpha::_castVote: voter already voted");
336:         uint96 votes = xvs.getPriorVotes(voter, proposal.startBlock); // <= FOUND
337: 
338:         if (support) {
339:             proposal.forVotes = add256(proposal.forVotes, votes);
340:         } else {
341:             proposal.againstVotes = add256(proposal.againstVotes, votes);
342:         }
343: 
344:         receipt.hasVoted = true;
345:         receipt.support = support;
346:         receipt.votes = votes;
347: 
348:         emit VoteCast(voter, proposalId, support, votes);
349:     }

[Gas-15] Use != 0 instead of > 0

Resolution

Replace spotted instances with != 0 for uints as this uses less gas

Num of instances: 4

Findings

Click to show findings

['295']

295:         require(proposalCount >= proposalId && proposalId > 0, "GovernorAlpha::state: invalid proposal id"); // <= FOUND

['133']

132:         
133:         require(msg.value > 0, "OmnichainProposalSender: value cannot be zero"); // <= FOUND

['223']

223:         require(originalValue_ > 0, "OmnichainProposalSender: invalid native amount"); // <= FOUND

['127']

126:         
127:         require(b > 0, errorMessage); // <= FOUND

[Gas-16] Integer increments by one can be unchecked to save on gas fees

Resolution

Using unchecked increments in Solidity can save on gas fees by bypassing built-in overflow checks, thus optimizing gas usage, but requires careful assessment of potential risks and edge cases to avoid unintended consequences.

Num of instances: 19

Findings

Click to show findings

['179']

143:     function propose(
144:         address[] memory targets,
145:         uint[] memory values,
146:         string[] memory signatures,
147:         bytes[] memory calldatas,
148:         string memory description
149:     ) public returns (uint) {
150:         require(
151:             xvs.getPriorVotes(msg.sender, sub256(block.number, 1)) > proposalThreshold(),
152:             "GovernorAlpha::propose: proposer votes below proposal threshold"
153:         );
154:         require(
155:             targets.length == values.length &&
156:                 targets.length == signatures.length &&
157:                 targets.length == calldatas.length,
158:             "GovernorAlpha::propose: proposal function information arity mismatch"
159:         );
160:         require(targets.length != 0, "GovernorAlpha::propose: must provide actions");
161:         require(targets.length <= proposalMaxOperations(), "GovernorAlpha::propose: too many actions");
162: 
163:         uint latestProposalId = latestProposalIds[msg.sender];
164:         if (latestProposalId != 0) {
165:             ProposalState proposersLatestProposalState = state(latestProposalId);
166:             require(
167:                 proposersLatestProposalState != ProposalState.Active,
168:                 "GovernorAlpha::propose: found an already active proposal"
169:             );
170:             require(
171:                 proposersLatestProposalState != ProposalState.Pending,
172:                 "GovernorAlpha::propose: found an already pending proposal"
173:             );
174:         }
175: 
176:         uint startBlock = add256(block.number, votingDelay());
177:         uint endBlock = add256(startBlock, votingPeriod());
178: 
179:         proposalCount++; // <= FOUND
180:         Proposal memory newProposal = Proposal({
181:             id: proposalCount,
182:             proposer: msg.sender,
183:             eta: 0,
184:             targets: targets,
185:             values: values,
186:             signatures: signatures,
187:             calldatas: calldatas,
188:             startBlock: startBlock,
189:             endBlock: endBlock,
190:             forVotes: 0,
191:             againstVotes: 0,
192:             canceled: false,
193:             executed: false
194:         });
195: 
196:         proposals[newProposal.id] = newProposal;
197:         latestProposalIds[newProposal.proposer] = newProposal.id;
198: 
199:         emit ProposalCreated(
200:             newProposal.id,
201:             msg.sender,
202:             targets,
203:             values,
204:             signatures,
205:             calldatas,
206:             startBlock,
207:             endBlock,
208:             description
209:         );
210:         return newProposal.id;
211:     }

['220']

213:     function queue(uint proposalId) public {
214:         require(
215:             state(proposalId) == ProposalState.Succeeded,
216:             "GovernorAlpha::queue: proposal can only be queued if it is succeeded"
217:         );
218:         Proposal storage proposal = proposals[proposalId];
219:         uint eta = add256(block.timestamp, timelock.delay());
220:         for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
221:             _queueOrRevert(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], eta);
222:         }
223:         proposal.eta = eta;
224:         emit ProposalQueued(proposalId, eta);
225:     }

['242']

235:     function execute(uint proposalId) public payable {
236:         require(
237:             state(proposalId) == ProposalState.Queued,
238:             "GovernorAlpha::execute: proposal can only be executed if it is queued"
239:         );
240:         Proposal storage proposal = proposals[proposalId];
241:         proposal.executed = true;
242:         for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
243:             timelock.executeTransaction.value(proposal.values[i])(
244:                 proposal.targets[i],
245:                 proposal.values[i],
246:                 proposal.signatures[i],
247:                 proposal.calldatas[i],
248:                 proposal.eta
249:             );
250:         }
251:         emit ProposalExecuted(proposalId);
252:     }

['266']

254:     function cancel(uint proposalId) public {
255:         ProposalState state = state(proposalId);
256:         require(state != ProposalState.Executed, "GovernorAlpha::cancel: cannot cancel executed proposal");
257: 
258:         Proposal storage proposal = proposals[proposalId];
259:         require(
260:             msg.sender == guardian ||
261:                 xvs.getPriorVotes(proposal.proposer, sub256(block.number, 1)) < proposalThreshold(),
262:             "GovernorAlpha::cancel: proposer above threshold"
263:         );
264: 
265:         proposal.canceled = true;
266:         for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
267:             timelock.cancelTransaction(
268:                 proposal.targets[i],
269:                 proposal.values[i],
270:                 proposal.signatures[i],
271:                 proposal.calldatas[i],
272:                 proposal.eta
273:             );
274:         }
275: 
276:         emit ProposalCanceled(proposalId);
277:     }

['179']

143:     function propose(
144:         address[] memory targets,
145:         uint[] memory values,
146:         string[] memory signatures,
147:         bytes[] memory calldatas,
148:         string memory description
149:     ) public returns (uint) {
150:         require(
151:             xvs.getPriorVotes(msg.sender, sub256(block.number, 1)) > proposalThreshold(),
152:             "GovernorAlpha::propose: proposer votes below proposal threshold"
153:         );
154:         require(
155:             targets.length == values.length &&
156:                 targets.length == signatures.length &&
157:                 targets.length == calldatas.length,
158:             "GovernorAlpha::propose: proposal function information arity mismatch"
159:         );
160:         require(targets.length != 0, "GovernorAlpha::propose: must provide actions");
161:         require(targets.length <= proposalMaxOperations(), "GovernorAlpha::propose: too many actions");
162: 
163:         uint latestProposalId = latestProposalIds[msg.sender];
164:         if (latestProposalId != 0) {
165:             ProposalState proposersLatestProposalState = state(latestProposalId);
166:             require(
167:                 proposersLatestProposalState != ProposalState.Active,
168:                 "GovernorAlpha::propose: found an already active proposal"
169:             );
170:             require(
171:                 proposersLatestProposalState != ProposalState.Pending,
172:                 "GovernorAlpha::propose: found an already pending proposal"
173:             );
174:         }
175: 
176:         uint startBlock = add256(block.number, votingDelay());
177:         uint endBlock = add256(startBlock, votingPeriod());
178: 
179:         proposalCount++; // <= FOUND
180:         Proposal memory newProposal = Proposal({
181:             id: proposalCount,
182:             proposer: msg.sender,
183:             eta: 0,
184:             targets: targets,
185:             values: values,
186:             signatures: signatures,
187:             calldatas: calldatas,
188:             startBlock: startBlock,
189:             endBlock: endBlock,
190:             forVotes: 0,
191:             againstVotes: 0,
192:             canceled: false,
193:             executed: false
194:         });
195: 
196:         proposals[newProposal.id] = newProposal;
197:         latestProposalIds[newProposal.proposer] = newProposal.id;
198: 
199:         emit ProposalCreated(
200:             newProposal.id,
201:             msg.sender,
202:             targets,
203:             values,
204:             signatures,
205:             calldatas,
206:             startBlock,
207:             endBlock,
208:             description
209:         );
210:         return newProposal.id;
211:     }

['220']

213:     function queue(uint proposalId) public {
214:         require(
215:             state(proposalId) == ProposalState.Succeeded,
216:             "GovernorAlpha::queue: proposal can only be queued if it is succeeded"
217:         );
218:         Proposal storage proposal = proposals[proposalId];
219:         uint eta = add256(block.timestamp, timelock.delay());
220:         for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
221:             _queueOrRevert(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], eta);
222:         }
223:         proposal.eta = eta;
224:         emit ProposalQueued(proposalId, eta);
225:     }

['242']

235:     function execute(uint proposalId) public payable {
236:         require(
237:             state(proposalId) == ProposalState.Queued,
238:             "GovernorAlpha::execute: proposal can only be executed if it is queued"
239:         );
240:         Proposal storage proposal = proposals[proposalId];
241:         proposal.executed = true;
242:         for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
243:             timelock.executeTransaction.value(proposal.values[i])(
244:                 proposal.targets[i],
245:                 proposal.values[i],
246:                 proposal.signatures[i],
247:                 proposal.calldatas[i],
248:                 proposal.eta
249:             );
250:         }
251:         emit ProposalExecuted(proposalId);
252:     }

['266']

254:     function cancel(uint proposalId) public {
255:         ProposalState state = state(proposalId);
256:         require(state != ProposalState.Executed, "GovernorAlpha::cancel: cannot cancel executed proposal");
257: 
258:         Proposal storage proposal = proposals[proposalId];
259:         require(
260:             msg.sender == guardian ||
261:                 xvs.getPriorVotes(proposal.proposer, sub256(block.number, 1)) < proposalThreshold(),
262:             "GovernorAlpha::cancel: proposer above threshold"
263:         );
264: 
265:         proposal.canceled = true;
266:         for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
267:             timelock.cancelTransaction(
268:                 proposal.targets[i],
269:                 proposal.values[i],
270:                 proposal.signatures[i],
271:                 proposal.calldatas[i],
272:                 proposal.eta
273:             );
274:         }
275: 
276:         emit ProposalCanceled(proposalId);
277:     }

['136']

111:     function initialize(
112:         address xvsVault_,
113:         ProposalConfig[] memory proposalConfigs_,
114:         TimelockInterface[] memory timelocks,
115:         address guardian_
116:     ) public {
117:         require(address(proposalTimelocks[0]) == address(0), "GovernorBravo::initialize: cannot initialize twice");
118:         require(msg.sender == admin, "GovernorBravo::initialize: admin only");
119:         require(xvsVault_ != address(0), "GovernorBravo::initialize: invalid xvs address");
120:         require(guardian_ != address(0), "GovernorBravo::initialize: invalid guardian");
121:         require(
122:             timelocks.length == uint8(ProposalType.CRITICAL) + 1,
123:             "GovernorBravo::initialize:number of timelocks should match number of governance routes"
124:         );
125:         require(
126:             proposalConfigs_.length == uint8(ProposalType.CRITICAL) + 1,
127:             "GovernorBravo::initialize:number of proposal configs should match number of governance routes"
128:         );
129: 
130:         xvsVault = XvsVaultInterface(xvsVault_);
131:         proposalMaxOperations = 10;
132:         guardian = guardian_;
133: 
134:         
135:         uint256 arrLength = proposalConfigs_.length;
136:         for (uint256 i; i < arrLength; ++i) { // <= FOUND
137:             require(
138:                 proposalConfigs_[i].votingPeriod >= MIN_VOTING_PERIOD,
139:                 "GovernorBravo::initialize: invalid min voting period"
140:             );
141:             require(
142:                 proposalConfigs_[i].votingPeriod <= MAX_VOTING_PERIOD,
143:                 "GovernorBravo::initialize: invalid max voting period"
144:             );
145:             require(
146:                 proposalConfigs_[i].votingDelay >= MIN_VOTING_DELAY,
147:                 "GovernorBravo::initialize: invalid min voting delay"
148:             );
149:             require(
150:                 proposalConfigs_[i].votingDelay <= MAX_VOTING_DELAY,
151:                 "GovernorBravo::initialize: invalid max voting delay"
152:             );
153:             require(
154:                 proposalConfigs_[i].proposalThreshold >= MIN_PROPOSAL_THRESHOLD,
155:                 "GovernorBravo::initialize: invalid min proposal threshold"
156:             );
157:             require(
158:                 proposalConfigs_[i].proposalThreshold <= MAX_PROPOSAL_THRESHOLD,
159:                 "GovernorBravo::initialize: invalid max proposal threshold"
160:             );
161:             require(address(timelocks[i]) != address(0), "GovernorBravo::initialize:invalid timelock address");
162: 
163:             proposalConfigs[i] = proposalConfigs_[i];
164:             proposalTimelocks[i] = timelocks[i];
165:         }
166:     }

['221']

181:     function propose(
182:         address[] memory targets,
183:         uint[] memory values,
184:         string[] memory signatures,
185:         bytes[] memory calldatas,
186:         string memory description,
187:         ProposalType proposalType
188:     ) public returns (uint) {
189:         
190:         require(initialProposalId != 0, "GovernorBravo::propose: Governor Bravo not active");
191:         require(
192:             xvsVault.getPriorVotes(msg.sender, sub256(block.number, 1)) >=
193:                 proposalConfigs[uint8(proposalType)].proposalThreshold,
194:             "GovernorBravo::propose: proposer votes below proposal threshold"
195:         );
196:         require(
197:             targets.length == values.length &&
198:                 targets.length == signatures.length &&
199:                 targets.length == calldatas.length,
200:             "GovernorBravo::propose: proposal function information arity mismatch"
201:         );
202:         require(targets.length != 0, "GovernorBravo::propose: must provide actions");
203:         require(targets.length <= proposalMaxOperations, "GovernorBravo::propose: too many actions");
204: 
205:         uint latestProposalId = latestProposalIds[msg.sender];
206:         if (latestProposalId != 0) {
207:             ProposalState proposersLatestProposalState = state(latestProposalId);
208:             require(
209:                 proposersLatestProposalState != ProposalState.Active,
210:                 "GovernorBravo::propose: one live proposal per proposer, found an already active proposal"
211:             );
212:             require(
213:                 proposersLatestProposalState != ProposalState.Pending,
214:                 "GovernorBravo::propose: one live proposal per proposer, found an already pending proposal"
215:             );
216:         }
217: 
218:         uint startBlock = add256(block.number, proposalConfigs[uint8(proposalType)].votingDelay);
219:         uint endBlock = add256(startBlock, proposalConfigs[uint8(proposalType)].votingPeriod);
220: 
221:         proposalCount++; // <= FOUND
222:         Proposal memory newProposal = Proposal({
223:             id: proposalCount,
224:             proposer: msg.sender,
225:             eta: 0,
226:             targets: targets,
227:             values: values,
228:             signatures: signatures,
229:             calldatas: calldatas,
230:             startBlock: startBlock,
231:             endBlock: endBlock,
232:             forVotes: 0,
233:             againstVotes: 0,
234:             abstainVotes: 0,
235:             canceled: false,
236:             executed: false,
237:             proposalType: uint8(proposalType)
238:         });
239: 
240:         proposals[newProposal.id] = newProposal;
241:         latestProposalIds[newProposal.proposer] = newProposal.id;
242: 
243:         emit ProposalCreated(
244:             newProposal.id,
245:             msg.sender,
246:             targets,
247:             values,
248:             signatures,
249:             calldatas,
250:             startBlock,
251:             endBlock,
252:             description,
253:             uint8(proposalType)
254:         );
255:         return newProposal.id;
256:     }

['269']

262:     function queue(uint proposalId) external {
263:         require(
264:             state(proposalId) == ProposalState.Succeeded,
265:             "GovernorBravo::queue: proposal can only be queued if it is succeeded"
266:         );
267:         Proposal storage proposal = proposals[proposalId];
268:         uint eta = add256(block.timestamp, proposalTimelocks[uint8(proposal.proposalType)].delay());
269:         for (uint i; i < proposal.targets.length; ++i) { // <= FOUND
270:             queueOrRevertInternal(
271:                 proposal.targets[i],
272:                 proposal.values[i],
273:                 proposal.signatures[i],
274:                 proposal.calldatas[i],
275:                 eta,
276:                 uint8(proposal.proposalType)
277:             );
278:         }
279:         proposal.eta = eta;
280:         emit ProposalQueued(proposalId, eta);
281:     }

['311']

304:     function execute(uint proposalId) external {
305:         require(
306:             state(proposalId) == ProposalState.Queued,
307:             "GovernorBravo::execute: proposal can only be executed if it is queued"
308:         );
309:         Proposal storage proposal = proposals[proposalId];
310:         proposal.executed = true;
311:         for (uint i; i < proposal.targets.length; ++i) { // <= FOUND
312:             proposalTimelocks[uint8(proposal.proposalType)].executeTransaction(
313:                 proposal.targets[i],
314:                 proposal.values[i],
315:                 proposal.signatures[i],
316:                 proposal.calldatas[i],
317:                 proposal.eta
318:             );
319:         }
320:         emit ProposalExecuted(proposalId);
321:     }

['340']

327:     function cancel(uint proposalId) external {
328:         require(state(proposalId) != ProposalState.Executed, "GovernorBravo::cancel: cannot cancel executed proposal");
329: 
330:         Proposal storage proposal = proposals[proposalId];
331:         require(
332:             msg.sender == guardian ||
333:                 msg.sender == proposal.proposer ||
334:                 xvsVault.getPriorVotes(proposal.proposer, sub256(block.number, 1)) <
335:                 proposalConfigs[proposal.proposalType].proposalThreshold,
336:             "GovernorBravo::cancel: proposer above threshold"
337:         );
338: 
339:         proposal.canceled = true;
340:         for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
341:             proposalTimelocks[proposal.proposalType].cancelTransaction(
342:                 proposal.targets[i],
343:                 proposal.values[i],
344:                 proposal.signatures[i],
345:                 proposal.calldatas[i],
346:                 proposal.eta
347:             );
348:         }
349: 
350:         emit ProposalCanceled(proposalId);
351:     }

['503']

498:     function _initiate(address governorAlpha) external {
499:         require(msg.sender == admin, "GovernorBravo::_initiate: admin only");
500:         require(initialProposalId == 0, "GovernorBravo::_initiate: can only initiate once");
501:         proposalCount = GovernorAlphaInterface(governorAlpha).proposalCount();
502:         initialProposalId = proposalCount;
503:         for (uint256 i; i < uint8(ProposalType.CRITICAL) + 1; ++i) { // <= FOUND
504:             proposalTimelocks[i].acceptAdmin();
505:         }
506:     }

['133']

95:     function propose(
96:         address[] memory targets,
97:         uint[] memory values,
98:         string[] memory signatures,
99:         bytes[] memory calldatas,
100:         string memory description
101:     ) public returns (uint) {
102:         
103:         require(initialProposalId != 0, "GovernorBravo::propose: Governor Bravo not active");
104:         require(
105:             xvsVault.getPriorVotes(msg.sender, sub256(block.number, 1)) > proposalThreshold,
106:             "GovernorBravo::propose: proposer votes below proposal threshold"
107:         );
108:         require(
109:             targets.length == values.length &&
110:                 targets.length == signatures.length &&
111:                 targets.length == calldatas.length,
112:             "GovernorBravo::propose: proposal function information arity mismatch"
113:         );
114:         require(targets.length != 0, "GovernorBravo::propose: must provide actions");
115:         require(targets.length <= proposalMaxOperations, "GovernorBravo::propose: too many actions");
116: 
117:         uint latestProposalId = latestProposalIds[msg.sender];
118:         if (latestProposalId != 0) {
119:             ProposalState proposersLatestProposalState = state(latestProposalId);
120:             require(
121:                 proposersLatestProposalState != ProposalState.Active,
122:                 "GovernorBravo::propose: one live proposal per proposer, found an already active proposal"
123:             );
124:             require(
125:                 proposersLatestProposalState != ProposalState.Pending,
126:                 "GovernorBravo::propose: one live proposal per proposer, found an already pending proposal"
127:             );
128:         }
129: 
130:         uint startBlock = add256(block.number, votingDelay);
131:         uint endBlock = add256(startBlock, votingPeriod);
132: 
133:         proposalCount++; // <= FOUND
134:         Proposal memory newProposal = Proposal({
135:             id: proposalCount,
136:             proposer: msg.sender,
137:             eta: 0,
138:             targets: targets,
139:             values: values,
140:             signatures: signatures,
141:             calldatas: calldatas,
142:             startBlock: startBlock,
143:             endBlock: endBlock,
144:             forVotes: 0,
145:             againstVotes: 0,
146:             abstainVotes: 0,
147:             canceled: false,
148:             executed: false
149:         });
150: 
151:         proposals[newProposal.id] = newProposal;
152:         latestProposalIds[newProposal.proposer] = newProposal.id;
153: 
154:         emit ProposalCreated(
155:             newProposal.id,
156:             msg.sender,
157:             targets,
158:             values,
159:             signatures,
160:             calldatas,
161:             startBlock,
162:             endBlock,
163:             description
164:         );
165:         return newProposal.id;
166:     }

['179']

172:     function queue(uint proposalId) external {
173:         require(
174:             state(proposalId) == ProposalState.Succeeded,
175:             "GovernorBravo::queue: proposal can only be queued if it is succeeded"
176:         );
177:         Proposal storage proposal = proposals[proposalId];
178:         uint eta = add256(block.timestamp, timelock.delay());
179:         for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
180:             queueOrRevertInternal(
181:                 proposal.targets[i],
182:                 proposal.values[i],
183:                 proposal.signatures[i],
184:                 proposal.calldatas[i],
185:                 eta
186:             );
187:         }
188:         proposal.eta = eta;
189:         emit ProposalQueued(proposalId, eta);
190:     }

['217']

210:     function execute(uint proposalId) external {
211:         require(
212:             state(proposalId) == ProposalState.Queued,
213:             "GovernorBravo::execute: proposal can only be executed if it is queued"
214:         );
215:         Proposal storage proposal = proposals[proposalId];
216:         proposal.executed = true;
217:         for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
218:             timelock.executeTransaction(
219:                 proposal.targets[i],
220:                 proposal.values[i],
221:                 proposal.signatures[i],
222:                 proposal.calldatas[i],
223:                 proposal.eta
224:             );
225:         }
226:         emit ProposalExecuted(proposalId);
227:     }

['245']

233:     function cancel(uint proposalId) external {
234:         require(state(proposalId) != ProposalState.Executed, "GovernorBravo::cancel: cannot cancel executed proposal");
235: 
236:         Proposal storage proposal = proposals[proposalId];
237:         require(
238:             msg.sender == guardian ||
239:                 msg.sender == proposal.proposer ||
240:                 xvsVault.getPriorVotes(proposal.proposer, sub256(block.number, 1)) < proposalThreshold,
241:             "GovernorBravo::cancel: proposer above threshold"
242:         );
243: 
244:         proposal.canceled = true;
245:         for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
246:             timelock.cancelTransaction(
247:                 proposal.targets[i],
248:                 proposal.values[i],
249:                 proposal.signatures[i],
250:                 proposal.calldatas[i],
251:                 proposal.eta
252:             );
253:         }
254: 
255:         emit ProposalCanceled(proposalId);
256:     }

['138']

124:     function execute(
125:         uint16 remoteChainId_,
126:         bytes calldata payload_,
127:         bytes calldata adapterParams_
128:     ) external payable whenNotPaused {
129:         _ensureAllowed("execute(uint16,bytes,bytes)");
130: 
131:         
132:         require(msg.value > 0, "OmnichainProposalSender: value cannot be zero");
133:         require(payload_.length != 0, "OmnichainProposalSender: Empty payload");
134: 
135:         bytes memory trustedRemote = trustedRemoteLookup[remoteChainId_];
136:         require(trustedRemote.length != 0, "OmnichainProposalSender: destination chain is not a trusted source");
137:         _validateProposal(remoteChainId_, payload_);
138:         uint256 _pId = ++proposalCount; // <= FOUND
139:         bytes memory payload = abi.encode(payload_, _pId);
140: 
141:         try
142:             LZ_ENDPOINT.send{ value: msg.value }(
143:                 remoteChainId_,
144:                 trustedRemote,
145:                 payload,
146:                 payable(msg.sender),
147:                 address(0),
148:                 adapterParams_
149:             )
150:         {
151:             emit ExecuteRemoteProposal(remoteChainId_, _pId, payload);
152:         } catch (bytes memory reason) {
153:             storedExecutionHashes[_pId] = keccak256(abi.encode(remoteChainId_, payload, adapterParams_, msg.value));
154:             emit StorePayload(_pId, remoteChainId_, payload, adapterParams_, msg.value, reason);
155:         }
156:     }

[Gas-17] Use byte32 in place of string

Resolution

For strings of 32 char strings and below you can use bytes32 instead as it's more gas efficient

Num of instances: 4

Findings

Click to show findings

['6']

6:     
7:     string public constant name = "Venus Governor Alpha"; // <= FOUND

['13']

13:     
14:     string public constant name = "Venus Governor Bravo"; // <= FOUND

['6']

6: string public constant name = "Venus Governor Alpha"; // <= FOUND

['13']

13: string public constant name = "Venus Governor Bravo"; // <= FOUND

[Gas-18] Default bool values are manually reset

Resolution

Using .delete is better than resetting a Solidity variable to its default value manually because it frees up storage space on the Ethereum blockchain, resulting in gas cost savings.

Num of instances: 2

Findings

Click to show findings

['154']

154:     function cancelTransaction(
155:         address target,
156:         uint value,
157:         string memory signature,
158:         bytes memory data,
159:         uint eta
160:     ) public {
161:         require(msg.sender == admin, "Timelock::cancelTransaction: Call must come from admin.");
162: 
163:         bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
164:         queuedTransactions[txHash] = false; // <= FOUND
165: 
166:         emit CancelTransaction(txHash, target, value, signature, data, eta);
167:     }

['177']

177:     function executeTransaction(
178:         address target,
179:         uint value,
180:         string memory signature,
181:         bytes memory data,
182:         uint eta
183:     ) public payable returns (bytes memory) {
184:         require(msg.sender == admin, "Timelock::executeTransaction: Call must come from admin.");
185: 
186:         bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
187:         require(queuedTransactions[txHash], "Timelock::executeTransaction: Transaction hasn't been queued.");
188:         require(getBlockTimestamp() >= eta, "Timelock::executeTransaction: Transaction hasn't surpassed time lock.");
189:         require(getBlockTimestamp() <= eta.add(GRACE_PERIOD), "Timelock::executeTransaction: Transaction is stale.");
190: 
191:         queuedTransactions[txHash] = false; // <= FOUND
192: 
193:         bytes memory callData;
194: 
195:         if (bytes(signature).length == 0) {
196:             callData = data;
197:         } else {
198:             callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data);
199:         }
200: 
201:         
202:         (bool success, bytes memory returnData) = target.call.value(value)(callData);
203:         require(success, "Timelock::executeTransaction: Transaction execution reverted.");
204: 
205:         emit ExecuteTransaction(txHash, target, value, signature, data, eta);
206: 
207:         return returnData;
208:     }

[Gas-19] Default int values are manually reset

Resolution

Using .delete is better than resetting a Solidity variable to its default value manually because it frees up storage space on the Ethereum blockchain, resulting in gas cost savings.

Num of instances: 1

Findings

Click to show findings

['220']

220:         for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND

[Gas-20] Mappings used within a function more than once should be cached to save gas

Resolution

Cache such mappings and perform operations on them, if operations include modifications to the mapping(s) then remember to equate the mapping to it's cached counterpart at the end

Num of instances: 4

Findings

Click to show findings

['296']

296:     function _nonblockingLzReceive(uint16, bytes memory, uint64, bytes memory payload_) internal virtual override { // <= FOUND
297:         (bytes memory payload, uint256 pId) = abi.decode(payload_, (bytes, uint256));
298:         (
299:             address[] memory targets,
300:             uint256[] memory values,
301:             string[] memory signatures,
302:             bytes[] memory calldatas,
303:             uint8 pType
304:         ) = abi.decode(payload, (address[], uint256[], string[], bytes[], uint8));
305:         require(proposals[pId].id == 0, "OmnichainGovernanceExecutor::_nonblockingLzReceive: duplicate proposal"); // <= FOUND
306:         require(
307:             targets.length == values.length &&
308:                 targets.length == signatures.length &&
309:                 targets.length == calldatas.length,
310:             "OmnichainGovernanceExecutor::_nonblockingLzReceive: proposal function information arity mismatch"
311:         );
312:         require(
313:             pType < uint8(type(ProposalType).max) + 1,
314:             "OmnichainGovernanceExecutor::_nonblockingLzReceive: invalid proposal type"
315:         );
316:         _isEligibleToReceive(targets.length);
317: 
318:         Proposal memory newProposal = Proposal({
319:             id: pId,
320:             eta: 0,
321:             targets: targets,
322:             values: values,
323:             signatures: signatures,
324:             calldatas: calldatas,
325:             canceled: false,
326:             executed: false,
327:             proposalType: pType
328:         });
329: 
330:         proposals[pId] = newProposal; // <= FOUND
331:         lastProposalReceived = pId;
332: 
333:         emit ProposalReceived(newProposal.id, targets, values, signatures, calldatas, pType);
334:         _queue(pId);
335:     }

['111']

111:     function initialize( // <= FOUND
112:         address xvsVault_,
113:         ProposalConfig[] memory proposalConfigs_,
114:         TimelockInterface[] memory timelocks,
115:         address guardian_
116:     ) public {
117:         require(address(proposalTimelocks[0]) == address(0), "GovernorBravo::initialize: cannot initialize twice"); // <= FOUND
118:         require(msg.sender == admin, "GovernorBravo::initialize: admin only");
119:         require(xvsVault_ != address(0), "GovernorBravo::initialize: invalid xvs address");
120:         require(guardian_ != address(0), "GovernorBravo::initialize: invalid guardian");
121:         require(
122:             timelocks.length == uint8(ProposalType.CRITICAL) + 1,
123:             "GovernorBravo::initialize:number of timelocks should match number of governance routes"
124:         );
125:         require(
126:             proposalConfigs_.length == uint8(ProposalType.CRITICAL) + 1,
127:             "GovernorBravo::initialize:number of proposal configs should match number of governance routes"
128:         );
129: 
130:         xvsVault = XvsVaultInterface(xvsVault_);
131:         proposalMaxOperations = 10;
132:         guardian = guardian_;
133: 
134:         
135:         uint256 arrLength = proposalConfigs_.length;
136:         for (uint256 i; i < arrLength; ++i) {
137:             require(
138:                 proposalConfigs_[i].votingPeriod >= MIN_VOTING_PERIOD,
139:                 "GovernorBravo::initialize: invalid min voting period"
140:             );
141:             require(
142:                 proposalConfigs_[i].votingPeriod <= MAX_VOTING_PERIOD,
143:                 "GovernorBravo::initialize: invalid max voting period"
144:             );
145:             require(
146:                 proposalConfigs_[i].votingDelay >= MIN_VOTING_DELAY,
147:                 "GovernorBravo::initialize: invalid min voting delay"
148:             );
149:             require(
150:                 proposalConfigs_[i].votingDelay <= MAX_VOTING_DELAY,
151:                 "GovernorBravo::initialize: invalid max voting delay"
152:             );
153:             require(
154:                 proposalConfigs_[i].proposalThreshold >= MIN_PROPOSAL_THRESHOLD,
155:                 "GovernorBravo::initialize: invalid min proposal threshold"
156:             );
157:             require(
158:                 proposalConfigs_[i].proposalThreshold <= MAX_PROPOSAL_THRESHOLD,
159:                 "GovernorBravo::initialize: invalid max proposal threshold"
160:             );
161:             require(address(timelocks[i]) != address(0), "GovernorBravo::initialize:invalid timelock address");
162: 
163:             proposalConfigs[i] = proposalConfigs_[i];
164:             proposalTimelocks[i] = timelocks[i]; // <= FOUND
165:         }
166:     }

['177']

177:     function executeTransaction( // <= FOUND
178:         address target,
179:         uint value,
180:         string memory signature,
181:         bytes memory data,
182:         uint eta
183:     ) public payable returns (bytes memory) {
184:         require(msg.sender == admin, "Timelock::executeTransaction: Call must come from admin.");
185: 
186:         bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
187:         require(queuedTransactions[txHash], "Timelock::executeTransaction: Transaction hasn't been queued."); // <= FOUND
188:         require(getBlockTimestamp() >= eta, "Timelock::executeTransaction: Transaction hasn't surpassed time lock.");
189:         require(getBlockTimestamp() <= eta.add(GRACE_PERIOD), "Timelock::executeTransaction: Transaction is stale.");
190: 
191:         queuedTransactions[txHash] = false; // <= FOUND
192: 
193:         bytes memory callData;
194: 
195:         if (bytes(signature).length == 0) {
196:             callData = data;
197:         } else {
198:             callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data);
199:         }
200: 
201:         
202:         (bool success, bytes memory returnData) = target.call.value(value)(callData);
203:         require(success, "Timelock::executeTransaction: Transaction execution reverted.");
204: 
205:         emit ExecuteTransaction(txHash, target, value, signature, data, eta);
206: 
207:         return returnData;
208:     }

['217']

217:     function executeTransaction( // <= FOUND
218:         address target,
219:         uint256 value,
220:         string calldata signature,
221:         bytes calldata data,
222:         uint256 eta
223:     ) public payable returns (bytes memory) {
224:         require(msg.sender == admin, "Timelock::executeTransaction: Call must come from admin.");
225: 
226:         bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
227:         require(queuedTransactions[txHash], "Timelock::executeTransaction: Transaction hasn't been queued."); // <= FOUND
228:         require(getBlockTimestamp() >= eta, "Timelock::executeTransaction: Transaction hasn't surpassed time lock.");
229:         require(getBlockTimestamp() <= eta + GRACE_PERIOD(), "Timelock::executeTransaction: Transaction is stale.");
230: 
231:         delete (queuedTransactions[txHash]); // <= FOUND
232: 
233:         bytes memory callData;
234: 
235:         if (bytes(signature).length == 0) {
236:             callData = data;
237:         } else {
238:             callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data);
239:         }
240: 
241:         
242:         (bool success, bytes memory returnData) = target.call{ value: value }(callData);
243:         require(success, "Timelock::executeTransaction: Transaction execution reverted.");
244: 
245:         emit ExecuteTransaction(txHash, target, value, signature, data, eta);
246: 
247:         return returnData;
248:     }

[Gas-21] Use assembly to check for the zero address

Resolution

Using assembly for address comparisons in Solidity can save gas because it allows for more direct access to the Ethereum Virtual Machine (EVM), reducing the overhead of higher-level operations. Solidity's high-level abstraction simplifies coding but can introduce additional gas costs. Using assembly for simple operations like address comparisons can be more gas-efficient.

Num of instances: 17

Findings

Click to show findings

['37']

37:     function _setAccessControlManager(address accessControlManager_) internal {
38:         require(address(accessControlManager_) != address(0), "invalid acess control manager address"); // <= FOUND
39:         address oldAccessControlManager = address(_accessControlManager);
40:         _accessControlManager = IAccessControlManagerV5(accessControlManager_);
41:         emit NewAccessControlManager(oldAccessControlManager, accessControlManager_);
42:     }

['63']

63:     function _setAccessControlManager(address accessControlManager_) internal {
64:         require(address(accessControlManager_) != address(0), "invalid acess control manager address"); // <= FOUND
65:         address oldAccessControlManager = address(_accessControlManager);
66:         _accessControlManager = IAccessControlManagerV8(accessControlManager_);
67:         emit NewAccessControlManager(oldAccessControlManager, accessControlManager_);
68:     }

['47']

47:     function _setImplementation(address implementation_) public {
48:         require(msg.sender == admin, "GovernorBravoDelegator::_setImplementation: admin only");
49:         require(
50:             implementation_ != address(0), // <= FOUND
51:             "GovernorBravoDelegator::_setImplementation: invalid implementation address"
52:         );
53: 
54:         address oldImplementation = implementation;
55:         implementation = implementation_;
56: 
57:         emit NewImplementation(oldImplementation, implementation);
58:     }

['320']

320:     function castVoteBySig(uint proposalId, bool support, uint8 v, bytes32 r, bytes32 s) public {
321:         bytes32 domainSeparator = keccak256(
322:             abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainId(), address(this))
323:         );
324:         bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));
325:         bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
326:         address signatory = ecrecover(digest, v, r, s);
327:         require(signatory != address(0), "GovernorAlpha::castVoteBySig: invalid signature"); // <= FOUND
328:         return _castVote(signatory, proposalId, support);
329:     }

['356']

356:     function __abdicate() public {
357:         require(msg.sender == guardian, "GovernorAlpha::__abdicate: sender must be gov guardian");
358:         guardian = address(0); // <= FOUND
359:     }

['320']

320:     function castVoteBySig(uint proposalId, bool support, uint8 v, bytes32 r, bytes32 s) public {
321:         bytes32 domainSeparator = keccak256(
322:             abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainId(), address(this))
323:         );
324:         bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));
325:         bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
326:         address signatory = ecrecover(digest, v, r, s);
327:         require(signatory != address(0), "GovernorAlpha::castVoteBySig: invalid signature"); // <= FOUND
328:         return _castVote(signatory, proposalId, support);
329:     }

['111']

111:     function initialize(
112:         address xvsVault_,
113:         ProposalConfig[] memory proposalConfigs_,
114:         TimelockInterface[] memory timelocks,
115:         address guardian_
116:     ) public {
117:         require(address(proposalTimelocks[0]) == address(0), "GovernorBravo::initialize: cannot initialize twice"); // <= FOUND
118:         require(msg.sender == admin, "GovernorBravo::initialize: admin only");
119:         require(xvsVault_ != address(0), "GovernorBravo::initialize: invalid xvs address"); // <= FOUND
120:         require(guardian_ != address(0), "GovernorBravo::initialize: invalid guardian"); // <= FOUND
121:         require(
122:             timelocks.length == uint8(ProposalType.CRITICAL) + 1,
123:             "GovernorBravo::initialize:number of timelocks should match number of governance routes"
124:         );
125:         require(
126:             proposalConfigs_.length == uint8(ProposalType.CRITICAL) + 1,
127:             "GovernorBravo::initialize:number of proposal configs should match number of governance routes"
128:         );
129: 
130:         xvsVault = XvsVaultInterface(xvsVault_);
131:         proposalMaxOperations = 10;
132:         guardian = guardian_;
133: 
134:         
135:         uint256 arrLength = proposalConfigs_.length;
136:         for (uint256 i; i < arrLength; ++i) {
137:             require(
138:                 proposalConfigs_[i].votingPeriod >= MIN_VOTING_PERIOD,
139:                 "GovernorBravo::initialize: invalid min voting period"
140:             );
141:             require(
142:                 proposalConfigs_[i].votingPeriod <= MAX_VOTING_PERIOD,
143:                 "GovernorBravo::initialize: invalid max voting period"
144:             );
145:             require(
146:                 proposalConfigs_[i].votingDelay >= MIN_VOTING_DELAY,
147:                 "GovernorBravo::initialize: invalid min voting delay"
148:             );
149:             require(
150:                 proposalConfigs_[i].votingDelay <= MAX_VOTING_DELAY,
151:                 "GovernorBravo::initialize: invalid max voting delay"
152:             );
153:             require(
154:                 proposalConfigs_[i].proposalThreshold >= MIN_PROPOSAL_THRESHOLD,
155:                 "GovernorBravo::initialize: invalid min proposal threshold"
156:             );
157:             require(
158:                 proposalConfigs_[i].proposalThreshold <= MAX_PROPOSAL_THRESHOLD,
159:                 "GovernorBravo::initialize: invalid max proposal threshold"
160:             );

['337']

337:     function castVoteBySig(uint proposalId, uint8 support, uint8 v, bytes32 r, bytes32 s) external {
338:         bytes32 domainSeparator = keccak256(
339:             abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainIdInternal(), address(this))
340:         );
341:         bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));
342:         bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
343:         address signatory = ecrecover(digest, v, r, s);
344:         require(signatory != address(0), "GovernorBravo::castVoteBySig: invalid signature"); // <= FOUND
345:         emit VoteCast(signatory, proposalId, support, castVoteInternal(signatory, proposalId, support), "");
346:     }

['382']

382:     function _setGuardian(address newGuardian) external {
383:         require(msg.sender == guardian || msg.sender == admin, "GovernorBravo::_setGuardian: admin or guardian only");
384:         require(newGuardian != address(0), "GovernorBravo::_setGuardian: cannot live without a guardian"); // <= FOUND
385:         address oldGuardian = guardian;
386:         guardian = newGuardian;
387: 
388:         emit NewGuardian(oldGuardian, newGuardian);
389:     }

['489']

489:     function _acceptAdmin() external {
490:         
491:         require(
492:             msg.sender == pendingAdmin && msg.sender != address(0), // <= FOUND
493:             "GovernorBravo:_acceptAdmin: pending admin only"
494:         );
495: 
496:         
497:         address oldAdmin = admin;
498:         address oldPendingAdmin = pendingAdmin;
499: 
500:         
501:         admin = pendingAdmin;
502: 
503:         
504:         pendingAdmin = address(0); // <= FOUND
505: 
506:         emit NewAdmin(oldAdmin, admin);
507:         emit NewPendingAdmin(oldPendingAdmin, pendingAdmin);
508:     }

['51']

51:     function initialize(
52:         address timelock_,
53:         address xvsVault_,
54:         uint votingPeriod_,
55:         uint votingDelay_,
56:         uint proposalThreshold_,
57:         address guardian_
58:     ) public {
59:         require(address(timelock) == address(0), "GovernorBravo::initialize: can only initialize once"); // <= FOUND
60:         require(msg.sender == admin, "GovernorBravo::initialize: admin only");
61:         require(timelock_ != address(0), "GovernorBravo::initialize: invalid timelock address"); // <= FOUND
62:         require(xvsVault_ != address(0), "GovernorBravo::initialize: invalid xvs address"); // <= FOUND
63:         require(
64:             votingPeriod_ >= MIN_VOTING_PERIOD && votingPeriod_ <= MAX_VOTING_PERIOD,
65:             "GovernorBravo::initialize: invalid voting period"
66:         );
67:         require(
68:             votingDelay_ >= MIN_VOTING_DELAY && votingDelay_ <= MAX_VOTING_DELAY,
69:             "GovernorBravo::initialize: invalid voting delay"
70:         );
71:         require(
72:             proposalThreshold_ >= MIN_PROPOSAL_THRESHOLD && proposalThreshold_ <= MAX_PROPOSAL_THRESHOLD,
73:             "GovernorBravo::initialize: invalid proposal threshold"
74:         );
75:         require(guardian_ != address(0), "GovernorBravo::initialize: invalid guardian"); // <= FOUND
76: 
77:         timelock = TimelockInterface(timelock_);
78:         xvsVault = XvsVaultInterface(xvsVault_);
79:         votingPeriod = votingPeriod_;
80:         votingDelay = votingDelay_;
81:         proposalThreshold = proposalThreshold_;
82:         proposalMaxOperations = 10;
83:         guardian = guardian_;
84:     }

['337']

337:     function castVoteBySig(uint proposalId, uint8 support, uint8 v, bytes32 r, bytes32 s) external {
338:         bytes32 domainSeparator = keccak256(
339:             abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainIdInternal(), address(this))
340:         );
341:         bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));
342:         bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
343:         address signatory = ecrecover(digest, v, r, s);
344:         require(signatory != address(0), "GovernorBravo::castVoteBySig: invalid signature"); // <= FOUND
345:         emit VoteCast(signatory, proposalId, support, castVoteInternal(signatory, proposalId, support), "");
346:     }

['47']

47:     function _setImplementation(address implementation_) public {
48:         require(msg.sender == admin, "GovernorBravoDelegator::_setImplementation: admin only");
49:         require(
50:             implementation_ != address(0), // <= FOUND
51:             "GovernorBravoDelegator::_setImplementation: invalid implementation address"
52:         );
53: 
54:         address oldImplementation = implementation;
55:         implementation = implementation_;
56: 
57:         emit NewImplementation(oldImplementation, implementation);
58:     }

['43']

43:     function initialize(address accessControlManager_) external initializer {
44:         require(accessControlManager_ != address(0), "Address must not be zero"); // <= FOUND
45:         __AccessControlled_init(accessControlManager_);
46:     }

['94']

94:     function transferBridgeOwnership(address newOwner_) external {
95:         _checkAccessAllowed("transferBridgeOwnership(address)");
96:         require(newOwner_ != address(0), "Address must not be zero"); // <= FOUND
97:         OMNICHAIN_GOVERNANCE_EXECUTOR.transferOwnership(newOwner_);
98:     }

['98']

98:     function acceptAdmin() public {
99:         require(msg.sender == pendingAdmin, "Timelock::acceptAdmin: Call must come from pendingAdmin.");
100:         admin = msg.sender;
101:         pendingAdmin = address(0); // <= FOUND
102: 
103:         emit NewAdmin(admin);
104:     }

['127']

127:     function acceptAdmin() public {
128:         require(msg.sender == pendingAdmin, "Timelock::acceptAdmin: Call must come from pendingAdmin.");
129:         emit NewAdmin(admin, msg.sender);
130:         admin = msg.sender;
131:         pendingAdmin = address(0); // <= FOUND
132:     }

[Gas-22] Structs can be packed into fewer storage slots

Resolution

In Solidity, each storage slot has a size of 32 bytes. If a struct contains multiple uint values, it's efficient to pack these into as few storage slots as possible to optimize gas usage. The EVM (Ethereum Virtual Machine) charges gas for each storage operation, so minimizing the number of slots used can result in substantial gas savings. This can be achieved by ordering struct fields according to their size or by using smaller data types where possible. However, developers must balance these optimizations with the need for code clarity and the precision requirements of their application. Always ensure that data packing does not compromise the functionality or security of the contract.

Num of instances: 2

Findings

Click to show findings

['193']

193:     struct ProposalConfig {
194:         
195:         uint256 votingDelay; // <= FOUND
196:         
197:         uint256 votingPeriod; // <= FOUND
198:         
199:         uint256 proposalThreshold; // <= FOUND
200:     }

['29']

29:     struct Proposal {
30:         
31:         uint256 id; // <= FOUND
32:         
33:         uint256 eta; // <= FOUND
34:         
35:         address[] targets;
36:         
37:         uint256[] values; // <= FOUND
38:         
39:         string[] signatures;
40:         
41:         bytes[] calldatas;
42:         
43:         bool canceled;
44:         
45:         bool executed;
46:         
47:         uint8 proposalType;
48:     }

[Gas-23] Use bitmap to save gas

Resolution

Bitmaps in Solidity are essentially a way of representing a set of boolean values within an integer type variable such as uint256. Each bit in the integer represents a true or false value (1 or 0), thus allowing efficient storage of multiple boolean values.

Bitmaps can save gas in the Ethereum network because they condense a lot of information into a small amount of storage. In Ethereum, storage is one of the most significant costs in terms of gas usage. By reducing the amount of storage space needed, you can potentially save on gas fees.

Here's a quick comparison:

If you were to represent 256 different boolean values in the traditional way, you would have to declare 256 different bool variables. Given that each bool occupies a storage slot and each storage slot costs 20,000 gas to initialize, you would end up paying a considerable amount of gas.

On the other hand, if you were to use a bitmap, you could store these 256 boolean values within a single uint256 variable. In other words, you'd only pay for a single storage slot, resulting in significant gas savings.

However, it's important to note that while bitmaps can provide gas efficiencies, they do add complexity to the code, making it harder to read and maintain. Also, using bitmaps is efficient only when dealing with a large number of boolean variables that are frequently changed or accessed together.

In contrast, the straightforward counterpart to bitmaps would be using arrays or mappings to store boolean values, with each bool value occupying its own storage slot. This approach is simpler and more readable but could potentially be more expensive in terms of gas usage.

Num of instances: 8

Findings

Click to show findings

['241']

241:         proposal.executed = true; // <= FOUND

['265']

265:         proposal.canceled = true; // <= FOUND

['344']

344:         receipt.hasVoted = true; // <= FOUND

['347']

347:         queued[proposalId_] = true; // <= FOUND

['174']

174:         queuedTransactions[txHash] = true; // <= FOUND

['335']

335:         require(receipt.hasVoted == false, "GovernorAlpha::_castVote: voter already voted"); // <= FOUND

['360']

360:         require(receipt.hasVoted == false, "GovernorBravo::castVoteInternal: voter already voted"); // <= FOUND

['164']

164:         queuedTransactions[txHash] = false; // <= FOUND

[Gas-24] Use assembly hashing

Resolution

From a gas standpoint, the assembly version of the keccak256 hashing function can be more efficient than the high-level Solidity version. This is because Solidity has additional overhead when handling function calls and memory management, which can increase the gas cost.

Num of instances: 10

Findings

Click to show findings

['78']

78:         bytes32 role = keccak256(abi.encodePacked(contractAddress, functionSig)); // <= FOUND

['110']

110:         bytes32 role = keccak256(abi.encodePacked(msg.sender, functionSig)); // <= FOUND

['115']

115:             role = keccak256(abi.encodePacked(address(0), functionSig)); // <= FOUND

['321']

321:         bytes32 domainSeparator = keccak256( // <= FOUND
322:             abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainId(), address(this))
323:         );

['324']

324:         bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support)); // <= FOUND

['325']

325:         bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); // <= FOUND

['338']

338:         bytes32 domainSeparator = keccak256( // <= FOUND
339:             abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainIdInternal(), address(this))
340:         );

['285']

285:             bytes32 hashedPayload = keccak256(payload_); // <= FOUND

['153']

153:             storedExecutionHashes[_pId] = keccak256(abi.encode(remoteChainId_, payload, adapterParams_, msg.value)); // <= FOUND

['172']

172:         bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta)); // <= FOUND

[Gas-25] Use assembly to emit events

Resolution

With the use of inline assembly in Solidity, we can take advantage of low-level features like scratch space and the free memory pointer, offering more gas-efficient ways of emitting events. The scratch space is a certain area of memory where we can temporarily store data, and the free memory pointer indicates the next available memory slot. Using these, we can efficiently assemble event data without incurring additional memory expansion costs. However, safety is paramount: to avoid overwriting or leakage, we must cache the free memory pointer before use and restore it afterward, ensuring that it points to the correct memory location post-operation.

Num of instances: 47

Findings

Click to show findings

['41']

41:         emit NewAccessControlManager(oldAccessControlManager, accessControlManager_); // <= FOUND

['80']

80:         emit PermissionGranted(accountToPermit, contractAddress, functionSig); // <= FOUND

['98']

98:         emit PermissionRevoked(accountToRevoke, contractAddress, functionSig); // <= FOUND

['49']

49:         emit SetMaxDailyReceiveLimit(maxDailyReceiveLimit, limit_); // <= FOUND

['65']

65:         emit SetMaxDailyLimit(chainId_, chainIdToMaxDailyLimit[chainId_], limit_); // <= FOUND

['95']

95:         emit NewAccessControlManager(accessControlManager, accessControlManager_); // <= FOUND

['57']

57:         emit NewImplementation(oldImplementation, implementation); // <= FOUND

['199']

199:         emit ProposalCreated( // <= FOUND
200:             newProposal.id,
201:             msg.sender,
202:             targets,
203:             values,
204:             signatures,
205:             calldatas,
206:             startBlock,
207:             endBlock,
208:             description
209:         );

['224']

224:         emit ProposalQueued(proposalId, eta); // <= FOUND

['251']

251:         emit ProposalExecuted(proposalId); // <= FOUND

['276']

276:         emit ProposalCanceled(proposalId); // <= FOUND

['348']

348:         emit VoteCast(voter, proposalId, support, votes); // <= FOUND

['243']

243:         emit ProposalCreated( // <= FOUND
244:             newProposal.id,
245:             msg.sender,
246:             targets,
247:             values,
248:             signatures,
249:             calldatas,
250:             startBlock,
251:             endBlock,
252:             description,
253:             uint8(proposalType)
254:         );

['320']

320:         emit VoteCast(msg.sender, proposalId, support, castVoteInternal(msg.sender, proposalId, support), ""); // <= FOUND

['330']

330:         emit VoteCast(msg.sender, proposalId, support, castVoteInternal(msg.sender, proposalId, support), reason); // <= FOUND

['345']

345:         emit VoteCast(signatory, proposalId, support, castVoteInternal(signatory, proposalId, support), ""); // <= FOUND

['388']

388:         emit NewGuardian(oldGuardian, newGuardian); // <= FOUND

['463']

463:         emit ProposalMaxOperationsUpdated(oldProposalMaxOperations, proposalMaxOperations_); // <= FOUND

['483']

482:         
483:         emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin); // <= FOUND

['506']

506:         emit NewAdmin(oldAdmin, admin); // <= FOUND

['507']

507:         emit NewPendingAdmin(oldPendingAdmin, pendingAdmin); // <= FOUND

['404']

404:         emit VotingDelaySet(oldVotingDelay, votingDelay); // <= FOUND

['420']

420:         emit VotingPeriodSet(oldVotingPeriod, votingPeriod); // <= FOUND

['437']

437:         emit ProposalThresholdSet(oldProposalThreshold, proposalThreshold); // <= FOUND

['77']

77:                 emit FunctionRegistryChanged(signatures_[i], true); // <= FOUND

['80']

80:                 emit FunctionRegistryChanged(signatures_[i], false); // <= FOUND

['147']

147:         emit SetSrcChainId(srcChainId, srcChainId_); // <= FOUND

['165']

165:             emit TimelockAdded(i, address(proposalTimelocks[i]), address(timelocks_[i])); // <= FOUND

['190']

190:         emit ProposalExecuted(proposalId_); // <= FOUND

['225']

225:         emit ProposalCanceled(proposalId_); // <= FOUND

['287']

287:             emit ReceivePayloadFailed(srcChainId_, srcAddress_, nonce_, reason);  // <= FOUND

['333']

333:         emit ProposalReceived(newProposal.id, targets, values, signatures, calldatas, pType); // <= FOUND

['350']

350:         emit ProposalQueued(proposalId_, eta); // <= FOUND

['111']

111:         emit TrustedRemoteRemoved(remoteChainId_); // <= FOUND

['151']

151:             emit ExecuteRemoteProposal(remoteChainId_, _pId, payload); // <= FOUND

['154']

154:             emit StorePayload(_pId, remoteChainId_, payload, adapterParams_, msg.value, reason); // <= FOUND

['190']

190:         emit ClearPayload(pId_, hash); // <= FOUND

['234']

234:         emit FallbackWithdraw(to_, originalValue_); // <= FOUND

['255']

255:         emit SetTrustedRemoteAddress(remoteChainId_, oldRemoteAddress, trustedRemoteLookup[remoteChainId_]); // <= FOUND

['92']

92:         emit NewDelay(delay); // <= FOUND

['103']

103:         emit NewAdmin(admin); // <= FOUND

['145']

145:         emit NewPendingAdmin(pendingAdmin); // <= FOUND

['176']

176:         emit QueueTransaction(txHash, target, value, signature, data, eta); // <= FOUND

['203']

203:         emit CancelTransaction(txHash, target, value, signature, data, eta); // <= FOUND

['245']

245:         emit ExecuteTransaction(txHash, target, value, signature, data, eta); // <= FOUND

['94']

94:         emit NewDelay(delay, delay_); // <= FOUND

['129']

129:         emit NewAdmin(admin, msg.sender); // <= FOUND

[Gas-26] Use assembly in place of abi.decode to extract calldata values more efficiently

Resolution

Using inline assembly to extract calldata values can be more gas-efficient than using abi.decode in Solidity. Inline assembly gives more direct access to EVM operations, enabling optimized usage of calldata. However, assembly should be used judiciously as it's more prone to errors. Opt for this approach when performance is critical and the complexity it introduces is manageable.

Num of instances: 4

Findings

Click to show findings

['297']

297:         (bytes memory payload, uint256 pId) = abi.decode(payload_, (bytes, uint256)); // <= FOUND

['304']

298:         (
299:             address[] memory targets,
300:             uint256[] memory values,
301:             string[] memory signatures,
302:             bytes[] memory calldatas,
303:             uint8 pType
304:         ) = abi.decode(payload, (address[], uint256[], string[], bytes[], uint8)); // <= FOUND

['182']

182:         (bytes memory payload, ) = abi.decode(payload_, (bytes, uint256)); // <= FOUND

['298']

292:         (
293:             address[] memory targets,
294:             uint256[] memory values,
295:             string[] memory signatures,
296:             bytes[] memory calldatas,
297: 
298:         ) = abi.decode(payload_, (address[], uint[], string[], bytes[], uint8)); // <= FOUND

[Gas-27] Counting down in for statements is more gas efficient

Resolution

Looping downwards in Solidity is more gas efficient due to how the EVM compares variables. In a 'for' loop that counts down, the end condition is usually to compare with zero, which is cheaper than comparing with another number. As such, restructure your loops to count downwards where possible.

Num of instances: 15

Findings

Click to show findings

['220']

220:        for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
221:             _queueOrRevert(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], eta);
222:         }

['242']

242:        for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
243:             timelock.executeTransaction.value(proposal.values[i])(
244:                 proposal.targets[i],
245:                 proposal.values[i],
246:                 proposal.signatures[i],
247:                 proposal.calldatas[i],
248:                 proposal.eta
249:             );
250:         }

['266']

266:        for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
267:             timelock.cancelTransaction(
268:                 proposal.targets[i],
269:                 proposal.values[i],
270:                 proposal.signatures[i],
271:                 proposal.calldatas[i],
272:                 proposal.eta
273:             );
274:         }

['136']

136:        for (uint256 i; i < arrLength; ++i) { // <= FOUND
137:             require(
138:                 proposalConfigs_[i].votingPeriod >= MIN_VOTING_PERIOD,
139:                 "GovernorBravo::initialize: invalid min voting period"
140:             );
141:             require(
142:                 proposalConfigs_[i].votingPeriod <= MAX_VOTING_PERIOD,
143:                 "GovernorBravo::initialize: invalid max voting period"
144:             );
145:             require(
146:                 proposalConfigs_[i].votingDelay >= MIN_VOTING_DELAY,
147:                 "GovernorBravo::initialize: invalid min voting delay"
148:             );
149:             require(
150:                 proposalConfigs_[i].votingDelay <= MAX_VOTING_DELAY,
151:                 "GovernorBravo::initialize: invalid max voting delay"
152:             );
153:             require(
154:                 proposalConfigs_[i].proposalThreshold >= MIN_PROPOSAL_THRESHOLD,
155:                 "GovernorBravo::initialize: invalid min proposal threshold"
156:             );
157:             require(
158:                 proposalConfigs_[i].proposalThreshold <= MAX_PROPOSAL_THRESHOLD,
159:                 "GovernorBravo::initialize: invalid max proposal threshold"
160:             );
161:             require(address(timelocks[i]) != address(0), "GovernorBravo::initialize:invalid timelock address");
162: 
163:             proposalConfigs[i] = proposalConfigs_[i];
164:             proposalTimelocks[i] = timelocks[i];
165:         }

['269']

269:        for (uint i; i < proposal.targets.length; ++i) { // <= FOUND
270:             queueOrRevertInternal(
271:                 proposal.targets[i],
272:                 proposal.values[i],
273:                 proposal.signatures[i],
274:                 proposal.calldatas[i],
275:                 eta,
276:                 uint8(proposal.proposalType)
277:             );
278:         }

['311']

311:        for (uint i; i < proposal.targets.length; ++i) { // <= FOUND
312:             proposalTimelocks[uint8(proposal.proposalType)].executeTransaction(
313:                 proposal.targets[i],
314:                 proposal.values[i],
315:                 proposal.signatures[i],
316:                 proposal.calldatas[i],
317:                 proposal.eta
318:             );
319:         }

['340']

340:        for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
341:             proposalTimelocks[proposal.proposalType].cancelTransaction(
342:                 proposal.targets[i],
343:                 proposal.values[i],
344:                 proposal.signatures[i],
345:                 proposal.calldatas[i],
346:                 proposal.eta
347:             );
348:         }

['503']

503:        for (uint256 i; i < uint8(ProposalType.CRITICAL) + 1; ++i) { // <= FOUND
504:             proposalTimelocks[i].acceptAdmin();
505:         }

['179']

179:        for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
180:             queueOrRevertInternal(
181:                 proposal.targets[i],
182:                 proposal.values[i],
183:                 proposal.signatures[i],
184:                 proposal.calldatas[i],
185:                 eta
186:             );
187:         }

['217']

217:        for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
218:             timelock.executeTransaction(
219:                 proposal.targets[i],
220:                 proposal.values[i],
221:                 proposal.signatures[i],
222:                 proposal.calldatas[i],
223:                 proposal.eta
224:             );
225:         }

['83']

72:        for (uint256 i; i < signatureLength; ) {
73:             bytes4 sigHash = bytes4(keccak256(bytes(signatures_[i])));
74:             bytes memory signature = bytes(functionRegistry[sigHash]);
75:             if (active_[i] && signature.length == 0) {
76:                 functionRegistry[sigHash] = signatures_[i];
77:                 emit FunctionRegistryChanged(signatures_[i], true);
78:             } else if (!active_[i] && signature.length != 0) {
79:                 delete functionRegistry[sigHash];
80:                 emit FunctionRegistryChanged(signatures_[i], false);
81:             }
82:             unchecked {
83:                 ++i; // <= FOUND
84:             }
85:         }

['168']

163:        for (uint8 i; i < length; ) {
164:             ensureNonzeroAddress(address(timelocks_[i]));
165:             emit TimelockAdded(i, address(proposalTimelocks[i]), address(timelocks_[i]));
166:             proposalTimelocks[i] = timelocks_[i];
167:             unchecked {
168:                 ++i; // <= FOUND
169:             }
170:         }

['201']

192:        for (uint256 i; i < length; ) {
193:             timelock.executeTransaction(
194:                 proposal.targets[i],
195:                 proposal.values[i],
196:                 proposal.signatures[i],
197:                 proposal.calldatas[i],
198:                 eta
199:             );
200:             unchecked {
201:                 ++i; // <= FOUND
202:             }
203:         }

['236']

227:        for (uint256 i; i < length; ) {
228:             timelock.cancelTransaction(
229:                 proposal.targets[i],
230:                 proposal.values[i],
231:                 proposal.signatures[i],
232:                 proposal.calldatas[i],
233:                 eta
234:             );
235:             unchecked {
236:                 ++i; // <= FOUND
237:             }
238:         }

['362']

352:        for (uint256 i; i < length; ) {
353:             _queueOrRevertInternal(
354:                 proposal.targets[i],
355:                 proposal.values[i],
356:                 proposal.signatures[i],
357:                 proposal.calldatas[i],
358:                 eta,
359:                 proposalType
360:             );
361:             unchecked {
362:                 ++i; // <= FOUND
363:             }
364:         }

[Gas-28] Using private rather than public for constants and immutables, saves gas

Resolution

Using private visibility for constants and immutables in Solidity instead of public can save gas. This is because private elements are not included in the contract's ABI, reducing the deployment and interaction costs. To achieve better efficiency, it is recommended to use private visibility when external access is not needed.

Num of instances: 15

Findings

Click to show findings

['6']

6: string public constant name = "Venus Governor Alpha"; // <= FOUND

['105']

105: bytes32 public constant DOMAIN_TYPEHASH = // <= FOUND

['109']

109: bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,bool support)"); // <= FOUND

['13']

13: string public constant name = "Venus Governor Bravo"; // <= FOUND

['16']

16: uint public constant MIN_PROPOSAL_THRESHOLD = 150000e18;  // <= FOUND

['19']

19: uint public constant MAX_PROPOSAL_THRESHOLD = 300000e18;  // <= FOUND

['22']

22: uint public constant MIN_VOTING_PERIOD = 20 * 60 * 3;  // <= FOUND

['25']

25: uint public constant MAX_VOTING_PERIOD = 20 * 60 * 24 * 14;  // <= FOUND

['28']

28: uint public constant MIN_VOTING_DELAY = 1; // <= FOUND

['31']

31: uint public constant MAX_VOTING_DELAY = 20 * 60 * 24 * 7;  // <= FOUND

['34']

34: uint public constant quorumVotes = 600000e18;  // <= FOUND

['41']

41: bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,uint8 support)"); // <= FOUND

['52']

52: uint public constant GRACE_PERIOD = 14 days; // <= FOUND

['55']

55: uint public constant MINIMUM_DELAY = 1 hours; // <= FOUND

['58']

58: uint public constant MAXIMUM_DELAY = 30 days; // <= FOUND

[Gas-29] Mark Functions That Revert For Normal Users As payable

Resolution

In Solidity, marking functions as payable allows them to accept Ether. If a function is known to revert for regular users (non-admin or specific roles) but needs to be accessible to others, marking it as payable can be beneficial. This ensures that even if a regular user accidentally sends Ether to the function, the Ether won't be trapped, as the function reverts, returning the funds. This can save gas by avoiding unnecessary failure handling in the function itself. Resolution: Carefully assess the roles and access patterns, and mark functions that should revert for regular users as payable to handle accidental Ether transfers.

Num of instances: 9

Findings

Click to show findings

['48']

48:     function setAccessControlManager(address accessControlManager_) external onlyOwner {
49:         _setAccessControlManager(accessControlManager_);
50:     }

['48']

48:     function setMaxDailyReceiveLimit(uint256 limit_) external onlyOwner {
49:         emit SetMaxDailyReceiveLimit(maxDailyReceiveLimit, limit_);
50:         maxDailyReceiveLimit = limit_;
51:     }

['57']

57:     function pause() external onlyOwner {
58:         _pause();
59:     }

['65']

65:     function unpause() external onlyOwner {
66:         _unpause();
67:     }

['93']

93:     function setAccessControlManager(address accessControlManager_) external onlyOwner {
94:         ensureNonzeroAddress(accessControlManager_);
95:         emit NewAccessControlManager(accessControlManager, accessControlManager_);
96:         accessControlManager = accessControlManager_;
97:     }

['69']

69:     function upsertSignature(string[] calldata signatures_, bool[] calldata active_) external onlyOwner {
70:         uint256 signatureLength = signatures_.length;
71:         require(signatureLength == active_.length, "Input arrays must have the same length");
72:         for (uint256 i; i < signatureLength; ) {
73:             bytes4 sigHash = bytes4(keccak256(bytes(signatures_[i])));
74:             bytes memory signature = bytes(functionRegistry[sigHash]);
75:             if (active_[i] && signature.length == 0) {
76:                 functionRegistry[sigHash] = signatures_[i];
77:                 emit FunctionRegistryChanged(signatures_[i], true);
78:             } else if (!active_[i] && signature.length != 0) {
79:                 delete functionRegistry[sigHash];
80:                 emit FunctionRegistryChanged(signatures_[i], false);
81:             }
82:             unchecked {
83:                 ++i;
84:             }
85:         }
86:     }

['146']

146:     function setSrcChainId(uint16 srcChainId_) external onlyOwner {
147:         emit SetSrcChainId(srcChainId, srcChainId_);
148:         srcChainId = srcChainId_;
149:     }

['157']

157:     function addTimelocks(ITimelock[] memory timelocks_) external onlyOwner {
158:         uint8 length = uint8(type(ProposalType).max) + 1;
159:         require(
160:             timelocks_.length == length,
161:             "OmnichainGovernanceExecutor::initialize:number of timelocks _should match the number of governance routes"
162:         );
163:         for (uint8 i; i < length; ) {
164:             ensureNonzeroAddress(address(timelocks_[i]));
165:             emit TimelockAdded(i, address(proposalTimelocks[i]), address(timelocks_[i]));
166:             proposalTimelocks[i] = timelocks_[i];
167:             unchecked {
168:                 ++i;
169:             }
170:         }
171:     }

['214']

214:     function fallbackWithdraw(
215:         address to_,
216:         uint256 pId_,
217:         uint16 remoteChainId_,
218:         bytes calldata payload_,
219:         bytes calldata adapterParams_,
220:         uint256 originalValue_
221:     ) external onlyOwner nonReentrant {
222:         ensureNonzeroAddress(to_);
223:         require(originalValue_ > 0, "OmnichainProposalSender: invalid native amount");
224:         require(payload_.length != 0, "OmnichainProposalSender: Empty payload");
225: 
226:         bytes32 hash = storedExecutionHashes[pId_];
227:         require(hash != bytes32(0), "OmnichainProposalSender: no stored payload");
228: 
229:         bytes memory execution = abi.encode(remoteChainId_, payload_, adapterParams_, originalValue_);
230:         require(keccak256(execution) == hash, "OmnichainProposalSender: invalid execution params");
231: 
232:         delete storedExecutionHashes[pId_];
233: 
234:         emit FallbackWithdraw(to_, originalValue_);
235:         emit ClearPayload(pId_, hash);
236: 
237:         
238:         (bool sent, ) = to_.call{ value: originalValue_ }("");
239:         require(sent, "Call failed");
240:     }

[Gas-30] Function names can be optimized

Resolution

Function names in Solidity contracts can be optimized to save gas during both deployment and execution. Method IDs are the first four bytes of the keccak256 hash of the function signature, and having two leading zero bytes can save 128 gas each during deployment. Additionally, renaming functions to have lower method IDs can save 22 gas per call, per sorted position shifted. This optimization leverages the way EVM handles data storage, making the execution more efficient. While these savings might seem minor, they can add up in contracts with numerous calls, contributing to more economical and efficient code.

Num of instances: 23

Findings

Click to show findings

['12']

12: contract AccessControlledV5  // <= FOUND

['52']

52: contract AccessControlManager is AccessControl, IAccessControlManagerV8  // <= FOUND

['17']

17: contract BaseOmnichainControllerSrc is Ownable, Pausable  // <= FOUND

['11']

11: contract GovernorBravoDelegatorV1 is GovernorBravoDelegatorStorage, GovernorBravoEventsV1  // <= FOUND

['4']

4: contract GovernorAlpha  // <= FOUND

['4']

4: contract GovernorAlpha2  // <= FOUND

['73']

73: contract GovernorBravoDelegate is GovernorBravoDelegateStorageV2, GovernorBravoEvents  // <= FOUND

['11']

11: contract GovernorBravoDelegateV1 is GovernorBravoDelegateStorageV1, GovernorBravoEventsV1  // <= FOUND

['11']

11: contract GovernorBravoDelegator is GovernorBravoDelegatorStorage, GovernorBravoEvents  // <= FOUND

['9']

9: contract GovernorBravoEvents  // <= FOUND

['73']

73: contract GovernorBravoDelegatorStorage  // <= FOUND

['88']

88: contract GovernorBravoDelegateStorageV1 is GovernorBravoDelegatorStorage  // <= FOUND

['186']

186: contract GovernorBravoDelegateStorageV2 is GovernorBravoDelegateStorageV1  // <= FOUND

['12']

12: contract GovernorBravoEventsV1  // <= FOUND

['88']

88: contract GovernorBravoDelegateStorageV1 is GovernorBravoDelegatorStorage  // <= FOUND

['16']

16: contract OmnichainExecutorOwner is AccessControlledV8  // <= FOUND

['19']

19: contract OmnichainGovernanceExecutor is ReentrancyGuard, BaseOmnichainControllerDest  // <= FOUND

['19']

19: contract OmnichainProposalSender is ReentrancyGuard, BaseOmnichainControllerSrc  // <= FOUND

['10']

10: contract Timelock  // <= FOUND

['12']

12: contract TimelockV8  // <= FOUND

['15']

15: abstract contract AccessControlledV8 is Initializable, Ownable2StepUpgradeable  // <= FOUND

['17']

17: abstract contract BaseOmnichainControllerDest is NonblockingLzApp, Pausable  // <= FOUND

['16']

16: library SafeMath  // <= FOUND

[Gas-31] Consider migrating require statements to custom errors

Resolution

Using custom errors instead of 'require' statements in Solidity can lead to gas savings and improve developer experience. Custom errors provide explicit error messages, aiding in troubleshooting. Moreover, custom errors are cheaper as they don't require a string literal, thus saving gas. Hence, developers should replace 'require' statements with custom errors wherever possible for efficiency and readability.

Num of instances: 129

Findings

Click to show findings

['38']

38:         require(address(accessControlManager_) != address(0), "invalid acess control manager address"); // <= FOUND

['93']

93:         
94:         require(receivedInWindow <= maxDailyReceiveLimit, "Daily Transaction Limit Exceeded"); // <= FOUND

['126']

126:         
127:         require(commandsSentInWindow <= maxDailyLimit, "Daily Transaction Limit Exceeded"); // <= FOUND

['128']

128:         
129:         require(lastProposalSentTimestamp != currentBlockTimestamp, "Multiple bridging in a proposal"); // <= FOUND

['141']

141:         require( // <= FOUND
142:             IAccessControlManagerV8(accessControlManager).isAllowedToCall(msg.sender, functionSig_),
143:             "access denied"
144:         ); // <= FOUND

['48']

48:         require(msg.sender == admin, "GovernorBravoDelegator::_setImplementation: admin only"); // <= FOUND

['49']

49:         require( // <= FOUND
50:             implementation_ != address(0),
51:             "GovernorBravoDelegator::_setImplementation: invalid implementation address"
52:         ); // <= FOUND

['150']

150:         require( // <= FOUND
151:             xvs.getPriorVotes(msg.sender, sub256(block.number, 1)) > proposalThreshold(),
152:             "GovernorAlpha::propose: proposer votes below proposal threshold"
153:         ); // <= FOUND

['154']

154:         require( // <= FOUND
155:             targets.length == values.length &&
156:                 targets.length == signatures.length &&
157:                 targets.length == calldatas.length,
158:             "GovernorAlpha::propose: proposal function information arity mismatch"
159:         ); // <= FOUND

['160']

160:         require(targets.length != 0, "GovernorAlpha::propose: must provide actions"); // <= FOUND

['161']

161:         require(targets.length <= proposalMaxOperations(), "GovernorAlpha::propose: too many actions"); // <= FOUND

['166']

166:             require( // <= FOUND
167:                 proposersLatestProposalState != ProposalState.Active,
168:                 "GovernorAlpha::propose: found an already active proposal"
169:             ); // <= FOUND

['170']

170:             require( // <= FOUND
171:                 proposersLatestProposalState != ProposalState.Pending,
172:                 "GovernorAlpha::propose: found an already pending proposal"
173:             ); // <= FOUND

['214']

214:         require( // <= FOUND
215:             state(proposalId) == ProposalState.Succeeded,
216:             "GovernorAlpha::queue: proposal can only be queued if it is succeeded"
217:         ); // <= FOUND

['228']

228:         require( // <= FOUND
229:             !timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))),
230:             "GovernorAlpha::_queueOrRevert: proposal action already queued at eta"
231:         ); // <= FOUND

['236']

236:         require( // <= FOUND
237:             state(proposalId) == ProposalState.Queued,
238:             "GovernorAlpha::execute: proposal can only be executed if it is queued"
239:         ); // <= FOUND

['256']

256:         require(state != ProposalState.Executed, "GovernorAlpha::cancel: cannot cancel executed proposal"); // <= FOUND

['259']

259:         require( // <= FOUND
260:             msg.sender == guardian ||
261:                 xvs.getPriorVotes(proposal.proposer, sub256(block.number, 1)) < proposalThreshold(),
262:             "GovernorAlpha::cancel: proposer above threshold"
263:         ); // <= FOUND

['295']

295:         require(proposalCount >= proposalId && proposalId > 0, "GovernorAlpha::state: invalid proposal id"); // <= FOUND

['327']

327:         require(signatory != address(0), "GovernorAlpha::castVoteBySig: invalid signature"); // <= FOUND

['332']

332:         require(state(proposalId) == ProposalState.Active, "GovernorAlpha::_castVote: voting is closed"); // <= FOUND

['335']

335:         require(receipt.hasVoted == false, "GovernorAlpha::_castVote: voter already voted"); // <= FOUND

['352']

352:         require(msg.sender == guardian, "GovernorAlpha::__acceptAdmin: sender must be gov guardian"); // <= FOUND

['357']

357:         require(msg.sender == guardian, "GovernorAlpha::__abdicate: sender must be gov guardian"); // <= FOUND

['362']

362:         require(msg.sender == guardian, "GovernorAlpha::__queueSetTimelockPendingAdmin: sender must be gov guardian"); // <= FOUND

['367']

367:         require(msg.sender == guardian, "GovernorAlpha::__executeSetTimelockPendingAdmin: sender must be gov guardian"); // <= FOUND

['373']

373:         require(c >= a, "addition overflow"); // <= FOUND

['378']

378:         require(b <= a, "subtraction underflow"); // <= FOUND

['117']

117:         require(address(proposalTimelocks[0]) == address(0), "GovernorBravo::initialize: cannot initialize twice"); // <= FOUND

['60']

60:         require(msg.sender == admin, "GovernorBravo::initialize: admin only"); // <= FOUND

['62']

62:         require(xvsVault_ != address(0), "GovernorBravo::initialize: invalid xvs address"); // <= FOUND

['75']

75:         require(guardian_ != address(0), "GovernorBravo::initialize: invalid guardian"); // <= FOUND

['121']

121:         require( // <= FOUND
122:             timelocks.length == uint8(ProposalType.CRITICAL) + 1,
123:             "GovernorBravo::initialize:number of timelocks should match number of governance routes"
124:         ); // <= FOUND

['125']

125:         require( // <= FOUND
126:             proposalConfigs_.length == uint8(ProposalType.CRITICAL) + 1,
127:             "GovernorBravo::initialize:number of proposal configs should match number of governance routes"
128:         ); // <= FOUND

['137']

137:             require( // <= FOUND
138:                 proposalConfigs_[i].votingPeriod >= MIN_VOTING_PERIOD,
139:                 "GovernorBravo::initialize: invalid min voting period"
140:             ); // <= FOUND

['141']

141:             require( // <= FOUND
142:                 proposalConfigs_[i].votingPeriod <= MAX_VOTING_PERIOD,
143:                 "GovernorBravo::initialize: invalid max voting period"
144:             ); // <= FOUND

['145']

145:             require( // <= FOUND
146:                 proposalConfigs_[i].votingDelay >= MIN_VOTING_DELAY,
147:                 "GovernorBravo::initialize: invalid min voting delay"
148:             ); // <= FOUND

['149']

149:             require( // <= FOUND
150:                 proposalConfigs_[i].votingDelay <= MAX_VOTING_DELAY,
151:                 "GovernorBravo::initialize: invalid max voting delay"
152:             ); // <= FOUND

['153']

153:             require( // <= FOUND
154:                 proposalConfigs_[i].proposalThreshold >= MIN_PROPOSAL_THRESHOLD,
155:                 "GovernorBravo::initialize: invalid min proposal threshold"
156:             ); // <= FOUND

['157']

157:             require( // <= FOUND
158:                 proposalConfigs_[i].proposalThreshold <= MAX_PROPOSAL_THRESHOLD,
159:                 "GovernorBravo::initialize: invalid max proposal threshold"
160:             ); // <= FOUND

['161']

161:             require(address(timelocks[i]) != address(0), "GovernorBravo::initialize:invalid timelock address"); // <= FOUND

['103']

103:         
104:         require(initialProposalId != 0, "GovernorBravo::propose: Governor Bravo not active"); // <= FOUND

['191']

191:         require( // <= FOUND
192:             xvsVault.getPriorVotes(msg.sender, sub256(block.number, 1)) >=
193:                 proposalConfigs[uint8(proposalType)].proposalThreshold,
194:             "GovernorBravo::propose: proposer votes below proposal threshold"
195:         ); // <= FOUND

['108']

108:         require( // <= FOUND
109:             targets.length == values.length &&
110:                 targets.length == signatures.length &&
111:                 targets.length == calldatas.length,
112:             "GovernorBravo::propose: proposal function information arity mismatch"
113:         ); // <= FOUND

['114']

114:         require(targets.length != 0, "GovernorBravo::propose: must provide actions"); // <= FOUND

['115']

115:         require(targets.length <= proposalMaxOperations, "GovernorBravo::propose: too many actions"); // <= FOUND

['120']

120:             require( // <= FOUND
121:                 proposersLatestProposalState != ProposalState.Active,
122:                 "GovernorBravo::propose: one live proposal per proposer, found an already active proposal"
123:             ); // <= FOUND

['124']

124:             require( // <= FOUND
125:                 proposersLatestProposalState != ProposalState.Pending,
126:                 "GovernorBravo::propose: one live proposal per proposer, found an already pending proposal"
127:             ); // <= FOUND

['173']

173:         require( // <= FOUND
174:             state(proposalId) == ProposalState.Succeeded,
175:             "GovernorBravo::queue: proposal can only be queued if it is succeeded"
176:         ); // <= FOUND

['291']

291:         require( // <= FOUND
292:             !proposalTimelocks[proposalType].queuedTransactions(
293:                 keccak256(abi.encode(target, value, signature, data, eta))
294:             ),
295:             "GovernorBravo::queueOrRevertInternal: identical proposal action already queued at eta"
296:         ); // <= FOUND

['211']

211:         require( // <= FOUND
212:             state(proposalId) == ProposalState.Queued,
213:             "GovernorBravo::execute: proposal can only be executed if it is queued"
214:         ); // <= FOUND

['234']

234:         require(state(proposalId) != ProposalState.Executed, "GovernorBravo::cancel: cannot cancel executed proposal"); // <= FOUND

['331']

331:         require( // <= FOUND
332:             msg.sender == guardian ||
333:                 msg.sender == proposal.proposer ||
334:                 xvsVault.getPriorVotes(proposal.proposer, sub256(block.number, 1)) <
335:                 proposalConfigs[proposal.proposalType].proposalThreshold,
336:             "GovernorBravo::cancel: proposer above threshold"
337:         ); // <= FOUND

['290']

290:         require( // <= FOUND
291:             proposalCount >= proposalId && proposalId > initialProposalId,
292:             "GovernorBravo::state: invalid proposal id"
293:         ); // <= FOUND

['344']

344:         require(signatory != address(0), "GovernorBravo::castVoteBySig: invalid signature"); // <= FOUND

['356']

356:         require(state(proposalId) == ProposalState.Active, "GovernorBravo::castVoteInternal: voting is closed"); // <= FOUND

['357']

357:         require(support <= 2, "GovernorBravo::castVoteInternal: invalid vote type"); // <= FOUND

['360']

360:         require(receipt.hasVoted == false, "GovernorBravo::castVoteInternal: voter already voted"); // <= FOUND

['383']

383:         require(msg.sender == guardian || msg.sender == admin, "GovernorBravo::_setGuardian: admin or guardian only"); // <= FOUND

['384']

384:         require(newGuardian != address(0), "GovernorBravo::_setGuardian: cannot live without a guardian"); // <= FOUND

['446']

446:         require(msg.sender == admin, "GovernorBravo::_initiate: admin only"); // <= FOUND

['447']

447:         require(initialProposalId == 0, "GovernorBravo::_initiate: can only initiate once"); // <= FOUND

['459']

459:         require(msg.sender == admin, "GovernorBravo::_setProposalMaxOperations: admin only"); // <= FOUND

['473']

473:         
474:         require(msg.sender == admin, "GovernorBravo:_setPendingAdmin: admin only"); // <= FOUND

['491']

491:         
492:         require( // <= FOUND
493:             msg.sender == pendingAdmin && msg.sender != address(0),
494:             "GovernorBravo:_acceptAdmin: pending admin only"
495:         ); // <= FOUND

['59']

59:         require(address(timelock) == address(0), "GovernorBravo::initialize: can only initialize once"); // <= FOUND

['61']

61:         require(timelock_ != address(0), "GovernorBravo::initialize: invalid timelock address"); // <= FOUND

['63']

63:         require( // <= FOUND
64:             votingPeriod_ >= MIN_VOTING_PERIOD && votingPeriod_ <= MAX_VOTING_PERIOD,
65:             "GovernorBravo::initialize: invalid voting period"
66:         ); // <= FOUND

['67']

67:         require( // <= FOUND
68:             votingDelay_ >= MIN_VOTING_DELAY && votingDelay_ <= MAX_VOTING_DELAY,
69:             "GovernorBravo::initialize: invalid voting delay"
70:         ); // <= FOUND

['71']

71:         require( // <= FOUND
72:             proposalThreshold_ >= MIN_PROPOSAL_THRESHOLD && proposalThreshold_ <= MAX_PROPOSAL_THRESHOLD,
73:             "GovernorBravo::initialize: invalid proposal threshold"
74:         ); // <= FOUND

['104']

104:         require( // <= FOUND
105:             xvsVault.getPriorVotes(msg.sender, sub256(block.number, 1)) > proposalThreshold,
106:             "GovernorBravo::propose: proposer votes below proposal threshold"
107:         ); // <= FOUND

['199']

199:         require( // <= FOUND
200:             !timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))),
201:             "GovernorBravo::queueOrRevertInternal: identical proposal action already queued at eta"
202:         ); // <= FOUND

['237']

237:         require( // <= FOUND
238:             msg.sender == guardian ||
239:                 msg.sender == proposal.proposer ||
240:                 xvsVault.getPriorVotes(proposal.proposer, sub256(block.number, 1)) < proposalThreshold,
241:             "GovernorBravo::cancel: proposer above threshold"
242:         ); // <= FOUND

['396']

396:         require(msg.sender == admin, "GovernorBravo::_setVotingDelay: admin only"); // <= FOUND

['397']

397:         require( // <= FOUND
398:             newVotingDelay >= MIN_VOTING_DELAY && newVotingDelay <= MAX_VOTING_DELAY,
399:             "GovernorBravo::_setVotingDelay: invalid voting delay"
400:         ); // <= FOUND

['412']

412:         require(msg.sender == admin, "GovernorBravo::_setVotingPeriod: admin only"); // <= FOUND

['413']

413:         require( // <= FOUND
414:             newVotingPeriod >= MIN_VOTING_PERIOD && newVotingPeriod <= MAX_VOTING_PERIOD,
415:             "GovernorBravo::_setVotingPeriod: invalid voting period"
416:         ); // <= FOUND

['429']

429:         require(msg.sender == admin, "GovernorBravo::_setProposalThreshold: admin only"); // <= FOUND

['430']

430:         require( // <= FOUND
431:             newProposalThreshold >= MIN_PROPOSAL_THRESHOLD && newProposalThreshold <= MAX_PROPOSAL_THRESHOLD,
432:             "GovernorBravo::_setProposalThreshold: invalid proposal threshold"
433:         ); // <= FOUND

['34']

34:         require(omnichainGovernanceExecutor_ != address(0), "Address must not be zero"); // <= FOUND

['44']

44:         require(accessControlManager_ != address(0), "Address must not be zero"); // <= FOUND

['56']

56:         require(bytes(fun).length != 0, "Function not found"); // <= FOUND

['59']

59:         require(ok, "call failed"); // <= FOUND

['71']

71:         require(signatureLength == active_.length, "Input arrays must have the same length"); // <= FOUND

['96']

96:         require(newOwner_ != address(0), "Address must not be zero"); // <= FOUND

['159']

159:         require( // <= FOUND
160:             timelocks_.length == length,
161:             "OmnichainGovernanceExecutor::initialize:number of timelocks _should match the number of governance routes"
162:         ); // <= FOUND

['179']

179:         require( // <= FOUND
180:             state(proposalId_) == ProposalState.Queued,
181:             "OmnichainGovernanceExecutor::execute: proposal can only be executed if it is queued"
182:         ); // <= FOUND

['213']

213:         require( // <= FOUND
214:             state(proposalId_) == ProposalState.Queued,
215:             "OmnichainGovernanceExecutor::cancel: proposal should be queued and not executed"
216:         ); // <= FOUND

['218']

218:         require(msg.sender == GUARDIAN, "OmnichainGovernanceExecutor::cancel: sender must be guardian"); // <= FOUND

['276']

276:         require(srcChainId_ == srcChainId, "OmnichainGovernanceExecutor::_blockingLzReceive: invalid source chain id"); // <= FOUND

['305']

305:         require(proposals[pId].id == 0, "OmnichainGovernanceExecutor::_nonblockingLzReceive: duplicate proposal"); // <= FOUND

['306']

306:         require( // <= FOUND
307:             targets.length == values.length &&
308:                 targets.length == signatures.length &&
309:                 targets.length == calldatas.length,
310:             "OmnichainGovernanceExecutor::_nonblockingLzReceive: proposal function information arity mismatch"
311:         ); // <= FOUND

['312']

312:         require( // <= FOUND
313:             pType < uint8(type(ProposalType).max) + 1,
314:             "OmnichainGovernanceExecutor::_nonblockingLzReceive: invalid proposal type"
315:         ); // <= FOUND

['384']

384:         require( // <= FOUND
385:             !proposalTimelocks[proposalType_].queuedTransactions(
386:                 keccak256(abi.encode(target_, value_, signature_, data_, eta_))
387:             ),
388:             "OmnichainGovernanceExecutor::queueOrRevertInternal: identical proposal action already queued at eta"
389:         ); // <= FOUND

['132']

132:         
133:         require(msg.value > 0, "OmnichainProposalSender: value cannot be zero"); // <= FOUND

['133']

133:         require(payload_.length != 0, "OmnichainProposalSender: Empty payload"); // <= FOUND

['136']

136:         require(trustedRemote.length != 0, "OmnichainProposalSender: destination chain is not a trusted source"); // <= FOUND

['180']

180:         require(hash != bytes32(0), "OmnichainProposalSender: no stored payload"); // <= FOUND

['186']

186:         require(keccak256(execution) == hash, "OmnichainProposalSender: invalid execution params"); // <= FOUND

['223']

223:         require(originalValue_ > 0, "OmnichainProposalSender: invalid native amount"); // <= FOUND

['239']

239:         require(sent, "Call failed"); // <= FOUND

['251']

251:         require(remoteChainId_ != 0, "ChainId must not be zero"); // <= FOUND

['299']

299:         require( // <= FOUND
300:             targets.length == values.length &&
301:                 targets.length == signatures.length &&
302:                 targets.length == calldatas.length,
303:             "OmnichainProposalSender: proposal function information arity mismatch"
304:         ); // <= FOUND

['41']

41:         require(c >= a, errorMessage); // <= FOUND

['69']

69:         require(b <= a, errorMessage); // <= FOUND

['93']

93:         require(c / a == b, "SafeMath: multiplication overflow"); // <= FOUND

['126']

126:         
127:         require(b > 0, errorMessage); // <= FOUND

['160']

160:         require(b != 0, errorMessage); // <= FOUND

['73']

73:         require(delay_ >= MINIMUM_DELAY, "Timelock::constructor: Delay must exceed minimum delay."); // <= FOUND

['74']

74:         require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay."); // <= FOUND

['91']

91:         require(msg.sender == address(this), "Timelock::setDelay: Call must come from Timelock."); // <= FOUND

['88']

88:         require(delay_ >= MINIMUM_DELAY, "Timelock::setDelay: Delay must exceed minimum delay."); // <= FOUND

['128']

128:         require(msg.sender == pendingAdmin, "Timelock::acceptAdmin: Call must come from pendingAdmin."); // <= FOUND

['141']

141:         require(msg.sender == address(this), "Timelock::setPendingAdmin: Call must come from Timelock."); // <= FOUND

['166']

166:         require(msg.sender == admin, "Timelock::queueTransaction: Call must come from admin."); // <= FOUND

['134']

134:         require( // <= FOUND
135:             eta >= getBlockTimestamp().add(delay),
136:             "Timelock::queueTransaction: Estimated execution block must satisfy delay."
137:         ); // <= FOUND

['197']

197:         require(msg.sender == admin, "Timelock::cancelTransaction: Call must come from admin."); // <= FOUND

['224']

224:         require(msg.sender == admin, "Timelock::executeTransaction: Call must come from admin."); // <= FOUND

['227']

227:         require(queuedTransactions[txHash], "Timelock::executeTransaction: Transaction hasn't been queued."); // <= FOUND

['228']

228:         require(getBlockTimestamp() >= eta, "Timelock::executeTransaction: Transaction hasn't surpassed time lock."); // <= FOUND

['189']

189:         require(getBlockTimestamp() <= eta.add(GRACE_PERIOD), "Timelock::executeTransaction: Transaction is stale."); // <= FOUND

['243']

243:         require(success, "Timelock::executeTransaction: Transaction execution reverted."); // <= FOUND

['74']

74:         require(delay_ >= MINIMUM_DELAY(), "Timelock::constructor: Delay must exceed minimum delay."); // <= FOUND

['75']

75:         require(delay_ <= MAXIMUM_DELAY(), "Timelock::setDelay: Delay must not exceed maximum delay."); // <= FOUND

['92']

92:         require(delay_ >= MINIMUM_DELAY(), "Timelock::setDelay: Delay must exceed minimum delay."); // <= FOUND

['167']

167:         require( // <= FOUND
168:             eta >= getBlockTimestamp() + delay,
169:             "Timelock::queueTransaction: Estimated execution block must satisfy delay."
170:         ); // <= FOUND

['173']

173:         require(!queuedTransactions[txHash], "Timelock::queueTransaction: transaction already queued."); // <= FOUND

['200']

200:         require(queuedTransactions[txHash], "Timelock::cancelTransaction: transaction is not queued yet."); // <= FOUND

['229']

229:         require(getBlockTimestamp() <= eta + GRACE_PERIOD(), "Timelock::executeTransaction: Transaction is stale."); // <= FOUND

[Gas-32] Refactor event to avoid emitting empty data

Resolution

Emitting events with empty data in a contract is an inefficient use of gas in Solidity. Such emissions not only consume unnecessary gas but also clutter the logs with irrelevant information. A smart approach would be to refactor the events to only emit when there is meaningful data to be logged. This can be achieved by putting conditional statements to check the relevance of the data before emitting the event. By avoiding the emission of empty data, the contract becomes more gas-efficient, and the logs remain clean and focused on essential information, thereby aiding in debugging and monitoring.

Num of instances: 4

Findings

Click to show findings

['319']

319:     function castVote(uint proposalId, uint8 support) external { // <= FOUND
320:         emit VoteCast(msg.sender, proposalId, support, castVoteInternal(msg.sender, proposalId, support), ""); // <= FOUND
321:     }

['319']

319:     function castVote(uint proposalId, uint8 support) external { // <= FOUND
320:         emit VoteCast(msg.sender, proposalId, support, castVoteInternal(msg.sender, proposalId, support), ""); // <= FOUND
321:     }

['337']

337:     function castVoteBySig(uint proposalId, uint8 support, uint8 v, bytes32 r, bytes32 s) external { // <= FOUND
338:         bytes32 domainSeparator = keccak256(
339:             abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainIdInternal(), address(this))
340:         );
341:         bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));
342:         bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
343:         address signatory = ecrecover(digest, v, r, s);
344:         require(signatory != address(0), "GovernorBravo::castVoteBySig: invalid signature");
345:         emit VoteCast(signatory, proposalId, support, castVoteInternal(signatory, proposalId, support), ""); // <= FOUND
346:     }

['337']

337:     function castVoteBySig(uint proposalId, uint8 support, uint8 v, bytes32 r, bytes32 s) external { // <= FOUND
338:         bytes32 domainSeparator = keccak256(
339:             abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainIdInternal(), address(this))
340:         );
341:         bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));
342:         bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
343:         address signatory = ecrecover(digest, v, r, s);
344:         require(signatory != address(0), "GovernorBravo::castVoteBySig: invalid signature");
345:         emit VoteCast(signatory, proposalId, support, castVoteInternal(signatory, proposalId, support), ""); // <= FOUND
346:     }

[Gas-33] Avoid indexing dynamic types

Resolution

Indexing dynamic types in Ethereum events can be problematic because the indexed data is stored in topics, which have a size limit. If the dynamic data exceeds this limit, the contract will run out of gas. Dynamic types like strings or arrays can vary in length, making them unpredictable in size when indexed. Resolution: Use static-sized data types or hashes of dynamic data as indexed event parameters. For example, instead of indexing a string directly, use its keccak256 hash. This approach ensures that the indexed data size is constant, preventing potential out-of-gas errors.

Num of instances: 2

Findings

Click to show findings

['112']

112: event ReceivePayloadFailed(uint16 indexed srcChainId, bytes indexed srcAddress, uint64 nonce, bytes reason); // <= FOUND

['30']

30: event FunctionRegistryChanged(string indexed signature, bool active); // <= FOUND

[Gas-34] Where a value is casted more than once, consider caching the result to save gas

Resolution

Casting values multiple times in Solidity can be gas-inefficient. When a value undergoes repeated type conversions, the EVM must execute additional operations for each cast, consuming more gas than necessary. To optimize for gas efficiency, cache the result of the initial cast in a local variable and reuse it, rather than performing multiple casts. This not only conserves gas but also enhances code readability, reducing potential error points. For example, instead of repeatedly casting an address to uint256, cast once, store the result in a local variable, and reference that variable in subsequent operations.

Num of instances: 3

Findings

Click to show findings

['181']

181:     function propose(
182:         address[] memory targets,
183:         uint[] memory values,
184:         string[] memory signatures,
185:         bytes[] memory calldatas,
186:         string memory description,
187:         ProposalType proposalType
188:     ) public returns (uint) {
189:         
190:         require(initialProposalId != 0, "GovernorBravo::propose: Governor Bravo not active");
191:         require(
192:             xvsVault.getPriorVotes(msg.sender, sub256(block.number, 1)) >=
193:                 proposalConfigs[uint8(proposalType)].proposalThreshold, // <= FOUND
194:             "GovernorBravo::propose: proposer votes below proposal threshold"
195:         );
196:         require(
197:             targets.length == values.length &&
198:                 targets.length == signatures.length &&
199:                 targets.length == calldatas.length,
200:             "GovernorBravo::propose: proposal function information arity mismatch"
201:         );
202:         require(targets.length != 0, "GovernorBravo::propose: must provide actions");
203:         require(targets.length <= proposalMaxOperations, "GovernorBravo::propose: too many actions");
204: 
205:         uint latestProposalId = latestProposalIds[msg.sender];
206:         if (latestProposalId != 0) {
207:             ProposalState proposersLatestProposalState = state(latestProposalId);
208:             require(
209:                 proposersLatestProposalState != ProposalState.Active,
210:                 "GovernorBravo::propose: one live proposal per proposer, found an already active proposal"
211:             );
212:             require(
213:                 proposersLatestProposalState != ProposalState.Pending,
214:                 "GovernorBravo::propose: one live proposal per proposer, found an already pending proposal"
215:             );
216:         }
217: 
218:         uint startBlock = add256(block.number, proposalConfigs[uint8(proposalType)].votingDelay); // <= FOUND
219:         uint endBlock = add256(startBlock, proposalConfigs[uint8(proposalType)].votingPeriod); // <= FOUND
220: 
221:         proposalCount++;
222:         Proposal memory newProposal = Proposal({
223:             id: proposalCount,
224:             proposer: msg.sender,
225:             eta: 0,
226:             targets: targets,
227:             values: values,
228:             signatures: signatures,
229:             calldatas: calldatas,
230:             startBlock: startBlock,
231:             endBlock: endBlock,
232:             forVotes: 0,
233:             againstVotes: 0,
234:             abstainVotes: 0,
235:             canceled: false,
236:             executed: false,
237:             proposalType: uint8(proposalType) // <= FOUND
238:         });
239: 
240:         proposals[newProposal.id] = newProposal;
241:         latestProposalIds[newProposal.proposer] = newProposal.id;
242: 
243:         emit ProposalCreated(
244:             newProposal.id,
245:             msg.sender,
246:             targets,
247:             values,
248:             signatures,
249:             calldatas,
250:             startBlock,
251:             endBlock,
252:             description,
253:             uint8(proposalType) // <= FOUND
254:         );
255:         return newProposal.id;
256:     }

['177']

177:     function executeTransaction(
178:         address target,
179:         uint value,
180:         string memory signature,
181:         bytes memory data,
182:         uint eta
183:     ) public payable returns (bytes memory) {
184:         require(msg.sender == admin, "Timelock::executeTransaction: Call must come from admin.");
185: 
186:         bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
187:         require(queuedTransactions[txHash], "Timelock::executeTransaction: Transaction hasn't been queued.");
188:         require(getBlockTimestamp() >= eta, "Timelock::executeTransaction: Transaction hasn't surpassed time lock.");
189:         require(getBlockTimestamp() <= eta.add(GRACE_PERIOD), "Timelock::executeTransaction: Transaction is stale.");
190: 
191:         queuedTransactions[txHash] = false;
192: 
193:         bytes memory callData;
194: 
195:         if (bytes(signature).length == 0) { // <= FOUND
196:             callData = data;
197:         } else {
198:             callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data); // <= FOUND
199:         }
200: 
201:         
202:         (bool success, bytes memory returnData) = target.call.value(value)(callData);
203:         require(success, "Timelock::executeTransaction: Transaction execution reverted.");
204: 
205:         emit ExecuteTransaction(txHash, target, value, signature, data, eta);
206: 
207:         return returnData;
208:     }

['217']

217:     function executeTransaction(
218:         address target,
219:         uint256 value,
220:         string calldata signature,
221:         bytes calldata data,
222:         uint256 eta
223:     ) public payable returns (bytes memory) {
224:         require(msg.sender == admin, "Timelock::executeTransaction: Call must come from admin.");
225: 
226:         bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
227:         require(queuedTransactions[txHash], "Timelock::executeTransaction: Transaction hasn't been queued.");
228:         require(getBlockTimestamp() >= eta, "Timelock::executeTransaction: Transaction hasn't surpassed time lock.");
229:         require(getBlockTimestamp() <= eta + GRACE_PERIOD(), "Timelock::executeTransaction: Transaction is stale.");
230: 
231:         delete (queuedTransactions[txHash]);
232: 
233:         bytes memory callData;
234: 
235:         if (bytes(signature).length == 0) { // <= FOUND
236:             callData = data;
237:         } else {
238:             callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data); // <= FOUND
239:         }
240: 
241:         
242:         (bool success, bytes memory returnData) = target.call{ value: value }(callData);
243:         require(success, "Timelock::executeTransaction: Transaction execution reverted.");
244: 
245:         emit ExecuteTransaction(txHash, target, value, signature, data, eta);
246: 
247:         return returnData;
248:     }

[Gas-35] The following mappings can be replaced with a bit mask

Resolution

Bit masks and mappings are data management techniques in programming. Bit masks are efficient for storing boolean flags within an integer's limits, using bitwise operations, but can be error-prone. Mappings, common in Solidity, offer key-value storage with quick lookups but can't directly iterate over keys and can't differentiate unset keys from default values. While bit masks are space-efficient for limited boolean data, mappings are versatile for diverse datasets. Choosing between them hinges on the specific needs of the application.

Num of instances: 1

Findings

Click to show findings

['85']

85:     mapping(uint256 => bool) public queued; // <= FOUND

[Gas-36] Unnecessary casting as variable is already of the same type

Resolution

Unnecessary casting of a variable to the same type is redundant and can contribute to gas inefficiency and code clutter. This situation commonly arises when developers, perhaps due to oversight or misunderstanding, explicitly cast a variable to its existing type. For example, casting a uint256 variable to uint256 again does not change its type or value but adds unnecessary operations to the code.

Resolution: Developers should scrutinize their code to identify and remove any unnecessary type casting. Utilizing linters or static analysis tools can aid in detecting such redundancies. Ensuring that the code is clean and efficient not only saves gas but also enhances readability and maintainability.

Num of instances: 2

Findings

Click to show findings

['37']

37:     function _setAccessControlManager(address accessControlManager_) internal {
38:         require(address(accessControlManager_) != address(0), "invalid acess control manager address"); // <= FOUND
39:         address oldAccessControlManager = address(_accessControlManager);
40:         _accessControlManager = IAccessControlManagerV5(accessControlManager_);
41:         emit NewAccessControlManager(oldAccessControlManager, accessControlManager_);
42:     }

['63']

63:     function _setAccessControlManager(address accessControlManager_) internal {
64:         require(address(accessControlManager_) != address(0), "invalid acess control manager address"); // <= FOUND
65:         address oldAccessControlManager = address(_accessControlManager);
66:         _accessControlManager = IAccessControlManagerV8(accessControlManager_);
67:         emit NewAccessControlManager(oldAccessControlManager, accessControlManager_);
68:     }

[Gas-37] Simple checks for zero uint can be done using assembly to save gas

Resolution

Using assembly for simple zero checks on unsigned integers can save gas due to lower-level, optimized operations.

Resolution: Implement inline assembly with Solidity's assembly block to perform zero checks. Ensure thorough testing and verification, as assembly lacks the safety checks of high-level Solidity, potentially introducing vulnerabilities if not used carefully.

Num of instances: 13

Findings

Click to show findings

['447']

447:         require(initialProposalId == 0, "GovernorBravo::_initiate: can only initiate once"); // <= FOUND

['305']

305:         require(proposals[pId].id == 0, "OmnichainGovernanceExecutor::_nonblockingLzReceive: duplicate proposal"); // <= FOUND

['160']

160:         require(targets.length != 0, "GovernorAlpha::propose: must provide actions"); // <= FOUND

['103']

103:         
104:         require(initialProposalId != 0, "GovernorBravo::propose: Governor Bravo not active"); // <= FOUND

['114']

114:         require(targets.length != 0, "GovernorBravo::propose: must provide actions"); // <= FOUND

['56']

56:         require(bytes(fun).length != 0, "Function not found"); // <= FOUND

['133']

133:         require(payload_.length != 0, "OmnichainProposalSender: Empty payload"); // <= FOUND

['136']

136:         require(trustedRemote.length != 0, "OmnichainProposalSender: destination chain is not a trusted source"); // <= FOUND

['251']

251:         require(remoteChainId_ != 0, "ChainId must not be zero"); // <= FOUND

['160']

160:         require(b != 0, errorMessage); // <= FOUND

['132']

132:         
133:         require(msg.value > 0, "OmnichainProposalSender: value cannot be zero"); // <= FOUND

['223']

223:         require(originalValue_ > 0, "OmnichainProposalSender: invalid native amount"); // <= FOUND

['126']

126:         
127:         require(b > 0, errorMessage); // <= FOUND

[Gas-38] Using nested if to save gas

Resolution

Using nested if statements instead of logical AND (&&) operators can potentially save gas in Solidity contracts. When a series of conditions are connected with &&, all conditions must be evaluated even if the first one fails. In contrast, nested if statements allow for short-circuiting; if the first condition fails, the rest are skipped, saving gas. This approach is more gas-efficient, especially when dealing with complex or gas-intensive conditions. However, it's crucial to balance gas savings with code readability and maintainability, ensuring that the code remains clear and easy to understand.

Num of instances: 2

Findings

Click to show findings

['75']

75:             if (active_[i] && signature.length == 0) { // <= FOUND

['78']

78:             } else if (!active_[i] && signature.length != 0) { // <= FOUND

[Gas-39] Consider splitting complex if statements into multiple steps

Resolution

Splitting complex checks into multiple steps in smart contracts can enhance readability and potentially save gas. Complex conditions often require more computational power, increasing gas costs. By breaking down these conditions into simpler, individual checks, some computations can be avoided, especially if earlier checks fail, leveraging short-circuit evaluation in logical operations. This approach not only makes the code easier to understand and maintain but can also lead to more efficient execution, as unnecessary computations are minimized. Structuring contracts to evaluate conditions in a step-by-step manner can lead to significant gas savings and improved contract performance.

Num of instances: 4

Findings

Click to show findings

['303']

303:         } else if (proposal.forVotes <= proposal.againstVotes || proposal.forVotes < quorumVotes()) { // <= FOUND

['301']

301:         } else if (proposal.forVotes <= proposal.againstVotes || proposal.forVotes < quorumVotes) { // <= FOUND

['75']

75:             if (active_[i] && signature.length == 0) { // <= FOUND

['78']

78:             } else if (!active_[i] && signature.length != 0) { // <= FOUND

[Gas-40] Stack variable cost less than state variables while used in emiting event

Resolution

When emitting events in Solidity, using stack variables (local variables within a function) instead of state variables can lead to significant gas savings. Stack variables reside in memory only for the duration of the function execution and are less costly to access compared to state variables, which are stored on the blockchain. When an event is emitted, accessing these stack variables requires less gas than fetching data from state variables, which involves reading from the contract's storage - a more expensive operation. Thus, for efficiency, prefer using local variables within functions for event emission, especially in functions that are called frequently.

Num of instances: 14

Findings

Click to show findings

['506']

506:         emit NewAdmin(oldAdmin, admin); // <= FOUND

['507']

507:         emit NewPendingAdmin(oldPendingAdmin, pendingAdmin); // <= FOUND

['57']

57:         emit NewImplementation(oldImplementation, implementation); // <= FOUND

['404']

404:         emit VotingDelaySet(oldVotingDelay, votingDelay); // <= FOUND

['420']

420:         emit VotingPeriodSet(oldVotingPeriod, votingPeriod); // <= FOUND

['437']

437:         emit ProposalThresholdSet(oldProposalThreshold, proposalThreshold); // <= FOUND

['95']

95:         emit NewAccessControlManager(accessControlManager, accessControlManager_); // <= FOUND

['129']

129:         emit NewAdmin(admin, msg.sender); // <= FOUND

['147']

147:         emit SetSrcChainId(srcChainId, srcChainId_); // <= FOUND

['94']

94:         emit NewDelay(delay, delay_); // <= FOUND

['49']

49:         emit SetMaxDailyReceiveLimit(maxDailyReceiveLimit, limit_); // <= FOUND

['103']

103:         emit NewAdmin(admin); // <= FOUND

['145']

145:         emit NewPendingAdmin(pendingAdmin); // <= FOUND

['92']

92:         emit NewDelay(delay); // <= FOUND

[Gas-41] Stack variable cost less than mappings while used in emiting event

Resolution

When emitting events in Solidity, using stack variables (local variables within a function) instead of mappings can lead to significant gas savings. Stack variables reside in memory only for the duration of the function execution and are less costly to access compared to mappings, which are stored on the blockchain. When an event is emitted, accessing these stack variables requires less gas than fetching data from mappings, which involves reading from the contract's storage - a more expensive operation. Thus, for efficiency, prefer using local variables within functions for event emission, especially in functions that are called frequently.

Num of instances: 3

Findings

Click to show findings

['65']

65:         emit SetMaxDailyLimit(chainId_, chainIdToMaxDailyLimit[chainId_], limit_); // <= FOUND

['165']

165:             emit TimelockAdded(i, address(proposalTimelocks[i]), address(timelocks_[i])); // <= FOUND

['255']

255:         emit SetTrustedRemoteAddress(remoteChainId_, oldRemoteAddress, trustedRemoteLookup[remoteChainId_]); // <= FOUND

[Gas-42] Avoid emitting event on every iteration

Resolution

Emitting events within a loop can cause significant gas consumption due to repeated I/O operations. Instead, accumulate changes in memory and emit a single event post-loop with aggregated data. This approach improves contract efficiency, reduces gas costs, and simplifies event tracking for event listeners.

Num of instances: 1

Findings

Click to show findings

['165']

163:        for (uint8 i; i < length; ) {
164:             ensureNonzeroAddress(address(timelocks_[i]));
165:             emit TimelockAdded(i, address(proposalTimelocks[i]), address(timelocks_[i])); // <= FOUND
166:             proposalTimelocks[i] = timelocks_[i];
167:             unchecked {
168:                 ++i;
169:             }
170:         }

[Gas-43] Low level call can be optimized with assembly

Resolution

Optimizing low-level calls using assembly in Solidity can be beneficial, particularly when dealing with function return data. Typically, even if return data from a low-level call is not used, Solidity still allocates memory to store it, which incurs gas costs. By using assembly, developers can bypass the automatic memory allocation for unused return data. This manual optimization involves handling the call at the assembly level and deliberately choosing not to store the return data in memory when it's not needed.

Num of instances: 7

Findings

Click to show findings

['239']

238:         
239:         (bool sent, ) = to_.call{ value: originalValue_ }(""); // <= FOUND

['243']

242:         
243:         (bool success, bytes memory returnData) = target.call{ value: value }(callData); // <= FOUND

['58']

58:         (bool ok, bytes memory res) = address(OMNICHAIN_GOVERNANCE_EXECUTOR).call(data_); // <= FOUND

['238']

238:         
239:         (bool sent, ) = to_.call{ value: originalValue_ }(""); // <= FOUND

['242']

242:         
243:         (bool success, bytes memory returnData) = target.call{ value: value }(callData); // <= FOUND

['58']

58:         (bool ok, bytes memory res) = address(OMNICHAIN_GOVERNANCE_EXECUTOR).call(data_); // <= FOUND

['238']

238:         
239:         (bool sent, ) = to_.call{ value: originalValue_ }(""); // <= FOUND

[Gas-44] Constants are cheaper than Enums

Resolution

Using constants instead of enums in Solidity can lead to gas savings. Constants, particularly for primitive data types like uint or bool, are directly substituted by the Solidity compiler at compile time, effectively hardcoding their values into the bytecode. This means that accessing a constant does not incur any runtime gas costs, as no storage or memory access is needed. In contrast, enums, while useful for readability and ensuring valid value ranges, are stored in contract storage or memory, incurring gas costs for reads and writes. For scenarios where an enum's value range aligns with a simple constant (like true/false or small integer ranges), substituting with constants can be a more gas-efficient choice.

Num of instances: 4

Findings

Click to show findings

['87']

87:     enum ProposalState { // <= FOUND
88:         Pending,
89:         Active,
90:         Canceled,
91:         Defeated,
92:         Succeeded,
93:         Queued,
94:         Expired,
95:         Executed
96:     }

['23']

23:     enum ProposalType { // <= FOUND
24:         NORMAL,
25:         FASTTRACK,
26:         CRITICAL
27:     }

['23']

23:     enum ProposalType { // <= FOUND
24:         NORMAL,
25:         FASTTRACK,
26:         CRITICAL
27:     }

['51']

51:     enum ProposalState { // <= FOUND
52:         Canceled,
53:         Queued,
54:         Executed
55:     }

[Gas-45] ++X costs slightly less gas than X++ (same with --)

Resolution

Move the ++/-- action to the left of the variable

Num of instances: 2

Findings

Click to show findings

['220']

220:         for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND

['179']

179:         proposalCount++; // <= FOUND

[Gas-46] Solidity versions 0.8.19 and above are more gas efficient

Resolution

Solidity version 0.8.19 introduced a array of gas optimizations which make contracts which use it more efficient. Provided compatability it may be beneficial to upgrade to this version

Num of instances: 3

Findings

Click to show findings

['2']

2: pragma solidity 0.5.16;

['2']

2: pragma solidity 0.8.13;

['1']

1: pragma solidity ^0.5.16;

[Gas-47] Variable declared within iteration

Resolution

Please elaborate and generalise the following with detail and feel free to use your own knowledge and lmit ur words to 100 words please:

Num of instances: 1

Findings

Click to show findings

['72']

72:        for (uint256 i; i < signatureLength; ) { // <= FOUND
73:             bytes4 sigHash = bytes4(keccak256(bytes(signatures_[i]))); // <= FOUND
74:             bytes memory signature = bytes(functionRegistry[sigHash]); // <= FOUND
75:             if (active_[i] && signature.length == 0) {
76:                 functionRegistry[sigHash] = signatures_[i];
77:                 emit FunctionRegistryChanged(signatures_[i], true);
78:             } else if (!active_[i] && signature.length != 0) {
79:                 delete functionRegistry[sigHash];
80:                 emit FunctionRegistryChanged(signatures_[i], false);
81:             }
82:             unchecked {
83:                 ++i;
84:             }
85:         }

[Gas-48] Calling .length in a for loop wastes gas

Resolution

Rather than calling .length for an array in a for loop declaration, it is far more gas efficient to cache this length before and use that instead. This will prevent the array length from being called every loop iteration

Num of instances: 8

Findings

Click to show findings

['220']

220: for (uint i = 0; i < proposal.targets.length; i++)  // <= FOUND

['220']

220: for (uint i = 0; i < proposal.targets.length; i++)  // <= FOUND

['220']

220: for (uint i = 0; i < proposal.targets.length; i++)  // <= FOUND

['269']

269: for (uint i; i < proposal.targets.length; ++i)  // <= FOUND

['269']

269: for (uint i; i < proposal.targets.length; ++i)  // <= FOUND

['220']

220: for (uint i = 0; i < proposal.targets.length; i++)  // <= FOUND

['220']

220: for (uint i = 0; i < proposal.targets.length; i++)  // <= FOUND

['220']

220: for (uint i = 0; i < proposal.targets.length; i++)  // <= FOUND

[Gas-49] Internal functions only used once can be inlined to save gas

Resolution

If a internal function is only used once it doesn't make sense to modularise it unless the function which does call the function would be overly long and complex otherwise

Num of instances: 8

Findings

Click to show findings

['48']

48:     function _checkAccessAllowed(string memory signature) internal view  // <= FOUND

['48']

48:     function _checkAccessAllowed(string memory signature) internal view  // <= FOUND

['37']

37:     function __AccessControlled_init_unchained(address accessControlManager_) internal onlyInitializing  // <= FOUND

['78']

78:     function _isEligibleToReceive(uint256 noOfCommands_) internal  // <= FOUND

['109']

109:     function _isEligibleToSend(uint16 dstChainId_, uint256 noOfCommands_) internal  // <= FOUND

['268']

268:     function _blockingLzReceive( // <= FOUND
269:         uint16 srcChainId_,
270:         bytes memory srcAddress_,
271:         uint64 nonce_,
272:         bytes memory payload_
273:     ) internal virtual override whenNotPaused 

['376']

376:     function _queueOrRevertInternal( // <= FOUND
377:         address target_,
378:         uint256 value_,
379:         string memory signature_,
380:         bytes memory data_,
381:         uint256 eta_,
382:         uint8 proposalType_
383:     ) internal 

['84']

84:     function mul(uint256 a, uint256 b) internal pure returns (uint256)  // <= FOUND

[Gas-50] Constructors can be marked as payable to save deployment gas

Num of instances: 12

Findings

Click to show findings

['61']

61:     constructor() {
62:         
63:         
64:         _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
65:     }

['38']

38:     constructor(address endpoint_) NonblockingLzApp(endpoint_) {
39:         ensureNonzeroAddress(endpoint_);
40:     }

['51']

51:     constructor(address accessControlManager_) {
52:         ensureNonzeroAddress(accessControlManager_);
53:         accessControlManager = accessControlManager_;
54:     }

['12']

12:     constructor(
13:         address timelock_,
14:         address xvsVault_,
15:         address admin_,
16:         address implementation_,
17:         uint votingPeriod_,
18:         uint votingDelay_,
19:         uint proposalThreshold_,
20:         address guardian_
21:     ) public {
22:         
23:         admin = msg.sender;
24: 
25:         delegateTo(
26:             implementation_,
27:             abi.encodeWithSignature(
28:                 "initialize(address,address,uint256,uint256,uint256,address)",
29:                 timelock_,
30:                 xvsVault_,
31:                 votingPeriod_,
32:                 votingDelay_,
33:                 proposalThreshold_,
34:                 guardian_
35:             )
36:         );
37: 
38:         _setImplementation(implementation_);
39: 
40:         admin = admin_;
41:     }

['136']

136:     constructor(address timelock_, address xvs_, address guardian_) public {
137:         timelock = TimelockInterface(timelock_);
138:         xvs = XVSInterface(xvs_);
139:         guardian = guardian_;
140:     }

['136']

136:     constructor(address timelock_, address xvs_, address guardian_, uint256 lastProposalId_) public {
137:         timelock = TimelockInterface(timelock_);
138:         xvs = XVSInterface(xvs_);
139:         guardian = guardian_;
140:         proposalCount = lastProposalId_;
141:     }

['12']

12:     constructor(
13:         address timelock_,
14:         address xvsVault_,
15:         address admin_,
16:         address implementation_,
17:         uint votingPeriod_,
18:         uint votingDelay_,
19:         uint proposalThreshold_,
20:         address guardian_
21:     ) public {
22:         
23:         admin = msg.sender;
24: 
25:         delegateTo(
26:             implementation_,
27:             abi.encodeWithSignature(
28:                 "initialize(address,address,uint256,uint256,uint256,address)",
29:                 timelock_,
30:                 xvsVault_,
31:                 votingPeriod_,
32:                 votingDelay_,
33:                 proposalThreshold_,
34:                 guardian_
35:             )
36:         );
37: 
38:         _setImplementation(implementation_);
39: 
40:         admin = admin_;
41:     }

['33']

33:     constructor(address omnichainGovernanceExecutor_) {
34:         require(omnichainGovernanceExecutor_ != address(0), "Address must not be zero");
35:         OMNICHAIN_GOVERNANCE_EXECUTOR = IOmnichainGovernanceExecutor(omnichainGovernanceExecutor_);
36:         _disableInitializers();
37:     }

['134']

134:     constructor(address endpoint_, address guardian_, uint16 srcChainId_) BaseOmnichainControllerDest(endpoint_) {
135:         ensureNonzeroAddress(guardian_);
136:         GUARDIAN = guardian_;
137:         srcChainId = srcChainId_;
138:     }

['77']

77:     constructor(
78:         ILayerZeroEndpoint lzEndpoint_,
79:         address accessControlManager_
80:     ) BaseOmnichainControllerSrc(accessControlManager_) {
81:         ensureNonzeroAddress(address(lzEndpoint_));
82:         LZ_ENDPOINT = lzEndpoint_;
83:     }

['72']

72:     constructor(address admin_, uint delay_) public {
73:         require(delay_ >= MINIMUM_DELAY, "Timelock::constructor: Delay must exceed minimum delay.");
74:         require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay.");
75: 
76:         admin = admin_;
77:         delay = delay_;
78:     }

['73']

73:     constructor(address admin_, uint256 delay_) {
74:         require(delay_ >= MINIMUM_DELAY(), "Timelock::constructor: Delay must exceed minimum delay.");
75:         require(delay_ <= MAXIMUM_DELAY(), "Timelock::setDelay: Delay must not exceed maximum delay.");
76:         ensureNonzeroAddress(admin_);
77: 
78:         admin = admin_;
79:         delay = delay_;
80:     }

[Gas-51] Use assembly scratch space to build calldata for external calls

Resolution

Using Solidity's assembly scratch space for constructing calldata in external calls with one or two arguments can be a gas-efficient approach. This method leverages the designated memory area (the first 64 bytes of memory) for temporary data storage during assembly operations. By directly writing arguments into this scratch space, it eliminates the need for additional memory allocation typically required for calldata preparation. This technique can lead to notable gas savings, especially in high-frequency or gas-sensitive operations. However, it requires careful implementation to avoid data corruption and should be used with a thorough understanding of low-level EVM operations and memory handling. Proper testing and validation are crucial when employing such optimizations.

Num of instances: 3

Findings

Click to show findings

['58']

58:         (bool ok, bytes memory res) = address(OMNICHAIN_GOVERNANCE_EXECUTOR).call(data_); // <= FOUND

['67']

67:         (bool success, bytes memory returnData) = callee.delegatecall(data); // <= FOUND

['82']

82:         
83:         (bool success, ) = implementation.delegatecall(msg.data); // <= FOUND

[Gas-52] Use assembly scratch space to build calldata for event emits

Resolution

Utilizing Solidity's assembly scratch space to build calldata for emitting events with just one or two arguments can optimize gas usage. The scratch space, a designated area in the first 64 bytes of memory, is ideal for temporary storage during assembly-level operations. By directly writing the event arguments into this area, developers can bypass the standard memory allocation process required for event emission. This approach results in gas savings, particularly for contracts where events are frequently emitted. However, such low-level optimization requires a deep understanding of Ethereum Virtual Machine (EVM) mechanics and careful coding to prevent data mishandling. Rigorous testing is essential to ensure the integrity and correct functionality of the contract.

Num of instances: 29

Findings

Click to show findings

['251']

251:         emit ProposalExecuted(proposalId); // <= FOUND

['276']

276:         emit ProposalCanceled(proposalId); // <= FOUND

['190']

190:         emit ProposalExecuted(proposalId_); // <= FOUND

['225']

225:         emit ProposalCanceled(proposalId_); // <= FOUND

['111']

111:         emit TrustedRemoteRemoved(remoteChainId_); // <= FOUND

['92']

92:         emit NewDelay(delay); // <= FOUND

['103']

103:         emit NewAdmin(admin); // <= FOUND

['145']

145:         emit NewPendingAdmin(pendingAdmin); // <= FOUND

['41']

41:         emit NewAccessControlManager(oldAccessControlManager, accessControlManager_); // <= FOUND

['49']

49:         emit SetMaxDailyReceiveLimit(maxDailyReceiveLimit, limit_); // <= FOUND

['95']

95:         emit NewAccessControlManager(accessControlManager, accessControlManager_); // <= FOUND

['57']

57:         emit NewImplementation(oldImplementation, implementation); // <= FOUND

['224']

224:         emit ProposalQueued(proposalId, eta); // <= FOUND

['388']

388:         emit NewGuardian(oldGuardian, newGuardian); // <= FOUND

['463']

463:         emit ProposalMaxOperationsUpdated(oldProposalMaxOperations, proposalMaxOperations_); // <= FOUND

['482']

482:         
483:         emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin); // <= FOUND

['506']

506:         emit NewAdmin(oldAdmin, admin); // <= FOUND

['507']

507:         emit NewPendingAdmin(oldPendingAdmin, pendingAdmin); // <= FOUND

['404']

404:         emit VotingDelaySet(oldVotingDelay, votingDelay); // <= FOUND

['420']

420:         emit VotingPeriodSet(oldVotingPeriod, votingPeriod); // <= FOUND

['437']

437:         emit ProposalThresholdSet(oldProposalThreshold, proposalThreshold); // <= FOUND

['77']

77:                 emit FunctionRegistryChanged(signatures_[i], true); // <= FOUND

['80']

80:                 emit FunctionRegistryChanged(signatures_[i], false); // <= FOUND

['147']

147:         emit SetSrcChainId(srcChainId, srcChainId_); // <= FOUND

['350']

350:         emit ProposalQueued(proposalId_, eta); // <= FOUND

['190']

190:         emit ClearPayload(pId_, hash); // <= FOUND

['234']

234:         emit FallbackWithdraw(to_, originalValue_); // <= FOUND

['94']

94:         emit NewDelay(delay, delay_); // <= FOUND

['129']

129:         emit NewAdmin(admin, msg.sender); // <= FOUND

[Gas-53] Assigning to structs can be more efficient

Resolution

Rather defining the struct in a single line, it is more efficient to declare an empty struct and then assign each struct element individually. This can net quite a large gas saving of 130 per instance.

Num of instances: 5

Findings

Click to show findings

['143']

143:     function propose(
144:         address[] memory targets,
145:         uint[] memory values,
146:         string[] memory signatures,
147:         bytes[] memory calldatas,
148:         string memory description
149:     ) public returns (uint) {
150:         require(
151:             xvs.getPriorVotes(msg.sender, sub256(block.number, 1)) > proposalThreshold(),
152:             "GovernorAlpha::propose: proposer votes below proposal threshold"
153:         );
154:         require(
155:             targets.length == values.length &&
156:                 targets.length == signatures.length &&
157:                 targets.length == calldatas.length,
158:             "GovernorAlpha::propose: proposal function information arity mismatch"
159:         );
160:         require(targets.length != 0, "GovernorAlpha::propose: must provide actions");
161:         require(targets.length <= proposalMaxOperations(), "GovernorAlpha::propose: too many actions");
162: 
163:         uint latestProposalId = latestProposalIds[msg.sender];
164:         if (latestProposalId != 0) {
165:             ProposalState proposersLatestProposalState = state(latestProposalId);
166:             require(
167:                 proposersLatestProposalState != ProposalState.Active,
168:                 "GovernorAlpha::propose: found an already active proposal"
169:             );
170:             require(
171:                 proposersLatestProposalState != ProposalState.Pending,
172:                 "GovernorAlpha::propose: found an already pending proposal"
173:             );
174:         }
175: 
176:         uint startBlock = add256(block.number, votingDelay());
177:         uint endBlock = add256(startBlock, votingPeriod());
178: 
179:         proposalCount++;
180:         Proposal memory newProposal = Proposal({ // <= FOUND
181:             id: proposalCount,
182:             proposer: msg.sender,
183:             eta: 0,
184:             targets: targets,
185:             values: values,
186:             signatures: signatures,
187:             calldatas: calldatas,
188:             startBlock: startBlock,
189:             endBlock: endBlock,
190:             forVotes: 0,
191:             againstVotes: 0,
192:             canceled: false,
193:             executed: false
194:         });
195: 
196:         proposals[newProposal.id] = newProposal;
197:         latestProposalIds[newProposal.proposer] = newProposal.id;
198: 
199:         emit ProposalCreated(
200:             newProposal.id,
201:             msg.sender,
202:             targets,
203:             values,
204:             signatures,
205:             calldatas,
206:             startBlock,
207:             endBlock,
208:             description
209:         );
210:         return newProposal.id;
211:     }

['143']

143:     function propose(
144:         address[] memory targets,
145:         uint[] memory values,
146:         string[] memory signatures,
147:         bytes[] memory calldatas,
148:         string memory description
149:     ) public returns (uint) {
150:         require(
151:             xvs.getPriorVotes(msg.sender, sub256(block.number, 1)) > proposalThreshold(),
152:             "GovernorAlpha::propose: proposer votes below proposal threshold"
153:         );
154:         require(
155:             targets.length == values.length &&
156:                 targets.length == signatures.length &&
157:                 targets.length == calldatas.length,
158:             "GovernorAlpha::propose: proposal function information arity mismatch"
159:         );
160:         require(targets.length != 0, "GovernorAlpha::propose: must provide actions");
161:         require(targets.length <= proposalMaxOperations(), "GovernorAlpha::propose: too many actions");
162: 
163:         uint latestProposalId = latestProposalIds[msg.sender];
164:         if (latestProposalId != 0) {
165:             ProposalState proposersLatestProposalState = state(latestProposalId);
166:             require(
167:                 proposersLatestProposalState != ProposalState.Active,
168:                 "GovernorAlpha::propose: found an already active proposal"
169:             );
170:             require(
171:                 proposersLatestProposalState != ProposalState.Pending,
172:                 "GovernorAlpha::propose: found an already pending proposal"
173:             );
174:         }
175: 
176:         uint startBlock = add256(block.number, votingDelay());
177:         uint endBlock = add256(startBlock, votingPeriod());
178: 
179:         proposalCount++;
180:         Proposal memory newProposal = Proposal({ // <= FOUND
181:             id: proposalCount,
182:             proposer: msg.sender,
183:             eta: 0,
184:             targets: targets,
185:             values: values,
186:             signatures: signatures,
187:             calldatas: calldatas,
188:             startBlock: startBlock,
189:             endBlock: endBlock,
190:             forVotes: 0,
191:             againstVotes: 0,
192:             canceled: false,
193:             executed: false
194:         });
195: 
196:         proposals[newProposal.id] = newProposal;
197:         latestProposalIds[newProposal.proposer] = newProposal.id;
198: 
199:         emit ProposalCreated(
200:             newProposal.id,
201:             msg.sender,
202:             targets,
203:             values,
204:             signatures,
205:             calldatas,
206:             startBlock,
207:             endBlock,
208:             description
209:         );
210:         return newProposal.id;
211:     }

['95']

95:     function propose(
96:         address[] memory targets,
97:         uint[] memory values,
98:         string[] memory signatures,
99:         bytes[] memory calldatas,
100:         string memory description
101:     ) public returns (uint) {
102:         
103:         require(initialProposalId != 0, "GovernorBravo::propose: Governor Bravo not active");
104:         require(
105:             xvsVault.getPriorVotes(msg.sender, sub256(block.number, 1)) > proposalThreshold,
106:             "GovernorBravo::propose: proposer votes below proposal threshold"
107:         );
108:         require(
109:             targets.length == values.length &&
110:                 targets.length == signatures.length &&
111:                 targets.length == calldatas.length,
112:             "GovernorBravo::propose: proposal function information arity mismatch"
113:         );
114:         require(targets.length != 0, "GovernorBravo::propose: must provide actions");
115:         require(targets.length <= proposalMaxOperations, "GovernorBravo::propose: too many actions");
116: 
117:         uint latestProposalId = latestProposalIds[msg.sender];
118:         if (latestProposalId != 0) {
119:             ProposalState proposersLatestProposalState = state(latestProposalId);
120:             require(
121:                 proposersLatestProposalState != ProposalState.Active,
122:                 "GovernorBravo::propose: one live proposal per proposer, found an already active proposal"
123:             );
124:             require(
125:                 proposersLatestProposalState != ProposalState.Pending,
126:                 "GovernorBravo::propose: one live proposal per proposer, found an already pending proposal"
127:             );
128:         }
129: 
130:         uint startBlock = add256(block.number, votingDelay);
131:         uint endBlock = add256(startBlock, votingPeriod);
132: 
133:         proposalCount++;
134:         Proposal memory newProposal = Proposal({ // <= FOUND
135:             id: proposalCount,
136:             proposer: msg.sender,
137:             eta: 0,
138:             targets: targets,
139:             values: values,
140:             signatures: signatures,
141:             calldatas: calldatas,
142:             startBlock: startBlock,
143:             endBlock: endBlock,
144:             forVotes: 0,
145:             againstVotes: 0,
146:             abstainVotes: 0,
147:             canceled: false,
148:             executed: false
149:         });
150: 
151:         proposals[newProposal.id] = newProposal;
152:         latestProposalIds[newProposal.proposer] = newProposal.id;
153: 
154:         emit ProposalCreated(
155:             newProposal.id,
156:             msg.sender,
157:             targets,
158:             values,
159:             signatures,
160:             calldatas,
161:             startBlock,
162:             endBlock,
163:             description
164:         );
165:         return newProposal.id;
166:     }

['181']

181:     function propose(
182:         address[] memory targets,
183:         uint[] memory values,
184:         string[] memory signatures,
185:         bytes[] memory calldatas,
186:         string memory description,
187:         ProposalType proposalType
188:     ) public returns (uint) {
189:         
190:         require(initialProposalId != 0, "GovernorBravo::propose: Governor Bravo not active");
191:         require(
192:             xvsVault.getPriorVotes(msg.sender, sub256(block.number, 1)) >=
193:                 proposalConfigs[uint8(proposalType)].proposalThreshold,
194:             "GovernorBravo::propose: proposer votes below proposal threshold"
195:         );
196:         require(
197:             targets.length == values.length &&
198:                 targets.length == signatures.length &&
199:                 targets.length == calldatas.length,
200:             "GovernorBravo::propose: proposal function information arity mismatch"
201:         );
202:         require(targets.length != 0, "GovernorBravo::propose: must provide actions");
203:         require(targets.length <= proposalMaxOperations, "GovernorBravo::propose: too many actions");
204: 
205:         uint latestProposalId = latestProposalIds[msg.sender];
206:         if (latestProposalId != 0) {
207:             ProposalState proposersLatestProposalState = state(latestProposalId);
208:             require(
209:                 proposersLatestProposalState != ProposalState.Active,
210:                 "GovernorBravo::propose: one live proposal per proposer, found an already active proposal"
211:             );
212:             require(
213:                 proposersLatestProposalState != ProposalState.Pending,
214:                 "GovernorBravo::propose: one live proposal per proposer, found an already pending proposal"
215:             );
216:         }
217: 
218:         uint startBlock = add256(block.number, proposalConfigs[uint8(proposalType)].votingDelay);
219:         uint endBlock = add256(startBlock, proposalConfigs[uint8(proposalType)].votingPeriod);
220: 
221:         proposalCount++;
222:         Proposal memory newProposal = Proposal({ // <= FOUND
223:             id: proposalCount,
224:             proposer: msg.sender,
225:             eta: 0,
226:             targets: targets,
227:             values: values,
228:             signatures: signatures,
229:             calldatas: calldatas,
230:             startBlock: startBlock,
231:             endBlock: endBlock,
232:             forVotes: 0,
233:             againstVotes: 0,
234:             abstainVotes: 0,
235:             canceled: false,
236:             executed: false,
237:             proposalType: uint8(proposalType)
238:         });
239: 
240:         proposals[newProposal.id] = newProposal;
241:         latestProposalIds[newProposal.proposer] = newProposal.id;
242: 
243:         emit ProposalCreated(
244:             newProposal.id,
245:             msg.sender,
246:             targets,
247:             values,
248:             signatures,
249:             calldatas,
250:             startBlock,
251:             endBlock,
252:             description,
253:             uint8(proposalType)
254:         );
255:         return newProposal.id;
256:     }

['296']

296:     function _nonblockingLzReceive(uint16, bytes memory, uint64, bytes memory payload_) internal virtual override { // <= FOUND
297:         (bytes memory payload, uint256 pId) = abi.decode(payload_, (bytes, uint256));
298:         (
299:             address[] memory targets,
300:             uint256[] memory values,
301:             string[] memory signatures,
302:             bytes[] memory calldatas,
303:             uint8 pType
304:         ) = abi.decode(payload, (address[], uint256[], string[], bytes[], uint8));
305:         require(proposals[pId].id == 0, "OmnichainGovernanceExecutor::_nonblockingLzReceive: duplicate proposal");
306:         require(
307:             targets.length == values.length &&
308:                 targets.length == signatures.length &&
309:                 targets.length == calldatas.length,
310:             "OmnichainGovernanceExecutor::_nonblockingLzReceive: proposal function information arity mismatch"
311:         );
312:         require(
313:             pType < uint8(type(ProposalType).max) + 1,
314:             "OmnichainGovernanceExecutor::_nonblockingLzReceive: invalid proposal type"
315:         );
316:         _isEligibleToReceive(targets.length);
317: 
318:         Proposal memory newProposal = Proposal({ // <= FOUND
319:             id: pId,
320:             eta: 0,
321:             targets: targets,
322:             values: values,
323:             signatures: signatures,
324:             calldatas: calldatas,
325:             canceled: false,
326:             executed: false,
327:             proposalType: pType
328:         });
329: 
330:         proposals[pId] = newProposal;
331:         lastProposalReceived = pId;
332: 
333:         emit ProposalReceived(newProposal.id, targets, values, signatures, calldatas, pType);
334:         _queue(pId);
335:     }

[Gas-54] There are comparisons to boolean literals (true and false), these can be simplified to save gas

Resolution

In Solidity, gas optimization is crucial due to the cost associated with executing operations on the Ethereum network. Simplifying comparisons to boolean literals is one such optimization technique. Instead of explicitly comparing a boolean expression to true or false, you can use the expression directly or its negation. For example, replace if (x == true) with if (x) and if (x == false) with if (!x). This simplification reduces the bytecode size of the contract, thereby saving gas. It also enhances code readability and clarity.

Num of instances: 2

Findings

Click to show findings

['335']

335:         require(receipt.hasVoted == false, "GovernorAlpha::_castVote: voter already voted"); // <= FOUND

['360']

360:         require(receipt.hasVoted == false, "GovernorBravo::castVoteInternal: voter already voted"); // <= FOUND

[Gas-55] Only emit event in setter function if the state variable was changed

Resolution

Emitting events in setter functions of smart contracts only when state variables change saves gas. This is because emitting events consumes gas, and unnecessary events, where no actual state change occurs, lead to wasteful consumption.

Num of instances: 4

Findings

Click to show findings

['48']

48:     function setMaxDailyReceiveLimit(uint256 limit_) external onlyOwner { // <= FOUND
49:         emit SetMaxDailyReceiveLimit(maxDailyReceiveLimit, limit_); // <= FOUND
50:         maxDailyReceiveLimit = limit_;
51:     }

['63']

63:     function setMaxDailyLimit(uint16 chainId_, uint256 limit_) external { // <= FOUND
64:         _ensureAllowed("setMaxDailyLimit(uint16,uint256)");
65:         emit SetMaxDailyLimit(chainId_, chainIdToMaxDailyLimit[chainId_], limit_); // <= FOUND
66:         chainIdToMaxDailyLimit[chainId_] = limit_;
67:     }

['93']

93:     function setAccessControlManager(address accessControlManager_) external onlyOwner { // <= FOUND
94:         ensureNonzeroAddress(accessControlManager_);
95:         emit NewAccessControlManager(accessControlManager, accessControlManager_); // <= FOUND
96:         accessControlManager = accessControlManager_;
97:     }

['146']

146:     function setSrcChainId(uint16 srcChainId_) external onlyOwner { // <= FOUND
147:         emit SetSrcChainId(srcChainId, srcChainId_); // <= FOUND
148:         srcChainId = srcChainId_;
149:     }

[Gas-56] It is a waste of GAS to emit variable literals

Resolution

Emitting variable literals (true, false, 'hello', 1 etc...) in events is inefficient, as it consumes extra gas without providing added value. These literals are fixed values that can be accessed or hardcoded elsewhere in the smart contract or application, making their inclusion in events redundant and an unnecessary drain on resources during transaction execution.

Num of instances: 4

Findings

Click to show findings

['80']

80:                 emit FunctionRegistryChanged(signatures_[i], false); // <= FOUND

['77']

77:                 emit FunctionRegistryChanged(signatures_[i], true); // <= FOUND

['320']

320:         emit VoteCast(msg.sender, proposalId, support, castVoteInternal(msg.sender, proposalId, support), ""); // <= FOUND

['345']

345:         emit VoteCast(signatory, proposalId, support, castVoteInternal(signatory, proposalId, support), ""); // <= FOUND

[Gas-57] Use OZ Array.unsafeAccess() to avoid repeated array length checks

Resolution

The OpenZeppelin Array.unsafeAccess() method is a optimization strategy for Solidity, aimed at reducing gas costs by bypassing automatic length checks on storage array accesses. In Solidity, every access to an array element involves a hidden gas cost due to a length check, ensuring that accesses do not exceed the array bounds. However, if a developer has already verified the array's bounds earlier in the function or knows through logic that the access is safe, directly accessing the array elements without redundant length checks can save gas. This approach requires careful consideration to avoid out-of-bounds errors, as it trades off safety checks for efficiency.

Num of instances: 30

Findings

Click to show findings

['221']

221:             _queueOrRevert(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], eta); // <= FOUND

['243']

243:             timelock.executeTransaction.value(proposal.values[i])( // <= FOUND
244:                 proposal.targets[i], // <= FOUND
245:                 proposal.values[i], // <= FOUND
246:                 proposal.signatures[i], // <= FOUND
247:                 proposal.calldatas[i], // <= FOUND
248:                 proposal.eta
249:             );

['267']

267:             timelock.cancelTransaction(
268:                 proposal.targets[i], // <= FOUND
269:                 proposal.values[i], // <= FOUND
270:                 proposal.signatures[i], // <= FOUND
271:                 proposal.calldatas[i], // <= FOUND
272:                 proposal.eta
273:             );

['137']

137:             require(
138:                 proposalConfigs_[i].votingPeriod >= MIN_VOTING_PERIOD, // <= FOUND
139:                 "GovernorBravo::initialize: invalid min voting period"
140:             );

['141']

141:             require(
142:                 proposalConfigs_[i].votingPeriod <= MAX_VOTING_PERIOD, // <= FOUND
143:                 "GovernorBravo::initialize: invalid max voting period"
144:             );

['145']

145:             require(
146:                 proposalConfigs_[i].votingDelay >= MIN_VOTING_DELAY, // <= FOUND
147:                 "GovernorBravo::initialize: invalid min voting delay"
148:             );

['149']

149:             require(
150:                 proposalConfigs_[i].votingDelay <= MAX_VOTING_DELAY, // <= FOUND
151:                 "GovernorBravo::initialize: invalid max voting delay"
152:             );

['153']

153:             require(
154:                 proposalConfigs_[i].proposalThreshold >= MIN_PROPOSAL_THRESHOLD, // <= FOUND
155:                 "GovernorBravo::initialize: invalid min proposal threshold"
156:             );

['157']

157:             require(
158:                 proposalConfigs_[i].proposalThreshold <= MAX_PROPOSAL_THRESHOLD, // <= FOUND
159:                 "GovernorBravo::initialize: invalid max proposal threshold"
160:             );

['161']

161:             require(address(timelocks[i]) != address(0), "GovernorBravo::initialize:invalid timelock address"); // <= FOUND

['163']

163:             proposalConfigs[i] = proposalConfigs_[i]; // <= FOUND

['164']

164:             proposalTimelocks[i] = timelocks[i]; // <= FOUND

['270']

270:             queueOrRevertInternal(
271:                 proposal.targets[i], // <= FOUND
272:                 proposal.values[i], // <= FOUND
273:                 proposal.signatures[i], // <= FOUND
274:                 proposal.calldatas[i], // <= FOUND
275:                 eta,
276:                 uint8(proposal.proposalType)
277:             );

['312']

312:             proposalTimelocks[uint8(proposal.proposalType)].executeTransaction(
313:                 proposal.targets[i], // <= FOUND
314:                 proposal.values[i], // <= FOUND
315:                 proposal.signatures[i], // <= FOUND
316:                 proposal.calldatas[i], // <= FOUND
317:                 proposal.eta
318:             );

['341']

341:             proposalTimelocks[proposal.proposalType].cancelTransaction(
342:                 proposal.targets[i], // <= FOUND
343:                 proposal.values[i], // <= FOUND
344:                 proposal.signatures[i], // <= FOUND
345:                 proposal.calldatas[i], // <= FOUND
346:                 proposal.eta
347:             );

['504']

504:             proposalTimelocks[i].acceptAdmin(); // <= FOUND

['180']

180:             queueOrRevertInternal(
181:                 proposal.targets[i], // <= FOUND
182:                 proposal.values[i], // <= FOUND
183:                 proposal.signatures[i], // <= FOUND
184:                 proposal.calldatas[i], // <= FOUND
185:                 eta
186:             );

['218']

218:             timelock.executeTransaction(
219:                 proposal.targets[i], // <= FOUND
220:                 proposal.values[i], // <= FOUND
221:                 proposal.signatures[i], // <= FOUND
222:                 proposal.calldatas[i], // <= FOUND
223:                 proposal.eta
224:             );

['73']

73:             bytes4 sigHash = bytes4(keccak256(bytes(signatures_[i]))); // <= FOUND

['75']

75:             if (active_[i] && signature.length == 0) { // <= FOUND

['76']

76:                 functionRegistry[sigHash] = signatures_[i]; // <= FOUND

['77']

77:                 emit FunctionRegistryChanged(signatures_[i], true); // <= FOUND

['78']

78:             } else if (!active_[i] && signature.length != 0) { // <= FOUND

['80']

80:                 emit FunctionRegistryChanged(signatures_[i], false); // <= FOUND

['164']

164:             ensureNonzeroAddress(address(timelocks_[i])); // <= FOUND

['165']

165:             emit TimelockAdded(i, address(proposalTimelocks[i]), address(timelocks_[i])); // <= FOUND

['166']

166:             proposalTimelocks[i] = timelocks_[i]; // <= FOUND

['193']

193:             timelock.executeTransaction(
194:                 proposal.targets[i], // <= FOUND
195:                 proposal.values[i], // <= FOUND
196:                 proposal.signatures[i], // <= FOUND
197:                 proposal.calldatas[i], // <= FOUND
198:                 eta
199:             );

['228']

228:             timelock.cancelTransaction(
229:                 proposal.targets[i], // <= FOUND
230:                 proposal.values[i], // <= FOUND
231:                 proposal.signatures[i], // <= FOUND
232:                 proposal.calldatas[i], // <= FOUND
233:                 eta
234:             );

['353']

353:             _queueOrRevertInternal(
354:                 proposal.targets[i], // <= FOUND
355:                 proposal.values[i], // <= FOUND
356:                 proposal.signatures[i], // <= FOUND
357:                 proposal.calldatas[i], // <= FOUND
358:                 eta,
359:                 proposalType
360:             );

[Gas-58] State variable read in a loop

Resolution

Reading a state variable inside a loop in a smart contract can unnecessarily increase gas consumption, as each read operation from the blockchain state is costly. This inefficiency becomes pronounced in loops with many iterations. To optimize gas usage, it's advisable to read the state variable once before the loop starts, store its value in a local (memory) variable, and then use this local variable within the loop. This approach minimizes the number of state read operations, thereby reducing the gas cost associated with executing the contract function, making the smart contract more efficient and cost-effective to run.

Num of instances: 3

Findings

Click to show findings

['269']

269:        for (uint i; i < proposal.targets.length; ++i) { // <= FOUND
270:             queueOrRevertInternal(
271:                 proposal.targets[i],
272:                 proposal.values[i],
273:                 proposal.signatures[i],
274:                 proposal.calldatas[i],
275:                 eta,
276:                 uint8(proposal.proposalType) // <= FOUND
277:             ); // <= FOUND
278:         }

['311']

311:        for (uint i; i < proposal.targets.length; ++i) { // <= FOUND
312:             proposalTimelocks[uint8(proposal.proposalType)].executeTransaction( // <= FOUND
313:                 proposal.targets[i],
314:                 proposal.values[i],
315:                 proposal.signatures[i],
316:                 proposal.calldatas[i],
317:                 proposal.eta
318:             ); // <= FOUND
319:         }

['340']

340:        for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
341:             proposalTimelocks[proposal.proposalType].cancelTransaction(
342:                 proposal.targets[i],
343:                 proposal.values[i],
344:                 proposal.signatures[i],
345:                 proposal.calldatas[i],
346:                 proposal.eta
347:             ); // <= FOUND
348:         }

[Gas-59] Write direct outcome, instead of performing mathematical operations for constant state variables

Resolution

In Solidity, it's highly efficient to directly assign constant values to state variables when these values are known at compile time and will not change. This practice avoids unnecessary computational operations and reduces gas costs for deploying and interacting with smart contracts.

Num of instances: 3

Findings

Click to show findings

['22']

22: uint public constant MIN_VOTING_PERIOD = 20 * 60 * 3;  // <= FOUND

['25']

25: uint public constant MAX_VOTING_PERIOD = 20 * 60 * 24 * 14;  // <= FOUND

['31']

31: uint public constant MAX_VOTING_DELAY = 20 * 60 * 24 * 7;  // <= FOUND

[Gas-60] It is more efficient to use block.timestamp directly rather than calling a function to return it

Resolution

Creating a function to return block.timestamp is gas inefficient due to the overhead of function storage and execution, while directly accessing block.timestamp in your contract code or as a function argument is a more gas-efficient alternative.

Num of instances: 2

Findings

Click to show findings

['212']

210:     function getBlockTimestamp() internal view returns (uint) {
211:         
212:         return block.timestamp; // <= FOUND
213:     }

['256']

254:     function getBlockTimestamp() internal view returns (uint256) {
255:         
256:         return block.timestamp; // <= FOUND
257:     }

[Gas-61] Use of memory instead of storage for struct/array state variables

Resolution

In Solidity, choosing between memory and storage for variables, especially when dealing with structs or arrays, is crucial for optimizing gas costs. Variables declared as storage are pointers to the blockchain data, leading to lower gas consumption when fields are accessed or modified, as they don't require reading the entire structure. In contrast, memory variables copy the entire struct or array from storage, incurring significant gas costs, especially for large or complex structures. Therefore, use storage for state variables or when working within functions to manipulate existing contract data. Reserve memory for temporary data or when data needs to be passed to external functions as copies, ensuring efficient use of gas and avoiding unnecessary costs.

Num of instances: 3

Findings

Click to show findings

['55']

55:         string memory fun = functionRegistry[msg.sig]; // <= FOUND

['135']

135:         bytes memory trustedRemote = trustedRemoteLookup[remoteChainId_]; // <= FOUND

['253']

253:         bytes memory oldRemoteAddress = trustedRemoteLookup[remoteChainId_]; // <= FOUND

[Gas-62] Public functions not called internally

Resolution

Public functions that aren't used internally in Solidity contracts should be made external to optimize gas usage and improve contract efficiency. External functions can only be called from outside the contract, and their arguments are directly read from the calldata, which is more gas-efficient than loading them into memory, as is the case for public functions. By using external visibility, developers can reduce gas consumption for external calls and ensure that the contract operates more cost-effectively for users. Moreover, setting the appropriate visibility level for functions also enhances code readability and maintainability, promoting a more secure and well-structured contract design.

Num of instances: 28

Findings

Click to show findings

['77']

77:     function giveCallPermission(address contractAddress, string calldata functionSig, address accountToPermit) public 

['91']

91:     function revokeCallPermission(
92:         address contractAddress,
93:         string calldata functionSig,
94:         address accountToRevoke
95:     ) public 

['128']

128:     function hasPermission(
129:         address account,
130:         address contractAddress,
131:         string calldata functionSig
132:     ) public view returns (bool) 

['72']

72:     function renounceOwnership() public override 

['72']

72:     function renounceOwnership() public override 

['143']

143:     function propose(
144:         address[] memory targets,
145:         uint[] memory values,
146:         string[] memory signatures,
147:         bytes[] memory calldatas,
148:         string memory description
149:     ) public returns (uint) 

['143']

143:     function propose(
144:         address[] memory targets,
145:         uint[] memory values,
146:         string[] memory signatures,
147:         bytes[] memory calldatas,
148:         string memory description
149:     ) public returns (uint) 

['181']

181:     function propose(
182:         address[] memory targets,
183:         uint[] memory values,
184:         string[] memory signatures,
185:         bytes[] memory calldatas,
186:         string memory description,
187:         ProposalType proposalType
188:     ) public returns (uint) 

['143']

143:     function propose(
144:         address[] memory targets,
145:         uint[] memory values,
146:         string[] memory signatures,
147:         bytes[] memory calldatas,
148:         string memory description
149:     ) public returns (uint) 

['254']

254:     function cancel(uint proposalId) public 

['254']

254:     function cancel(uint proposalId) public 

['233']

233:     function cancel(uint proposalId) external 

['233']

233:     function cancel(uint proposalId) external 

['212']

212:     function cancel(uint256 proposalId_) external 

['279']

279:     function getActions(
280:         uint proposalId
281:     )
282:         public
283:         view
284:         returns (address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas)
285:     

['263']

263:     function getActions(
264:         uint proposalId
265:     )
266:         external
267:         view
268:         returns (address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas)
269:     

['290']

290:     function getReceipt(uint proposalId, address voter) public view returns (Receipt memory) 

['280']

280:     function getReceipt(uint proposalId, address voter) external view returns (Receipt memory) 

['320']

320:     function castVoteBySig(uint proposalId, bool support, uint8 v, bytes32 r, bytes32 s) public 

['320']

320:     function castVoteBySig(uint proposalId, bool support, uint8 v, bytes32 r, bytes32 s) public 

['337']

337:     function castVoteBySig(uint proposalId, uint8 support, uint8 v, bytes32 r, bytes32 s) external 

['337']

337:     function castVoteBySig(uint proposalId, uint8 support, uint8 v, bytes32 r, bytes32 s) external 

['351']

351:     function __acceptAdmin() public 

['356']

356:     function __abdicate() public 

['361']

361:     function __queueSetTimelockPendingAdmin(address newPendingAdmin, uint eta) public 

['366']

366:     function __executeSetTimelockPendingAdmin(address newPendingAdmin, uint eta) public 

['86']

86:     function setDelay(uint delay_) public 

['90']

90:     function setDelay(uint256 delay_) public 

[Gas-63] do-while is cheaper than for-loops when the initial check can be skipped

Resolution

In smart contract optimization, do-while loops can outperform for-loops cost-wise when the initial iteration's condition check is unnecessary. By deferring the condition check to the loop's end, do-while ensures the loop body executes at least once, saving gas when initial conditions are already met, a crucial aspect for Web3 auditors focusing on efficiency. These however should be used carefully as if an input array has a length of 0, the while loop shouldn't run, as such this should only ne utilized if it is known for a fact the contents of the iteration will be run at least once.

Num of instances: 16

Findings

Click to show findings

['220']

220:        for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
221:             _queueOrRevert(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], eta);
222:         }

['242']

242:        for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
243:             timelock.executeTransaction.value(proposal.values[i])(
244:                 proposal.targets[i],
245:                 proposal.values[i],
246:                 proposal.signatures[i],
247:                 proposal.calldatas[i],
248:                 proposal.eta
249:             );
250:         }

['266']

266:        for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
267:             timelock.cancelTransaction(
268:                 proposal.targets[i],
269:                 proposal.values[i],
270:                 proposal.signatures[i],
271:                 proposal.calldatas[i],
272:                 proposal.eta
273:             );
274:         }

['220']

220:        for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
221:             _queueOrRevert(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], eta);
222:         }

['136']

136:        for (uint256 i; i < arrLength; ++i) { // <= FOUND
137:             require(
138:                 proposalConfigs_[i].votingPeriod >= MIN_VOTING_PERIOD,
139:                 "GovernorBravo::initialize: invalid min voting period"
140:             );
141:             require(
142:                 proposalConfigs_[i].votingPeriod <= MAX_VOTING_PERIOD, // <= FOUND
143:                 "GovernorBravo::initialize: invalid max voting period"
144:             );
145:             require(
146:                 proposalConfigs_[i].votingDelay >= MIN_VOTING_DELAY,
147:                 "GovernorBravo::initialize: invalid min voting delay"
148:             );
149:             require(
150:                 proposalConfigs_[i].votingDelay <= MAX_VOTING_DELAY, // <= FOUND
151:                 "GovernorBravo::initialize: invalid max voting delay"
152:             );
153:             require(
154:                 proposalConfigs_[i].proposalThreshold >= MIN_PROPOSAL_THRESHOLD,
155:                 "GovernorBravo::initialize: invalid min proposal threshold"
156:             );
157:             require(
158:                 proposalConfigs_[i].proposalThreshold <= MAX_PROPOSAL_THRESHOLD, // <= FOUND
159:                 "GovernorBravo::initialize: invalid max proposal threshold"
160:             );
161:             require(address(timelocks[i]) != address(0), "GovernorBravo::initialize:invalid timelock address");
162: 
163:             proposalConfigs[i] = proposalConfigs_[i];
164:             proposalTimelocks[i] = timelocks[i];
165:         }

['269']

269:        for (uint i; i < proposal.targets.length; ++i) { // <= FOUND
270:             queueOrRevertInternal(
271:                 proposal.targets[i],
272:                 proposal.values[i],
273:                 proposal.signatures[i],
274:                 proposal.calldatas[i],
275:                 eta,
276:                 uint8(proposal.proposalType)
277:             );
278:         }

['311']

311:        for (uint i; i < proposal.targets.length; ++i) { // <= FOUND
312:             proposalTimelocks[uint8(proposal.proposalType)].executeTransaction(
313:                 proposal.targets[i],
314:                 proposal.values[i],
315:                 proposal.signatures[i],
316:                 proposal.calldatas[i],
317:                 proposal.eta
318:             );
319:         }

['340']

340:        for (uint i = 0; i < proposal.targets.length; i++) { // <= FOUND
341:             proposalTimelocks[proposal.proposalType].cancelTransaction(
342:                 proposal.targets[i],
343:                 proposal.values[i],
344:                 proposal.signatures[i],
345:                 proposal.calldatas[i],
346:                 proposal.eta
347:             );
348:         }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment