CoinFabrik was asked to audit the contracts for the PoorPleb project. No git repository was provided with the source code.
The scope for this audit includes and is limited to the following file:
PoorPleb.sol: It contains the PoorPleb contract. It is a standard ERC20 contract with the extra option to claim tokens for some addresses. Its sha256, calculated using the sha256sum utility, is
No other files were audited. Its dependencies are assumed to work according to their documentation. Also, no tests were reviewed for this audit.
During this audit we found no critical issues, one medium issue and several minor issues. Also, two enhancements were proposed.
Fixes were published in the git repository, commit
a81549c9ef980813178af0fd0115b247a9e80071. All issues were resolved by the development team. All enhancements were implemented by the development team.
CoinFabrik was provided with the source code. Our auditors spent one week auditing the source code provided, which includes understanding the context of use, analyzing the boundaries of the expected behavior of each contract and function, understanding the implementation by the development team (including dependencies beyond the scope to be audited) and identifying possible situations in which the code allows the caller to reach a state that exposes some vulnerability. Without being limited to them, the audit process included the following analyses:
- Arithmetic errors
- Outdated version of Solidity compiler
- Race conditions
- Reentrancy attacks
- Misuse of block timestamps
- Denial of service attacks
- Excessive gas usage
- Missing or misused function qualifiers
- Needlessly complex code and contract interactions
- Poor or nonexistent error handling
- Insufficient validation of the input parameters
- Incorrect handling of cryptographic signatures
- Centralization and upgradeability
After delivering a report with our findings, the development team had the opportunity to comment on every finding and fix the issues they considered convenient. Once fixed and/or commented, our team ran a second review process to verify that the changes to the code effectively solve the issues found and do not unintentionally add new ones. This report includes the final status after the second review.
In the following table we summarize the security issues we found in this audit. The severity classification criteria and the status meaning are explained below. This table does not include the enhancements we suggest to implement, which are described in a specific section after the security issues.
|MI-01||No Zero Check||Minor||Resolved|
|MI-03||Solidity Version Pinning||Minor||Resolved|
Security risks are classified as follows:
- Critical: These are issues that we manage to exploit. They compromise the system seriously. They must be fixed immediately.
- Medium: These are potentially exploitable issues. Even though we did not manage to exploit them or their impact is not clear, they might represent a security risk in the near future. We suggest fixing them as soon as possible.
- Minor: These issues represent problems that are relatively small or difficult to take advantage of, but might be exploited in combination with other issues. These kinds of issues do not block deployments in production environments. They should be taken into account and be fixed when possible.
An issue detected by this audit has one of the following statuses:
- Unresolved: The issue has not been resolved.
- Acknowledged: The issue remains in the code, but is a result of an intentional decision.
- Resolved: Adjusted program implementation to eliminate the risk.
- Partially resolved: Adjusted program implementation to eliminate part of the risk. The other part remains in the code, but is a result of an intentional decision.
- Mitigated: Implemented actions to minimize the impact or likelihood of the risk.
Critical Severity Issues
No issues found.
Medium Severity Issues
ME-01 Multiple claimPA() Calls
In the second phase, the
claimPA() may be called several times after the owner calls the
setPA() function, minting the PA reward several times to different addresses.
Add an internal boolean variable claimedPA to check if the PA has claimed its reward, set it to true when the
claimPA() function is executed and change the require statement in line 66 to use it. This change would also allow an executor of the
claim() function to run the
Resolved. Recommendation followed.
Minor Severity Issues
MI-01 No Zero Check
setPA() function does not check if the new account to be set is zero. This may lead to an invalid PA.
Check that account != 0 before assigning it.
Resolved. Recommendation followed.
MI-02 Phases Overlap
If a block happens to have the allowClaimAllDate timestamp, both
claimPA() will be able to be called, allowing a call to
claim() after a call to
Change the condition in the require statement in line 65 to block.timestamp > allowClaimAllDate. Note the lack of “=“.
Resolved. Both checks in
claimPA() were changed to fail if block.timestamp == allowClaimAllDate. This is not an issue, given that
claimPA() can be called in the next blocks.
MI-03 Solidity Version Pinning
- PoorPleb.sol: 2
The pragma solidity statement in line 2 allows the PoorPleb contract to be compiled with newer versions of the solidity compiler. This may introduce unintended bugs when new compilers are released.
Pin the solidity version in the pragma, not allowing newer compilers.
Resolved. Recommendation followed.
These items do not represent a security risk. They are best practices that we suggest implementing.
EN-01 Version Control
The audited code is not under version control, such as git or mercurial.
Track the versions of the developed code using a version control system.
Implemented. The corrected version is published in https://github.com/PoorPleb/PoorPlebSM.git, commit a81549c9ef980813178af0fd0115b247a9e80071.
EN-02 Automated Tests
While the development team provided us with some automated tests for the PoorPleb contract, they do not run properly.
Fix the tests and check that they have the proper coverage.
Implemented. Tests in the corrected version pass.
The considerations stated in this section are not right or wrong. We do not suggest any action to fix them. But we consider that they may be of interest to other stakeholders of the project, including users of the audited contracts, token holders or project investors.
The pleb address (PA in the source code) can be set by the contract owner multiple times. The development team informed us that they intend to renounce the ownership of the contract as soon as the contract is launched.
The audited contract cannot be upgraded.
These are the privileged roles that we identified on the PoorPleb contract.
The address with the owner role can set the pleb address (PA). Also, given that the contract is a standard OpenZeppelin Ownable, it can transfer the ownership to a different address or renounce its ownership, effectively making the contract ownerless. It must be noted that the development team informed us that they intend to run with no admin keys, so they will renounce the contract ownership as soon as the contract is launched.
Any address can call the
claimPA() function after 69 days. When this function is called, the address with the PA role will get N * 420_369.0 tokens, where N is the number of addresses that successfully called the
claim() function during the first phase.
The PoorPleb contract has two different phases.
The first phase happens for the first 69 days. During that phase, any address that can provide a proof to the
claim() function will be awarded 1420369 tokens. Each address may do the claim once in this phase
After those 69 days:
- addresses can no longer claim their rewards using the
- any address can call the
claimPA()function, awarding the
PA N * 420_369.0tokens to the PA. And addresses
PP tokens are minted in 3 different situations.
When the PoorPleb contract is deployed, 1_666_725_026_387.72 tokens are minted and assigned to the deployer address. Those 1_666_725_026_387.72 tokens correspond to over 1_100_000 claims.
During the first phase, when an address successfully calls the
claim() function, 1_420_369.0 tokens are minted and awarded to the caller of the function.
During the second phase, on a successful call of the
N * 420_369.0 are minted and awarded to the pleb address. N is the number of addresses that successfully called the
claim() function in the first phase.
- 2022-10-11 – Initial report.
- 2022-10-17 – Check fixes in commit