-
LPFarm.sol
owner
can updateMultiplier, add, set, setRewardsPerSecond.
-
Stake.sol
owner
can updateMultiplier, setRewardPerSecond.
-
BaseJumpRateModelV2.sol
owner
can updateJumpRateModel.
-
Comptroller.sol
admin
is the deployer of the contract, can initMining, reInitMining, setPriceOracle, setCloseFactor, setCollateralFactor, setLiquidationIncentive, supportMarket, setBorrowCapGuardian, setPauseGuardian, setMintPaused, setBorrowPaused … etc.
-
CToken.sol
admin
can initialize CToken Contract.
-
Call updatePool()
when updating BONUS_MULTIPLIER
and rewardsPerSecond
. (Stake.sol
, LPFarm.sol
)Medium
Description: UpdatePool
needs to be called when updating BONUS_MULTIPLIER
and rewardsPerSecond
.
Recommendation: Pool’s reward variables need to be updated. So UpdatePool
needs to be called when updating BONUS_MULTIPLIER
and rewardsPerSecond
.
Result: Resolved in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8.
-
Insufficient reward token balance in pool might cause transaction to fail when users try to claim their accumulated rewards. (Stake.sol
, LPFarm.sol
) Medium
Description: The reward token transfer to user will fail if there is no enough reward tokens in pool and user will not be able to stake
, withdraw
and claimReward
Recommendation: We recommend to change the safeRewardsTransfer()
to the followings:
function safeRewardsTransfer(address to, uint amount) internal {
uint rewardTokenBalance = IERC20(rewardsToken).balanceOf(address(this));
if(amount > rewardTokenBalance) {
TransferHelper.safeTransfer(rewardsToken, to, rewardTokenBalance);
} else {
TransferHelper.safeTransfer(rewardsToken, to, amount);
}
}
Result: Resolved in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8.
-
As the Solidity version ^0.5.16
is defined in the pragma, the arithmetic calculation should be treated carefully. The function princeInternal()
and getUnderlyingPrice()
should be revised by using SafeMath
library in openzeppelin. (OracleAnchoredView.priceInternal()
: L52, OracleAnchoredView.getUnderlyingPrice()
: L59) Medium
Description: The int256(data.rate) / 1e10
and 1e28 * rate / config.baseUnit
are not safe.
Recommendation: Either update the compiler version to be greater than or equal to 0.8.0, or the function should be revised by using SafeMath
library in openzeppelin.
Result: Resolved by using SafeMath
library in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8.
-
RequireisMiningInit
to be true in reInitMining
. (Comptroller.reInitMining()
: L105) Minor
Description: RequireisMiningInit
to be true in reInitMining
.
Recommendation:
function reInitMining(uint256 _reductionPeriod) external{
require(msg.sender == admin,"only admin can call this function");
require(isMiningInit, "mining not inited");
reductionPeriod = _reductionPeriod;
startSpeedTime = getBlockTimestamp();
}
Result: reInitMining()
is removed from Comptroller.sol
in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8.
-
nextTotalBorrows
should be less or equal to borrowCap
(Comptroller.borrowAllowed()
: L390)Minor
Description:nextTotalBorrows
should be less or equal to borrowCap
.
Recommendation: Replace with the suggested method below.
require(nextTotalBorrows <= borrowCap, "market borrow cap reached");
Result: Resolved in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8.
-
uint endSpeedTime
depends on literal value (1440 day
). (updateCompSupplyIndex()
: L1161, updateCompBorrowIndex()
: L1199) Minor
Description: uint endSpeedTime
depends on 1440 day
instead of uint reductionPeriod
.
Recommendation: Replace with the suggested method below.
uint endSpeedTime = startSpeedTime + reductionPeriod.mul(maxExp);
Result: Resolved in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8.
-
Missing error message in getAccountLimits()
(CompoundLens.sol
: L242). Minor
Description: The require(errorCode == 0)
statement misses its error message.
Recommendation: Add error message for the require statement.
Result: Resolved in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8.
-
lpTokenTotal[pool.lpToken]
is not a secure way of reading lp token amount. (LPFarm.sol
) Minor
Description: generally speaking, use a mapping to record token in/out amount is not a good practice. A small mistake in withdraw deduction might create flashloan attack oppotunities.
Recommendation: pool.lpToken.balanceOf(address(this))
is a more secure way of reading the lp token balance.
uint256 lpSupply = pool.lpToken.balanceOf(address(this));
Result: This suggestion is not adopted.
-
Based on the above suggestion, mapping (address => uint) public lpTokenTotal
can be deleted. (LPFarm.sol
)Minor
Description: if above suggestion been implemented, then there is no need to have the lpTokenTotal mapping.
Recommendation: using mapping to store lpToken balance is not a secure way of reading balance. Thus, remove this mapping, change to only use IERC20 interface balanceOf()
is a safer choice.
Result: This suggestion is not adopted.
-
Maximum Borrow Rate is Too Large for Emerald Chain. The value now is still 0.0005%/block (CTokenInterface.sol
) Minor
Description: We should decrease this value for Emerald Chain.
Recommendation: If we have changed the unit of block to second, we should change the value in terms of a time duration.
Result: Resolved in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8.
-
bool internal _notEntered;
in CTokenInterfaces.sol should be removed and use the nonReentrant instead (CTokenInterface.sol
) Minor
Description: Since we are using nonReentrant, the mutex variable should be removed.
Recommendation: Use nonReentrant
instead.
Result: This suggestion is not adopted.
-
Consider using Solidity >= 0.8.0 to remove the CarefulMath.sol, for all files using addUInt, subUint, mulUint, and divUint.Minor
Description: After 0.8.0, the overflow and underflow problems are considered by EVM.
Recommendation: Using pragma solidity ^0.8.0
Result: This suggestion is not adopted.
-
Should sanitize zero address in function _setPendingAdmin(address newPendingAdmin) public returns (uint)
. (Unitroller.sol
) Minor
Description: The newPendingAdmin
should not be zero address.
Recommendation: Require newPendingAdmin
is not zero address.
Result: This suggestion is not adopted.
-
Should sanitize zero address in constructor(address admin_, uint delay_)
. (Timelock.sol
) Minor
Description: The admin
should not be zero address.
Recommendation: Require admin
is not zero address. Also consider passing msg.sender
to admin
instead of passing admin_
.
Result: Zero address check for admin_
is added, but the input parameter admin_
instead of suggested msg.sender
is still assigned to admin
in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8.
-
Should sanitize zero address in constructor(address _owner,address _guardian)
. (FTPGuardian.sol
) Minor
Description: The _owner
and _guardian
should not be zero address.
Recommendation: Require _owner
and _guardian
are not zero address. Also consider passing msg.sender
to owner
instead of passing _owner
.
Result: Resolved in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8.
-
Use Openzeppelin Contract Wizards to Generate Ftp.sol
Informational
Description: current contract code is not updated version of ERC20, although it serves the purposes, but switch to latest version can make project code looks more professional
Recommendation: generate an ERC20 contract from OpenZeppelin Wizard.

