C4-001 : transferOwnership should be two step process
Impact - LOW
"LiquidityFarming" inherit OpenZeppelin's OwnableUpgradable contract which enables the onlyOwner role to transfer ownership to another address. It's possible that the onlyOwner role mistakenly transfers ownership to the wrong address, resulting in a loss of the onlyOwner role. The current ownership transfer process involves the current owner calling Unlock.transferOwnership(). This function checks the new owner is not the zero address and proceeds to write the new owner's address into the owner's state variable. If the nominated EOA account is not a valid account, it is entirely possible the owner may accidentally transfer ownership to an uncontrolled account, breaking all functions with the onlyOwner() modifier. Lack of two-step procedure for critical operations leaves them error-prone
if the address is incorrect, the new address will take on the functionality of the new role immediately
for Ex : -Alice deploys a new version of the whitehack group address. When she invokes the whitehack group address setter to replace the address, she accidentally enters the wrong address. The new address now has access to the role immediately and is too late to revert
The contract is inherited from the Ownable which includes transferOwnership.
Tools Used
None
Recommended Mitigation Steps
Implement zero address check and Consider implementing a two step process where the owner nominates an account and the nominated account needs to call an acceptOwnership() function for the transfer of ownership to fully succeed. This ensures the nominated EOA account is a valid and active account.
C4-002 : Missing events for admin only functions that change critical parameters
Impact - Non critical
The admin only functions that change critical parameters should emit events. Events allow capturing the changed parameters so that off-chain tools/interfaces can register such changes with timelocks that allow users to evaluate them and consider if they would like to engage/exit based on how they perceive the changes as affecting the trustworthiness of the protocol or profitability of the implemented financial services. The alternative of directly querying on-chain contract state for such changes is not considered practical for most users/usages.
Missing events and timelocks do not promote transparency and if such changes immediately affect users’ perception of fairness or trustworthiness, they could exit the protocol causing a reduction in liquidity which could negatively impact protocol TVL and reputation.
There are owner functions that do not emit any events in the contracts.
Add events to all admin/privileged functions that change critical parameters.
C4-003 : All Contract Are Not Upgradeable contracts
Impact - LOW
Pausable contract is not inherited from Upgradeable contracts.
The contract is not upgradeable. For the using the upgradeable contract, The following steps should be followed.
- Instead of Pausable, we use the upgradable variant PausableUpgradeable
- Replace the constructor with the initializer method, with the initializer modifier. Note that you need to - manually call the __{contract}_init(); method of every parent contract with appropriate parameters.
We advise the client to carefully manage the executor accounts' private key to avoid any potential risks
of being hacked. In general, we strongly recommend centralized privileges or roles in the protocol to be
improved via a decentralized mechanism or smart-contract-based accounts with enhanced security
practices, e.g., Multi-Signature wallets.
Indicatively, here is some feasible suggestions that would also mitigate the potential risk at the different
level in term of short-term and long-term goal:
Time-lock with reasonable latency, e.g. 48 hours, for awareness on privileged operations;
Assignment of privileged roles to multi-signature wallets to prevent a single point of failure due to the
private key;
Introduction of a DAO/governance/voting module to increase transparency and user involvement.
C4-005: Front-runnable Initializers
Impact : LOW
All contract initializers were missing access controls, allowing any user to initialize the contract. By front-running the contract deployers to initialize the contract, the incorrect parameters may be supplied, leaving the contract needing to be redeployed.
While the code that can be run in contract constructors is limited, setting the owner in the contract's constructor to the msg.sender and adding the onlyOwner modifier to all initializers would be a sufficient level of access control.
C4-006 : Use of Block.timestamp
Impact - Non-Critical
Block timestamps have historically been used for a variety of applications, such as entropy for random numbers (see the Entropy Illusion for further details), locking funds for periods of time, and various state-changing conditional statements that are time-dependent. Miners have the ability to adjust timestamps slightly, which can prove to be dangerous if block timestamps are used incorrectly in smart contracts.
Block timestamps should not be used for entropy or generating random numbers—i.e., they should not be the deciding factor (either directly or through some derivation) for winning a game or changing an important state.
Time-sensitive logic is sometimes required; e.g., for unlocking contracts (time-locking), completing an ICO after a few weeks, or enforcing expiry dates. It is sometimes recommended to use block.number and an average block time to estimate times; with a 10 second block time, 1 week equates to approximately, 60480 blocks. Thus, specifying a block number at which to change a contract state can be more secure, as miners are unable to easily manipulate the block number.
C4-007: TransferOverhead Variable Is Not Used In The Tokens
Impact : LOW
In the smart contracts, TransferOverhead has been used gas over head calculation. However this variable is not anywhere in the contract. Only It is set in the contract.
C4-001 : transferOwnership should be two step process
Impact - LOW
"LiquidityFarming" inherit OpenZeppelin's OwnableUpgradable contract which enables the onlyOwner role to transfer ownership to another address. It's possible that the onlyOwner role mistakenly transfers ownership to the wrong address, resulting in a loss of the onlyOwner role. The current ownership transfer process involves the current owner calling Unlock.transferOwnership(). This function checks the new owner is not the zero address and proceeds to write the new owner's address into the owner's state variable. If the nominated EOA account is not a valid account, it is entirely possible the owner may accidentally transfer ownership to an uncontrolled account, breaking all functions with the onlyOwner() modifier. Lack of two-step procedure for critical operations leaves them error-prone if the address is incorrect, the new address will take on the functionality of the new role immediately
for Ex : -Alice deploys a new version of the whitehack group address. When she invokes the whitehack group address setter to replace the address, she accidentally enters the wrong address. The new address now has access to the role immediately and is too late to revert
Proof of Concept
Tools Used
None
Recommended Mitigation Steps
Implement zero address check and Consider implementing a two step process where the owner nominates an account and the nominated account needs to call an acceptOwnership() function for the transfer of ownership to fully succeed. This ensures the nominated EOA account is a valid and active account.
C4-002 : Missing events for admin only functions that change critical parameters
Impact - Non critical
The admin only functions that change critical parameters should emit events. Events allow capturing the changed parameters so that off-chain tools/interfaces can register such changes with timelocks that allow users to evaluate them and consider if they would like to engage/exit based on how they perceive the changes as affecting the trustworthiness of the protocol or profitability of the implemented financial services. The alternative of directly querying on-chain contract state for such changes is not considered practical for most users/usages.
Missing events and timelocks do not promote transparency and if such changes immediately affect users’ perception of fairness or trustworthiness, they could exit the protocol causing a reduction in liquidity which could negatively impact protocol TVL and reputation.
There are owner functions that do not emit any events in the contracts.
Proof of Concept
See similar High-severity H03 finding OpenZeppelin’s Audit of Audius (https://blog.openzeppelin.com/audius-contracts-audit/#high) and Medium-severity M01 finding OpenZeppelin’s Audit of UMA Phase 4 (https://blog.openzeppelin.com/uma-audit-phase-4/)
Tools Used
None
Recommended Mitigation Steps
Add events to all admin/privileged functions that change critical parameters.
C4-003 : All Contract Are Not Upgradeable contracts
Impact - LOW
Pausable contract is not inherited from Upgradeable contracts. The contract is not upgradeable. For the using the upgradeable contract, The following steps should be followed.
Proof of Concept
Tools Used
None
Recommended Mitigation Steps
Ensure that the function is inherited from the upgradeable contracts. PausableUpgradable contract be seen from the below.
https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/master/contracts/security/PausableUpgradeable.sol
C4-004 : Centralization Risk
Impact - LOW
The system is heavily relies on the ExecutorManager. Therefore, It contains centralization risk If the execution manager is EOA and captured.
Proof of Concept
Tools Used
None
Recommended Mitigation Steps
We advise the client to carefully manage the executor accounts' private key to avoid any potential risks of being hacked. In general, we strongly recommend centralized privileges or roles in the protocol to be improved via a decentralized mechanism or smart-contract-based accounts with enhanced security practices, e.g., Multi-Signature wallets.
C4-005: Front-runnable Initializers
Impact : LOW
All contract initializers were missing access controls, allowing any user to initialize the contract. By front-running the contract deployers to initialize the contract, the incorrect parameters may be supplied, leaving the contract needing to be redeployed.
Proof of Concept
https://github.com/code-423n4/2022-03-biconomy/blob/main/contracts/hyphen/token/LPToken.sol#L36
https://github.com/code-423n4/2022-03-biconomy/blob/main/contracts/hyphen/LiquidityPool.sol#L87
https://github.com/code-423n4/2022-03-biconomy/blob/main/contracts/hyphen/LiquidityProviders.sol#L78
Tools Used
Manual Code Review
Recommended Mitigation Steps
While the code that can be run in contract constructors is limited, setting the owner in the contract's constructor to the
msg.sender
and adding theonlyOwner
modifier to all initializers would be a sufficient level of access control.C4-006 : Use of Block.timestamp
Impact - Non-Critical
Block timestamps have historically been used for a variety of applications, such as entropy for random numbers (see the Entropy Illusion for further details), locking funds for periods of time, and various state-changing conditional statements that are time-dependent. Miners have the ability to adjust timestamps slightly, which can prove to be dangerous if block timestamps are used incorrectly in smart contracts.
Proof of Concept
Tools Used
Manual Code Review
Recommended Mitigation Steps
Block timestamps should not be used for entropy or generating random numbers—i.e., they should not be the deciding factor (either directly or through some derivation) for winning a game or changing an important state.
Time-sensitive logic is sometimes required; e.g., for unlocking contracts (time-locking), completing an ICO after a few weeks, or enforcing expiry dates. It is sometimes recommended to use block.number and an average block time to estimate times; with a 10 second block time, 1 week equates to approximately, 60480 blocks. Thus, specifying a block number at which to change a contract state can be more secure, as miners are unable to easily manipulate the block number.
C4-007: TransferOverhead Variable Is Not Used In The Tokens
Impact : LOW
In the smart contracts, TransferOverhead has been used gas over head calculation. However this variable is not anywhere in the contract. Only It is set in the contract.
Proof of Concept
https://github.com/code-423n4/2022-03-biconomy/blob/db8a1fdddd02e8cc209a4c73ffbb3de210e4a81a/contracts/hyphen/token/TokenManager.sol#L61
Tools Used
Manual Code Review
Recommended Mitigation Steps
Ensure that gas Over head is adjusted in the LQ pool. If It is not used, consider deleting variable.