Before doing these computations, add a non-zero check to the variables aforementioned.
nonReentrant modifier unused
PROBLEM
Some external functions calling the ERC20 methods safeTransfer or safeTransferFrom do not have the nonReentrant modifier and are hence unprotected to reentrancy (besides the gas limit on the methods)
There is no restriction on the numbers of pool added, either in _addFundingPool() or setFundingPoolWeight(). Hence, the for loop in CitadelMinter.sol is unbounded, which could result in the gas required to perform the function call exceeding the gas limit if the number of pools is too high, preventing the distribution of citadel tokens to the funding pools.
SEVERITY
Low
PROOF OF CONCEPT
Instances include:
CitadelMinter.sol
CitadelMinter.sol:344:
TOOLS USED
Manual Analysis
MITIGATION
Add an upper limit to the total amount of funding pools, and add a check before adding a pool in setFundingPoolWeight(): 274
Typos
PROBLEM
There are a few typos in some comments
SEVERITY
Non-Critical
PROOF OF CONCEPT
Instances include:
CitadelMinter.sol
CitadelMinter.sol: 101 expection
Funding.sol
Funding.sol: 289 cumulatiive
TOOLS USED
Manual Analysis
MITIGATION
Correct the typos.
Comment Missing function parameter
PROBLEM
Some of the function comments are missing function parameters or returns
When there are mappings that use the same key value, having separate fields is error prone, for instance in case of deletion or with future new fields.
SEVERITY
Non-Critical
PROOF OF CONCEPT
Instances include:
KnightingRound.sol
KnightingRound.sol: 47 mapping(address => uint256) public boughtAmounts;
KnightingRound.sol: 51 mapping(address => bool) public hasClaimed;
StakedCitadel.sol
StakedCitadel.sol: 94: mapping(address => uint256) public additionalTokensEarned;
StakedCitadel.sol: 95: mapping(address => uint256) public lastAdditionalTokenAmount;
TOOLS USED
Manual Analysis
MITIGATION
Group the related data in a struct and use one mapping. For instance, for the StakedCitadel.sol mappings, the mitigation could be:
It is not recommended to emit events before the end of the computations, as the function might revert based on conditions ahead of the event emission
SEVERITY
Non-Critical
PROOF OF CONCEPT
Instances include:
Funding.sol
Funding.sol: 328 Sweep(_token, amount) //emitted before amount transferred to saleRecipient
KnightingRound.sol
KnightingRound.sol: 413 Sweep(_token, amount) //emitted before amount transferred to saleRecipient
TOOLS USED
Manual Analysis
MITIGATION
Place the event emission in the last position in the function.
Unused storage variable
PROBLEM
Though they are allowed in Solidity and do not pose a direct security issue, it is best practice to avoid declaring storage variables that are not used.
They cause an increase in computations (and unnecessary gas consumption) and can decrease readability of the code
SEVERITY
Non-Critical
PROOF OF CONCEPT
Instances include:
GlobalAccessControl.sol
GlobalAccessControl.sol:51:
//storage variable transferFromDisabled never initialized or read.
TOOLS USED
Manual Analysis
MITIGATION
Remove this storage variable
Redundant variable declaration
PROBLEM
It is best to use as few local variables as possible to avoid unnecessary computations.
SEVERITY
Non-Critical
PROOF OF CONCEPT
Instances include:
StakedCitadel.sol
StakedCitadel.sol: 773
uint256 _before = token.balanceOf(address(this));
//_pool is already initialized with token.balanceOf(address(this));
TOOLS USED
Manual Analysis
MITIGATION
remove the declaration of _before line 773, and replace _before line 776 with _pool
For clarity, you can rename _before and _after as _poolBefore and _poolAfter
Incorrect variable type
PROBLEM
cachedXCitadel is an instance of IVault, not an address. Even if the code compiles and works, the syntax in the deposit() call is misleading.
SEVERITY
Non-Critical
PROOF OF CONCEPT
Instances include:
CitadelMinter.sol
CitadelMinter.sol: 195
IVault(cachedXCitadel).deposit(lockingAmount);
//cachedXCitadel is not an address
TOOLS USED
Manual Analysis
MITIGATION
replace IVault(cachedXCitadel) with
cachedXCitadel
Uint256 alias
PROBLEM
uint is an alias for uint256.
It is better to use uint256: it brings readability and consistency in the code, and it future proofs it in case of any changes to the alias of uint
QA Report
Table of Contents
Check zero denominator
PROBLEM
When a division is computed, it must be ensured that the denominator is non-zero to prevent failure of the function call.
SEVERITY
Low
PROOF OF CONCEPT
Instances include:
Funding.sol
StakeCitadel.sol
TOOLS USED
Manual Analysis
MITIGATION
Before doing these computations, add a non-zero check to the variables aforementioned.
nonReentrant modifier unused
PROBLEM
Some external functions calling the ERC20 methods
safeTransfer
orsafeTransferFrom
do not have the nonReentrant modifier and are hence unprotected to reentrancy (besides the gas limit on the methods)SEVERITY
Low
PROOF OF CONCEPT
Instances include:
CitadelToken.sol
KnightingRound.sol
StakedCitadel.sol
StakedCitadelVester.sol
TOOLS USED
Manual Analysis
MITIGATION
Use the nonReentrant modifier on these functions.
Unbounded for loop
PROBLEM
There is no restriction on the numbers of pool added, either in
_addFundingPool()
orsetFundingPoolWeight()
. Hence, the for loop inCitadelMinter.sol
is unbounded, which could result in the gas required to perform the function call exceeding the gas limit if the number of pools is too high, preventing the distribution of citadel tokens to the funding pools.SEVERITY
Low
PROOF OF CONCEPT
Instances include:
CitadelMinter.sol
TOOLS USED
Manual Analysis
MITIGATION
Add an upper limit to the total amount of funding pools, and add a check before adding a pool in
setFundingPoolWeight(): 274
Typos
PROBLEM
There are a few typos in some comments
SEVERITY
Non-Critical
PROOF OF CONCEPT
Instances include:
CitadelMinter.sol
Funding.sol
TOOLS USED
Manual Analysis
MITIGATION
Correct the typos.
Comment Missing function parameter
PROBLEM
Some of the function comments are missing function parameters or returns
SEVERITY
Non-Critical
PROOF OF CONCEPT
Instances include:
Funding.sol
GlobalAccessControl.sol
KnightingRound.sol
StakedCitadel.sol
TOOLS USED
Manual Analysis
MITIGATION
Add a comment for these parameters
Function missing comments
PROBLEM
Some functions are missing comments.
SEVERITY
Non-Critical
PROOF OF CONCEPT
Instances include:
CitadelMinter.sol
Funding.sol
GlobalAccessControl.sol
KnightingRound.sol
StakedCitadelVester.sol
SupplySchedule.sol
TOOLS USED
Manual Analysis
MITIGATION
Add comments to these functions
Related data should be grouped in struct
PROBLEM
When there are mappings that use the same key value, having separate fields is error prone, for instance in case of deletion or with future new fields.
SEVERITY
Non-Critical
PROOF OF CONCEPT
Instances include:
KnightingRound.sol
StakedCitadel.sol
TOOLS USED
Manual Analysis
MITIGATION
Group the related data in a struct and use one mapping. For instance, for the
StakedCitadel.sol
mappings, the mitigation could be:And it would be used as a state variable:
Events emitted early
PROBLEM
It is not recommended to emit events before the end of the computations, as the function might revert based on conditions ahead of the event emission
SEVERITY
Non-Critical
PROOF OF CONCEPT
Instances include:
Funding.sol
KnightingRound.sol
TOOLS USED
Manual Analysis
MITIGATION
Place the event emission in the last position in the function.
Unused storage variable
PROBLEM
Though they are allowed in Solidity and do not pose a direct security issue, it is best practice to avoid declaring storage variables that are not used.
They cause an increase in computations (and unnecessary gas consumption) and can decrease readability of the code
SEVERITY
Non-Critical
PROOF OF CONCEPT
Instances include:
GlobalAccessControl.sol
TOOLS USED
Manual Analysis
MITIGATION
Remove this storage variable
Redundant variable declaration
PROBLEM
It is best to use as few local variables as possible to avoid unnecessary computations.
SEVERITY
Non-Critical
PROOF OF CONCEPT
Instances include:
StakedCitadel.sol
TOOLS USED
Manual Analysis
MITIGATION
remove the declaration of
_before
line 773, and replace_before
line 776 with_pool
For clarity, you can rename
_before
and_after
as_poolBefore
and_poolAfter
Incorrect variable type
PROBLEM
cachedXCitadel
is an instance ofIVault
, not an address. Even if the code compiles and works, the syntax in thedeposit()
call is misleading.SEVERITY
Non-Critical
PROOF OF CONCEPT
Instances include:
CitadelMinter.sol
TOOLS USED
Manual Analysis
MITIGATION
replace
IVault(cachedXCitadel)
withcachedXCitadel
Uint256 alias
PROBLEM
uint
is an alias foruint256
.It is better to use uint256: it brings readability and consistency in the code, and it future proofs it in case of any changes to the alias of uint
SEVERITY
Non-Critical
PROOF OF CONCEPT
Instances include:
Funding.sol
TOOLS USED
Manual Analysis
MITIGATION
replace
uint
withuint256