Result: Resolved in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8
-
Move all variables to a storage contract. (Stake.sol
) Informational
Description: Stake.sol
is an upgradable contract. Move all variables to a storage contract to prevent storage clashes during future contract upgrades.
Recommendation: We recommend to move all variables to a storage contract and let the Stake.sol
inherit the storage contract.
contract StakeStorage {
address public stakeToken;
address public rewardsToken;
uint public rewardsPerSecond;
uint public BONUS_MULTIPLIER;
uint public startTime;
uint public endTime;
uint public stakeTokenTotal;
uint public accRewardsPerShare;
uint public lastRewardBlockTimestamp;
mapping(address => uint) public userStakeTime;
mapping(address => uint) public stakeBalance;
mapping(address => uint) public rewardDebt;
}
contract Stake is Initializable, OwnableUpgradeable, ReentrancyGuardUpgradeable, StakeStorage {
}
Result: Resolved in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8.
-
Non-constant variable BONUS_MULTIPLIER
should be in camel case. Informational
Description: Non-constant variable BONUS_MULTIPLIER
should be camel case.
Recommendation: Use bounsMultiplier
to replace BONUS_MULTIPLIER
.
Result: Resolved in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8.
-
Lack of parameter checks (Stake.initialize()
L30, Stake.updateMultiplier()
L39, Stake.setRewardsPerSecond()
L43) Informational
Description: No parameter checks when assigning values to variables.
Recommendation: Add zero address check for address
and numbers range check for uint
Result:
All modifications below are done in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8.
- Only zero address check is added in
Stake.initialize()
, but not numbers range check.
- Numbers range check is added in
Stake.updateMultiplier()
.
- Numbers range check is added in
Stake.setRewardsPerSecond()
-
Magic number 1e12
. (Stake.sol
, LPFarm.sol
) Informational
Description: Magic number 1e12
.
Recommendation: Make 1e12
a constant variable.
All modifications above are done in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8
-
updateStakingPool()
can be replaced by a single line of code. (LPFarm.updateStakingPool()
: L87)Informational
Description: There is no need to calculate totalAllocPoint each time adding a pool, simply a line of update in set()
function.
Recommendation: Below is the suggested method.
function set(
uint256 _pid,
uint256 _allocPoint,
bool _withUpdate
) public onlyOwner {
if (_withUpdate) {
massUpdatePools();
}
totalAllocPoint = totalAllocPoint.sub(poolInfo[_pid].allocPoint).add(
_allocPoint
);
poolInfo[_pid].allocPoint = _allocPoint;
}
Result: This suggestion is not adopted.
-
The address of the reward token should be immutable (LPFarm.sol
: L22). Informational
Description: The address of the reward token should be immutable.
Recommendation: Change address public rewardToken;
to address immutable public rewardToken;
Result: This suggestion is not adopted.
-
The code totalAllocPoint = totalAllocPoint + _allocPoint;
should be removed from the function add
(LPFarm.sol
: L68) Informational
Description: In this function, we call function updateStakingPool()
in the end, where we recalculate the totalAllocPoint
.
**Recommendation
**: Remove line 68.
Result: Resolved in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8
-
Missing indexed
for important arguments in event ActionPaused
, CompGranted
,CompGranted
, MarketEntered
, MarketExited
, and MarketListed
(IComptroller
)Informational
Description: The argument cToken
in event ActionPaused
, MarketEntered
, MarketExited
and MarketListed
, recipient
in event CompGranted
should be indexed.
Recommendation: The argument cToken
should be indexed.
Result: This suggestion is not adopted.
-
Lack of parameter checks (Comptroller.initMining()
: L97, Comptroller.reInitMining()
: L105) Informational
Description: No parameter checks when assigning values to variables.
Recommendation: Add number range check for _reductionPeriod
Result: Resolved in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8
-
Access control on msg.sender
address can be abstracted to modifiers. (Comptroller.initMining()
L97, Comptroller.reInitMining()
L105, Comptroller._setPriceOracle()
L845, Comptroller._setCloseFactor()
L867, Comptroller._setCollateralFactor
L885, Comptroller._setPauseGuardian()
L1047)Informational
Description: The repeated uses of checking msg.sender
can be abstracted to a modifier.
Recommendation: Replace with the suggested method below.
modifier onlyAdmin(){
require(msg.sender == admin);
_;
}
Result: This suggestion is not adopted.
-
Constant variables name could be upper case in Comptroller.sol
. (compInitialIndex
: L74, closeFactorMinMantissa
: L77, closeFactorMaxMantissa
: L80, collateralFactorMaxMantissa
: L83, maxExp
: L85) Informational
Description: Constant variables names could be upper case.
Recommendation: Replace with the variable name with their upper case formats.
Result: This suggestion is not adopted.
-
Contract InterestRateModel
should be defined as an interface (Line 7). (InterestRateModel.sol
)Informational
Description: InterestRateModel should be defined as an interface rather than a contract
Recommendation: Change it to be an interface.
Result: This suggestion is not adopted.
-
The redundant owner
in event NewGuardian(address owner,address oldGuardian,address newGuardian)
(FTPGuardian.sol
: L4). Informational
Description: The owner
is redundant in the event field since owner
is unchangeable.
Recommendation: Remove the address owner
field in the event declaration (L4) and its use (L16).
Result: Resolved in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8
-
Nominating the same guardian is allowed in setGuardian()
(FTPGuardian.sol
: L12). Informational
Description: The owner
can set new guardian with the same address as the old guardian.
Recommendation: Require the newGuardian
is not equal to the guardian
.
Result: Resolved in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8
-
Define FTP addres as a constant or immutable. (ComptrollerG3.sol
: L1396, ComptrollerG6.sol
:L1377) Informational
Description: Define FTP addres as a constant or immutable…
Recommendation: Define FTP addres as a constant or immutable.
Result: This suggestion is not adopted.
tags:
Final Report
Fountain Protocol Audit
This report presents Verilog’s smart contract auditing engagement with Fountain Protocol. Fountain Protocol is one of the first Lending protocols on the Emerald Paratime of Oasis Network.
Table of Content
Project Summary
Fountain Protocol is a high capital efficiency, one-stop capital management platform for users’ DeFi Assets. Fountain Protocol can take advantage of the extremely efficient and low-cost Oasis Network and create a fund pool with a diverse source of revenue and DeFi applications.
Service Scope
Our audit is conducted on the main branch, with commit hash cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8.
Our engagement with Fountain Protocol includes the following two services:
Pre-Audit Consulting Service
As a part of the pre-audit service, the Verilog team worked closely with the Fountain development team to discuss potential vulnerability and smart contract development best practices in a timely fashion. Verilog team is very appreciative of establishing an efficient and effective communication channel with the Fountain team, as new findings were often exchanged promptly and fixes were deployed quickly, during the preliminary report stage.
Audit Service
The Verilog team conducted a thorough study of the Fountain code, with the Fountain architecture graph and UML graph presented below in the Fountain Architecture section. The list of findings, along with the severity and solution, is available under section Findings & Improvement Suggestions.
Architecture
These are the major smart contracts in the Fountain Protocol:
Ftp.sol
: Ftp Governance Token Smart ContractIComptroller.sol
: compound controller interfaceCompoundLens.sol
TransferHelper.sol
LPFarm.sol
: liquidity mining contractStake.sol
: single token staking contractBaseJumpRateModelV2.sol
: logic for Compound’s Jump Rate ModelCarefulMath.so
l: math libraryCErc20.sol
: Compound’s CErc20 ContractCEther.sol
: Compound’s CEther ContractComptroller.sol
: Compound’s Comptroller ContractCToken.sol
: Compound’s CToken ContractPrivileged Roles
LPFarm.sol
owner
can updateMultiplier, add, set, setRewardsPerSecond.Stake.sol
owner
can updateMultiplier, setRewardPerSecond.BaseJumpRateModelV2.sol
owner
can updateJumpRateModel.Comptroller.sol
admin
is the deployer of the contract, can initMining, reInitMining, setPriceOracle, setCloseFactor, setCollateralFactor, setLiquidationIncentive, supportMarket, setBorrowCapGuardian, setPauseGuardian, setMintPaused, setBorrowPaused … etc.CToken.sol
admin
can initialize CToken Contract.Findings & Suggestions for Improvement
InformationalMinorMediumMajorCritical
Critical
Ftp.sol
) CriticalDescription: Arithmetic operations reverting on underflow and overflow is a feature only available in solidity
^0.8.0
.Recommendation: Use
SafeMath
library or change solidity version to^0.8.0
.Result: Resolved in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8.
Major
none ;)
Medium
Call
updatePool()
when updatingBONUS_MULTIPLIER
andrewardsPerSecond
. (Stake.sol
,LPFarm.sol
)MediumDescription:
UpdatePool
needs to be called when updatingBONUS_MULTIPLIER
andrewardsPerSecond
.Recommendation: Pool’s reward variables need to be updated. So
UpdatePool
needs to be called when updatingBONUS_MULTIPLIER
andrewardsPerSecond
.Result: Resolved in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8.
Insufficient reward token balance in pool might cause transaction to fail when users try to claim their accumulated rewards. (
Stake.sol
,LPFarm.sol
) MediumDescription: The reward token transfer to user will fail if there is no enough reward tokens in pool and user will not be able to
stake
,withdraw
andclaimReward
Recommendation: We recommend to change the
safeRewardsTransfer()
to the followings:Result: Resolved in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8.
As the Solidity version
^0.5.16
is defined in the pragma, the arithmetic calculation should be treated carefully. The functionprinceInternal()
andgetUnderlyingPrice()
should be revised by usingSafeMath
library in openzeppelin. (OracleAnchoredView.priceInternal()
: L52,OracleAnchoredView.getUnderlyingPrice()
: L59) MediumDescription: The
int256(data.rate) / 1e10
and1e28 * rate / config.baseUnit
are not safe.Recommendation: Either update the compiler version to be greater than or equal to 0.8.0, or the function should be revised by using
SafeMath
library in openzeppelin.Result: Resolved by using
SafeMath
library in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8.Minor
Require
isMiningInit
to be true inreInitMining
. (Comptroller.reInitMining()
: L105) MinorDescription: Require
isMiningInit
to be true inreInitMining
.Recommendation:
Result:
reInitMining()
is removed fromComptroller.sol
in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8.nextTotalBorrows
should be less or equal toborrowCap
(Comptroller.borrowAllowed()
: L390)MinorDescription:
nextTotalBorrows
should be less or equal toborrowCap
.Recommendation: Replace with the suggested method below.
Result: Resolved in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8.
uint endSpeedTime
depends on literal value (1440 day
). (updateCompSupplyIndex()
: L1161,updateCompBorrowIndex()
: L1199) MinorDescription:
uint endSpeedTime
depends on1440 day
instead ofuint reductionPeriod
.Recommendation: Replace with the suggested method below.
Result: Resolved in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8.
Missing error message in
getAccountLimits()
(CompoundLens.sol
: L242). MinorDescription: The
require(errorCode == 0)
statement misses its error message.Recommendation: Add error message for the require statement.
Result: Resolved in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8.
lpTokenTotal[pool.lpToken]
is not a secure way of reading lp token amount. (LPFarm.sol
) MinorDescription: generally speaking, use a mapping to record token in/out amount is not a good practice. A small mistake in withdraw deduction might create flashloan attack oppotunities.
Recommendation:
pool.lpToken.balanceOf(address(this))
is a more secure way of reading the lp token balance.Result: This suggestion is not adopted.
Based on the above suggestion,
mapping (address => uint) public lpTokenTotal
can be deleted. (LPFarm.sol
)MinorDescription: if above suggestion been implemented, then there is no need to have the lpTokenTotal mapping.
Recommendation: using mapping to store lpToken balance is not a secure way of reading balance. Thus, remove this mapping, change to only use IERC20 interface
balanceOf()
is a safer choice.Result: This suggestion is not adopted.
Maximum Borrow Rate is Too Large for Emerald Chain. The value now is still 0.0005%/block (
CTokenInterface.sol
) MinorDescription: We should decrease this value for Emerald Chain.
Recommendation: If we have changed the unit of block to second, we should change the value in terms of a time duration.
Result: Resolved in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8.
bool internal _notEntered;
in CTokenInterfaces.sol should be removed and use the nonReentrant instead (CTokenInterface.sol
) MinorDescription: Since we are using nonReentrant, the mutex variable should be removed.
Recommendation: Use
nonReentrant
instead.Result: This suggestion is not adopted.
Consider using Solidity >= 0.8.0 to remove the CarefulMath.sol, for all files using addUInt, subUint, mulUint, and divUint.Minor
Description: After 0.8.0, the overflow and underflow problems are considered by EVM.
Recommendation: Using pragma solidity ^0.8.0
Result: This suggestion is not adopted.
Should sanitize zero address in
function _setPendingAdmin(address newPendingAdmin) public returns (uint)
. (Unitroller.sol
) MinorDescription: The
newPendingAdmin
should not be zero address.Recommendation: Require
newPendingAdmin
is not zero address.Result: This suggestion is not adopted.
Should sanitize zero address in
constructor(address admin_, uint delay_)
. (Timelock.sol
) MinorDescription: The
admin
should not be zero address.Recommendation: Require
admin
is not zero address. Also consider passingmsg.sender
toadmin
instead of passingadmin_
.Result: Zero address check for
admin_
is added, but the input parameteradmin_
instead of suggestedmsg.sender
is still assigned toadmin
in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8.Should sanitize zero address in
constructor(address _owner,address _guardian)
. (FTPGuardian.sol
) MinorDescription: The
_owner
and_guardian
should not be zero address.Recommendation: Require
_owner
and_guardian
are not zero address. Also consider passingmsg.sender
toowner
instead of passing_owner
.Result: Resolved in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8.
Informational
Use Openzeppelin Contract Wizards to Generate

