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/gnosis/gp-v2-contracts/blob/main/networks.json, as indicated in the Appendix. The contracts were located on the bottom left of the front page.
2. Is the code actively being used? (%)
Activity is 400 transactions a day on contract Gnosis Protocol: GPv2Settlement, as indicated in the Appendix.
3. Is there a public software repository? (Y/N)
GitHub: https://github.com/gnosis/cowswap
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? (%)
With 1712 commits and 72 branches, Cowswap's development history is beautifully rich and detailed.
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)
The documents cover basic software functions.
8. Does the software function documentation fully (100%) cover the deployed contracts? (%)
The software documentation fully covers both deployed contracts.
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 (%)
The CowSwap documentation provides a clear association between code and documents via non explicit traceability at https://docs.cowswap.exchange/smart-contracts/settlement-contract/signature-schemes.
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) (%)
GPv2/CowSwap has 97% code coverage according to the coveralls report found at https://coveralls.io/github/gnosis/gp-v2-contracts?branch=main.
13. Scripts and instructions to run the tests? (Y/N)
Scripts/Instructions location: https://github.com/gnosis/cowswap
14. Report of the results (%)
The GPv2/CowSwap code coverage report is available at https://coveralls.io/github/gnosis/gp-v2-contracts?branch=main, and is also linked in their contracts' GitHub repo.
15. Formal Verification test done (%)
No formal verification could be found.
This section looks at the 3rd party software audits done. It is explained in this document.
17. Did 3rd Party audits take place? (%)
One audit was released shortly after Cowswap's deployment, but was started beforehand. While the results are public it is very short at just 3 pages.
18. Is the bug bounty acceptable high? (%)
The GPv2/CowSwap Bug Bounty program rewards participating users with up to 50k for bug finds. Program information can be found at https://medium.com/@gnosisPM/announcing-gnosis-protocol-v2-bug-bounty-f3e44e934566.
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 (%)
The documents detailing the planned decentralisation process clearly state the status of access controls.
20. Is the information clear and complete (%)
All the core executing contracts are immutable.
21. Is the information in non-technical terms that pertain to the investments (%)
The contracts are immutable, and this is clearly identified.
22. Is there Pause Control documentation including records of tests (%)
No pause control can be used on the GPv2/CowSwap core executing smart contracts due to their immutability.
1
2
3 constructor(GPv2Authentication authenticator_, IVault vault_) {
4 authenticator = authenticator_;
5 vault = vault_;
6 vaultRelayer = new GPv2VaultRelayer(vault_);
7 }
8
9 // solhint-disable-next-line no-empty-blocks
10 receive() external payable {
11 // NOTE: Include an empty receive function so that the settlement
12 // contract can receive Ether from contract interactions.
13 }
14
15 /// @dev This modifier is called by settle function to block any non-listed
16 /// senders from settling batches.
17 modifier onlySolver {
18 require(authenticator.isSolver(msg.sender), "GPv2: not a solver");
19 _;
20 }
21
22 /// @dev Modifier to ensure that an external function is only callable as a
23 /// settlement interaction.
24 modifier onlyInteraction {
25 require(address(this) == msg.sender, "GPv2: not an interaction");
26 _;
27 }
28
29 /// @dev Settle the specified orders at a clearing price. Note that it is
30 /// the responsibility of the caller to ensure that all GPv2 invariants are
31 /// upheld for the input settlement, otherwise this call will revert.
32 /// Namely:
33 /// - All orders are valid and signed
34 /// - Accounts have sufficient balance and approval.
35 /// - Settlement contract has sufficient balance to execute trades. Note
36 /// this implies that the accumulated fees held in the contract can also
37 /// be used for settlement. This is OK since:
38 /// - Solvers need to be authorized
39 /// - Misbehaving solvers will be slashed for abusing accumulated fees for
40 /// settlement
41 /// - Critically, user orders are entirely protected
42 ///
43 /// @param tokens An array of ERC20 tokens to be traded in the settlement.
44 /// Trades encode tokens as indices into this array.
45 /// @param clearingPrices An array of clearing prices where the `i`-th price
46 /// is for the `i`-th token in the [`tokens`] array.
47 /// @param trades Trades for signed orders.
48 /// @param interactions Smart contract interactions split into three
49 /// separate lists to be run before the settlement, during the settlement
50 /// and after the settlement respectively.
51 function settle(
52 IERC20[] calldata tokens,
53 uint256[] calldata clearingPrices,
54 GPv2Trade.Data[] calldata trades,
55 GPv2Interaction.Data[][3] calldata interactions
56 ) external nonReentrant onlySolver {
57 executeInteractions(interactions[0]);
58
59 (
60 GPv2Transfer.Data[] memory inTransfers,
61 GPv2Transfer.Data[] memory outTransfers
62 ) = computeTradeExecutions(tokens, clearingPrices, trades);
63
64 vaultRelayer.transferFromAccounts(inTransfers);
65
66 executeInteractions(interactions[1]);
67
68 vault.transferToAccounts(outTransfers);
69
70 executeInteractions(interactions[2]);
71
72 emit Settlement(msg.sender);
73 }
74
75 /// @dev Settle an order directly against Balancer V2 pools.
76 ///
77 /// @param swaps The Balancer V2 swap steps to use for trading.
78 /// @param tokens An array of ERC20 tokens to be traded in the settlement.
79 /// Swaps and the trade encode tokens as indices into this array.
80 /// @param trade The trade to match directly against Balancer liquidity. The
81 /// order will always be fully executed, so the trade's `executedAmount`
82 /// field is used to represent a swap limit amount.
83 function swap(
84 IVault.BatchSwapStep[] calldata swaps,
85 IERC20[] calldata tokens,
86 GPv2Trade.Data calldata trade
87 ) external nonReentrant onlySolver {
88 RecoveredOrder memory recoveredOrder = allocateRecoveredOrder();
89 GPv2Order.Data memory order = recoveredOrder.data;
90 recoverOrderFromTrade(recoveredOrder, tokens, trade);
91
92 IVault.SwapKind kind =
93 order.kind == GPv2Order.KIND_SELL
94 ? IVault.SwapKind.GIVEN_IN
95 : IVault.SwapKind.GIVEN_OUT;
96
97 IVault.FundManagement memory funds;
98 funds.sender = recoveredOrder.owner;
99 funds.fromInternalBalance =
100 order.sellTokenBalance == GPv2Order.BALANCE_INTERNAL;
101 funds.recipient = payable(recoveredOrder.receiver);
102 funds.toInternalBalance =
103 order.buyTokenBalance == GPv2Order.BALANCE_INTERNAL;
104
105 int256[] memory limits = new int256[](tokens.length);
106 uint256 limitAmount = trade.executedAmount;
107 // NOTE: Array allocation initializes elements to 0, so we only need to
108 // set the limits we care about. This ensures that the swap will respect
109 // the order's limit price.
110 if (order.kind == GPv2Order.KIND_SELL) {
111 require(limitAmount >= order.buyAmount, "GPv2: limit too low");
112 limits[trade.sellTokenIndex] = order.sellAmount.toInt256();
113 limits[trade.buyTokenIndex] = -limitAmount.toInt256();
114 } else {
115 require(limitAmount <= order.sellAmount, "GPv2: limit too high");
116 limits[trade.sellTokenIndex] = limitAmount.toInt256();
117 limits[trade.buyTokenIndex] = -order.buyAmount.toInt256();
118 }
119
120 GPv2Transfer.Data memory feeTransfer;
121 feeTransfer.account = recoveredOrder.owner;
122 feeTransfer.token = order.sellToken;
123 feeTransfer.amount = order.feeAmount;
124 feeTransfer.balance = order.sellTokenBalance;
125
126 int256[] memory tokenDeltas =
127 vaultRelayer.batchSwapWithFee(
128 kind,
129 swaps,
130 tokens,
131 funds,
132 limits,
133 // NOTE: Specify a deadline to ensure that an expire order
134 // cannot be used to trade.
135 order.validTo,
136 feeTransfer
137 );
138
139 bytes memory orderUid = recoveredOrder.uid;
140 uint256 executedSellAmount =
141 tokenDeltas[trade.sellTokenIndex].toUint256();
142 uint256 executedBuyAmount =
143 (-tokenDeltas[trade.buyTokenIndex]).toUint256();
144
145 // NOTE: Check that the orders were completely filled and update their
146 // filled amounts to avoid replaying them. The limit price and order
147 // validity have already been verified when executing the swap through
148 // the `limit` and `deadline` parameters.
149 require(filledAmount[orderUid] == 0, "GPv2: order filled");
150 if (order.kind == GPv2Order.KIND_SELL) {
151 require(
152 executedSellAmount == order.sellAmount,
153 "GPv2: sell amount not respected"
154 );
155 filledAmount[orderUid] = order.sellAmount;
156 } else {
157 require(
158 executedBuyAmount == order.buyAmount,
159 "GPv2: buy amount not respected"
160 );
161 filledAmount[orderUid] = order.buyAmount;
162 }
163
164 emit Trade(
165 recoveredOrder.owner,
166 order.sellToken,
167 order.buyToken,
168 executedSellAmount,
169 executedBuyAmount,
170 order.feeAmount,
171 orderUid
172 );
173 emit Settlement(msg.sender);
174 }
175
176 /// @dev Invalidate onchain an order that has been signed offline.
177 ///
178 /// @param orderUid The unique identifier of the order that is to be made
179 /// invalid after calling this function. The user that created the order
180 /// must be the the sender of this message. See [`extractOrderUidParams`]
181 /// for details on orderUid.
182 function invalidateOrder(bytes calldata orderUid) external {
183 (, address owner, ) = orderUid.extractOrderUidParams();
184 require(owner == msg.sender, "GPv2: caller does not own order");
185 filledAmount[orderUid] = uint256(-1);
186 emit OrderInvalidated(owner, orderUid);
187 }
188
189 /// @dev Free storage from the filled amounts of **expired** orders to claim
190 /// a gas refund. This method can only be called as an interaction.
191 ///
192 /// @param orderUids The unique identifiers of the expired order to free
193 /// storage for.
194 function freeFilledAmountStorage(bytes[] calldata orderUids)
195 external
196 onlyInteraction
197 {
198 freeOrderStorage(filledAmount, orderUids);
199 }
200
201 /// @dev Free storage from the pre signatures of **expired** orders to claim
202 /// a gas refund. This method can only be called as an interaction.
203 ///
204 /// @param orderUids The unique identifiers of the expired order to free
205 /// storage for.
206 function freePreSignatureStorage(bytes[] calldata orderUids)
207 external
208 onlyInteraction
209 {
210 freeOrderStorage(preSignature, orderUids);
211 }
212
213 /// @dev Process all trades one at a time returning the computed net in and
214 /// out transfers for the trades.
215 ///
216 /// This method reverts if processing of any single trade fails. See
217 /// [`computeTradeExecution`] for more details.
218 ///
219 /// @param tokens An array of ERC20 tokens to be traded in the settlement.
220 /// @param clearingPrices An array of token clearing prices.
221 /// @param trades Trades for signed orders.
222 // @return inTransfers Array of in transfers of executed sell amounts.
223 // @return outTransfers Array of out transfers of executed buy amounts.
224 function computeTradeExecutions(
225 IERC20[] calldata tokens,
226 uint256[] calldata clearingPrices,
227 GPv2Trade.Data[] calldata trades
228 )
229 internal
230 returns (
231 GPv2Transfer.Data[] memory inTransfers,
232 GPv2Transfer.Data[] memory outTransfers
233 )
234 {
235 RecoveredOrder memory recoveredOrder = allocateRecoveredOrder();
236
237 inTransfers = new GPv2Transfer.Data[](trades.length);
238 outTransfers = new GPv2Transfer.Data[](trades.length);
239
240 for (uint256 i = 0; i < trades.length; i++) {
241 GPv2Trade.Data calldata trade = trades[i];
242
243 recoverOrderFromTrade(recoveredOrder, tokens, trade);
244 computeTradeExecution(
245 recoveredOrder,
246 clearingPrices[trade.sellTokenIndex],
247 clearingPrices[trade.buyTokenIndex],
248 trade.executedAmount,
249 inTransfers[i],
250 outTransfers[i]
251 );
252 }
253 }
Comments to Code: 718 / 990 = 73 %
Tests to Code: 3673 / 990 = 371 %