Skip to content

Commit 7c293bf

Browse files
committed
partially implemented COW test for collateral swap
also found a bug which caused another test to succeed when it shouldn't (afaict) will have to finish this tomorrow
1 parent 67342e9 commit 7c293bf

File tree

2 files changed

+204
-8
lines changed

2 files changed

+204
-8
lines changed

test/CowEvcCollateralSwapWrapper.t.sol

Lines changed: 200 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,28 @@ contract CowEvcCollateralSwapWrapperTest is CowBaseTest {
8383
vm.stopPrank();
8484
}
8585

86+
/// @notice Helper to set up a leveraged position for any user
87+
/// @dev More flexible version that accepts owner, account, and vault parameters
88+
function _setupLeveragedPositionFor(
89+
address owner,
90+
address account,
91+
address collateralAsset,
92+
address collateralVault,
93+
address borrowVault,
94+
uint256 collateralAmount,
95+
uint256 borrowAmount
96+
) internal {
97+
vm.startPrank(owner);
98+
IERC20(collateralAsset).approve(collateralVault, type(uint256).max);
99+
EVC.enableCollateral(account, collateralVault);
100+
EVC.enableController(account, borrowVault);
101+
IERC4626(collateralVault).deposit(collateralAmount, account);
102+
vm.stopPrank();
103+
104+
vm.prank(account);
105+
IBorrowing(borrowVault).borrow(borrowAmount, owner);
106+
}
107+
86108
struct SettlementData {
87109
bytes orderUid;
88110
GPv2Order.Data orderData;
@@ -126,6 +148,23 @@ contract CowEvcCollateralSwapWrapperTest is CowBaseTest {
126148
);
127149
}
128150

151+
/// @notice Create permit signature for any user
152+
function _createPermitSignatureFor(
153+
CowEvcCollateralSwapWrapper.CollateralSwapParams memory params,
154+
uint256 userPrivateKey
155+
) internal returns (bytes memory) {
156+
ecdsa.setPrivateKey(userPrivateKey);
157+
return ecdsa.signPermit(
158+
params.owner,
159+
address(collateralSwapWrapper),
160+
uint256(uint160(address(collateralSwapWrapper))),
161+
0,
162+
params.deadline,
163+
0,
164+
collateralSwapWrapper.getSignedCalldata(params)
165+
);
166+
}
167+
129168
/// @notice Encode wrapper data with length prefix
130169
function _encodeWrapperData(CowEvcCollateralSwapWrapper.CollateralSwapParams memory params, bytes memory signature)
131170
internal
@@ -146,26 +185,26 @@ contract CowEvcCollateralSwapWrapperTest is CowBaseTest {
146185
}
147186

