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 Mainnet that gets reviewed and its corresponding software repository. The document explaining these questions is here.
1. Are the executing code addresses readily available? (%)
They are available at website https://88mph.app/docs/addresses, which feature the v2 addresses. There is also https://docs.88mph.app/developer-docs/addresses, featuring the V3 addresses as indicated in the Appendix. The docs are not centralized in the docs linked from the app's homepage (https://docs.88mph.app/) and to find them requires additional research. The developer docs are listed in the homepage, with traceability to the contract addresses, but presentation in JSON format requires additional work to view the contract addressses themselves.
2. Is the code actively being used? (%)
Activity is 18 transactions a day on contract 0x8888801aF4d980682e47f1A9036e589479e835C5, as indicated in the Appendix.
3. Is there a public software repository? (Y/N)
GitHub: https://github.com/88mphapp
Is there a public software repository with the code at a minimum, but also normally test and scripts. Even if the repository was created just to hold the files and has just 1 transaction, it gets a "Yes". For teams with private repositories, this answer is "No"
4. Is there a development history visible? (%)
88mph has a strong 367 commits and 23 branches, making its development history well documented.
This metric checks if the software repository demonstrates a strong steady history. This is normally demonstrated by commits, branches and releases in a software repository. A healthy history demonstrates a history of more than a month (at a minimum).
5. Is the team public (not anonymous)? (Y/N)
The team consists of Guillame Palayer and Zefram Lou.
For a "Yes" in this question, the real names of some team members must be public on the website or other documentation (LinkedIn, etc). If the team is anonymous, then this question is a "No".
This section looks at the software documentation. The document explaining these questions is here.
7. Are the basic software functions documented? (Y/N)
Core software functions such as DInterest, Withdrawal & Deposit are clearly documented. https://docs.88mph.app/developer-docs/smart-contract-architecture
8. Does the software function documentation fully (100%) cover the deployed contracts? (%)
Smart contracts, governance, and other deployed functions are clearly covered in the documentation. Not all functions are documented.
9. Are there sufficiently detailed comments for all functions within the deployed contract code (%)
The Comments to Code (CtC) ratio is the primary metric for this score.
10. Is it possible to trace from software documentation to the implementation in code (%)
88mph has clear code explanations for all major functions in the documents, though traceability is nonexplicit.
11. Full test suite (Covers all the deployed code) (%)
This score is guided by the Test to Code ratio (TtC). Generally a good test to code ratio is over 100%. However the reviewers best judgement is the final deciding factor.
12. Code coverage (Covers all the deployed lines of code, or explains misses) (%)
Neither auditors nor the protocol itself successfully generated a code coverage score. Trail of bits did a "coverage" part of their audit, but did not include percentage values of code coverage, and this can therefore not be counted into the scoring.
13. Scripts and instructions to run the tests? (Y/N)
Scrips/Instructions location: https://github.com/88mphapp/88mph-contracts
14. Report of the results (%)
There is no report of the test's results.
15. Formal Verification test done (%)
There is no formal verification of this protocol.
16. Stress Testing environment (%)
88mph has been deployed on the Rinkeby test network in full.
This section looks at the 3rd party software audits done. It is explained in this document.
17. Did 3rd Party audits take place? (%)
Three audits have been released for V3: Trail of Bits, Code423n4, and PeckShield. Two other audits have been made for previous versions, and all audits were completed before the code was implemented.
18. Is the bug bounty acceptable high? (%)
88mph uses Immunefi's bug bounty program at the same time as offering a $100,420 maximum bounty.
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.
19. Can a user clearly and quickly find the status of the access controls (%)
Governance has controls to the access controls, which is clearly labelled under the governance section.
20. Is the information clear and complete (%)
There is no information on access controls beyond a mention in a medium article, which has since been changed. There is a mention of change capabilities under governance in the documents.
21. Is the information in non-technical terms that pertain to the investments (%)
There is no mention of admin controls.
22. Is there Pause Control documentation including records of tests (%)
There is no mention of a pause control function in the documents. There is one documented use in a medium article, in which the pause control was used to prevent user fund theft on the 7th of June, 2021.
1pragma solidity 0.8.4;
2
3import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
4import {SafeERC20} from "./libs/SafeERC20.sol";
5import {
6 ReentrancyGuardUpgradeable
7} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
8import {
9 AddressUpgradeable
10} from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
11import {BoringOwnable} from "./libs/BoringOwnable.sol";
12import {
13 MulticallUpgradeable
14} from "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol";
15import {MoneyMarket} from "./moneymarkets/MoneyMarket.sol";
16import {IFeeModel} from "./models/fee/IFeeModel.sol";
17import {IInterestModel} from "./models/interest/IInterestModel.sol";
18import {NFT} from "./tokens/NFT.sol";
19import {FundingMultitoken} from "./tokens/FundingMultitoken.sol";
20import {MPHMinter} from "./rewards/MPHMinter.sol";
21import {IInterestOracle} from "./models/interest-oracle/IInterestOracle.sol";
22import {PRBMathUD60x18} from "prb-math/contracts/PRBMathUD60x18.sol";
23import {Rescuable} from "./libs/Rescuable.sol";
24import {console} from "hardhat/console.sol";
25
26/**
27 @title DeLorean Interest -- It's coming back from the future!
28 @author Zefram Lou
29 @notice The main pool contract for fixed-rate deposits
30 @dev The contract to interact with for most actions
31 */
32contract DInterest is
33 ReentrancyGuardUpgradeable,
34 BoringOwnable,
35 Rescuable,
36 MulticallUpgradeable
37{
38 using SafeERC20 for ERC20;
39 using AddressUpgradeable for address;
40 using PRBMathUD60x18 for uint256;
41
42 // Constants
43 uint256 internal constant PRECISION = 10**18;
44 /**
45 @dev used for sumOfRecordedFundedPrincipalAmountDivRecordedIncomeIndex
46 */
47 uint256 internal constant EXTRA_PRECISION = 10**27;
48 /**
49 @dev used for funding.principalPerToken
50 */
51 uint256 internal constant ULTRA_PRECISION = 2**128;
52 /**
53 @dev Specifies the threshold for paying out funder interests
54 */
55 uint256 internal constant FUNDER_PAYOUT_THRESHOLD_DIVISOR = 10**10;
56
57 // User deposit data
58 // Each deposit has an ID used in the depositNFT, which is equal to its index in `deposits` plus 1
59 struct Deposit {
60 uint256 virtualTokenTotalSupply; // depositAmount + interestAmount, behaves like a zero coupon bond
61 uint256 interestRate; // interestAmount = interestRate * depositAmount
62 uint256 feeRate; // feeAmount = feeRate * depositAmount
63 uint256 averageRecordedIncomeIndex; // Average income index at time of deposit, used for computing deposit surplus
64 uint64 maturationTimestamp; // Unix timestamp after which the deposit may be withdrawn, in seconds
65 uint64 fundingID; // The ID of the associated Funding struct. 0 if not funded.
66 }
67 Deposit[] internal deposits;
68
69 // Funding data
70 // Each funding has an ID used in the fundingMultitoken, which is equal to its index in `fundingList` plus 1
71 struct Funding {
72 uint64 depositID; // The ID of the associated Deposit struct.
73 uint64 lastInterestPayoutTimestamp; // Unix timestamp of the most recent interest payout, in seconds
74 uint256 recordedMoneyMarketIncomeIndex; // the income index at the last update (creation or withdrawal)
75 uint256 principalPerToken; // The amount of stablecoins that's earning interest for you per funding token you own. Scaled to 18 decimals regardless of stablecoin decimals.
76 }
77 Funding[] internal fundingList;
78 // the sum of (recordedFundedPrincipalAmount / recordedMoneyMarketIncomeIndex) of all fundings
79 uint256 public sumOfRecordedFundedPrincipalAmountDivRecordedIncomeIndex;
80
81 // Params
82 /**
83 @dev Maximum deposit period, in seconds
84 */
85 uint64 public MaxDepositPeriod;
86 /**
87 @dev Minimum deposit amount, in stablecoins
88 */
89 uint256 public MinDepositAmount;
90
91 // Global variables
92 uint256 public totalDeposit;
93 uint256 public totalInterestOwed;
94 uint256 public totalFeeOwed;
95 uint256 public totalFundedPrincipalAmount;
96
97 // External smart contracts
98 IFeeModel public feeModel;
99 IInterestModel public interestModel;
100 IInterestOracle public interestOracle;
101 NFT public depositNFT;
102 FundingMultitoken public fundingMultitoken;
103 MPHMinter public mphMinter;
104
105 // Extra params
106 /**
107 @dev The maximum amount of deposit in the pool. Set to 0 to disable the cap.
108 */
109 uint256 public GlobalDepositCap;
110
111 // Events
112 event EDeposit(
113 address indexed sender,
114 uint256 indexed depositID,
115 uint256 depositAmount,
116 uint256 interestAmount,
117 uint256 feeAmount,
118 uint64 maturationTimestamp
119 );
120 event ETopupDeposit(
121 address indexed sender,
122 uint64 indexed depositID,
123 uint256 depositAmount,
124 uint256 interestAmount,
125 uint256 feeAmount
126 );
127 event ERolloverDeposit(
128 address indexed sender,
129 uint64 indexed depositID,
130 uint64 indexed newDepositID
131 );
132 event EWithdraw(
133 address indexed sender,
134 uint256 indexed depositID,
135 bool indexed early,
136 uint256 virtualTokenAmount,
137 uint256 feeAmount
138 );
139 event EFund(
140 address indexed sender,
141 uint64 indexed fundingID,
142 uint256 fundAmount,
143 uint256 tokenAmount
144 );
145 event EPayFundingInterest(
146 uint256 indexed fundingID,
147 uint256 interestAmount,
148 uint256 refundAmount
149 );
150 event ESetParamAddress(
151 address indexed sender,
152 string indexed paramName,
153 address newValue
154 );
155 event ESetParamUint(
156 address indexed sender,
157 string indexed paramName,
158 uint256 newValue
159 );
160
161 function __DInterest_init(
162 uint64 _MaxDepositPeriod,
163 uint256 _MinDepositAmount,
164 address _feeModel,
165 address _interestModel,
166 address _interestOracle,
167 address _depositNFT,
168 address _fundingMultitoken,
169 address _mphMinter
170 ) internal initializer {
171 __ReentrancyGuard_init();
172 __Ownable_init();
173
174 feeModel = IFeeModel(_feeModel);
175 interestModel = IInterestModel(_interestModel);
176 interestOracle = IInterestOracle(_interestOracle);
177 depositNFT = NFT(_depositNFT);
178 fundingMultitoken = FundingMultitoken(_fundingMultitoken);
179 mphMinter = MPHMinter(_mphMinter);
180 MaxDepositPeriod = _MaxDepositPeriod;
181 MinDepositAmount = _MinDepositAmount;
182 }
183
184 /**
185 @param _MaxDepositPeriod The maximum deposit period, in seconds
186 @param _MinDepositAmount The minimum deposit amount, in stablecoins
187 @param _feeModel Address of the FeeModel contract that determines how fees are charged
188 @param _interestModel Address of the InterestModel contract that determines how much interest to offer
189 @param _interestOracle Address of the InterestOracle contract that provides the average interest rate
190 @param _depositNFT Address of the NFT representing ownership of deposits (owner must be set to this DInterest contract)
191 @param _fundingMultitoken Address of the ERC1155 multitoken representing ownership of fundings (this DInterest contract must have the minter-burner role)
192 @param _mphMinter Address of the contract for handling minting MPH to users
193 */
194 function initialize(
195 uint64 _MaxDepositPeriod,
196 uint256 _MinDepositAmount,
197 address _feeModel,
198 address _interestModel,
199 address _interestOracle,
200 address _depositNFT,
201 address _fundingMultitoken,
202 address _mphMinter
203 ) external virtual initializer {
204 __DInterest_init(
205 _MaxDepositPeriod,
206 _MinDepositAmount,
207 _feeModel,
208 _interestModel,
209 _interestOracle,
210 _depositNFT,
211 _fundingMultitoken,
212 _mphMinter
213 )
214
215
Comments to Code: 803 / 3532 = 23 %
Tests to Code: 4138 / 3532 = 117 %