PandAI Earn smart contracts security audit report performed by chhajershrenik.
Commit: bce9e71647a72bdf0704cb09787c0baf267e082f
In total, 2 issues were reported, including:
-
1 medium severity issue.
-
1 low severity issue.
In total, 6 notes were reported, including:
-
1 note.
-
5 owner privileges.
3.1. PandAi/USDT price manipulation is possible by an arbitrary user by trading PandAi/USDT tokens into the liquidity pool address
An arbitrary user can inflate the price of PandAi/USDT value by staking high volume in the lpaddress
and deflate the value by unstaking the previous deposit. The contract deployed at lpaddress
is not in the scope of this audit.
For an uninitialized ApprovalLevel
of a wallet, the wallet must be allowed to claim $1000 as a daily limit. The statement incorrectly checks the claimUsdt
value to DAILY_CLAIM_LIMIT
.
Consider updating the following statement in the function canClaim()
claimUsdt / (10 ** usdtToken.decimals()) < DAILY_CLAIM_LIMIT
to
claimUsdt / (10 ** usdtToken.decimals()) <= DAILY_CLAIM_LIMIT
- Contract PandAIEarn inherits the traits of OpenZeppelin AccessControl contract allowing admin to manage admin's, the role could be renounced leading to locking out of access to critical functions of the contract.
- Functions
pause()
andunpause
allows the admin to disable or enable the ability for the user's deposit USDT tokens or claim rewards respectively. But still allows users to withdraw requests and can withdraw deposit USDT tokens from the contract. - Function
setLpAddress()
allows the admin to change the liquidity pool address to any wallet, the value of PandAi tokens would be affected which is computed using the functiongetPandaiWorthOf()
based on the amount of USDT tokens and PandAi tokens available innewLpAddress
wallet. - Function
withdrawTreasury()
allows the admin to withdraw USDT tokens from the contract to the admin address. - Function
setUserApprovalLevel()
allows admin to change the claim status of any wallet address toNotApproved
,Approved
, andForbidden
limiting the user's ability to withdraw or restrict a user from withdrawing USDT tokens deposited in the contract based on the approval status. Any wallet with an uninitialized approval level cannot claim rewards if the daily reward is greater than $1000.
Since the owner has unlimited rights to do everything, the ownership must be transferred to a multi-sig contract.
- Unorganized and non-standardized docstrings
The contracts in the code base contain unorganized and non-standardized docstring does not explain the contract's state and functionalities in detail. This hinders reviewers’ understanding of the code’s intention, which is fundamental to correctly assess not only security but also correctness. Additionally, detailed docstrings improve readability and ease maintenance. They should explicitly explain the purpose or intention of the functions, the scenarios under which they can fail, the roles allowed to call them, the values returned, and the events emitted.
Consider thoroughly documenting all functions (and their parameters) that are part of the contracts’ public API. Functions implementing sensitive functionality, even if not public, should be documented as well. When writing docstrings, consider following the Ethereum Natural Specification Format (NatSpec).
- Open-source contact.
- The contract should pass a bug bounty after the completion of the security audit.
- Public testing.
- Automated anomaly detection systems. - NOT IMPLEMENTED. A simple anomaly detection algorithm is recommended to be implemented to detect behavior that is atypical compared to normal for this contract. For instance, the contract must halt deposits in case a large amount is withdrawn in a short period until the owner or the community of the contract approves further operations.
- Multisig owner account.
- Standard ERC20-related issues. - NOT IMPLEMENTED. It is known that every contract can potentially receive an unintended ERC20-token deposit without the ability to reject it even if the contract is not intended to receive or hold tokens. As a result, it is recommended to implement a function that will allow extracting any arbitrary number of tokens from the contract.
- Crosschain address collisions. ETH, ETC, CLO, etc. It is possible that a transaction can be sent to the address of your contract at another chain (as a result of a user mistake or some software fault). It is recommended that you deploy a "mock contract" that would allow you to withdraw any tokens from that address or prevent any funds deposits. Note that you can reject transactions of native tokens deposited, but you can not reject the deposits of ERC20 tokens. You can use this source code as a mock contract: extractor contract source code. The address of a new contract deployed using
CREATE (0xf0)
opcode is assigned following this schemekeccak256(rlp([sender, nonce]))
. Therefore you need to use the same address that was originally used at the main chain to deploy the mock contract at a transaction with thenonce
that matches that on the original chain. Example: If you have deployed your main contract with address 0x010101 at your 2021th transaction then you need to increase your nonce of 0x010101 address to 2020 at the chain where your mock contract will be deployed. Then you can deploy your mock contract with your 2021th transaction, and it will receive the same address as your mainnet contract.
Got it, thank you!!