148187
/// @notice Setup user approvals for collateral swap on subaccount
149-
function _setupSubaccountApprovals(address account, CowEvcCollateralSwapWrapper.CollateralSwapParams memory params)
188+
function _setupSubaccountApprovals(CowEvcCollateralSwapWrapper.CollateralSwapParams memory params)
150189
internal
151190
{
152-
vm.startPrank(user);
191+
vm.startPrank(params.owner);
153192

154193
// Approve vault shares from main account for settlement
155194
IEVault(params.fromVault).approve(COW_SETTLEMENT.vaultRelayer(), type(uint256).max);
156195

157196
// Approve transfer of vault shares from the subaccount to wrapper
158197
IEVC.BatchItem[] memory items = new IEVC.BatchItem[](1);
159198
items[0] = IEVC.BatchItem({
160-
onBehalfOfAccount: account,
199+
onBehalfOfAccount: params.account,
161200
targetContract: params.fromVault,
162201
value: 0,
163202
data: abi.encodeCall(IERC20.approve, (address(collateralSwapWrapper), type(uint256).max))
164203
});
165204
EVC.batch(items);
166205

167206
// Set wrapper as operator for the subaccount
168-
EVC.setAccountOperator(account, address(collateralSwapWrapper), true);
207+
EVC.setAccountOperator(params.account, address(collateralSwapWrapper), true);
169208

170209
// Pre-approve the operation hash
171210
bytes32 hash = collateralSwapWrapper.getApprovalHash(params);
@@ -305,7 +344,7 @@ contract CowEvcCollateralSwapWrapperTest is CowBaseTest {
305344
vm.stopPrank();
306345

307346
// Setup subaccount approvals and pre-approved hash
308-
_setupSubaccountApprovals(account, params);
347+
_setupSubaccountApprovals(params);
309348

310349
// Record balances before swap
311350
uint256 susdsBalanceBefore = IERC20(ESUSDS).balanceOf(account);
@@ -402,7 +441,7 @@ contract CowEvcCollateralSwapWrapperTest is CowBaseTest {
402441
// User signs the order on cowswap (already done in setupCowOrder)
403442

404443
// Setup subaccount approvals and pre-approved hash
405-
_setupSubaccountApprovals(account, params);
444+
_setupSubaccountApprovals(params);
406445

407446
// Record balances and debt before swap
408447
uint256 susdsBalanceBefore = IERC20(ESUSDS).balanceOf(account);
@@ -434,4 +473,159 @@ contract CowEvcCollateralSwapWrapperTest is CowBaseTest {
434473
assertGt(IERC20(EWBTC).balanceOf(account), wbtcBalanceBefore, "Account should have more EWBTC after swap");
435474
assertEq(IEVault(EWETH).debtOf(account), debtBefore, "Debt should remain unchanged after swap");
436475
}
476+
477+
/// @notice Test that the wrapper can handle being called three times in the same chain
478+
/// @dev Two users close positions in the same direction (long SUSDS), one user closes opposite (long WETH)
479+
function test_CollateralSwapWrapper_ThreeUsers_TwoSameOneOpposite() external {
480+
vm.skip(bytes(forkRpcUrl).length == 0);
481+
482+
// Configure vault LTVs for both directions
483+
vm.startPrank(IEVault(ESUSDS).governorAdmin());
484+
IEVault(ESUSDS).setLTV(EWETH, 0.9e4, 0.9e4, 0);
485+
IEVault(ESUSDS).setLTV(EWBTC, 0.9e4, 0.9e4, 0);
486+
vm.stopPrank();
487+
vm.startPrank(IEVault(EWETH).governorAdmin());
488+
IEVault(EWETH).setLTV(ESUSDS, 0.9e4, 0.9e4, 0);
489+
IEVault(EWETH).setLTV(EWBTC, 0.9e4, 0.9e4, 0);
490+
vm.stopPrank();
491+
492+
// Setup accounts
493+
address account1 = address(uint160(user) ^ 1);
494+
address account2 = address(uint160(user2) ^ 1);
495+
address account3 = address(uint160(user3) ^ 1);
496+
497+
// Setup User1: Long SUSDS (SUSDS collateral, WETH debt). ~1 ETH debt
498+
deal(SUSDS, user, 10000 ether);
499+
_setupLeveragedPositionFor(user, account1, SUSDS, ESUSDS, EWETH, 3500 ether, 1 ether);
500+
501+
// Setup User2: Long SUSDS (SUSDS collateral, WETH debt). ~3 ETH debt
502+
deal(SUSDS, user2, 10000 ether);
503+
_setupLeveragedPositionFor(user2, account2, SUSDS, ESUSDS, EWETH, 10000 ether, 3 ether);
504+
505+
// Setup User3: Long WETH (WETH collateral, SUSDS debt). ~5000 SUSDS debt
506+
deal(WETH, user3, 3 ether);
507+
_setupLeveragedPositionFor(user3, account3, WETH, EWETH, ESUSDS, 3 ether, 5000 ether);
508+
509+
// Verify positions exist
510+
assertEq(IEVault(EWETH).debtOf(account1), 1 ether, "User1 should have WETH debt");
511+
assertEq(IEVault(EWETH).debtOf(account2), 3 ether, "User2 should have WETH debt");
512+
assertEq(IEVault(ESUSDS).debtOf(account3), 5000 ether, "User3 should have SUSDS debt");
513+
514+
// Create params for all users
515+
CowEvcCollateralSwapWrapper.CollateralSwapParams memory params1 = CowEvcCollateralSwapWrapper.CollateralSwapParams({
516+
owner: user,
517+
account: account1,
518+
deadline: block.timestamp + 1 hours,
519+
fromVault: ESUSDS,
520+
toVault: EWETH,
521+
swapAmount: 1000 ether,
522+
kind: GPv2Order.KIND_SELL
523+
});
524+
525+
CowEvcCollateralSwapWrapper.CollateralSwapParams memory params2 = CowEvcCollateralSwapWrapper.CollateralSwapParams({
526+
owner: user2,
527+
account: account2,
528+
deadline: block.timestamp + 1 hours,
529+
fromVault: ESUSDS,
530+
toVault: EWBTC,
531+
swapAmount: 0.005e8, // about 500 ESUSDS
532+
kind: GPv2Order.KIND_BUY
533+
});
534+
535+
CowEvcCollateralSwapWrapper.CollateralSwapParams memory params3 = CowEvcCollateralSwapWrapper.CollateralSwapParams({
536+
owner: user3,
537+
account: account3,
538+
deadline: block.timestamp + 1 hours,
539+
fromVault: EWETH,
540+
toVault: ESUSDS,
541+
swapAmount: 1000 ether,
542+
kind: GPv2Order.KIND_BUY
543+
});
544+
545+
// Create permit signatures for all users
546+
bytes memory permitSignature1 = _createPermitSignatureFor(params1, privateKey);
547+
bytes memory permitSignature2 = _createPermitSignatureFor(params2, privateKey2);
548+
bytes memory permitSignature3 = _createPermitSignatureFor(params3, privateKey3);
549+
550+
// Setup approvals for all users
551+
_setupSubaccountApprovals(params1);
552+
_setupSubaccountApprovals(params2);
553+
_setupSubaccountApprovals(params3);
554+
555+
// Create settlement with all three trades
556+
uint32 validTo = uint32(block.timestamp + 1 hours);
557+
558+
address[] memory tokens = new address[](3);
559+
tokens[0] = ESUSDS;
560+
tokens[1] = EWETH;
561+
tokens[2] = EWBTC;
562+
563+
uint256[] memory clearingPrices = new uint256[](3);
564+
clearingPrices[0] = 1 ether; // eSUSDS price
565+
clearingPrices[1] = 2500 ether; // eWETH price
566+
clearingPrices[2] = 100000 ether * 1e10; // eWBTC price
567+
568+
ICowSettlement.Trade[] memory trades = new ICowSettlement.Trade[](3);
569+
(trades[0],,) =
570+
setupCowOrder(tokens, 0, 1, params1.swapAmount, 0, validTo, user, account1, false);
571+
(trades[1],,) =
572+
setupCowOrder(tokens, 0, 2, 1e24, params2.swapAmount, validTo, user2, account2, true);
573+
(trades[2],,) =
574+
setupCowOrder(tokens, 1, 0, 1e24, params3.swapAmount, validTo, user3, account3, true);
575+
576+
// Setup interactions
577+
ICowSettlement.Interaction[][3] memory interactions;
578+
interactions[0] = new ICowSettlement.Interaction[](0);
579+
interactions[1] = new ICowSettlement.Interaction[](4);
580+
interactions[2] = new ICowSettlement.Interaction[](0);
581+
582+
// We pull the money out of the euler vaults (we only need to withdraw for the BTC exchange)
583+
interactions[1][0] = getWithdrawInteraction(
584+
ESUSDS, 500 ether
585+
);
586+
// we dont need to withdraw the WETH vault because we already have the WETH to direct exchange
587+
//interactions[1][1] = getWithdrawInteraction(EWETH, );
588+
589+
// We swap. Since WETH <> SUSD trades are already coincidence of wants, we trade only the SUSD -> WBTC trade
590+
interactions[1][1] = getSwapInteraction(SUSDS, WBTC, 500 ether);
591+
592+
// We deposit back into WBTC
593+
interactions[1][2] = getDepositInteraction(EWBTC, 0.005e8);
594+
595+
// We "skim" to get the tokens
596+
interactions[1][3] = getSkimInteraction(EWBTC);
597+
598+
// Encode settlement data
599+
bytes memory settleData = abi.encodeCall(ICowSettlement.settle, (tokens, clearingPrices, trades, interactions));
600+
601+
// Chain wrapper data
602+
bytes memory wrapper1Data = abi.encode(params1, permitSignature1);
603+
bytes memory wrapper2Data = abi.encode(params2, permitSignature2);
604+
bytes memory wrapper3Data = abi.encode(params3, permitSignature3);
605+
606+
bytes memory wrapperData = abi.encodePacked(
607+
uint16(wrapper1Data.length),
608+
wrapper1Data,
609+
address(collateralSwapWrapper),
610+
uint16(wrapper2Data.length),
611+
wrapper2Data,
612+
address(collateralSwapWrapper),
613+
uint16(wrapper3Data.length),
614+
wrapper3Data
615+
);
616+
617+
// Execute wrapped settlement
618+
address[] memory targets = new address[](1);
619+
bytes[] memory datas = new bytes[](1);
620+
targets[0] = address(collateralSwapWrapper);
621+
datas[0] = abi.encodeCall(CowWrapper.wrappedSettle, (settleData, wrapperData));
622+
solver.runBatch(targets, datas);
623+
624+
// Verify all positions closed successfully
625+
assertEq(IEVault(EWETH).debtOf(account1), 1 ether, "User1 should have WETH debt");
626+
assertEq(IEVault(EWETH).debtOf(account2), 3 ether, "User2 should have WETH debt");
627+
assertEq(IEVault(ESUSDS).debtOf(account3), 5000 ether, "User3 should have SUSDS debt");
628+
629+
// TODO: check collaterals
630+
}
437631
}

test/helpers/CowBaseTest.sol

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,11 @@ contract CowBaseTest is Test {
9292

9393
// deal small amount to the settlement contract that serve as buffer (just makes tests easier...)
9494
deal(SUSDS, address(COW_SETTLEMENT), 100e18);
95-
deal(WETH, address(COW_SETTLEMENT), 100e18);
95+
deal(WETH, address(COW_SETTLEMENT), 0.05e18);
96+
deal(WBTC, address(COW_SETTLEMENT), 0.001e8);
9697
deal(ESUSDS, address(COW_SETTLEMENT), 100e18);
97-
deal(EWETH, address(COW_SETTLEMENT), 100e18);
98+
deal(EWETH, address(COW_SETTLEMENT), 0.05e18);
99+
deal(EWBTC, address(COW_SETTLEMENT), 0.001e8);
98100

99101
// Set the approval for MilkSwap in the settlement as a convenience
100102
vm.startPrank(address(COW_SETTLEMENT));

0 commit comments

Comments
 (0)