Ftp.sol
InformationalDescription: current contract code is not updated version of ERC20, although it serves the purposes, but switch to latest version can make project code looks more professional
Recommendation: generate an ERC20 contract from OpenZeppelin Wizard.
Result: Resolved in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8
Move all variables to a storage contract. (
Stake.sol
) InformationalDescription:
Stake.sol
is an upgradable contract. Move all variables to a storage contract to prevent storage clashes during future contract upgrades.Recommendation: We recommend to move all variables to a storage contract and let the
Stake.sol
inherit the storage contract.Result: Resolved in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8.
Non-constant variable
BONUS_MULTIPLIER
should be in camel case. InformationalDescription: Non-constant variable
BONUS_MULTIPLIER
should be camel case.Recommendation: Use
bounsMultiplier
to replaceBONUS_MULTIPLIER
.Result: Resolved in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8.
Lack of parameter checks (
Stake.initialize()
L30,Stake.updateMultiplier()
L39,Stake.setRewardsPerSecond()
L43) InformationalDescription: No parameter checks when assigning values to variables.
Recommendation: Add zero address check for
address
and numbers range check foruint
Result:
All modifications below are done in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8.
Stake.initialize()
, but not numbers range check.Stake.updateMultiplier()
.Stake.setRewardsPerSecond()
Magic number
1e12
. (Stake.sol
,LPFarm.sol
) InformationalDescription: Magic number
1e12
.Recommendation: Make
1e12
a constant variable.All modifications above are done in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8
updateStakingPool()
can be replaced by a single line of code. (LPFarm.updateStakingPool()
: L87)InformationalDescription: There is no need to calculate totalAllocPoint each time adding a pool, simply a line of update in
set()
function.Recommendation: Below is the suggested method.
Result: This suggestion is not adopted.
The address of the reward token should be immutable (
LPFarm.sol
: L22). InformationalDescription: The address of the reward token should be immutable.
Recommendation: Change
address public rewardToken;
toaddress immutable public rewardToken;
Result: This suggestion is not adopted.
The code
totalAllocPoint = totalAllocPoint + _allocPoint;
should be removed from the functionadd
(LPFarm.sol
: L68) InformationalDescription: In this function, we call function
updateStakingPool()
in the end, where we recalculate thetotalAllocPoint
.**Recommendation
**: Remove line 68.
Result: Resolved in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8
Missing
indexed
for important arguments in eventActionPaused
,CompGranted
,CompGranted
,MarketEntered
,MarketExited
, andMarketListed
(IComptroller
)InformationalDescription: The argument
cToken
in eventActionPaused
,MarketEntered
,MarketExited
andMarketListed
,recipient
in eventCompGranted
should be indexed.Recommendation: The argument
cToken
should be indexed.Result: This suggestion is not adopted.
Lack of parameter checks (
Comptroller.initMining()
: L97,Comptroller.reInitMining()
: L105) InformationalDescription: No parameter checks when assigning values to variables.
Recommendation: Add number range check for
_reductionPeriod
Result: Resolved in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8
Access control on
msg.sender
address can be abstracted to modifiers. (Comptroller.initMining()
L97,Comptroller.reInitMining()
L105,Comptroller._setPriceOracle()
L845,Comptroller._setCloseFactor()
L867,Comptroller._setCollateralFactor
L885,Comptroller._setPauseGuardian()
L1047)InformationalDescription: The repeated uses of checking
msg.sender
can be abstracted to a modifier.Recommendation: Replace with the suggested method below.
Result: This suggestion is not adopted.
Constant variables name could be upper case in
Comptroller.sol
. (compInitialIndex
: L74,closeFactorMinMantissa
: L77,closeFactorMaxMantissa
: L80,collateralFactorMaxMantissa
: L83,maxExp
: L85) InformationalDescription: Constant variables names could be upper case.
Recommendation: Replace with the variable name with their upper case formats.
Result: This suggestion is not adopted.
Contract
InterestRateModel
should be defined as an interface (Line 7). (InterestRateModel.sol
)InformationalDescription: InterestRateModel should be defined as an interface rather than a contract
Recommendation: Change it to be an interface.
Result: This suggestion is not adopted.
The redundant
owner
inevent NewGuardian(address owner,address oldGuardian,address newGuardian)
(FTPGuardian.sol
: L4). InformationalDescription: The
owner
is redundant in the event field sinceowner
is unchangeable.Recommendation: Remove the
address owner
field in the event declaration (L4) and its use (L16).Result: Resolved in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8
Nominating the same guardian is allowed in
setGuardian()
(FTPGuardian.sol
: L12). InformationalDescription: The
owner
can set new guardian with the same address as the old guardian.Recommendation: Require the
newGuardian
is not equal to theguardian
.Result: Resolved in commit cc16318c2db70fdc8fbfb52c26c1f7b9d15875f8
Define FTP addres as a constant or immutable. (
ComptrollerG3.sol
: L1396,ComptrollerG6.sol
:L1377) InformationalDescription: Define FTP addres as a constant or immutable…
Recommendation: Define FTP addres as a constant or immutable.
Result: This suggestion is not adopted.
Disclaimer
Verilog receives compensation from one or more clients for performing the smart contract and auditing analysis contained in these reports. The report created is solely for Clients and published with their consent. As such, the scope of our audit is limited to a review of code, and only the code we note as being within the scope of our audit detailed in this report. It is important to note that the Solidity code itself presents unique and unquantifiable risks since the Solidity language itself remains under current development and is subject to unknown risks and flaws. Our sole goal is to help reduce the attack vectors and the high level of variance associated with utilizing new and consistently changing technologies. Thus, Verilog in no way claims any guarantee of security or functionality of the technology we agree to analyze.
In addition, Verilog reports do not provide any indication of the technologies proprietors, business, business model, or legal compliance. As such, reports do not provide investment advice and should not be used to make decisions about investment or involvement with any particular project. Verilog has the right to distribute the Report through other means, including via Verilog publications and other distributions. Verilog makes the reports available to parties other than the Clients (i.e., “third parties”) – on its website in hopes that it can help the blockchain ecosystem develop technical best practices in this rapidly evolving area of innovation.