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.frax.finance/smart-contracts/frax, as indicated in the Appendix.
2. How active is the primary contract? (%)
Contract FRAX is extremely active.
3. Does the protocol have a public software repository? (Y/N)
4. Is there a development history visible? (%)
At 479 commits, it's clear Frax's development history is as moving as it is inspirational.
5. Is the team public (not anonymous)?
Many team members are public and cross-reference their employment. This is documented in the Appendix.
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.frax.finance/
7. Is the protocol's software architecture documented? (Y/N)
This protocol's software architecture is documented in their documentation.
8. Does the software documentation fully cover the deployed contracts' source code? (%)
There is full coverage of deployed contracts in their software function documentation.
9. Is it possible to trace the documented software to its implementation in the protocol's source code? (%)
There is no traceability, but strong association between software documentation and implemented code.
10. Has the protocol tested their deployed code? (%)
With a Test to Code ratio of 719%, this protocol has clearly undergone an astonishing amount of testing. In all my time writing reviews, no other protocol has seen so much testing.
11. How covered is the protocol's code? (%)
No testing for code coverage was found, but this protocol has clearly undergone significant testing.
12. Does the protocol provide scripts and instructions to run their tests? (Y/N)
There are no scripts provided for protocol testing.
13. Is there a detailed report of the protocol's test results?(%)
No report was found.
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 Ganache at https://docs.frax.finance/smart-contracts/miscellaneous.
This section looks at the 3rd party software audits done. It is explained in this document.
16. Is the protocol sufficiently audited? (%)
Frax has been audited once, before launch. 3 major findings were not corrected.
17. Is the bounty value acceptably high (%)
Frax offers up to $10m with a static bug bounty program. This is an incredible amount of money to offer to hackers and presents a strong incentive to secure Frax's code.
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 clearly documented at this location. This was quick to find.
19. Are relevant contracts clearly labelled as upgradeable or immutable? (%)
The relevant contracts are not identified.
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 not identified.
22. Is the protocol's admin control information easy to understand? (%)
This information is in software specific language.
23. Is there sufficient Pause Control documentation? (%)
This protocol dose not have a pause control, and this is identified.
24. Is there sufficient Timelock documentation? (%)
This protocol's timelock documentation can be found at this location.
25. Is the Timelock of an adequate length? (Y/N)
The timelock is 48 hours.
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? (%)
The protocol's oracle source is documented at this location. The contracts dependent are identified. There is relevant software function documentation.
27. Is front running mitigated by this protocol? (Y/N)
This protocol documents front running mitigation techniques.
28. Can flashloan attacks be applied to the protocol, and if so, are those flashloan attack risks mitigated? (Y/N)
This protocol documents flashloan countermeasures at this location.
1// Reviewer(s) / Contributor(s)
2// Jason Huan: https://github.com/jasonhuan
3// Sam Kazemian: https://github.com/samkazemian
4// Dennis: github.com/denett
5// Hameed
6
7import "../Math/SafeMath.sol";
8import "./IFrax.sol";
9import "../FXS/IFxs.sol";
10import "../Frax/Pools/FraxPoolV3.sol";
11import "../Frax/Pools/IFraxPool.sol";
12import "../ERC20/ERC20.sol";
13import "../Staking/Owned.sol";
14import '../Uniswap/TransferHelper.sol';
15import '../Misc_AMOs/IAMO.sol';
16
17contract FraxAMOMinter is Owned {
18 // SafeMath automatically included in Solidity >= 8.0.0
19
20 /* ========== STATE VARIABLES ========== */
21
22 // Core
23 IFrax public FRAX = IFrax(0x853d955aCEf822Db058eb8505911ED77F175b99e);
24 IFxs public FXS = IFxs(0x3432B6A60D23Ca0dFCa7761B7ab56459D9C964D0);
25 ERC20 public collateral_token;
26 FraxPoolV3 public pool = FraxPoolV3(0x2fE065e6FFEf9ac95ab39E5042744d695F560729);
27 IFraxPool public old_pool = IFraxPool(0x1864Ca3d47AaB98Ee78D11fc9DCC5E7bADdA1c0d);
28 address public timelock_address;
29 address public custodian_address;
30
31 // Collateral related
32 address public collateral_address;
33 uint256 public col_idx;
34
35 // AMO addresses
36 address[] public amos_array;
37 mapping(address => bool) public amos; // Mapping is also used for faster verification
38
39 // Price constants
40 uint256 private constant PRICE_PRECISION = 1e6;
41
42 // Max amount of collateral the contract can borrow from the FraxPool
43 int256 public collat_borrow_cap = int256(10000000e6);
44
45 // Max amount of FRAX and FXS this contract can mint
46 int256 public frax_mint_cap = int256(100000000e18);
47 int256 public fxs_mint_cap = int256(100000000e18);
48
49 // Minimum collateral ratio needed for new FRAX minting
50 uint256 public min_cr = 810000;
51
52 // Frax mint balances
53 mapping(address => int256) public frax_mint_balances; // Amount of FRAX the contract minted, by AMO
54 int256 public frax_mint_sum = 0; // Across all AMOs
55
56 // Fxs mint balances
57 mapping(address => int256) public fxs_mint_balances; // Amount of FXS the contract minted, by AMO
58 int256 public fxs_mint_sum = 0; // Across all AMOs
59
60 // Collateral borrowed balances
61 mapping(address => int256) public collat_borrowed_balances; // Amount of collateral the contract borrowed, by AMO
62 int256 public collat_borrowed_sum = 0; // Across all AMOs
63
64 // FRAX balance related
65 uint256 public fraxDollarBalanceStored = 0;
66
67 // Collateral balance related
68 uint256 public missing_decimals;
69 uint256 public collatDollarBalanceStored = 0;
70
71 // AMO balance corrections
72 mapping(address => int256[2]) public correction_offsets_amos;
73 // [amo_address][0] = AMO's frax_val_e18
74 // [amo_address][1] = AMO's collat_val_e18
75
76 /* ========== CONSTRUCTOR ========== */
77
78 constructor (
79 address _owner_address,
80 address _custodian_address,
81 address _timelock_address,
82 address _collateral_address,
83 address _pool_address
84 ) Owned(_owner_address) {
85 custodian_address = _custodian_address;
86 timelock_address = _timelock_address;
87
88 // Pool related
89 pool = FraxPoolV3(_pool_address);
90
91 // Collateral related
92 collateral_address = _collateral_address;
93 col_idx = pool.collateralAddrToIdx(_collateral_address);
94 collateral_token = ERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
95 missing_decimals = uint(18) - collateral_token.decimals();
96 }
97
98 /* ========== MODIFIERS ========== */
99
100 modifier onlyByOwnGov() {
101 require(msg.sender == timelock_address || msg.sender == owner, "Not owner or timelock");
102 _;
103 }
104
105 modifier validAMO(address amo_address) {
106 require(amos[amo_address], "Invalid AMO");
107 _;
108 }
109
110 /* ========== VIEWS ========== */
111
112 function collatDollarBalance() external view returns (uint256) {
113 (, uint256 collat_val_e18) = dollarBalances();
114 return collat_val_e18;
115 }
116
117 function dollarBalances() public view returns (uint256 frax_val_e18, uint256 collat_val_e18) {
118 frax_val_e18 = fraxDollarBalanceStored;
119 collat_val_e18 = collatDollarBalanceStored;
120 }
121
122 function allAMOAddresses() external view returns (address[] memory) {
123 return amos_array;
124 }
125
126 function allAMOsLength() external view returns (uint256) {
127 return amos_array.length;
128 }
129
130 function fraxTrackedGlobal() external view returns (int256) {
131 return int256(fraxDollarBalanceStored) - frax_mint_sum - (collat_borrowed_sum * int256(10 ** missing_decimals));
132 }
133
134 function fraxTrackedAMO(address amo_address) external view returns (int256) {
135 (uint256 frax_val_e18, ) = IAMO(amo_address).dollarBalances();
136 int256 frax_val_e18_corrected = int256(frax_val_e18) + correction_offsets_amos[amo_address][0];
137 return frax_val_e18_corrected - frax_mint_balances[amo_address] - ((collat_borrowed_balances[amo_address]) * int256(10 ** missing_decimals));
138 }
139
140 /* ========== PUBLIC FUNCTIONS ========== */
141
142 // Callable by anyone willing to pay the gas
143 function syncDollarBalances() public {
144 uint256 total_frax_value_d18 = 0;
145 uint256 total_collateral_value_d18 = 0;
146 for (uint i = 0; i < amos_array.length; i++){
147 // Exclude null addresses
148 address amo_address = amos_array[i];
149 if (amo_address != address(0)){
150 (uint256 frax_val_e18, uint256 collat_val_e18) = IAMO(amo_address).dollarBalances();
151 total_frax_value_d18 += uint256(int256(frax_val_e18) + correction_offsets_amos[amo_address][0]);
152 total_collateral_value_d18 += uint256(int256(collat_val_e18) + correction_offsets_amos[amo_address][1]);
153 }
154 }
155 fraxDollarBalanceStored = total_frax_value_d18;
156 collatDollarBalanceStored = total_collateral_value_d18;
157 }
158
159 /* ========== OLD POOL / BACKWARDS COMPATIBILITY ========== */
160
161 function oldPoolRedeem(uint256 frax_amount) external onlyByOwnGov {
162 uint256 redemption_fee = old_pool.redemption_fee();
163 uint256 col_price_usd = old_pool.getCollateralPrice();
164 uint256 global_collateral_ratio = FRAX.global_collateral_ratio();
165 uint256 redeem_amount_E6 = ((frax_amount * (uint256(1e6) - redemption_fee)) / 1e6) / (10 ** missing_decimals);
166 uint256 expected_collat_amount = (redeem_amount_E6 * global_collateral_ratio) / 1e6;
167 expected_collat_amount = (expected_collat_amount * 1e6) / col_price_usd;
168
169 require((collat_borrowed_sum + int256(expected_collat_amount)) <= collat_borrow_cap, "Borrow cap");
170 collat_borrowed_sum += int256(expected_collat_amount);
171
172 // Mint the frax
173 FRAX.pool_mint(address(this), frax_amount);
174
175 // Redeem the frax
176 FRAX.approve(address(old_pool), frax_amount);
177 old_pool.redeemFractionalFRAX(frax_amount, 0, 0);
178 }
179
180 function oldPoolCollectAndGive(address destination_amo) external onlyByOwnGov validAMO(destination_amo) {
181 // Get the amount to be collected
182 uint256 collat_amount = old_pool.redeemCollateralBalances(address(this));
183
184 // Collect the redemption
185 old_pool.collectRedemption();
186
187 // Mark the destination amo's borrowed amount
188 collat_borrowed_balances[destination_amo] += int256(collat_amount);
189
190 // Give the collateral to the AMO
191 TransferHelper.safeTransfer(collateral_address, destination_amo, collat_amount);
192
193 // Sync
194 syncDollarBalances();
195 }
196
197 /* ========== OWNER / GOVERNANCE FUNCTIONS ONLY ========== */
198 // Only owner or timelock can call, to limit risk
199
200 // ------------------------------------------------------------------
201 // ------------------------------ FRAX ------------------------------
202 // ------------------------------------------------------------------
203
204 // This contract is essentially marked as a 'pool' so it can call OnlyPools functions like pool_mint and pool_burn_from
205 // on the main FRAX contract
206 function mintFraxForAMO(address destination_amo, uint256 frax_amount) external onlyByOwnGov validAMO(destination_amo) {
207 int256 frax_amt_i256 = int256(frax_amount);
208
209 // Make sure you aren't minting more than the mint cap
210 require((frax_mint_sum + frax_amt_i256) <= frax_mint_cap, "Mint cap reached");
211 frax_mint_balances[destination_amo] += frax_amt_i256;
212 frax_mint_sum += frax_amt_i256;
213
214 // Make sure the FRAX minting wouldn't push the CR down too much
215 // This is also a sanity check for the int256 math
216 uint256 current_collateral_E18 = FRAX.globalCollateralValue();
217 uint256 cur_frax_supply = FRAX.totalSupply();
218 uint256 new_frax_supply = cur_frax_supply + frax_amount;
219 uint256 new_cr = (current_collateral_E18 * PRICE_PRECISION) / new_frax_supply;
220 require(new_cr >= min_cr, "CR would be too low");
221
222 // Mint the FRAX to the AMO
223 FRAX.pool_mint(destination_amo, frax_amount);
224
225 // Sync
226 syncDollarBalances();
227 }
228
229 function burnFraxFromAMO(uint256 frax_amount) external validAMO(msg.sender) {
230 int256 frax_amt_i256 = int256(frax_amount);
231
232 // Burn first
233 FRAX.pool_burn_from(msg.sender, frax_amount);
234
235 // Then update the balances
236 frax_mint_balances[msg.sender] -= frax_amt_i256;
237 frax_mint_sum -= frax_amt_i256;
238
239 // Sync
240 syncDollarBalances();
241 }
242