Skip to content

Commit d8a2766

Browse files
committed
chore: migrate E2E internalBalances test to Foundry
1 parent 5957d67 commit d8a2766

File tree

1 file changed

+254
-0
lines changed

1 file changed

+254
-0
lines changed

test/e2e/InternalBalances.t.sol

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
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 {StandardERC20} from "../src/NonStandardERC20.sol";
7+
import {IERC20} from "src/contracts/interfaces/IERC20.sol";
8+
import {IVault} from "src/contracts/interfaces/IVault.sol";
9+
10+
import {
11+
GPv2Interaction,
12+
GPv2Order,
13+
GPv2Signing,
14+
GPv2Trade,
15+
SettlementEncoder
16+
} from "../libraries/encoders/SettlementEncoder.sol";
17+
import {Registry, TokenRegistry} from "../libraries/encoders/TokenRegistry.sol";
18+
import {ERC20NoReturn, ERC20ReturningUint} from "../src/NonStandardERC20.sol";
19+
import {ForkedTest} from "./ForkedTest.t.sol";
20+
21+
using SettlementEncoder for SettlementEncoder.State;
22+
using TokenRegistry for TokenRegistry.State;
23+
using TokenRegistry for Registry;
24+
25+
interface IBalancerVault is IVault {
26+
function setRelayerApproval(address, address, bool) external;
27+
function getInternalBalance(address user, IERC20[] memory tokens) external view returns (uint256[] memory);
28+
function hasApprovedRelayer(address, address) external view returns (bool);
29+
}
30+
31+
contract InternalBalancesTest is ForkedTest {
32+
IERC20 token1;
33+
IERC20 token2;
34+
35+
function setUp() public override {
36+
super.setUp();
37+
38+
token1 = IERC20(address(new StandardERC20()));
39+
token2 = IERC20(address(new StandardERC20()));
40+
41+
token1 = IERC20(address(token1));
42+
token2 = IERC20(address(token2));
43+
44+
GPv2Interaction.Data[] memory interactions = new GPv2Interaction.Data[](2);
45+
interactions[0] = GPv2Interaction.Data({
46+
target: address(token1),
47+
value: 0,
48+
callData: abi.encodeCall(IERC20.approve, (address(vault), type(uint256).max))
49+
});
50+
interactions[1] = GPv2Interaction.Data({
51+
target: address(token2),
52+
value: 0,
53+
callData: abi.encodeCall(IERC20.approve, (address(vault), type(uint256).max))
54+
});
55+
SettlementEncoder.EncodedSettlement memory encodedSettlement = encoder.encode(interactions);
56+
vm.prank(solver);
57+
settle(encodedSettlement);
58+
}
59+
60+
function test_should_settle_orders_buying_and_selling_with_internal_balances() external {
61+
Vm.Wallet memory trader1 = vm.createWallet("trader1");
62+
Vm.Wallet memory trader2 = vm.createWallet("trader2");
63+
Vm.Wallet memory trader3 = vm.createWallet("trader3");
64+
Vm.Wallet memory trader4 = vm.createWallet("trader4");
65+
vm.label(trader1.addr, "trader1");
66+
vm.label(trader2.addr, "trader2");
67+
vm.label(trader3.addr, "trader3");
68+
vm.label(trader4.addr, "trader4");
69+
vm.label(address(token1), "token1");
70+
vm.label(address(token2), "token2");
71+
vm.label(vaultRelayer, "vaultRelayer");
72+
vm.label(address(vault), "vault");
73+
74+
// mint some tokens to trader1
75+
_mintTokens(token1, trader1.addr, 1.001 ether);
76+
77+
// approve tokens to the balancer vault and approve the settlement contract to
78+
// be able to spend the balancer internal/external balances
79+
vm.startPrank(trader1.addr);
80+
token1.approve(address(vault), type(uint256).max);
81+
IBalancerVault(address(vault)).setRelayerApproval(trader1.addr, vaultRelayer, true);
82+
vm.stopPrank();
83+
84+
// place order for selling 1 token1 for 500 token2
85+
encoder.signEncodeTrade(
86+
vm,
87+
trader1,
88+
GPv2Order.Data({
89+
sellToken: token1,
90+
buyToken: token2,
91+
receiver: trader1.addr,
92+
sellAmount: 1 ether,
93+
buyAmount: 500 ether,
94+
validTo: 0xffffffff,
95+
appData: bytes32(uint256(1)),
96+
feeAmount: 0.001 ether,
97+
kind: GPv2Order.KIND_SELL,
98+
partiallyFillable: false,
99+
sellTokenBalance: GPv2Order.BALANCE_EXTERNAL,
100+
buyTokenBalance: GPv2Order.BALANCE_ERC20
101+
}),
102+
domainSeparator,
103+
GPv2Signing.Scheme.Eip712,
104+
0
105+
);
106+
107+
// mint some tokens to trader2
108+
_mintTokens(token2, trader2.addr, 300.3 ether);
109+
110+
// approve tokens to the balancer vault and deposit some tokens to balancer internal
111+
// balance
112+
vm.startPrank(trader2.addr);
113+
token2.approve(address(vault), type(uint256).max);
114+
IVault.UserBalanceOp[] memory ops = new IVault.UserBalanceOp[](1);
115+
ops[0] = IVault.UserBalanceOp({
116+
kind: IVault.UserBalanceOpKind.DEPOSIT_INTERNAL,
117+
asset: token2,
118+
amount: 300.3 ether,
119+
sender: trader2.addr,
120+
recipient: payable(trader2.addr)
121+
});
122+
vault.manageUserBalance(ops);
123+
IBalancerVault(address(vault)).setRelayerApproval(trader2.addr, vaultRelayer, true);
124+
vm.stopPrank();
125+
126+
// place order for buying 0.5 token1 with max 300 token2
127+
encoder.signEncodeTrade(
128+
vm,
129+
trader2,
130+
GPv2Order.Data({
131+
sellToken: token2,
132+
buyToken: token1,
133+
receiver: trader2.addr,
134+
sellAmount: 300 ether,
135+
buyAmount: 0.5 ether,
136+
validTo: 0xffffffff,
137+
appData: bytes32(uint256(1)),
138+
feeAmount: 0.3 ether,
139+
kind: GPv2Order.KIND_BUY,
140+
partiallyFillable: false,
141+
sellTokenBalance: GPv2Order.BALANCE_INTERNAL,
142+
buyTokenBalance: GPv2Order.BALANCE_ERC20
143+
}),
144+
domainSeparator,
145+
GPv2Signing.Scheme.Eip712,
146+
0
147+
);
148+
149+
// mint some tokens to trader3
150+
_mintTokens(token1, trader3.addr, 2.002 ether);
151+
152+
// approve the tokens to cow vault relayer
153+
vm.prank(trader3.addr);
154+
token1.approve(vaultRelayer, type(uint256).max);
155+
156+
// place order for selling 2 token1 for min 1000 token2
157+
encoder.signEncodeTrade(
158+
vm,
159+
trader3,
160+
GPv2Order.Data({
161+
sellToken: token1,
162+
buyToken: token2,
163+
receiver: trader3.addr,
164+
sellAmount: 2 ether,
165+
buyAmount: 1000 ether,
166+
validTo: 0xffffffff,
167+
appData: bytes32(uint256(1)),
168+
feeAmount: 0.002 ether,
169+
kind: GPv2Order.KIND_SELL,
170+
partiallyFillable: false,
171+
sellTokenBalance: GPv2Order.BALANCE_ERC20,
172+
buyTokenBalance: GPv2Order.BALANCE_INTERNAL
173+
}),
174+
domainSeparator,
175+
GPv2Signing.Scheme.Eip712,
176+
0
177+
);
178+
179+
// mint some tokens to trader4
180+
_mintTokens(token2, trader4.addr, 1501.5 ether);
181+
182+
// approve tokens to the balancer vault and deposit some tokens to balancer internal
183+
// balance
184+
vm.startPrank(trader4.addr);
185+
token2.approve(address(vault), type(uint256).max);
186+
ops = new IVault.UserBalanceOp[](1);
187+
ops[0] = IVault.UserBalanceOp({
188+
kind: IVault.UserBalanceOpKind.DEPOSIT_INTERNAL,
189+
asset: token2,
190+
amount: 1501.5 ether,
191+
sender: trader4.addr,
192+
recipient: payable(trader4.addr)
193+
});
194+
IBalancerVault(address(vault)).manageUserBalance(ops);
195+
IBalancerVault(address(vault)).setRelayerApproval(trader4.addr, vaultRelayer, true);
196+
vm.stopPrank();
197+
198+
// place order to buy 2.5 token1 with max 1500 token2
199+
encoder.signEncodeTrade(
200+
vm,
201+
trader4,
202+
GPv2Order.Data({
203+
sellToken: token2,
204+
buyToken: token1,
205+
receiver: trader4.addr,
206+
sellAmount: 1500 ether,
207+
buyAmount: 2.5 ether,
208+
validTo: 0xffffffff,
209+
appData: bytes32(uint256(1)),
210+
feeAmount: 1.5 ether,
211+
kind: GPv2Order.KIND_BUY,
212+
partiallyFillable: false,
213+
sellTokenBalance: GPv2Order.BALANCE_INTERNAL,
214+
buyTokenBalance: GPv2Order.BALANCE_INTERNAL
215+
}),
216+
domainSeparator,
217+
GPv2Signing.Scheme.Eip712,
218+
0
219+
);
220+
221+
// set token prices
222+
IERC20[] memory tokens = new IERC20[](2);
223+
tokens[0] = token1;
224+
tokens[1] = token2;
225+
uint256[] memory prices = new uint256[](2);
226+
prices[0] = 550;
227+
prices[1] = 1;
228+
encoder.tokenRegistry.tokenRegistry().setPrices(tokens, prices);
229+
230+
// settle the orders
231+
SettlementEncoder.EncodedSettlement memory encodedSettlement = encoder.encode(settlement);
232+
vm.prank(solver);
233+
settle(encodedSettlement);
234+
235+
assertEq(token2.balanceOf(trader1.addr), 550 ether, "trader1 amountOut not as expected");
236+
assertEq(token1.balanceOf(trader2.addr), 0.5 ether, "trader2 amountOut not as expected");
237+
assertEq(_getInternalBalance(address(token2), trader3.addr), 1100 ether, "trader3 amountOut not as expected");
238+
assertEq(_getInternalBalance(address(token1), trader4.addr), 2.5 ether, "trader4 amountOut not as expected");
239+
240+
assertEq(token1.balanceOf(address(settlement)), 0.003 ether, "token1 settlement fee amount not as expected");
241+
assertEq(token2.balanceOf(address(settlement)), 1.8 ether, "token2 settlement fee amount not as expected");
242+
}
243+
244+
function _mintTokens(IERC20 token, address to, uint256 amt) internal {
245+
StandardERC20(address(token)).mint(to, amt);
246+
}
247+
248+
function _getInternalBalance(address token, address who) internal view returns (uint256) {
249+
IERC20[] memory tokens = new IERC20[](1);
250+
tokens[0] = IERC20(token);
251+
uint256[] memory bals = IBalancerVault(address(vault)).getInternalBalance(who, tokens);
252+
return bals[0];
253+
}
254+
}

0 commit comments

Comments
 (0)