If you notice some outdated information please let us know!
PASS
The final review score is indicated as a percentage. The percentage is calculated as Achieved Points due to MAX Possible Points. For each element the answer can be either Yes/No or a percentage. For a detailed breakdown of the individual weights of each question, please consult this document.
Very simply, the audit looks for the following declarations from the developer's site. With these declarations, it is reasonable to trust the smart contracts.
This report is for informational purposes only and does not constitute investment advice of any kind, nor does it constitute an offer to provide investment advisory or other services. Nothing in this report shall be considered a solicitation or offer to buy or sell any security, token, future, option or other financial instrument or to offer or provide any investment advice or service to any person in any jurisdiction. Nothing contained in this report constitutes investment advice or offers any opinion with respect to the suitability of any security, and the views expressed in this report should not be taken as advice to buy, sell or hold any security. The information in this report should not be relied upon for the purpose of investing. In preparing the information contained in this report, we have not taken into account the investment needs, objectives and financial circumstances of any particular investor. This information has no regard to the specific investment objectives, financial situation and particular needs of any specific recipient of this information and investments discussed may not be suitable for all investors.
Any views expressed in this report by us were prepared based upon the information available to us at the time such views were written. The views expressed within this report are limited to DeFiSafety and the author and do not reflect those of any additional or third party and are strictly based upon DeFiSafety, its authors, interpretations and evaluation of relevant data. Changed or additional information could cause such views to change. All information is subject to possible correction. Information may quickly become unreliable for various reasons, including changes in market conditions or economic circumstances.
This completed report is copyright (c) DeFiSafety 2023. Permission is given to copy in whole, retaining this copyright label.
This section looks at the code deployed on the relevant chain that gets reviewed and its corresponding software repository. The document explaining these questions is here.
1. Are the smart contract addresses easy to find? (%)
They can be found at https://docs.convexfinance.com/convexfinance/faq/contract-addresses, as indicated in the Appendix.
2. How active is the primary contract? (%)
Contract Booster is used 100+ times a day, as indicated in the Appendix.
3. Does the protocol have a public software repository? (Y/N)
Location: https://github.com/convex-eth
4. Is there a development history visible? (%)
At 361 commits, Convex clearly does a good job of allowing others to see it in their rear-view mirrors.
5. Is the team public (not anonymous)?
One team member is public.
The difference between this and the old link is solely the link. This section looks at the software documentation. The document explaining these questions is here.
6. Is there a whitepaper? (Y/N)
Location: https://docs.convexfinance.com/convexfinance/
7. Is the protocol's software architecture documented? (Y/N)
This protocol's software architecture is well documented.
8. Does the software documentation fully cover the deployed contracts' source code? (%)
There is coverage of major deployed contracts by software function documentation.
9. Is it possible to trace the documented software to its implementation in the protocol's source code? (%)
There is explicit traceability between important software documentation and implemented code.
10. Has the protocol tested their deployed code? (%)
Code examples are in the Appendix at the end of this report. As per the SLOC, there is 120% testing to code (TtC). This score is guided by the Test to Code ratio (TtC). Generally a good test to code ratio is over 100%. However, the reviewer's best judgement is the final deciding factor.
11. How covered is the protocol's code? (%)
There are no documented tests for code coverage. However, there is evidence of good testing.
12. Does the protocol provide scripts and instructions to run their tests? (Y/N)
Test scripts are provided.
13. Is there a detailed report of the protocol's test results?(%)
There are no test reports.
14. Has the protocol undergone Formal Verification? (Y/N)
This protocol has not undergone formal verification.
15. Were the smart contracts deployed to a testnet? (Y/N)
This protocol has documented deployment to a testnet. They forked mainnet.
This section looks at the 3rd party software audits done. It is explained in this document.
16. Is the protocol sufficiently audited? (%)
Convex was audited once before deployment.
17. Is the bounty value acceptably high (%)
Convex offers an inactive bug bounty of $250K.
This section covers the documentation of special access controls for a DeFi protocol. The admin access controls are the contracts that allow updating contracts or coefficients in the protocol. Since these contracts can allow the protocol admins to "change the rules", complete disclosure of capabilities is vital for user's transparency. It is explained in this document.
18. Is the protocol's admin control information easy to find?
Admin control information was documented at this location. This was quick to find.
19. Are relevant contracts clearly labelled as upgradeable or immutable? (%)
The relevant contracts are clearly identified as immutable / upgradeable, as identified here. This admin control information is some of the best we've seen and other protocols would do well to emulate such a detailed, specific and concise page.
20. Is the type of smart contract ownership clearly indicated? (%)
Ownership is clearly indicated in this location.
21. Are the protocol's smart contract change capabilities described? (%)
Smart contract change capabilities are well identified for all Convex contracts.
22. Is the protocol's admin control information easy to understand? (%)
The Convex admin control information is not in software specific language and relates to user investment safety by assuring the protocol's non-custodial nature.
23. Is there sufficient Pause Control documentation? (%)
Convex's pause control is documented and well explained in this location. There is no evidence of testing.
24. Is there sufficient Timelock documentation? (%)
Convex doesn't use timelocks, and explains this in this location.
25. Is the Timelock of an adequate length? (Y/N)
Convex does not use a timelock.
This section goes over the documentation that a protocol may or may not supply about their Oracle usage. Oracles are a fundamental part of DeFi as they are responsible for relaying tons of price data information to thousands of protocols using blockchain technology. Not only are they important for price feeds, but they are also an essential component of transaction verification and security. This is explained in this document.
26. Is the protocol's Oracle sufficiently documented? (%)
Convex does not use a data source, and this is justified at this location.
27. Is front running mitigated by this protocol? (Y/N)
Convex cannot be front run, since it is price-agnostic.
28. Can flashloan attacks be applied to the protocol, and if so, are those flashloan attack risks mitigated? (Y/N)
Flashloans are not applicable to this protocol, as identified.
1import "./Interfaces.sol";
2import '@openzeppelin/contracts/math/SafeMath.sol';
3import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
4import '@openzeppelin/contracts/utils/Address.sol';
5import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
6
7
8contract Booster{
9 using SafeERC20 for IERC20;
10 using Address for address;
11 using SafeMath for uint256;
12
13 address public constant crv = address(0xD533a949740bb3306d119CC777fa900bA034cd52);
14 address public constant registry = address(0x0000000022D53366457F9d5E68Ec105046FC4383);
15 uint256 public constant distributionAddressId = 4;
16 address public constant voteOwnership = address(0xE478de485ad2fe566d49342Cbd03E49ed7DB3356);
17 address public constant voteParameter = address(0xBCfF8B0b9419b9A88c44546519b1e909cF330399);
18
19 uint256 public lockIncentive = 1000; //incentive to crv stakers
20 uint256 public stakerIncentive = 450; //incentive to native token stakers
21 uint256 public earmarkIncentive = 50; //incentive to users who spend gas to make calls
22 uint256 public platformFee = 0; //possible fee to build treasury
23 uint256 public constant MaxFees = 2000;
24 uint256 public constant FEE_DENOMINATOR = 10000;
25
26 address public owner;
27 address public feeManager;
28 address public poolManager;
29 address public immutable staker;
30 address public immutable minter;
31 address public rewardFactory;
32 address public stashFactory;
33 address public tokenFactory;
34 address public rewardArbitrator;
35 address public voteDelegate;
36 address public treasury;
37 address public stakerRewards; //cvx rewards
38 address public lockRewards; //cvxCrv rewards(crv)
39 address public lockFees; //cvxCrv vecrv fees
40 address public feeDistro;
41 address public feeToken;
42
43 bool public isShutdown;
44
45 struct PoolInfo {
46 address lptoken;
47 address token;
48 address gauge;
49 address crvRewards;
50 address stash;
51 bool shutdown;
52 }
53
54 //index(pid) -> pool
55 PoolInfo[] public poolInfo;
56 mapping(address => bool) public gaugeMap;
57
58 event Deposited(address indexed user, uint256 indexed poolid, uint256 amount);
59 event Withdrawn(address indexed user, uint256 indexed poolid, uint256 amount);
60
61 constructor(address _staker, address _minter) public {
62 isShutdown = false;
63 staker = _staker;
64 owner = msg.sender;
65 voteDelegate = msg.sender;
66 feeManager = msg.sender;
67 poolManager = msg.sender;
68 feeDistro = address(0); //address(0xA464e6DCda8AC41e03616F95f4BC98a13b8922Dc);
69 feeToken = address(0); //address(0x6c3F90f043a72FA612cbac8115EE7e52BDe6E490);
70 treasury = address(0);
71 minter = _minter;
72 }
73
74
75 /// SETTER SECTION ///
76
77 function setOwner(address _owner) external {
78 require(msg.sender == owner, "!auth");
79 owner = _owner;
80 }
81
82 function setFeeManager(address _feeM) external {
83 require(msg.sender == feeManager, "!auth");
84 feeManager = _feeM;
85 }
86
87 function setPoolManager(address _poolM) external {
88 require(msg.sender == poolManager, "!auth");
89 poolManager = _poolM;
90 }
91
92 function setFactories(address _rfactory, address _sfactory, address _tfactory) external {
93 require(msg.sender == owner, "!auth");
94
95 //reward factory only allow this to be called once even if owner
96 //removes ability to inject malicious staking contracts
97 //token factory can also be immutable
98 if(rewardFactory == address(0)){
99 rewardFactory = _rfactory;
100 tokenFactory = _tfactory;
101 }
102
103 //stash factory should be considered more safe to change
104 //updating may be required to handle new types of gauges
105 stashFactory = _sfactory;
106 }
107
108 function setArbitrator(address _arb) external {
109 require(msg.sender==owner, "!auth");
110 rewardArbitrator = _arb;
111 }
112
113 function setVoteDelegate(address _voteDelegate) external {
114 require(msg.sender==voteDelegate, "!auth");
115 voteDelegate = _voteDelegate;
116 }
117
118 function setRewardContracts(address _rewards, address _stakerRewards) external {
119 require(msg.sender == owner, "!auth");
120
121 //reward contracts are immutable or else the owner
122 //has a means to redeploy and mint cvx via rewardClaimed()
123 if(lockRewards == address(0)){
124 lockRewards = _rewards;
125 stakerRewards = _stakerRewards;
126 }
127 }
128
129 // Set reward token and claim contract, get from Curve's registry
130 function setFeeInfo() external {
131 require(msg.sender==feeManager, "!auth");
132
133 feeDistro = IRegistry(registry).get_address(distributionAddressId);
134 address _feeToken = IFeeDistro(feeDistro).token();
135 if(feeToken != _feeToken){
136 //create a new reward contract for the new token
137 lockFees = IRewardFactory(rewardFactory).CreateTokenRewards(_feeToken,lockRewards,address(this));
138 feeToken = _feeToken;
139 }
140 }
141
142 function setFees(uint256 _lockFees, uint256 _stakerFees, uint256 _callerFees, uint256 _platform) external{
143 require(msg.sender==feeManager, "!auth");
144
145 uint256 total = _lockFees.add(_stakerFees).add(_callerFees).add(_platform);
146 require(total <= MaxFees, ">MaxFees");
147
148 //values must be within certain ranges
149 if(_lockFees >= 1000 && _lockFees <= 1500
150 && _stakerFees >= 300 && _stakerFees <= 600
151 && _callerFees >= 10 && _callerFees <= 100
152 && _platform <= 200){
153 lockIncentive = _lockFees;
154 stakerIncentive = _stakerFees;
155 earmarkIncentive = _callerFees;
156 platformFee = _platform;
157 }
158 }
159
160 function setTreasury(address _treasury) external {
161 require(msg.sender==feeManager, "!auth");
162 treasury = _treasury;
163 }
164
165 /// END SETTER SECTION ///
166
167
168 function poolLength() external view returns (uint256) {
169 return poolInfo.length;
170 }
171
172 //create a new pool
173 function addPool(address _lptoken, address _gauge, uint256 _stashVersion) external returns(bool){
174 require(msg.sender==poolManager && !isShutdown, "!add");
175 require(_gauge != address(0) && _lptoken != address(0),"!param");
176
177 //the next pool's pid
178 uint256 pid = poolInfo.length;
179
180 //create a tokenized deposit
181 address token = ITokenFactory(tokenFactory).CreateDepositToken(_lptoken);
182 //create a reward contract for crv rewards
183 address newRewardPool = IRewardFactory(rewardFactory).CreateCrvRewards(pid,token);
184 //create a stash to handle extra incentives
185 address stash = IStashFactory(stashFactory).CreateStash(pid,_gauge,staker,_stashVersion);
186
187 //add the new pool
188 poolInfo.push(
189 PoolInfo({
190 lptoken: _lptoken,
191 token: token,
192 gauge: _gauge,
193 crvRewards: newRewardPool,
194 stash: stash,
195 shutdown: false
196 })
197 );
198 gaugeMap[_gauge] = true;
199 //give stashes access to rewardfactory and voteproxy
200 // voteproxy so it can grab the incentive tokens off the contract after claiming rewards
201 // reward factory so that stashes can make new extra reward contracts if a new incentive is added to the gauge
202 if(stash != address(0)){
203 poolInfo[pid].stash = stash;
204 IStaker(staker).setStashAccess(stash,true);
205 IRewardFactory(rewardFactory).setAccess(stash,true);
206 }
207 return true;
208 }
209
210 //shutdown pool
211 function shutdownPool(uint256 _pid) external returns(bool){
212 require(msg.sender==poolManager, "!auth");
213 PoolInfo storage pool = poolInfo[_pid];
214
215 //withdraw from gauge
216 try IStaker(staker).withdrawAll(pool.lptoken,pool.gauge){
217 }catch{}
218
219 pool.shutdown = true;
220 gaugeMap[pool.gauge] = false;
221 return true;
222 }
223
224 //shutdown this contract.
225 // unstake and pull all lp tokens to this address
226 // only allow withdrawals
227 function shutdownSystem() external{
228 require(msg.sender == owner, "!auth");
229 isShutdown = true;
230
231 for(uint i=0; i < poolInfo.length; i++){
232 PoolInfo storage pool = poolInfo[i];
233 if (pool.shutdown) continue;
234
235 address token = pool.lptoken;
236 address gauge = pool.gauge;
237
238 //withdraw from gauge
239 try IStaker(staker).withdrawAll(token,gauge){
240 pool.shutdown = true;
241 }catch{}
242 }
243 }
244
245
246 //deposit lp tokens and stake
247 function deposit(uint256 _pid, uint256 _amount, bool _stake) public returns(bool){
248 require(!isShutdown,"shutdown");
249 PoolInfo storage pool = poolInfo[_pid];
250 require(pool.shutdown == false, "pool is closed");
251
252 //send to proxy to stake
253 address lptoken = pool.lptoken;
254 IERC20(lptoken).safeTransferFrom(msg.sender, staker, _amount);
255
256 //stake
257 address gauge = pool.gauge;
258 require(gauge != address(0),"!gauge setting");
259 IStaker(staker).deposit(lptoken,gauge);
260
261 //some gauges claim rewards when depositing, stash them in a seperate contract until next claim
262 address stash = pool.stash;
263 if(stash != address(0)){
264 IStash(stash).stashRewards();
265 }
266
267 address token = pool.token;
268 if(_stake){
269 //mint here and send to rewards on user behalf
270 ITokenMinter(token).mint(address(this),_amount);
271 address rewardContract = pool.crvRewards;
272 IERC20(token).safeApprove(rewardContract,0);
273 IERC20(token).safeApprove(rewardContract,_amount);
274 IRewards(rewardContract).stakeFor(msg.sender,_amount);
275 }else{
276 //add user balance directly
277 ITokenMinter(token).mint(msg.sender,_amount);
278 }
279
280
281 emit Deposited(msg.sender, _pid, _amount);
282 return true;
283 }
284
285 //deposit all lp tokens and stake
286 function depositAll(uint256 _pid, bool _stake) external returns(bool){
287 address lptoken = poolInfo[_pid].lptoken;
288 uint256 balance = IERC20(lptoken).balanceOf(msg.sender);
289 deposit(_pid,balance,_stake);
290 return true;
291 }
292
Tests to Code: 5941 / 4973 = 119 %