Skip to content

Commit

Permalink
chore: migrate E2E internalBalances test to Foundry
Browse files Browse the repository at this point in the history
  • Loading branch information
meetmangukiya committed Sep 8, 2024
1 parent 5957d67 commit d8a2766
Showing 1 changed file with 254 additions and 0 deletions.
254 changes: 254 additions & 0 deletions test/e2e/InternalBalances.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
pragma solidity ^0.8;

import {Vm} from "forge-std/Vm.sol";

import {StandardERC20} from "../src/NonStandardERC20.sol";
import {IERC20} from "src/contracts/interfaces/IERC20.sol";
import {IVault} from "src/contracts/interfaces/IVault.sol";

import {
GPv2Interaction,
GPv2Order,
GPv2Signing,
GPv2Trade,
SettlementEncoder
} from "../libraries/encoders/SettlementEncoder.sol";
import {Registry, TokenRegistry} from "../libraries/encoders/TokenRegistry.sol";
import {ERC20NoReturn, ERC20ReturningUint} from "../src/NonStandardERC20.sol";
import {ForkedTest} from "./ForkedTest.t.sol";

using SettlementEncoder for SettlementEncoder.State;
using TokenRegistry for TokenRegistry.State;
using TokenRegistry for Registry;

interface IBalancerVault is IVault {
function setRelayerApproval(address, address, bool) external;
function getInternalBalance(address user, IERC20[] memory tokens) external view returns (uint256[] memory);
function hasApprovedRelayer(address, address) external view returns (bool);
}

