If you notice some outdated information please let us know!
-15%(Penalty)
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?(%)
Contracts are easily found.
2. How active is the primary contract? (%)
Contracts are interacted with more than 10 times a week.
3. Does the protocol have a public software repository? (Y/N)
Saddle uses GitHub
4. Is there a development history visible? (%)
At 296 commits, it's clear this development team has worn in their saddle when it comes to development history.
5. Is the team public (not anonymous)? (Y/N)
Two public team members contribute to Saddle's repository and independently confirm via LinkedIn their employment at Saddle.
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.
7. Is the protocol's software architecture documented? (Y/N)
Saddle has great software architecture documentation at https://docs.saddle.finance/solidity-docs/stakeabletokenwrapper.
8. Does the software documentation fully cover the deployed contracts' source code? (%)
Deployed contracts are documented [extensively.]https://docs.saddle.finance/solidity-docs/swap)
9. Is it possible to trace the documented software to its implementation in the protocol's source code? (%)
There is clear association between code and documentation with explicit traceability.
10. Has the protocol tested their deployed code? (%)
At 426% TtC, Saddle has done substantial testing on its smart contracts.
11. How covered is the protocol's code? (%)
GitHub reports 99% code coverage.
12. Does the protocol provide scripts and instructions to run their tests? (Y/N)
Scripts are provided.
13. Is there a detailed report of the protocol's test results?(%)
No report was found, but with 99% code coverage this protocol earns 70%.
14. Has the protocol undergone Formal Verification? (Y/N)
This protocol has not undergone formal verification. It plans to in the near future - this score will be updated accordlingly.
15. Were the smart contracts deployed to a testnet? (Y/N)
Saddle documents Ropsten usage.
This section looks at the 3rd party software audits done. It is explained in this document.
16. Is the protocol sufficiently audited? (%)
Saddle was audited 3 times before release.
17. Is the bounty value acceptably high (%)
Saddle offer $100k using an active program. Saddle should be commended for recently doubling the value of this bug 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.
18. Is the protocol's admin control information easy to find?
Admin control information is easy to find.
19. Are relevant contracts clearly labelled as upgradeable or immutable? (%)
All contracts are clearly labelled as immutable.
20. Is the type of smart contract ownership clearly indicated? (%)
Ownership is in the Gnosis Safe.
21. Are the protocol's smart contract change capabilities described? (%)
Contracts are immutable.
22. Is the protocol's admin control information easy to understand? (%)
This information is in comprehensible language.
23. Is there sufficient Pause Control documentation? (%)
There is a pause control mentioned with good elaboration. There is no evidence of testing.
24. Is there sufficient Timelock documentation? (%)
There is no timelock needed.
25. Is the Timelock of an adequate length? (Y/N)
No timelock is deemed necessary.
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? (%)
Saddle does not use an oracle, and this is justified here.
27. Is front running mitigated by this protocol? (Y/N)
Front running countermeasures are detailed.
28. Can flashloan attacks be applied to the protocol, and if so, are those flashloan attack risks mitigated? (Y/N)
Saddle uses flashloans as a part of its offering, meaning countermeasures are not needed.
1pragma solidity 0.6.12;
2
3import "./SwapV1.sol";
4import "./interfaces/IFlashLoanReceiver.sol";
5
6/**
7 * @title Swap - A StableSwap implementation in solidity.
8 * @notice This contract is responsible for custody of closely pegged assets (eg. group of stablecoins)
9 * and automatic market making system. Users become an LP (Liquidity Provider) by depositing their tokens
10 * in desired ratios for an exchange of the pool token that represents their share of the pool.
11 * Users can burn pool tokens and withdraw their share of token(s).
12 *
13 * Each time a swap between the pooled tokens happens, a set fee incurs which effectively gets
14 * distributed to the LPs.
15 *
16 * In case of emergencies, admin can pause additional deposits, swaps, or single-asset withdraws - which
17 * stops the ratio of the tokens in the pool from changing.
18 * Users can always withdraw their tokens via multi-asset withdraws.
19 *
20 * @dev Most of the logic is stored as a library `SwapUtils` for the sake of reducing contract's
21 * deployment size.
22 */
23contract SwapFlashLoanV1 is SwapV1 {
24 // Total fee that is charged on all flashloans in BPS. Borrowers must repay the amount plus the flash loan fee.
25 // This fee is split between the protocol and the pool.
26 uint256 public flashLoanFeeBPS;
27 // Share of the flash loan fee that goes to the protocol in BPS. A portion of each flash loan fee is allocated
28 // to the protocol rather than the pool.
29 uint256 public protocolFeeShareBPS;
30 // Max BPS for limiting flash loan fee settings.
31 uint256 public constant MAX_BPS = 10000;
32
33 /*** EVENTS ***/
34 event FlashLoan(
35 address indexed receiver,
36 uint8 tokenIndex,
37 uint256 amount,
38 uint256 amountFee,
39 uint256 protocolFee
40 );
41
42 /**
43 * @notice Initializes this Swap contract with the given parameters.
44 * This will also clone a LPToken contract that represents users'
45 * LP positions. The owner of LPToken will be this contract - which means
46 * only this contract is allowed to mint/burn tokens.
47 *
48 * @param _pooledTokens an array of ERC20s this pool will accept
49 * @param decimals the decimals to use for each pooled token,
50 * eg 8 for WBTC. Cannot be larger than POOL_PRECISION_DECIMALS
51 * @param lpTokenName the long-form name of the token to be deployed
52 * @param lpTokenSymbol the short symbol for the token to be deployed
53 * @param _a the amplification coefficient * n * (n - 1). See the
54 * StableSwap paper for details
55 * @param _fee default swap fee to be initialized with
56 * @param _adminFee default adminFee to be initialized with
57 * @param _withdrawFee default withdrawFee to be initialized with
58 * @param lpTokenTargetAddress the address of an existing LPToken contract to use as a target
59 */
60 function initialize(
61 IERC20[] memory _pooledTokens,
62 uint8[] memory decimals,
63 string memory lpTokenName,
64 string memory lpTokenSymbol,
65 uint256 _a,
66 uint256 _fee,
67 uint256 _adminFee,
68 uint256 _withdrawFee,
69 address lpTokenTargetAddress
70 ) public virtual override initializer {
71 SwapV1.initialize(
72 _pooledTokens,
73 decimals,
74 lpTokenName,
75 lpTokenSymbol,
76 _a,
77 _fee,
78 _adminFee,
79 _withdrawFee,
80 lpTokenTargetAddress
81 );
82 flashLoanFeeBPS = 8; // 8 bps
83 protocolFeeShareBPS = 0; // 0 bps
84 }
85
86 /*** STATE MODIFYING FUNCTIONS ***/
87
88 /**
89 * @notice Borrow the specified token from this pool for this transaction only. This function will call
90 * `IFlashLoanReceiver(receiver).executeOperation` and the `receiver` must return the full amount of the token
91 * and the associated fee by the end of the callback transaction. If the conditions are not met, this call
92 * is reverted.
93 * @param receiver the address of the receiver of the token. This address must implement the IFlashLoanReceiver
94 * interface and the callback function `executeOperation`.
95 * @param token the protocol fee in bps to be applied on the total flash loan fee
96 * @param amount the total amount to borrow in this transaction
97 * @param params optional data to pass along to the callback function
98 */
99 function flashLoan(
100 address receiver,
101 IERC20 token,
102 uint256 amount,
103 bytes memory params
104 ) external nonReentrant {
105 uint8 tokenIndex = getTokenIndex(address(token));
106 uint256 availableLiquidityBefore = token.balanceOf(address(this));
107 uint256 protocolBalanceBefore = availableLiquidityBefore.sub(
108 swapStorage.balances[tokenIndex]
109 );
110 require(
111 amount > 0 && availableLiquidityBefore >= amount,
112 "invalid amount"
113 );
114
115 // Calculate the additional amount of tokens the pool should end up with
116 uint256 amountFee = amount.mul(flashLoanFeeBPS).div(10000);
117 // Calculate the portion of the fee that will go to the protocol
118 uint256 protocolFee = amountFee.mul(protocolFeeShareBPS).div(10000);
119 require(amountFee > 0, "amount is small for a flashLoan");
120
121 // Transfer the requested amount of tokens
122 token.safeTransfer(receiver, amount);
123
124 // Execute callback function on receiver
125 IFlashLoanReceiver(receiver).executeOperation(
126 address(this),
127 address(token),
128 amount,
129 amountFee,
130 params
131 );
132
133 uint256 availableLiquidityAfter = token.balanceOf(address(this));
134 require(
135 availableLiquidityAfter >= availableLiquidityBefore.add(amountFee),
136 "flashLoan fee is not met"
137 );
138
139 swapStorage.balances[tokenIndex] = availableLiquidityAfter
140 .sub(protocolBalanceBefore)
141 .sub(protocolFee);
142 emit FlashLoan(receiver, tokenIndex, amount, amountFee, protocolFee);
143 }
144
145 /*** ADMIN FUNCTIONS ***/
146
147 /**
148 * @notice Updates the flash loan fee parameters. This function can only be called by the owner.
149 * @param newFlashLoanFeeBPS the total fee in bps to be applied on future flash loans
150 * @param newProtocolFeeShareBPS the protocol fee in bps to be applied on the total flash loan fee
151 */
152 function setFlashLoanFees(
153 uint256 newFlashLoanFeeBPS,
154 uint256 newProtocolFeeShareBPS
155 ) external onlyOwner {
156 require(
157 newFlashLoanFeeBPS > 0 &&
158 newFlashLoanFeeBPS <= MAX_BPS &&
159 newProtocolFeeShareBPS <= MAX_BPS,
160 "fees are not in valid range"
161 );
162 flashLoanFeeBPS = newFlashLoanFeeBPS;
163 protocolFeeShareBPS = newProtocolFeeShareBPS;
164 }
165}