Closed ibjc closed 10 months ago
the audit report does seem like a combination of multiple model runs
latest findings from the ai audit!
Summary of Inverse Bonding contract:
Misleading Error Messages (Minor): This vulnerability may lead to confusion among users due to the shared error messages used in different contexts.
Improper Error Reporting (Major): This may cause unawareness of failure in case another contract reverts, hence continuing the execution of the smart contract leading to potential inconsistencies.
Incorrect Calculations (Severe): In the
_calcMintToken
function, inflated mint token value can be returned if the new amount added to the reserve balance exceeds the currentMissing Contract Versioning (Minor): Without versioning, it would be hard to distinguish between different versions of contracts during an upgrade situation.
Unsafe Typecast (Major): In function
updateFeeConfig
,ActionType actionType
is converted to uint256 without checking ranges for the enum.------- end of summary --------
The smart contract seems well-written and does not appear to contain any obvious vulnerabilities. It's worth noting that the contract uses well-established libraries such as OpenZeppelin for managing ERC20 tokens, as well as various security and access control features including SafeERC20, Ownable, Pausable, and Initializable.
It is important to be aware of some potential attack vectors that, while not necessarily explicit in this contract, can be common in Solidity contracts in general:
Misleading Error Messages: It is not a security issue per se, but it can cause users to misunderstand the contract. Some
require
statements share the same error messages, ERR_PARAM_ZERO and ERR_LIQUIDITY_TOO_SMALL, but are used in different contexts.Improper Error Reporting The contract doesn't use revert or require to handle some of the failures appropriately. This is specifically problematic in methods that invoke functions on other contracts (like the token contracts' transfer, transferFrom, mint, and burn methods).
For instance, in the sellTokens function, the contract sends back some ETH to the seller:
(bool sent,) = recipient.call{value: returnLiquidity}(""); require(sent, ERR_FAIL_SEND_ETHER);
If the recipient is another contract, and that contract reverts in its fallback function, the sellTokens function would not be aware of this, and would continue execution, potentially leading to inconsistencies.
Incorrect Calculation The
_calcMintToken
function may return incorrect values. When amount added to the reserve balance exceeds the reserve balance, thenewBalance
becomes larger than_reserveBalance
and thus the mint token could be drastically inflated.Missing contract versioning: The contract does not contain versioning. In case of an upgrade, it will be hard to distinguish between different versions of contracts.
Mitigation: The code should include a versioning scheme. This could be a simple uint that is increased with every new deployed version.
Unsafe typecast: In function
updateFeeConfig
,ActionType actionType
is being directly converted to uint256 without range check for the enum. This potentially could lead to a vulnerability if the function is called with invalidactionType
.Mitigation: Check if
ActionType actionType
actually corresponds to one of the defined enum items before converting it to uint256.
Courtesy of @yubrew, we received an AI contract audit!
Note that it is an alpha product/service, so keep in mind of potential ai "hallucinations".
here is the full audit report:
1. Reentrancy: The InverseBondingCurve contract transfers Ether in several functions including
removeLiquidityand
sellTokens`. However, it does not follow the Checks-Effects-Interactions pattern properly, meaning that state changes happen after the external calls. This may potentially allow for reentrancy attacks. Although such an attack isn't straightforward, because it would require the recipient to be a contract, it is a potential issue.The code lacks input validation for several externally-facing functions. For instance,
transfer
,transferFrom
, and theERC20
contract'sapprove
method lack input validation. An attacker may be able to gain unauthorized control of funds without the user's knowledge.There is no permission check on the method
mint
. The only thing preventing anyone from creating new tokens is a simpleonlyOwner
modifier. This can potentially lead to a massive supply of tokens if an attacker can take control of the owner's account.Lack of event logging: Key actions such as token transfers and state changes do not emit events, which makes tracking and auditing past transactions and changes difficult.
The
_calculateAndUpdateFee
function is underspecified, and combined with the lack of input validation, might be a source of vulnerabilities. Specifically, the return value oftokenAmount.mulDown(_lpFeePercent)
is subtracted fromtokenAmount
, but since_lpFeePercent
is user-controlled, an attacker can cause integer underflow here.Suggest to use 'SafeMath' for all arithmetic operations to avoid integer underflow/overflow.
private
, which means they can only be called from within the contract itself. This lack of visibility increases the difficulty of contract interaction and testing, and could potentially hide bugs.To exploit some of these vulnerabilities, specially for those where user input is not validated, it suffices to drive the contract into an undesired state (for example, by manipulating
_lpFeePercent
) and then call_calculateAndUpdateFee
. For the ownership vulnerability, if the owner's account is compromised tokens can be minted at will. To exploit a potential reentrancy attack, one could for example write a contract that callsremoveLiquidity
, and in its fallback function callremoveLiquidity
again.1) No vulnerability: All the contracts using “.call{value: _}” are properly using it. No reentrancy attack, DoS attack, or Ether theft could be performed.
2) Precision vulnerabilities: While no exploitable vulnerability was found, it is important to note that the methods "initialize", "addLiquidity", "removeLiquidity", "buyTokens" and "sellTokens" do present a precision problem due to the use of the "divDown" method from FixedPoint library, which may result in rounding errors, causing small imprecisions in calculations. This issue could be potentially exploited by calling these methods in a manner that maximizes rounding errors, facilitating imbalance and provide unexpected advantages in some specific and edge scenarios. Here is an example of how such an attack could look like:
This kind of precision attacks are complex and rare, and decided to be accepted by developers in many DeFi projects due to the high cost of performing them compared to potential gains. However, it is generally good to be aware of such issues and ensure there are maximum limits for any user actions to minimize potential impact.
3) Lack of input sanitization: All the functions of the contract assume that the owner and the users will use the contract properly. These assumptions can act as a potential vulnerability, always ensure to validate inputs where necessary.
Here are a few possible vulnerabilities I found in the contract:
sellTokens()
andremoveLiquidity()
, the contract attempts to transfer ETH using a low-level.call()
. If the receiving contract's fallback function runs out of gas or if the transfer fails for any other reason, these functions will revert which opens your contract up to a Denial of Service (DoS) attack.For
sellTokens()
:For
removeLiquidity()
:sellTokens()
andremoveLiquidity()
functions, before state updates. Although currently the state is updated before the funds are sent, if state updates are added after these calls in the future, the contract risk a reentrancy attack.For
sellTokens()
:For
removeLiquidity()
:Recommendations:
.call()
s, use Solidity's built-in mechanisms for sending Ether such astransfer()
.After observing the given smart contract codes, there are no explicit security vulnerabilities found. However, there are some potential dangers or points that the developer should pay attention to:
Integer Underflow/Overflow: In functions like
buyTokens
andsellTokens
, arithmetic operations are taking place, and although Solidity 0.8.x has built-in arithmetic overflow and underflow protection, for the older version, please ensure to use the SafeMath library to prevent potential arithmetic underflow or overflow errors.Upgradable Smart Contract: As an upgradable smart contract, it's crucial to protect proxy-based contracts to avoid the risk of being pwned if you get ownership of the proxy. This contract should test updates end to end and with manual analysis.
Initializable contracts: Functions marked as
initializer
can only be called once right after contract deployment. It's important to ensure the function does all the setup it needs to prevent future state corruption.Gas Limit: Some loops in this contract could exceed the gas limit if the array it's looping through grows too large.
Use external over public: In order to optimize gas, it's better to use
external
keyword overpublic
for functions that will not be called internally.Re-entrancy attack: Even though no explicit reentrancy attack vulnerability is found (as there is no call to an untrusted contract followed by state-changing operations), please always consider using ReentrancyGuard from the OpenZeppelin library for state-changing functions to protect your contract from reentrancy attacks.
Error Message Information Leakage: Writing too much info in error messages could potentially yield information risks.
Ensure proper access control: Keep careful control over which addresses have the ability to call specific functions. In the code provided, these seem to be appropriately restricted, but should be a point of consideration especially with the proxy upgrade pattern.
Remember, it's always important to conduct a complete official audit using automatic tools and manual checking by a skilled security expert before the contract goes live.
Re-Entrancy:
removeLiquidity
,buyTokens
, andsellTokens
methods are susceptible to re-entrancy attack as they transfer the funds before updating the status. An attacker can reenter the smart contract and drain its funds.Front Running:
buyTokens
andsellTokens
methods are vulnerable to front-running. Their prices are public, so miners or users with faster confirmation times can take advantage of better prices and traders can be victim to MEV (Miner Extractable Value).No Limit On gas:
claimProtocolReward
method there is no limit on the gas which can potentially lock a smart contract.Unprotected selfdestruct Function:
Missing event logs:
Ownership controls:
Methods can be exploited:
buyTokens
,sellTokens
,addLiquidity
,removeLiquidity
,initialize
,claimProtocolReward
,updateFeeOwner
.The contract code does not have any critical vulnerabilities. It's well-written with safe math operations, error checks, and requires suitable permissions for critical methods. However, there are a few areas to improve:
Consider Using
IERC20.transfer
rather thancall
:In the
removeLiquidity
function, the contract is using a low-level call to transfer Ether. This might expose the risk of reentrancy attacks. You can use the safeIERC20.transfer
instead.Recommended update:
External Calls After State Modifications:
It's recommended that external calls should be the last actions in a function due to the reentrancy attack. There are several places in the methods
removeLiquidity
,sellTokens
, andunstake
where some state changes (_burn
,_totalStaked
decrease) are made after Ether transfer.For instance, in
removeLiquidity
:Consider doing state modifications before Ether transfer.
Defensive Payment Programming:
It would be advisable to employ a "Check-Effects-Interaction" pattern especially in the
removeLiquidity
andsellTokens
function where user balance is updated.No Proper Handling of Contract Interactions:
The InverseBondingCurve contract interacts with other contracts such as IInverseBondingCurveToken. This exposes the contract to risks if the other contract is compromised or does not act as expected.
The comment 'TODO: add logic for transfer owner' could be a vulnerability:
If there's no logic for transferring ownership on the InverseBondingCurveToken contract, then this function should be created to evade a situation where there doesn't exist the possibility to transfer ownership if needed.
Re-entrancy Attack - As the
withdrawLiquidity
function has a state change that happens after the external call, this smart contract can be vulnerable to a re-entrancy attack. It's generally a best practice to first change the state and then transfer ETH token.Arithmetic Over/Under-Flows - Use SafeMath when handling arithmetic operations. Overflows and underflows can lead to unexpected results.
In the
claimReward
function, the else statement is empty which does nothing for any otherrewardType
not covered in the if-else statements. It's good practice to revert in the case where an invalidrewardType
is passed.Fee Escalation - An owner can manipulate the system to their advantage by manipulating fees through
updateFeeConfig
.Oracle Manipulation - The price of tokens is calculated within the contract using user-provided values. This introduces the risk of price manipulation. Use oracles if available for these types of calculations.
In the
updateFeeOwner
function, there is no check to prevent setting the fee owner to the zero address, this would effectively burn the fees.Lack of event logging -: Events are not being emitted after state changes in
mint
andburnFrom
functions of theInverseBondingCurveToken
contract making it difficult to track state changes.transfer
andtransferFrom
functions do not revert if_updateLpReward
encounters an error. Instead, they continue execution. This can be detrimental to the functionality of the contract.In the
claimProtocolReward
function, there is no check thatamount
is greater than zero before the transfer to the protocolFeeOwner is done.In
unstake
function, the check for the user's staking balance should come before_updateStakingReward(msg.sender)
is called.The contract uses
call{value: returnLiquidity}("")
to send Ether, which can make the contract vulnerable to fall back or by triggering unexpected behaviours in receiving contracts.claimReward
method does not ensure that the reward belongs to the user claiming or that any reward exists.Vulnerable to Front-Running Attacks: Transaction ordering and timing can be manipulated by miners or users who are willing to pay a high gas price. These users can potentially "front-run" transactions and adversely affect other user's transactions.
The
transfer
andtransferFrom
functions in theERC20Upgradeable
contract can be paused by the contract owner, however, the methods in the contract do not consider the paused status.Missed Function Access Control: Functions
initialize
,updateFeeConfig
andupdateFeeOwner
should ideally be restricted to be called only by the contract owner, but as implementation is not included, these can be triggered by any address.The contract tends to overuse of low-level calls
call
,delegatecall
. It's usually better to use high-level calls unless you know what you're doing.Gas Optimization: There is room for gas optimization in the code, as multiple repetitive calls to the same function or identical calculations are made.
Always use proper naming for methods, variables and event. It might not be security-related but in long term, these contracts are easy to understand & less prone to bug.
Please make the necessary corrections to mitigate these vulnerabilities.`