contract InternalBalancesTest is ForkedTest {
IERC20 token1;
IERC20 token2;

function setUp() public override {
super.setUp();

token1 = IERC20(address(new StandardERC20()));
token2 = IERC20(address(new StandardERC20()));

token1 = IERC20(address(token1));
token2 = IERC20(address(token2));

GPv2Interaction.Data[] memory interactions = new GPv2Interaction.Data[](2);
interactions[0] = GPv2Interaction.Data({
target: address(token1),
value: 0,
callData: abi.encodeCall(IERC20.approve, (address(vault), type(uint256).max))
});
interactions[1] = GPv2Interaction.Data({
target: address(token2),
value: 0,
callData: abi.encodeCall(IERC20.approve, (address(vault), type(uint256).max))
});
SettlementEncoder.EncodedSettlement memory encodedSettlement = encoder.encode(interactions);
vm.prank(solver);
settle(encodedSettlement);
}

function test_should_settle_orders_buying_and_selling_with_internal_balances() external {
Vm.Wallet memory trader1 = vm.createWallet("trader1");
Vm.Wallet memory trader2 = vm.createWallet("trader2");
Vm.Wallet memory trader3 = vm.createWallet("trader3");
Vm.Wallet memory trader4 = vm.createWallet("trader4");
vm.label(trader1.addr, "trader1");
vm.label(trader2.addr, "trader2");
vm.label(trader3.addr, "trader3");
vm.label(trader4.addr, "trader4");
vm.label(address(token1), "token1");
vm.label(address(token2), "token2");
vm.label(vaultRelayer, "vaultRelayer");
vm.label(address(vault), "vault");

// mint some tokens to trader1
_mintTokens(token1, trader1.addr, 1.001 ether);

// approve tokens to the balancer vault and approve the settlement contract to
// be able to spend the balancer internal/external balances
vm.startPrank(trader1.addr);
token1.approve(address(vault), type(uint256).max);
IBalancerVault(address(vault)).setRelayerApproval(trader1.addr, vaultRelayer, true);
vm.stopPrank();

// place order for selling 1 token1 for 500 token2
encoder.signEncodeTrade(
vm,
trader1,
GPv2Order.Data({
sellToken: token1,
buyToken: token2,
receiver: trader1.addr,
sellAmount: 1 ether,
buyAmount: 500 ether,
validTo: 0xffffffff,
appData: bytes32(uint256(1)),
feeAmount: 0.001 ether,
kind: GPv2Order.KIND_SELL,
partiallyFillable: false,
sellTokenBalance: GPv2Order.BALANCE_EXTERNAL,
buyTokenBalance: GPv2Order.BALANCE_ERC20
}),
domainSeparator,
GPv2Signing.Scheme.Eip712,
0
);

// mint some tokens to trader2
_mintTokens(token2, trader2.addr, 300.3 ether);

// approve tokens to the balancer vault and deposit some tokens to balancer internal
// balance
vm.startPrank(trader2.addr);
token2.approve(address(vault), type(uint256).max);
IVault.UserBalanceOp[] memory ops = new IVault.UserBalanceOp[](1);
ops[0] = IVault.UserBalanceOp({
kind: IVault.UserBalanceOpKind.DEPOSIT_INTERNAL,
asset: token2,
amount: 300.3 ether,
sender: trader2.addr,
recipient: payable(trader2.addr)
});
vault.manageUserBalance(ops);
IBalancerVault(address(vault)).setRelayerApproval(trader2.addr, vaultRelayer, true);
vm.stopPrank();

// place order for buying 0.5 token1 with max 300 token2
encoder.signEncodeTrade(
vm,
trader2,
GPv2Order.Data({
sellToken: token2,
buyToken: token1,
receiver: trader2.addr,
sellAmount: 300 ether,
buyAmount: 0.5 ether,
validTo: 0xffffffff,
appData: bytes32(uint256(1)),
feeAmount: 0.3 ether,
kind: GPv2Order.KIND_BUY,
partiallyFillable: false,
sellTokenBalance: GPv2Order.BALANCE_INTERNAL,
buyTokenBalance: GPv2Order.BALANCE_ERC20
}),
domainSeparator,
GPv2Signing.Scheme.Eip712,
0
);

// mint some tokens to trader3
_mintTokens(token1, trader3.addr, 2.002 ether);

// approve the tokens to cow vault relayer
vm.prank(trader3.addr);
token1.approve(vaultRelayer, type(uint256).max);

// place order for selling 2 token1 for min 1000 token2
encoder.signEncodeTrade(
vm,
trader3,
GPv2Order.Data({
sellToken: token1,
buyToken: token2,
receiver: trader3.addr,
sellAmount: 2 ether,
buyAmount: 1000 ether,
validTo: 0xffffffff,
appData: bytes32(uint256(1)),
feeAmount: 0.002 ether,
kind: GPv2Order.KIND_SELL,
partiallyFillable: false,
sellTokenBalance: GPv2Order.BALANCE_ERC20,
buyTokenBalance: GPv2Order.BALANCE_INTERNAL
}),
domainSeparator,
GPv2Signing.Scheme.Eip712,
0
);

// mint some tokens to trader4
_mintTokens(token2, trader4.addr, 1501.5 ether);

// approve tokens to the balancer vault and deposit some tokens to balancer internal
// balance
vm.startPrank(trader4.addr);
token2.approve(address(vault), type(uint256).max);
ops = new IVault.UserBalanceOp[](1);
ops[0] = IVault.UserBalanceOp({
kind: IVault.UserBalanceOpKind.DEPOSIT_INTERNAL,
asset: token2,
amount: 1501.5 ether,
sender: trader4.addr,
recipient: payable(trader4.addr)
});
IBalancerVault(address(vault)).manageUserBalance(ops);
IBalancerVault(address(vault)).setRelayerApproval(trader4.addr, vaultRelayer, true);
vm.stopPrank();

// place order to buy 2.5 token1 with max 1500 token2
encoder.signEncodeTrade(
vm,
trader4,
GPv2Order.Data({
sellToken: token2,
buyToken: token1,
receiver: trader4.addr,
sellAmount: 1500 ether,
buyAmount: 2.5 ether,
validTo: 0xffffffff,
appData: bytes32(uint256(1)),
feeAmount: 1.5 ether,
kind: GPv2Order.KIND_BUY,
partiallyFillable: false,
sellTokenBalance: GPv2Order.BALANCE_INTERNAL,
buyTokenBalance: GPv2Order.BALANCE_INTERNAL
}),
domainSeparator,
GPv2Signing.Scheme.Eip712,
0
);

// set token prices
IERC20[] memory tokens = new IERC20[](2);
tokens[0] = token1;
tokens[1] = token2;
uint256[] memory prices = new uint256[](2);
prices[0] = 550;
prices[1] = 1;
encoder.tokenRegistry.tokenRegistry().setPrices(tokens, prices);

// settle the orders
SettlementEncoder.EncodedSettlement memory encodedSettlement = encoder.encode(settlement);
vm.prank(solver);
settle(encodedSettlement);

assertEq(token2.balanceOf(trader1.addr), 550 ether, "trader1 amountOut not as expected");
assertEq(token1.balanceOf(trader2.addr), 0.5 ether, "trader2 amountOut not as expected");
assertEq(_getInternalBalance(address(token2), trader3.addr), 1100 ether, "trader3 amountOut not as expected");
assertEq(_getInternalBalance(address(token1), trader4.addr), 2.5 ether, "trader4 amountOut not as expected");

assertEq(token1.balanceOf(address(settlement)), 0.003 ether, "token1 settlement fee amount not as expected");
assertEq(token2.balanceOf(address(settlement)), 1.8 ether, "token2 settlement fee amount not as expected");
}

function _mintTokens(IERC20 token, address to, uint256 amt) internal {
StandardERC20(address(token)).mint(to, amt);
}

function _getInternalBalance(address token, address who) internal view returns (uint256) {
IERC20[] memory tokens = new IERC20[](1);
tokens[0] = IERC20(token);
uint256[] memory bals = IBalancerVault(address(vault)).getInternalBalance(who, tokens);
return bals[0];
}
}

0 comments on commit d8a2766

Please sign in to comment.