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://github.com/makerdao/spells-mainnet/blob/master/src/test/addresses_mainnet.sol, as indicated in the Appendix. The documents contain no mention of non-token contract addresses and digging through 250 repositories yielded few results.
2. Is the code actively being used? (%)
Activity is more than 10 transactions a day on contract Maker: MCD Join DAI, as indicated in the Appendix.
3. Is there a public software repository? (Y/N)
GitHub: https://github.com/makerdao
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? (%)
At 251 repositories with some containing in excess of 800 commits, Maker's development history can be tolerated.
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)
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)
Basic software functions are documented.
8. Does the software function documentation fully (100%) cover the deployed contracts? (%)
Maker's documentation is impressive and covers all deployed contracts in a highly organised fashion.
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 (%)
There is clear association from the documents to the code, but there is no explicit traceability to the implementation.
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) (%)
Code coverage is mentioned, but proof could not be found. Nevertheless, there's evidently heavy testing on this protocol.
13. Scripts and instructions to run the tests? (Y/N)
Scripts/Instructions location: https://github.com/makerdao/spells-mainnet#test
14. Report of the results (%)
No test report was found.
15. Formal Verification test done (%)
Although Maker says that they have undergone Formal Verification testing, there exists no link to a report of any kind. However we will score it as complete.
This section looks at the 3rd party software audits done. It is explained in this document.
17. Did 3rd Party audits take place? (%)
Maker has undergone multiple audits for many of its strapiucts, all of which were conducted shortly before or after contract deployment.
18. Is the bug bounty acceptable high? (%)
Maker offers a $100K HackerOne bug bounty program.
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 (%)
Maker's Governance and access controls documentations are clearly outlined in their docs. There is in-depth information regarding the governance smart contracts' functionalities, and the admin controls thereof.
20. Is the information clear and complete (%)
a) All contracts are clearly labelled as upgradeable (or not) -- 30% -- contracts are labelled as upgradeable. b) The type of ownership is clearly indicated (OnlyOwner / MultiSig / Defined Roles) -- 30% -- governance is indicated as the owner. c) The capabilities for change in the contracts are described -- 30% -- there is detailed information as to what can change in these contracts.
21. Is the information in non-technical terms that pertain to the investments (%)
This information is not in clear, non-software language, and does not pertain to users' investments' safety.
22. Is there Pause Control documentation including records of tests (%)
A pause control is well documented, though there is no mention of testing. The GitHub repository seems to be mostly deprecated.
1pragma solidity >=0.6.12;
2
3interface VatLike {
4 function move(address,address,uint256) external;
5 function flux(bytes32,address,address,uint256) external;
6 function ilks(bytes32) external returns (uint256, uint256, uint256, uint256, uint256);
7 function suck(address,address,uint256) external;
8}
9
10interface PipLike {
11 function peek() external returns (bytes32, bool);
12}
13
14interface SpotterLike {
15 function par() external returns (uint256);
16 function ilks(bytes32) external returns (PipLike, uint256);
17}
18
19interface DogLike {
20 function chop(bytes32) external returns (uint256);
21 function digs(bytes32, uint256) external;
22}
23
24interface ClipperCallee {
25 function clipperCall(address, uint256, uint256, bytes calldata) external;
26}
27
28interface AbacusLike {
29 function price(uint256, uint256) external view returns (uint256);
30}
31
32contract Clipper {
33 // --- Auth ---
34 mapping (address => uint256) public wards;
35 function rely(address usr) external auth { wards[usr] = 1; emit Rely(usr); }
36 function deny(address usr) external auth { wards[usr] = 0; emit Deny(usr); }
37 modifier auth {
38 require(wards[msg.sender] == 1, "Clipper/not-authorized");
39 _;
40 }
41
42 // --- Data ---
43 bytes32 immutable public ilk; // Collateral type of this Clipper
44 VatLike immutable public vat; // Core CDP Engine
45
46 DogLike public dog; // Liquidation module
47 address public vow; // Recipient of dai raised in auctions
48 SpotterLike public spotter; // Collateral price module
49 AbacusLike public calc; // Current price calculator
50
51 uint256 public buf; // Multiplicative factor to increase starting price [ray]
52 uint256 public tail; // Time elapsed before auction reset [seconds]
53 uint256 public cusp; // Percentage drop before auction reset [ray]
54 uint64 public chip; // Percentage of tab to suck from vow to incentivize keepers [wad]
55 uint192 public tip; // Flat fee to suck from vow to incentivize keepers [rad]
56 uint256 public chost; // Cache the ilk dust times the ilk chop to prevent excessive SLOADs [rad]
57
58 uint256 public kicks; // Total auctions
59 uint256[] public active; // Array of active auction ids
60
61 struct Sale {
62 uint256 pos; // Index in active array
63 uint256 tab; // Dai to raise [rad]
64 uint256 lot; // collateral to sell [wad]
65 address usr; // Liquidated CDP
66 uint96 tic; // Auction start time
67 uint256 top; // Starting price [ray]
68 }
69 mapping(uint256 => Sale) public sales;
70
71 uint256 internal locked;
72
73 // Levels for circuit breaker
74 // 0: no breaker
75 // 1: no new kick()
76 // 2: no new kick() or redo()
77 // 3: no new kick(), redo(), or take()
78 uint256 public stopped = 0;
79
80 // --- Events ---
81 event Rely(address indexed usr);
82 event Deny(address indexed usr);
83
84 event File(bytes32 indexed what, uint256 data);
85 event File(bytes32 indexed what, address data);
86
87 event Kick(
88 uint256 indexed id,
89 uint256 top,
90 uint256 tab,
91 uint256 lot,
92 address indexed usr,
93 address indexed kpr,
94 uint256 coin
95 );
96 event Take(
97 uint256 indexed id,
98 uint256 max,
99 uint256 price,
100 uint256 owe,
101 uint256 tab,
102 uint256 lot,
103 address indexed usr
104 );
105 event Redo(
106 uint256 indexed id,
107 uint256 top,
108 uint256 tab,
109 uint256 lot,
110 address indexed usr,
111 address indexed kpr,
112 uint256 coin
113 );
114
115 event Yank(uint256 id);
116
117 // --- Init ---
118 constructor(address vat_, address spotter_, address dog_, bytes32 ilk_) public {
119 vat = VatLike(vat_);
120 spotter = SpotterLike(spotter_);
121 dog = DogLike(dog_);
122 ilk = ilk_;
123 buf = RAY;
124 wards[msg.sender] = 1;
125 emit Rely(msg.sender);
126 }
127
128 // --- Synchronization ---
129 modifier lock {
130 require(locked == 0, "Clipper/system-locked");
131 locked = 1;
132 _;
133 locked = 0;
134 }
135
136 modifier isStopped(uint256 level) {
137 require(stopped < level, "Clipper/stopped-incorrect");
138 _;
139 }
140
141 // --- Administration ---
142 function file(bytes32 what, uint256 data) external auth lock {
143 if (what == "buf") buf = data;
144 else if (what == "tail") tail = data; // Time elapsed before auction reset
145 else if (what == "cusp") cusp = data; // Percentage drop before auction reset
146 else if (what == "chip") chip = uint64(data); // Percentage of tab to incentivize (max: 2^64 - 1 => 18.xxx WAD = 18xx%)
147 else if (what == "tip") tip = uint192(data); // Flat fee to incentivize keepers (max: 2^192 - 1 => 6.277T RAD)
148 else if (what == "stopped") stopped = data; // Set breaker (0, 1, 2, or 3)
149 else revert("Clipper/file-unrecognized-param");
150 emit File(what, data);
151 }
152 function file(bytes32 what, address data) external auth lock {
153 if (what == "spotter") spotter = SpotterLike(data);
154 else if (what == "dog") dog = DogLike(data);
155 else if (what == "vow") vow = data;
156 else if (what == "calc") calc = AbacusLike(data);
157 else revert("Clipper/file-unrecognized-param");
158 emit File(what, data);
159 }
160
161 // --- Math ---
162 uint256 constant BLN = 10 ** 9;
163 uint256 constant WAD = 10 ** 18;
164 uint256 constant RAY = 10 ** 27;
165
166 function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
167 z = x <= y ? x : y;
168 }
169 function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
170 require((z = x + y) >= x);
171 }
172 function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
173 require((z = x - y) <= x);
174 }
175 function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
176 require(y == 0 || (z = x * y) / y == x);
177 }
178 function wmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
179 z = mul(x, y) / WAD;
180 }
181 function rmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
182 z = mul(x, y) / RAY;
183 }
184 function rdiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
185 z = mul(x, RAY) / y;
186 }
187
188 // --- Auction ---
189
190 // get the price directly from the OSM
191 // Could get this from rmul(Vat.ilks(ilk).spot, Spotter.mat()) instead, but
192 // if mat has changed since the last poke, the resulting value will be
193 // incorrect.
194 function getFeedPrice() internal returns (uint256 feedPrice) {
195 (PipLike pip, ) = spotter.ilks(ilk);
196 (bytes32 val, bool has) = pip.peek();
197 require(has, "Clipper/invalid-price");
198 feedPrice = rdiv(mul(uint256(val), BLN), spotter.par());
199 }
200
201 // start an auction
202 // note: trusts the caller to transfer collateral to the contract
203 // The starting price `top` is obtained as follows:
204 //
205 // top = val * buf / par
206 //
207 // Where `val` is the collateral's unitary value in USD, `buf` is a
208 // multiplicative factor to increase the starting price, and `par` is a
209 // reference per DAI.
210 function kick(
211 uint256 tab, // Debt [rad]
212 uint256 lot, // Collateral [wad]
213 address usr, // Address that will receive any leftover collateral
214 address kpr // Address that will receive incentives
215 ) external auth lock isStopped(1) returns (uint256 id) {
216 / Input validation
217 require(tab > 0, "Clipper/zero-tab");
218 require(lot > 0, "Clipper/zero-lot");
219 require(usr != address(0), "Clipper/zero-usr");
220 id = ++kicks;
221 require(id > 0, "Clipper/overflow");
222
223 active.push(id);
224
225 sales[id].pos = active.length - 1;
226
227 sales[id].tab = tab;
228 sales[id].lot = lot;
229 sales[id].usr = usr;
230 sales[id].tic = uint96(block.timestamp);
231
232 uint256 top;
233 top = rmul(getFeedPrice(), buf);
234 require(top > 0, "Clipper/zero-top-price");
235 sales[id].top = top;
236
237 / incentive to kick auction
238 uint256 _tip = tip;
239 uint256 _chip = chip;
240 uint256 coin;
241 if (_tip > 0 || _chip > 0) {
242 coin = add(_tip, wmul(tab, _chip));
243 vat.suck(vow, kpr, coin);
244 }
245
Comments to Code: 756 / 2119 = 36 %
Tests to Code: 4710 / 2119 = 222 %