/**
* @dev The new owner accepts the gov transfer.
*/
function acceptGov() external {
address sender = _msgSender();
require(pendingGov() == sender, "Ownable2Step: caller is not the new gov");
_transferGov(sender);
}
}
[L-02] In Market.sol, gov address value, critical 0 address control is missing
With the constructor in the market.sol file, the initial address of gov is set with an argument of type address, but there is no check that prevents this address from starting with 0.
There is a risk that the gov variable is accidentally initialized to 0
Passing the address variable blank causes it to be set as 0 address.
The address initialized with 0 by mistake or forgetting cant be changed.
The value of gov is the value of the onlyGov modifier and should be msg.sender = gov, any function with this modifier will not work as this is not possible at 0 addresses
The biggest risk is; Until this value is found to be incorrect, the project can be used, users can trade, use the platform
src/Market.sol:
129 */
130: function setGov(address _gov) public onlyGov { gov = _gov; }
131:
132 /**
Proof of Concept
1- Platform is accidentally initialized with 0 gov
2- Users start using the platform, invest capital
3- It is impossible to update the gov value and the project has been started, there will be a serious reputation problem and users may lose money
This vulnerability can also be considered as a Rugpull risk.
Proof of Concept
gov powers;
10 results - 1 file
src/Market.sol:
117 //sets the oracle to a new oracle. Only callable by governance.
118: function setOracle(IOracle _oracle) public onlyGov { oracle = _oracle; }
123 //sets the borrow controller to a new borrow controller. Only callable by governance.
124: function setBorrowController(IBorrowController _borrowController) public onlyGov
129 //sets the address of governance. Only callable by governance.
130: function setGov(address _gov) public onlyGov { gov = _gov; }
135 //sets the lender to a new lender. The lender is allowed to recall dola from the contract.
136: function setLender(address _lender) public onlyGov { lender = _lender; }
141 //sets the pause guardian. The pause guardian can pause borrowing. Only callable by governance
142: function setPauseGuardian(address _pauseGuardian) public onlyGov
148 //sets the Collateral Factor requirement of the market as measured in basis points
149: function setCollateralFactorBps(uint _collateralFactorBps) public onlyGov {
160 //sets the Liquidation Factor of the market as denoted in basis points
161: function setLiquidationFactorBps(uint _liquidationFactorBps) public onlyGov {
171 //sets the Replenishment Incentive of the market as denoted in basis points
172: function setReplenismentIncentiveBps(uint _replenishmentIncentiveBps) public onlyGov {
182 //sets the Liquidation Incentive of the market as denoted in basis points
183: function setLiquidationIncentiveBps(uint _liquidationIncentiveBps) public onlyGov {
193 //sets the Liquidation Fee of the market as denoted in basis points.
194: function setLiquidationFeeBps(uint _liquidationFeeBps) public onlyGov {
Recommendation;
1- A timelock contract should be added to use gov privileges. In this way, users can be warned in case of a possible security weakness.
2- gov can be a Multisig wallet and this part is specified in the documentation
[L-04] Signature Malleability of EVM's ecrecover()
Description:
Description: The function calls the Solidity ecrecover() function directly to verify the given signatures. However, the ecrecover() EVM opcode allows malleable (non-unique) signatures and thus is susceptible to replay attacks.
Although a replay attack seems not possible for this contract, I recommend using the battle-tested OpenZeppelin's ECDSA library.
*Recommendation:**
Use the ecrecover function from OpenZeppelin's ECDSA library for signature verification. (Ensure using a version > 4.7.3 for there was a critical bug >= 4.1.0 < 4.7.3)
[L-05] Add updatable dola address function
Description:
The dolaaddress may change in the future, so add a function that ensures that this address can be changed safely.
Description:
Also check of the address to protect the code from 0x0 address problem just in case. This is best practice or instead of suggesting that they verify address != 0x0, you could add some good NatSpec comments explaining what is valid and what is invalid and what are the implications of accidentally using an invalid address.
Recommendation:
like this;
if (oracle == address(0)) revert ADDRESS_ZERO();
[N-03] Function writing that does not comply with the Solidity Style Guide
Context:
All Contracts
Description:
Order of Functions; ordering helps readers identify which functions they can call and to find the constructor and fallback definitions easier. But there are contracts in the project that do not comply with this.
Functions should be grouped according to their visibility and ordered:
constructor
receive function (if exists)
fallback function (if exists)
external
public
internal
private
within a grouping, place the view and pure functions last
[N-04] Compliance with Solidity Style rules in Constant expressions
Context:
src/DBR.sol:
13: uint8 public constant decimals = 18;
Recommendation:
Variables are declared as constant utilize the UPPER_CASE_WITH_UNDERSCORES format.
[N-05] Omissions in Events
Throughout the codebase, events are generally emitted when sensitive changes are made to the contracts. However, some events are missing important parameters
The events should include the new value and old value where possible:
Description:
In total 2 contracts, 7 unchecked are used, the functions used are critical. For this reason, there must be fuzzing tests in the tests of the project. Not seen in tests.
Recommendation:
Use should fuzzing test like Echidna.
As Alberto Cuesta Canada said:
Fuzzing is not easy, the tools are rough, and the math is hard, but it is worth it. Fuzzing gives me a level of confidence in my smart contracts that I didn’t have before. Relying just on unit testing anymore and poking around in a testnet seems reckless now.
Description:
Protocol has enabled optional compiler optimizations in Solidity.
There have been several optimization bugs with security implications. Moreover, optimizations are actively being developed. Solidity compiler optimizations are disabled by default, and it is unclear how many contracts in the wild actually use them.
Therefore, it is unclear how well they are being tested and exercised.
High-severity security issues due to optimization bugs have occurred in the past. A high-severity bug in the emscripten-generated solc-js compiler used by Truffle and Remix persisted until late 2018. The fix for this bug was not reported in the Solidity CHANGELOG.
Another high-severity optimization bug resulting in incorrect bit shift results was patched in Solidity 0.5.6. More recently, another bug due to the incorrect caching of keccak256 was reported.
A compiler audit of Solidity from November 2018 concluded that the optional optimizations may not be safe.
It is likely that there are latent bugs related to optimization and that new bugs will be introduced due to future optimizations.
Exploit Scenario
A latent or future bug in Solidity compiler optimizations—or in the Emscripten transpilation to solc-js—causes a security vulnerability in the contracts.
Recommendation:
Short term, measure the gas savings from optimizations and carefully weigh them against the possibility of an optimization-related bug.
Long term, monitor the development and adoption of Solidity compiler optimizations to assess their maturity.
[N-09] Avoid whitespaces while declaring mapping variables
Style Guide helps to maintain code layout consistent and make code more readable
Mappings : Avoid whitespaces while declaring mapping variables.
src/DBR.sol:
9: contract DolaBorrowingRights {
24: mapping (address => bool) public minters;
25: mapping (address => bool) public markets;
26: mapping (address => uint) public debts;
27: mapping (address => uint) public dueTokensAccrued;
28: mapping (address => uint) public lastUpdated;
[N-10] Require revert cause should be known
Context:
src/Fed.sol:
85 */
86: function expansion(IMarket market, uint amount) public {
93: require(globalSupply <= supplyCeiling);
Description:
Vulnerability related to description is not written to require, it must be written so that the user knows why the process is reverted.
This is an important debug analysis for programs and users that analyze transaction revert details such as Tenderly.
Recommendation:
src/Fed.sol:
85 */
86: function expansion(IMarket market, uint amount) public {
93: require(globalSupply <= supplyCeiling, "Should not exceed globalSupply ");
[N-11] NatSpec is missing
Description:
NatSpec is missing for the following functions , constructor and modifier:
Description: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, an outdated compiler version that might introduce bugs that affect the contract system negatively.
https://swcregistry.io/docs/SWC-103
[N-13] For functions, follow Solidity standard naming conventions
Description:
The above codes don't follow Solidity's standard naming convention,
internal and private functions : the mixedCase format starting with an underscore (_mixedCase starting with an underscore)
public and external functions : only mixedCase
constant variable : UPPER_CASE_WITH_UNDERSCORES (separated by uppercase and underscore)
[N-14] Missing Event for Critical Parameters Change
Description:
Events help non-contract tools to track changes, and events prevent users from being surprised by changes
Context:
22 results - 10 files
src/BorrowController.sol:
25 */
26: function setOperator(address _operator) public onlyOperator { operator = _operator; }
27
src/DBR.sol:
52 */
53: function setPendingOperator(address newOperator_) public onlyOperator {
54 pendingOperator = newOperator_;
61 */
62: function setReplenishmentPriceBps(uint newReplenishmentPriceBps_) public onlyOperator {
63 require(newReplenishmentPriceBps_ > 0, "replenishment price must be over 0");
src/Market.sol:
117 */
118: function setOracle(IOracle _oracle) public onlyGov { oracle = _oracle; }
119
123 */
124: function setBorrowController(IBorrowController _borrowController) public onlyGov { borrowController = _borrowController; }
125
129 */
130: function setGov(address _gov) public onlyGov { gov = _gov; }
131
135 */
136: function setLender(address _lender) public onlyGov { lender = _lender; }
137
141 */
142: function setPauseGuardian(address _pauseGuardian) public onlyGov { pauseGuardian = _pauseGuardian; }
143
148 */
149: function setCollateralFactorBps(uint _collateralFactorBps) public onlyGov {
150 require(_collateralFactorBps < 10000, "Invalid collateral factor");
160 */
161: function setLiquidationFactorBps(uint _liquidationFactorBps) public onlyGov {
162 require(_liquidationFactorBps > 0 && _liquidationFactorBps <= 10000, "Invalid liquidation factor");
171 */
172: function setReplenismentIncentiveBps(uint _replenishmentIncentiveBps) public onlyGov {
173 require(_replenishmentIncentiveBps > 0 && _replenishmentIncentiveBps < 10000, "Invalid replenishment incentive");
182 */
183: function setLiquidationIncentiveBps(uint _liquidationIncentiveBps) public onlyGov {
184 require(_liquidationIncentiveBps > 0 && _liquidationIncentiveBps + liquidationFeeBps < 10000, "Invalid liquidation incentive");
193 */
194: function setLiquidationFeeBps(uint _liquidationFeeBps) public onlyGov {
195 require(_liquidationFeeBps > 0 && _liquidationFeeBps + liquidationIncentiveBps < 10000, "Invalid liquidation fee");
src/Oracle.sol:
43 */
44: function setPendingOperator(address newOperator_) public onlyOperator { pendingOperator = newOperator_; }
45
52 */
53: function setFeed(address token, IChainlinkFeed feed, uint8 tokenDecimals) public onlyOperator { feeds[token] = FeedData(feed, tokenDecimals); }
54
60 */
61: function setFixedPrice(address token, uint price) public onlyOperator { fixedPrices[token] = price; }
Recommendation:
Add Event-Emit
[N-15] Open TODOS
Context:
src/escrows/INVEscrow.sol:
34 constructor(IXINV _xINV) {
35: xINV = _xINV; // TODO: Test whether an immutable variable will persist across proxies
36 }
Recommendation:
Use temporary TODOs as you work on a feature, but make sure to treat them before merging. Either add a link to a proper issue in your TODO, or remove it from the code.
[N-16] Add NatSpec Mapping comment
Description:
Add NatSpec comments describing mapping keys and values
Context:
6 results - 3 files
src/BorrowController.sol:
11: mapping(address => bool) public contractAllowlist;
src/DBR.sol:
19: mapping(address => uint256) public balances;
20: mapping(address => mapping(address => uint256)) public allowance;
23: mapping(address => uint256) public nonces;
24 mapping (address => bool) public minters;
src/Oracle.sol:
26 mapping (address => uint) public fixedPrices;
Recommendation:
/// @dev address(user) -> address(ERC0 Contract Address) -> uint256(allowance amount from user)
mapping(address => mapping(address => uint256)) public allowance;
[S-01] Add to blacklist function
Description:
Cryptocurrency mixing service, Tornado Cash, has been blacklisted in the OFAC.
A lot of blockchain companies, token projects, NFT Projects have blacklisted all Ethereum addresses owned by Tornado Cash listed in the US Treasury Department's sanction against the protocol.
https://home.treasury.gov/policy-issues/financial-sanctions/recent-actions/20220808
Some of these Projects;
USDC
Aave
Uniswap
Balancer
Infura
Alchemy
Opensea
dYdX
For this reason, every project in the Ethereum network must have a blacklist function, this is a good method to avoid legal problems in the future, apart from the current need.
Transactions from the project by an account funded by Tonadocash or banned by OFAC can lead to legal problems.Especially American citizens may want to get addresses to the blacklist legally, this is not an obligation
The ban on Tornado Cash makes little sense, because in the end, no one can prevent people from using other mixer smart contracts, or forking the existing ones. It neither hinders cybercrime, nor privacy.
Here is the most beautiful and close to the project example; Manifold
[S-03] Add NatSpec comments to the variables defined in Storage
Description:
I recommend adding NatSpec comments explaining the variables defined in Storage, their slots, their contents and definitions.
This improves code readability and control quality
Current Code;
src/DBR.sol:
8 */
9: contract DolaBorrowingRights {
10:
11: string public name;
12: string public symbol;
13: uint8 public constant decimals = 18;
14: uint256 public _totalSupply;
15: address public operator;
16: address public pendingOperator;
17: uint public totalDueTokensAccrued;
18: uint public replenishmentPriceBps;
19: mapping(address => uint256) public balances;
20: mapping(address => mapping(address => uint256)) public allowance;
21: uint256 internal immutable INITIAL_CHAIN_ID;
22: bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
23: mapping(address => uint256) public nonces;
24: mapping (address => bool) public minters;
25: mapping (address => bool) public markets;
26: mapping (address => uint) public debts; // user => debt across all tracked markets
27: mapping (address => uint) public dueTokensAccrued; // user => amount of due tokens accrued
28: mapping (address => uint) public lastUpdated; // user => last update timestamp
Recommendation Code;
/****** Slot 0 ******/
/**
* @notice Project name
* @dev Value is defined in the constructor
*/
string public name;
/****** Slot 1 ******/
/**
* @notice Project symbol
* @dev Value is defined in the constructor
*/
string public symbol
/****** Slot 2 ******/
/**
* @notice decimal
*/
uint8 public constant decimals = 18;
...
/****** End of storage ******/
Summary
Low Risk Issues List
safeSetgov
instead ofsetGov
functiongov
address value, critical 0 address control is missinggov
privileges can cause complete destruction of the project in a possible privateKey exploitdola
address functionTotal 5 issues
Non-Critical Issues List
0 address
checkFunction writing
that does not comply with theSolidity Style Guide
Total 16 issues
Suggestions
Total 3 suggestions
Details
[L-01] Use
safeSetgov
instead ofsetGov
functionContext:
Description:
Use a 2 structure
setGov
which is safer.safeSetgov
, use it is more secure due to 2-stage ownership transfer.Recommendation: Use
Ownable2Step.sol
Ownable2Step.sol[L-02] In Market.sol,
gov
address value, critical 0 address control is missinghttps://github.com/code-423n4/2022-10-inverse/blob/main/src/Market.sol#L77
Impact
With the constructor in the
market.sol
file, the initial address ofgov
is set with an argument of type address, but there is no check that prevents this address from starting with 0. There is a risk that thegov
variable is accidentally initialized to 0Passing the address variable blank causes it to be set as 0 address.
The address initialized with 0 by mistake or forgetting cant be changed.
The value of
gov
is the value of theonlyGov
modifier and should be msg.sender = gov, any function with this modifier will not work as this is not possible at 0 addressesThe biggest risk is; Until this value is found to be incorrect, the project can be used, users can trade, use the platform
Similar issue is also present in below function;
Proof of Concept
1- Platform is accidentally initialized with 0
gov
2- Users start using the platform, invest capital 3- It is impossible to update thegov
value and the project has been started, there will be a serious reputation problem and users may lose moneyTools Used
Manual Code Review
Recommended Mitigation Steps
Add an if block to the constructor ;
or add a constructor ;
[L-03] Very critical
gov
privileges can cause complete destruction of the project in a possible privateKey exploithttps://github.com/code-423n4/2022-10-inverse/blob/main/src/Market.sol#L92-L95
Vulnerability details
The contract’s
gov
is most impartant power role in this project. the gov is able to perform certain privileged activities.However,
gov
privileges are numerous and there is no timelock structure in the process of using these privileges.The
gov
is assumed to be an EOA, since the documents do not provide information on whether thegov
will be a multisign structure.In parallel with the private key thefts of the project owners, which have increased recently, this vulnerability has been stated as medium.
Similar vulnerability; Private keys stolen:
Hackers have stolen cryptocurrency worth around €552 million from a blockchain project linked to the popular online game Axie Infinity, in one of the largest cryptocurrency heists on record. Security issue : PrivateKey of the project officer was stolen: https://www.euronews.com/next/2022/03/30/blockchain-network-ronin-hit-by-552-million-crypto-heist
This vulnerability can also be considered as a Rugpull risk.
Proof of Concept
gov
powers;Recommendation;
1- A timelock contract should be added to use
gov
privileges. In this way, users can be warned in case of a possible security weakness.2-
gov
can be a Multisig wallet and this part is specified in the documentation[L-04] Signature Malleability of EVM's ecrecover()
Context:
Description: Description: The function calls the Solidity ecrecover() function directly to verify the given signatures. However, the ecrecover() EVM opcode allows malleable (non-unique) signatures and thus is susceptible to replay attacks.
Although a replay attack seems not possible for this contract, I recommend using the battle-tested OpenZeppelin's ECDSA library.
*Recommendation:** Use the ecrecover function from OpenZeppelin's ECDSA library for signature verification. (Ensure using a version > 4.7.3 for there was a critical bug >= 4.1.0 < 4.7.3)
[L-05] Add updatable
dola
address functionDescription: The
dola
address may change in the future, so add a function that ensures that this address can be changed safely.[N-01] Insufficient coverage
Description: The test coverage rate of the project is 89%. Testing all functions is best practice in terms of security criteria.
Due to its capacity, test coverage is expected to be 100%
[N-02]
0 address
checkContext: BorrowController.sol#L14 BorrowController.sol#L26 BorrowController.sol#L32 BorrowController.sol#L38 DBR.sol#L36 DBR.sol#L36 DBR.sol#L36 DBR.sol#L36 Fed.sol#L39 Market.sol#L77-L80
Description: Also check of the address to protect the code from 0x0 address problem just in case. This is best practice or instead of suggesting that they verify address != 0x0, you could add some good NatSpec comments explaining what is valid and what is invalid and what are the implications of accidentally using an invalid address.
Recommendation: like this;
if (oracle == address(0)) revert ADDRESS_ZERO();
[N-03]
Function writing
that does not comply with theSolidity Style Guide
Context: All Contracts
Description: Order of Functions; ordering helps readers identify which functions they can call and to find the constructor and fallback definitions easier. But there are contracts in the project that do not comply with this.
https://docs.soliditylang.org/en/v0.8.17/style-guide.html
Functions should be grouped according to their visibility and ordered:
constructor receive function (if exists) fallback function (if exists) external public internal private within a grouping, place the view and pure functions last
[N-04] Compliance with Solidity Style rules in Constant expressions
Context:
Recommendation: Variables are declared as constant utilize the UPPER_CASE_WITH_UNDERSCORES format.
[N-05] Omissions in Events
Throughout the codebase, events are generally emitted when sensitive changes are made to the contracts. However, some events are missing important parameters
The events should include the new value and old value where possible:
Events with no old value;
[N-06] Need Fuzzing test
Context: 35 results - 9 files Project uncheckeds list:
Description: In total 2 contracts, 7 unchecked are used, the functions used are critical. For this reason, there must be fuzzing tests in the tests of the project. Not seen in tests.
Recommendation: Use should fuzzing test like Echidna.
As Alberto Cuesta Canada said: Fuzzing is not easy, the tools are rough, and the math is hard, but it is worth it. Fuzzing gives me a level of confidence in my smart contracts that I didn’t have before. Relying just on unit testing anymore and poking around in a testnet seems reckless now.
https://medium.com/coinmonks/smart-contract-fuzzing-d9b88e0b0a05
[N-07] Use a more recent version of Solidity
Context: All contracts
Description: For security, it is best practice to use the latest Solidity version. For the security fix list in the versions; https://github.com/ethereum/solidity/blob/develop/Changelog.md
Recommendation: Old version of Solidity is used
(0.8.13)
, newer version can be used(0.8.17)
[N-08] Solidity compiler optimizations can be problematic
Context: foundry.toml#L6
Description: Protocol has enabled optional compiler optimizations in Solidity. There have been several optimization bugs with security implications. Moreover, optimizations are actively being developed. Solidity compiler optimizations are disabled by default, and it is unclear how many contracts in the wild actually use them.
Therefore, it is unclear how well they are being tested and exercised. High-severity security issues due to optimization bugs have occurred in the past. A high-severity bug in the emscripten-generated solc-js compiler used by Truffle and Remix persisted until late 2018. The fix for this bug was not reported in the Solidity CHANGELOG.
Another high-severity optimization bug resulting in incorrect bit shift results was patched in Solidity 0.5.6. More recently, another bug due to the incorrect caching of keccak256 was reported. A compiler audit of Solidity from November 2018 concluded that the optional optimizations may not be safe. It is likely that there are latent bugs related to optimization and that new bugs will be introduced due to future optimizations.
Exploit Scenario A latent or future bug in Solidity compiler optimizations—or in the Emscripten transpilation to solc-js—causes a security vulnerability in the contracts.
Recommendation: Short term, measure the gas savings from optimizations and carefully weigh them against the possibility of an optimization-related bug. Long term, monitor the development and adoption of Solidity compiler optimizations to assess their maturity.
[N-09] Avoid whitespaces while declaring mapping variables
Style Guide helps to maintain code layout consistent and make code more readable
Mappings : Avoid whitespaces while declaring mapping variables.
[N-10] Require revert cause should be known
Context:
Description: Vulnerability related to description is not written to require, it must be written so that the user knows why the process is reverted.
This is an important debug analysis for programs and users that analyze transaction revert details such as Tenderly.
Recommendation:
[N-11] NatSpec is missing
Description: NatSpec is missing for the following functions , constructor and modifier:
Context:
[N-12] Floating Pragma
Description: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, an outdated compiler version that might introduce bugs that affect the contract system negatively. https://swcregistry.io/docs/SWC-103
Context:
Recommendation: Lock the pragma version and also consider known bugs (https://github.com/ethereum/solidity/releases) for the compiler version that is chosen.
[N-13] For functions, follow Solidity standard naming conventions
Description:
The above codes don't follow Solidity's standard naming convention, internal and private functions : the mixedCase format starting with an underscore (_mixedCase starting with an underscore) public and external functions : only mixedCase constant variable : UPPER_CASE_WITH_UNDERSCORES (separated by uppercase and underscore)
Context:
[N-14] Missing Event for Critical Parameters Change
Description: Events help non-contract tools to track changes, and events prevent users from being surprised by changes
Context:
Recommendation: Add Event-Emit
[N-15] Open TODOS
Context:
Recommendation: Use temporary TODOs as you work on a feature, but make sure to treat them before merging. Either add a link to a proper issue in your TODO, or remove it from the code.
[N-16] Add NatSpec Mapping comment
Description: Add NatSpec comments describing mapping keys and values
Context:
Recommendation:
[S-01] Add to blacklist function
Description: Cryptocurrency mixing service, Tornado Cash, has been blacklisted in the OFAC. A lot of blockchain companies, token projects, NFT Projects have
blacklisted
all Ethereum addresses owned by Tornado Cash listed in the US Treasury Department's sanction against the protocol. https://home.treasury.gov/policy-issues/financial-sanctions/recent-actions/20220808Some of these Projects; USDC Aave Uniswap Balancer Infura Alchemy Opensea dYdX
For this reason, every project in the Ethereum network must have a blacklist function, this is a good method to avoid legal problems in the future, apart from the current need.
Transactions from the project by an account funded by Tonadocash or banned by OFAC can lead to legal problems.Especially American citizens may want to get addresses to the blacklist legally, this is not an obligation
If you think that such legal prohibitions should be made directly by validators, this may not be possible: https://www.paradigm.xyz/2022/09/base-layer-neutrality
The ban on Tornado Cash makes little sense, because in the end, no one can prevent people from using other mixer smart contracts, or forking the existing ones. It neither hinders cybercrime, nor privacy.
Here is the most beautiful and close to the project example; Manifold
Manifold Contract https://etherscan.io/address/0xe4e4003afe3765aca8149a82fc064c0b125b9e5a#code
Recommended Mitigation Steps add to Blacklist function and modifier.
[S-02] Generate perfect code headers every time
Description: I recommend using header for Solidity code layout and readability
https://github.com/transmissions11/headers
[S-03] Add NatSpec comments to the variables defined in Storage
Description: I recommend adding NatSpec comments explaining the variables defined in Storage, their slots, their contents and definitions.
This improves code readability and control quality
Current Code;
Recommendation Code;