Skip to content

Commit 5fcc9b6

Browse files
committed
ABI encoded trades. (gnosis/gp-v2-contracts#390)
This is a refinement and replacement for #381 This PR adds a new trade structure and relies on ABI This PR is very large, mostly because the decoding and signer recovering code was quite coupled, specifically regarding the unit tests. The `GPv2Encoding.test.ts` was split into two separate test modules and the reason for most of the code change (the original test file was around 500 LoC). If this PR is too hard to review, I can try and make the PR smaller by first just fixing the tests directly in `GPv2Encoding.test.ts` and then moving them out afterwards. ### Test Plan Adjusted tests to new structure.
1 parent c2f218f commit 5fcc9b6

14 files changed

Lines changed: 549 additions & 900 deletions

src/contracts/GPv2Settlement.sol

Lines changed: 36 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,16 @@ import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
99

1010
import "./GPv2AllowanceManager.sol";
1111
import "./interfaces/GPv2Authentication.sol";
12-
import "./libraries/GPv2Encoding.sol";
1312
import "./libraries/GPv2Interaction.sol";
1413
import "./libraries/GPv2Order.sol";
14+
import "./libraries/GPv2Trade.sol";
1515
import "./libraries/GPv2TradeExecution.sol";
1616

1717
/// @title Gnosis Protocol v2 Settlement Contract
1818
/// @author Gnosis Developers
1919
contract GPv2Settlement is ReentrancyGuard, StorageAccessible {
20-
using GPv2Encoding for bytes;
2120
using GPv2Order for bytes;
21+
using GPv2Signing for GPv2Signing.RecoveredOrder;
2222
using GPv2TradeExecution for GPv2TradeExecution.Data;
2323
using SafeMath for uint256;
2424

@@ -142,7 +142,7 @@ contract GPv2Settlement is ReentrancyGuard, StorageAccessible {
142142
/// Orders and interactions encode tokens as indices into this array.
143143
/// @param clearingPrices An array of clearing prices where the `i`-th price
144144
/// is for the `i`-th token in the [`tokens`] array.
145-
/// @param encodedTrades Encoded trades for signed orders.
145+
/// @param trades Trades for signed orders.
146146
/// @param interactions Smart contract interactions split into three
147147
/// separate lists to be run before the settlement, during the settlement
148148
/// and after the settlement respectively.
@@ -151,14 +151,14 @@ contract GPv2Settlement is ReentrancyGuard, StorageAccessible {
151151
function settle(
152152
IERC20[] calldata tokens,
153153
uint256[] calldata clearingPrices,
154-
bytes calldata encodedTrades,
154+
GPv2Trade.Data[] calldata trades,
155155
GPv2Interaction.Data[][3] calldata interactions,
156156
bytes[] calldata orderRefunds
157157
) external nonReentrant onlySolver {
158158
executeInteractions(interactions[0]);
159159

160160
GPv2TradeExecution.Data[] memory executedTrades =
161-
computeTradeExecutions(tokens, clearingPrices, encodedTrades);
161+
computeTradeExecutions(tokens, clearingPrices, trades);
162162
allowanceManager.transferIn(executedTrades);
163163

164164
executeInteractions(interactions[1]);
@@ -191,58 +191,63 @@ contract GPv2Settlement is ReentrancyGuard, StorageAccessible {
191191
/// [`computeTradeExecution`] for more details.
192192
/// @param tokens An array of ERC20 tokens to be traded in the settlement.
193193
/// @param clearingPrices An array of token clearing prices.
194-
/// @param encodedTrades Encoded trades for signed orders.
194+
/// @param trades Trades for signed orders.
195195
/// @return executedTrades Array of executed trades.
196196
function computeTradeExecutions(
197197
IERC20[] calldata tokens,
198198
uint256[] calldata clearingPrices,
199-
bytes calldata encodedTrades
199+
GPv2Trade.Data[] calldata trades
200200
) internal returns (GPv2TradeExecution.Data[] memory executedTrades) {
201-
(uint256 tradeCount, bytes calldata remainingEncodedTrades) =
202-
encodedTrades.decodeTradeCount();
203-
executedTrades = new GPv2TradeExecution.Data[](tradeCount);
204-
205-
GPv2Encoding.Trade memory trade;
206-
uint256 i = 0;
207-
while (remainingEncodedTrades.length != 0) {
208-
remainingEncodedTrades = remainingEncodedTrades.decodeTrade(
201+
GPv2Signing.RecoveredOrder memory recoveredOrder =
202+
GPv2Signing.allocateRecoveredOrder();
203+
204+
executedTrades = new GPv2TradeExecution.Data[](trades.length);
205+
for (uint256 i = 0; i < trades.length; i++) {
206+
GPv2Trade.Data calldata trade = trades[i];
207+
208+
recoveredOrder.recoverOrderFromTrade(
209209
domainSeparator,
210210
tokens,
211211
trade
212212
);
213213
computeTradeExecution(
214-
trade,
214+
recoveredOrder,
215215
clearingPrices[trade.sellTokenIndex],
216216
clearingPrices[trade.buyTokenIndex],
217+
trade.executedAmount,
218+
trade.feeDiscount,
217219
executedTrades[i]
218220
);
219-
i++;
220221
}
221-
222-
require(i == tradeCount, "GPv2: invalid trade encoding");
223222
}
224223

225224
/// @dev Compute the in and out transfer amounts for a single trade.
226225
/// This function reverts if:
227226
/// - The order has expired
228227
/// - The order's limit price is not respected.
229228
///
230-
/// @param trade The trade to process.
229+
/// @param recoveredOrder The recovered order to process.
231230
/// @param sellPrice The price of the order's sell token.
232231
/// @param buyPrice The price of the order's buy token.
232+
/// @param executedAmount The portion of the order to execute. This will be
233+
/// ignored for fill-or-kill orders.
234+
/// @param feeDiscount The discount to apply to the final executed fees.
233235
/// @param executedTrade Memory location for computed executed trade data.
234236
function computeTradeExecution(
235-
GPv2Encoding.Trade memory trade,
237+
GPv2Signing.RecoveredOrder memory recoveredOrder,
236238
uint256 sellPrice,
237239
uint256 buyPrice,
240+
uint256 executedAmount,
241+
uint256 feeDiscount,
238242
GPv2TradeExecution.Data memory executedTrade
239243
) internal {
240-
GPv2Order.Data memory order = trade.order;
244+
GPv2Order.Data memory order = recoveredOrder.data;
245+
bytes memory orderUid = recoveredOrder.uid;
241246

242247
// solhint-disable-next-line not-rely-on-time
243248
require(order.validTo >= block.timestamp, "GPv2: order expired");
244249

245-
executedTrade.owner = trade.owner;
250+
executedTrade.owner = recoveredOrder.owner;
246251
executedTrade.receiver = order.receiver;
247252
executedTrade.sellToken = order.sellToken;
248253
executedTrade.buyToken = order.buyToken;
@@ -282,7 +287,7 @@ contract GPv2Settlement is ReentrancyGuard, StorageAccessible {
282287

283288
if (order.kind == GPv2Order.SELL) {
284289
if (order.partiallyFillable) {
285-
executedSellAmount = trade.executedAmount;
290+
executedSellAmount = executedAmount;
286291
executedFeeAmount =
287292
order.feeAmount.mul(executedSellAmount) /
288293
order.sellAmount;
@@ -293,7 +298,7 @@ contract GPv2Settlement is ReentrancyGuard, StorageAccessible {
293298

294299
executedBuyAmount = executedSellAmount.mul(sellPrice) / buyPrice;
295300

296-
currentFilledAmount = filledAmount[trade.orderUid].add(
301+
currentFilledAmount = filledAmount[orderUid].add(
297302
executedSellAmount
298303
);
299304
require(
@@ -302,7 +307,7 @@ contract GPv2Settlement is ReentrancyGuard, StorageAccessible {
302307
);
303308
} else {
304309
if (order.partiallyFillable) {
305-
executedBuyAmount = trade.executedAmount;
310+
executedBuyAmount = executedAmount;
306311
executedFeeAmount =
307312
order.feeAmount.mul(executedBuyAmount) /
308313
order.buyAmount;
@@ -313,33 +318,31 @@ contract GPv2Settlement is ReentrancyGuard, StorageAccessible {
313318

314319
executedSellAmount = executedBuyAmount.mul(buyPrice) / sellPrice;
315320

316-
currentFilledAmount = filledAmount[trade.orderUid].add(
317-
executedBuyAmount
318-
);
321+
currentFilledAmount = filledAmount[orderUid].add(executedBuyAmount);
319322
require(
320323
currentFilledAmount <= order.buyAmount,
321324
"GPv2: order filled"
322325
);
323326
}
324327

325328
require(
326-
trade.feeDiscount <= executedFeeAmount,
329+
feeDiscount <= executedFeeAmount,
327330
"GPv2: fee discount too large"
328331
);
329-
executedFeeAmount = executedFeeAmount - trade.feeDiscount;
332+
executedFeeAmount = executedFeeAmount - feeDiscount;
330333

331334
executedTrade.sellAmount = executedSellAmount.add(executedFeeAmount);
332335
executedTrade.buyAmount = executedBuyAmount;
333336

334-
filledAmount[trade.orderUid] = currentFilledAmount;
337+
filledAmount[orderUid] = currentFilledAmount;
335338
emit Trade(
336339
executedTrade.owner,
337340
executedTrade.sellToken,
338341
executedTrade.buyToken,
339342
executedTrade.sellAmount,
340343
executedTrade.buyAmount,
341344
executedFeeAmount,
342-
trade.orderUid
345+
orderUid
343346
);
344347
}
345348

0 commit comments

Comments
 (0)