Skip to content

Commit 9b972bc

Browse files
authored
Merge branch 'e2e-migration' into e2e-migrate-balancerSwap
2 parents eb2039c + fbfd19f commit 9b972bc

File tree

3 files changed

+214
-241
lines changed

3 files changed

+214
-241
lines changed

test/e2e/SmartOrder.t.sol

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
// SPDX-License-Identifier: LGPL-3.0-or-later
2+
pragma solidity ^0.8;
3+
4+
import {Vm} from "forge-std/Vm.sol";
5+
6+
import {GPv2Settlement} from "src/contracts/GPv2Settlement.sol";
7+
import {EIP1271Verifier, GPv2EIP1271} from "src/contracts/interfaces/GPv2EIP1271.sol";
8+
import {IERC20} from "src/contracts/interfaces/IERC20.sol";
9+
import {GPv2Order} from "src/contracts/libraries/GPv2Order.sol";
10+
import {GPv2SafeERC20} from "src/contracts/libraries/GPv2SafeERC20.sol";
11+
import {SafeMath} from "src/contracts/libraries/SafeMath.sol";
12+
import {GPv2Signing} from "src/contracts/mixins/GPv2Signing.sol";
13+
14+
import {Sign} from "../libraries/Sign.sol";
15+
import {SettlementEncoder} from "../libraries/encoders/SettlementEncoder.sol";
16+
import {Registry, TokenRegistry} from "../libraries/encoders/TokenRegistry.sol";
17+
import {Helper, IERC20Mintable} from "./Helper.sol";
18+
19+
/// @title Proof of Concept Smart Order
20+
/// @author Gnosis Developers
21+
contract SmartSellOrder is EIP1271Verifier {
22+
using GPv2Order for GPv2Order.Data;
23+
using GPv2SafeERC20 for IERC20;
24+
using SafeMath for uint256;
25+
26+
bytes32 public constant APPDATA = keccak256("SmartSellOrder");
27+
28+
address public immutable owner;
29+
bytes32 public immutable domainSeparator;
30+
IERC20 public immutable sellToken;
31+
IERC20 public immutable buyToken;
32+
uint256 public immutable totalSellAmount;
33+
uint256 public immutable totalFeeAmount;
34+
uint32 public immutable validTo;
35+
36+
constructor(
37+
GPv2Settlement settlement,
38+
IERC20 sellToken_,
39+
IERC20 buyToken_,
40+
uint32 validTo_,
41+
uint256 totalSellAmount_,
42+
uint256 totalFeeAmount_
43+
) {
44+
owner = msg.sender;
45+
domainSeparator = settlement.domainSeparator();
46+
sellToken = sellToken_;
47+
buyToken = buyToken_;
48+
validTo = validTo_;
49+
totalSellAmount = totalSellAmount_;
50+
totalFeeAmount = totalFeeAmount_;
51+
52+
sellToken_.approve(address(settlement.vaultRelayer()), type(uint256).max);
53+
}
54+
55+
modifier onlyOwner() {
56+
require(msg.sender == owner, "not owner");
57+
_;
58+
}
59+
60+
function withdraw(uint256 amount) external onlyOwner {
61+
sellToken.safeTransfer(owner, amount);
62+
}
63+
64+
function close() external onlyOwner {
65+
uint256 balance = sellToken.balanceOf(address(this));
66+
if (balance != 0) {
67+
sellToken.safeTransfer(owner, balance);
68+
}
69+
}
70+
71+
function isValidSignature(bytes32 hash, bytes memory signature)
72+
external
73+
view
74+
override
75+
returns (bytes4 magicValue)
76+
{
77+
uint256 sellAmount = abi.decode(signature, (uint256));
78+
GPv2Order.Data memory order = orderForSellAmount(sellAmount);
79+
80+
if (order.hash(domainSeparator) == hash) {
81+
magicValue = GPv2EIP1271.MAGICVALUE;
82+
}
83+
}
84+
85+
function orderForSellAmount(uint256 sellAmount) public view returns (GPv2Order.Data memory order) {
86+
order.sellToken = sellToken;
87+
order.buyToken = buyToken;
88+
order.receiver = owner;
89+
order.sellAmount = sellAmount;
90+
order.buyAmount = buyAmountForSellAmount(sellAmount);
91+
order.validTo = validTo;
92+
order.appData = APPDATA;
93+
order.feeAmount = totalFeeAmount.mul(sellAmount).div(totalSellAmount);
94+
order.kind = GPv2Order.KIND_SELL;
95+
// NOTE: We counter-intuitively set `partiallyFillable` to `false`, even
96+
// if the smart order as a whole acts like a partially fillable order.
97+
// This is done since, once a settlement commits to a specific sell
98+
// amount, then it is expected to use it completely and not partially.
99+
order.partiallyFillable = false;
100+
order.sellTokenBalance = GPv2Order.BALANCE_ERC20;
101+
order.buyTokenBalance = GPv2Order.BALANCE_ERC20;
102+
}
103+
104+
function buyAmountForSellAmount(uint256 sellAmount) private view returns (uint256 buyAmount) {
105+
uint256 feeAdjustedBalance =
106+
sellToken.balanceOf(address(this)).mul(totalSellAmount).div(totalSellAmount.add(totalFeeAmount));
107+
uint256 soldAmount = totalSellAmount > feeAdjustedBalance ? totalSellAmount - feeAdjustedBalance : 0;
108+
109+
// NOTE: This is currently a silly price strategy where the xrate
110+
// increases linearly from 1:1 to 1:2 as the smart order gets filled.
111+
// This can be extended to more complex "price curves".
112+
buyAmount = sellAmount.mul(totalSellAmount.add(sellAmount).add(soldAmount)).div(totalSellAmount);
113+
}
114+
}
115+
116+
using SettlementEncoder for SettlementEncoder.State;
117+
using TokenRegistry for TokenRegistry.State;
118+
using TokenRegistry for Registry;
119+
120+
contract SmartOrderTest is Helper(false) {
121+
IERC20Mintable token1;
122+
IERC20Mintable token2;
123+
124+
function setUp() public override {
125+
super.setUp();
126+
127+
token1 = deployMintableErc20("TK1", "TK1");
128+
token2 = deployMintableErc20("TK2", "TK2");
129+
}
130+
131+
function test_permits_trader_allowance_with_settlement() external {
132+
Vm.Wallet memory trader1 = vm.createWallet("trader1");
133+
Vm.Wallet memory trader2 = vm.createWallet("trader2");
134+
135+
// mint some tokens
136+
token1.mint(trader1.addr, 1.01 ether);
137+
vm.prank(trader1.addr);
138+
// approve tokens to vault relayer
139+
token1.approve(vaultRelayer, type(uint256).max);
140+
// place order to buy 0.5 token2 with 1 token1 max
141+
encoder.signEncodeTrade(
142+
vm,
143+
trader1,
144+
GPv2Order.Data({
145+
kind: GPv2Order.KIND_BUY,
146+
partiallyFillable: false,
147+
sellToken: token1,
148+
buyToken: token2,
149+
sellAmount: 1 ether,
150+
buyAmount: 0.5 ether,
151+
feeAmount: 0.01 ether,
152+
validTo: 0xffffffff,
153+
appData: bytes32(uint256(1)),
154+
sellTokenBalance: GPv2Order.BALANCE_ERC20,
155+
buyTokenBalance: GPv2Order.BALANCE_ERC20,
156+
receiver: GPv2Order.RECEIVER_SAME_AS_OWNER
157+
}),
158+
domainSeparator,
159+
GPv2Signing.Scheme.Eip712,
160+
0
161+
);
162+
163+
vm.prank(trader2.addr);
164+
SmartSellOrder smartOrder = new SmartSellOrder(settlement, token2, token1, 0xffffffff, 1 ether, 0.1 ether);
165+
token2.mint(trader2.addr, 1.1 ether);
166+
vm.prank(trader2.addr);
167+
token2.transfer(address(smartOrder), 1.1 ether);
168+
169+
uint256 smartOrderSellAmount = 0.5 ether;
170+
GPv2Order.Data memory smartOrderTrade = smartOrder.orderForSellAmount(smartOrderSellAmount);
171+
GPv2Order.Data memory expectedOrder = GPv2Order.Data({
172+
kind: GPv2Order.KIND_SELL,
173+
partiallyFillable: false,
174+
sellToken: token2,
175+
buyToken: token1,
176+
receiver: trader2.addr,
177+
sellAmount: smartOrderSellAmount,
178+
buyAmount: 0.75 ether,
179+
feeAmount: 0.05 ether,
180+
validTo: 0xffffffff,
181+
appData: smartOrder.APPDATA(),
182+
sellTokenBalance: GPv2Order.BALANCE_ERC20,
183+
buyTokenBalance: GPv2Order.BALANCE_ERC20
184+
});
185+
assertEq(
186+
keccak256(abi.encode(smartOrderTrade)), keccak256(abi.encode(expectedOrder)), "smart order not as expected"
187+
);
188+
189+
encoder.encodeTrade(
190+
smartOrderTrade,
191+
Sign.Signature({
192+
scheme: GPv2Signing.Scheme.Eip1271,
193+
data: abi.encodePacked(address(smartOrder), abi.encode(smartOrderSellAmount))
194+
}),
195+
0
196+
);
197+
198+
// set token prices
199+
IERC20[] memory tokens = new IERC20[](2);
200+
tokens[0] = token1;
201+
tokens[1] = token2;
202+
uint256[] memory prices = new uint256[](2);
203+
prices[0] = 10;
204+
prices[1] = 15;
205+
encoder.tokenRegistry.tokenRegistry().setPrices(tokens, prices);
206+
207+
SettlementEncoder.EncodedSettlement memory encodedSettlement = encoder.encode(settlement);
208+
209+
vm.prank(solver);
210+
settle(encodedSettlement);
211+
212+
assertEq(token1.balanceOf(trader2.addr), 0.75 ether);
213+
}
214+
}

test/e2e/smartOrder.test.ts

Lines changed: 0 additions & 133 deletions
This file was deleted.

0 commit comments

Comments
 (0)