CertiK reviewed AIDOGE (ArbDoge AI Launchpad Audit) on 10 Aug 2023: 21 findings, 0 critical; 13 marked fixed, 8 acknowledged.
Report hosted on skynet.certik.com.
Trustblock does not review code.
Statuses are the security firm’s own, from its report.
| Severity | Open | Acknowledged | Fixed |
|---|---|---|---|
| Critical | 0 | 0 | 0 |
| High | 0 | 2 | 3 |
| Medium | 0 | 0 | 1 |
| Low | 0 | 6 | 9 |
In the `_doAddLp()` function, before adding liquidity into the pool, the contract set approvals of token and `WETH` for the spender. However, the contract adds liquidity via the `INonfungiblePositionManager` contract and incorrectly sets the `swapRouter` as the `spender`, which will cause the transaction reverted since the approval should be for the `nonfungiblePositionManager` to run function `mint()`. Also, the contract not set allowance with `AIERC20` for `nonfungiblePositionManager` as well.
In the `_doAddLp()`, the contract uses half token to swap for `WETH`, then adds liquidity into the pair. However, this functionality will not work due to two errors: - Incorrect parameter: passing of `address(this)` as `to` causes an error "UniswapV2: INVALID_TO" - Missing approval: adding liquidity requires approving corresponding amount first. ```solidity=211 _approve(address(this), address(swapRouter), tokenAmount); uint256 ethAmountBefore = WETH.balanceOf(address(this)); try swapRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens(half, 0, pathEth,address(this),block.timestamp){ uint256 ethAmount = WETH.balanceOf(address(this)) - ethAmountBefore; try swapRouter.addLiquidity(address(this), address(WETH), half, ethAmount, 0, 0, address(0), block.timestamp) { emit AddLiquidity(tokenAmount, ethAmount, block.timestamp); } catch {} } catch {} ``` ### Incorrect parameter The use of `address(this)` will fail and lead to the error `UniswapV2: INVALID_TO` because according to the `UniswapV2Pair` contract, the `to` address is not allowed to be one of the addresses in the `path` parameter included in function `swapExactTokensForTokensSupportingFeeOnTransferTokens()`. ```solidity require(to != _token0 && to != _token1, 'UniswapV2: INVALID_TO'); ``` ### Missing approval According to the `UniswapV2Router02` contract, the running of the function `addLiquidity()` will execute the `safeTransferFrom()` to send funds from the function caller to the `pair` address, therefore sufficient approvals for both tokens will be required to successfully perform this operation. ```solidity TransferHelper.safeTransferFrom(tokenA, msg.sender, pair, amountA); TransferHelper.safeTransferFrom(tokenB, msg.sender, pair, amountB); ``` **Note:** This finding pertains to the updated code provided by the team on 07/28/2023.
In the `_doAddLp()` function, the contract swaps half of current tokens for `WETH` first, then adds liquidity into the pool via the `INonfungiblePositionManager` contract. However, it is noticed that when adding liquidity into the pool, the `recipient` is set to the **zero-address**, which means the newly minted NFT position token is transferred to the zero address. In the `NonfungiblePositionManager` contract, there is a check for token ID when calling `decreaseLiquidity()`, `collect()` and `burn()` functions. Minting to the zero address will cause the check to fail when invoking these functions.
In the `_doAddLp()` function, the `ethAmountBefore` is the `WETH` token balance of current contract. However, when adding liquidity into the trade pair, the `ethAmount` is calculated by subtracting the `ethAmountBefore` from the `address(this).balance`. The `WETH` is an ERC20 token, and `address(this).balance` is the native token balance. ```solidity=222 uint256 ethAmountBefore = WETH.balanceOf(address(this)); try swapRouter.swapExactTokensForETHSupportingFeeOnTransferTokens(half, 0, pathEth, address(this),block.timestamp) uint256 ethAmount = address(this).balance - ethAmountBefore; ... ```
In the `_doAddLp()` function, the contract swaps half of the current tokens for `WETH` first and then adds liquidity to the pool via the `INonfungiblePositionManager` contract. However, the `NonfungiblePositionManager` contract requires the pool to be created and initialized before creating any position. The current implementation does not comply with these requirements.
In the previous version, the `doSwapBack()` function could only be called by the `owner` role. However, in the current version, it has been declared as a public and unrestricted function. It is important to note that this function not only performs the swap, but also carries out actions such as burning tokens and distributing tokens to the `bonusWallet`, `tradingPool.vault()`, and `teamWallet`. since this function allows any party to trigger the swap back operation, this will make the contract a potential target for exploits such as sandwich attacks. We would like to confirm if current implementation aligns with the intended design.
The linked function returns the length of `_pairs`, but its name is mistakenly set as `getMinterLength()`.
The contract has a `receive()` function or payable functions, making it able to receive native tokens. However, it does not have a function to withdraw the funds, which can lead to permanently locked tokens within the contract. ```solidity=309 receive() external payable {} ```
There should always be events emitted in the sensitive functions that are controlled by centralization roles.
Performing division by zero would raise an error and revert the transaction. ```solidity=165 uint256 amountBurn = taxAmount * burnFee / totalFee; ``` The expression `taxAmount * burnFee / totalFee` may divide by zero. Its divisor has has estimated interval [0, 1500]. --- ```solidity=166 uint256 amountLp = taxAmount * liquidityFee / totalFee; ``` The expression `taxAmount * liquidityFee / totalFee` may divide by zero. Its divisor has has estimated interval [0, 1500]. --- ```solidity=167 uint256 amountBouns = taxAmount * bounsFee / totalFee; ``` The expression `taxAmount * bounsFee / totalFee` may divide by zero. Its divisor has has estimated interval [0, 1500]. --- ```solidity=168 uint256 amountTrade = taxAmount * tradingPoolFee / totalFee; ``` The expression `taxAmount * tradingPoolFee / totalFee` may divide by zero. Its divisor has has estimated interval [0, 1500]. --- ```solidity=169 uint256 amountTeam = taxAmount * teamFee / totalFee; ``` The expression `taxAmount * teamFee / totalFee` may divide by zero. Its divisor has has estimated interval [0, 1500].
The smart contract does not check or store the return value of an external call in a local or state variable, which may introduce vulnerabilities due to the unhandled outcome. ```solidity=214 WETH.approve(address(swapRouter), outAmount); ``` --- ```solidity=230 nonfungiblePositionManager.mint(lpParams); ```
Inside `swapBack()` function, the contract will approve full tax amount for the `swapRouter` contract. ```solidity=170 _approve(address(this), address(swapRouter), taxAmount); ``` However, during the swap, there is another call to `_approve()` function. The previous approval will be overwritten.
In the `_doTransfer()` function, it is checked that `taxType` is not equal to `TaxType.Transfer` if `taxType == TaxType.Trade` is known to be true. Therefore, the linked condition is always true and the comparison is unless. ```solidity=128 if (taxType == TaxType.Trade) { ... } else if (taxType != TaxType.Transfer) { shouldTakeFee = false; } ```
The report names a repository or files. Open the report to see its scope.
Repository and files the report names (12)
1,044 projects reviewed · 1,375 reports · latest May 2025
See CertiK’s auditsSomething wrong on this page? A missing report, a wrong date, a finding that was fixed since. Tell us and we check it against its source.
Tell us