Skip to content

Commit bf9f0b4

Browse files
committed
CoW Swap finally working
1 parent 30eea7c commit bf9f0b4

File tree

14 files changed

+539
-57
lines changed

14 files changed

+539
-57
lines changed

contracts/guard/src/GuardV0Base.sol

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -728,9 +728,10 @@ abstract contract GuardV0Base is IGuard, Multicall, SwapCowSwap {
728728
}
729729

730730
// https://github.com/cowprotocol/contracts/tree/main/deployments
731-
function whitelistCowSwap(address settlementContract, string calldata notes) external {
731+
function whitelistCowSwap(address settlementContract, address relayerContract, string calldata notes) external {
732732
// Interaction by special _swapAndValidateCowSwap() internal function
733733
allowApprovalDestination(settlementContract, notes);
734+
allowApprovalDestination(relayerContract, notes);
734735
allowedCowSwaps[settlementContract] = true;
735736
emit CowSwapApproved(settlementContract, notes);
736737
}
@@ -740,14 +741,17 @@ abstract contract GuardV0Base is IGuard, Multicall, SwapCowSwap {
740741
*
741742
* Checks that an asset manager tries to perform a legit CowSwap swap.
742743
*
743-
* 1. Create a Order instance
744-
* 2. Calculate its hash, also known as orderUi
745-
* 3. Call setPreSignature(orderUid, True) on CowSwap
746-
* 4. Offchain logic can now take over to fill the order
747-
* 4.a) Read the emitted order data from OrderSigned event
748-
* 4.b) Submit to CowSwap offchain settlement system
749-
* 4.c) Wait for order to be filled
744+
* 1. Validate the swap is within our allowed whitelists
745+
* 2. Create a Order structure
746+
* 3. Calculate order data hash and prefix with additional information to create order UID
747+
* 4. Set up data needed to call ICowSettlement.setPreSignature(orderUid, True) from Gnosis Safe as a
748+
* 5. Return data to call setPreSignature(orderUid, True) on CowSwap by Safe
749+
* 6. Offchain logic can now take over to fill the order
750+
* 6.a) Read the emitted order data from OrderSigned event
751+
* 6.b) Submit to CowSwap offchain settlement system
752+
* 6.c) Wait for order to be filled
750753
*
754+
* Assume receiver is the same as owner that is the same as the Gnosis Safe address.
751755
*/
752756
function _swapAndValidateCowSwap(
753757
address settlementContract,
@@ -757,7 +761,7 @@ abstract contract GuardV0Base is IGuard, Multicall, SwapCowSwap {
757761
address tokenOut,
758762
uint256 amountIn,
759763
uint256 minAmountOut
760-
) internal returns (bytes memory) {
764+
) internal returns (PresignDeletaCallData memory) {
761765
// Assume sender is trade-executor hot wallet
762766
require(isAllowedCowSwap(settlementContract), "swapAndValidateCowSwap: Cow Swap not enabled");
763767
require(isAllowedSender(msg.sender), "swapAndValidateCowSwap: Sender not asset manager");
@@ -774,6 +778,7 @@ abstract contract GuardV0Base is IGuard, Multicall, SwapCowSwap {
774778
);
775779
return _signCowSwapOrder(
776780
settlementContract,
781+
receiver,
777782
order
778783
);
779784
}

contracts/guard/src/lib/SwapCowSwap.sol

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import {GPv2Order} from "./GPv2Order.sol";
1919
//
2020
// Construct order structure and does it "pre-sign" on CowSwap settlement contract
2121
//
22+
// Currently does the minimal sell order support only.
23+
//
2224
contract SwapCowSwap {
2325

2426
// How long are our CowSwap orders valid for
@@ -31,6 +33,14 @@ contract SwapCowSwap {
3133
uint256 indexed timestamp, bytes orderUid, GPv2Order.Data order, uint32 validTo, uint256 buyAmount, uint256 sellAmount
3234
);
3335

36+
// Gnosis Safe delegatecall information to presign CowSwap order
37+
struct PresignDeletaCallData {
38+
bytes orderUid;
39+
address targetAddress;
40+
bytes data;
41+
}
42+
43+
// Perform sell (exact in) order creation with a max slippage limit
3444
// Copied from https://github.com/InfiniFi-Labs/infinifi-protocol/blob/888c147c4d0f1848577463bc74680c86b7a5c0ff/src/integrations/farms/CoWSwapFarmBase.sol#L71
3545
function _createCowSwapOrder(bytes32 appdata, address receiver, address _tokenIn, address _tokenOut, uint256 _amountIn, uint256 _minAmountOut)
3646
internal
@@ -46,23 +56,37 @@ contract SwapCowSwap {
4656
validTo: uint32(block.timestamp + _SIGN_COOLDOWN),
4757
appData: appdata,
4858
feeAmount: 0,
49-
kind: GPv2Order.KIND_BUY,
59+
kind: GPv2Order.KIND_SELL,
5060
partiallyFillable: false,
5161
sellTokenBalance: GPv2Order.BALANCE_ERC20,
5262
buyTokenBalance: GPv2Order.BALANCE_ERC20
5363
});
5464
}
5565

5666
// Copied from https://github.com/InfiniFi-Labs/infinifi-protocol/blob/888c147c4d0f1848577463bc74680c86b7a5c0ff/src/integrations/farms/CoWSwapFarmBase.sol#L93C1-L102C6
57-
// The main entry point for performing a swap via CowSwap
58-
function _signCowSwapOrder(address settlementContract, GPv2Order.Data memory order) internal returns (bytes memory) {
67+
// Takes the order structure and prepares order UID.
68+
// This order UID must be passed to Gnosis Safe delegatecall to be called at ICowSettlement.setPreSignature(orderUid, true)
69+
function _signCowSwapOrder(address settlementContract, address owner, GPv2Order.Data memory order) internal returns (PresignDeletaCallData memory) {
5970
ICowSettlement settlement = ICowSettlement(payable(settlementContract));
6071
bytes32 orderDigest = GPv2Order.hash(order, settlement.domainSeparator());
72+
73+
// Fill in order UID bytes
6174
bytes memory orderUid = new bytes(GPv2Order.UID_LENGTH);
62-
GPv2Order.packOrderUidParams(orderUid, orderDigest, address(this), order.validTo);
63-
settlement.setPreSignature(orderUid, true);
75+
GPv2Order.packOrderUidParams(orderUid, orderDigest, owner, order.validTo);
76+
77+
// Notify our offchain logic about the created order UID
6478
emit OrderSigned(block.timestamp, orderUid, order, order.validTo, order.buyAmount, order.sellAmount);
65-
return orderUid;
79+
80+
return PresignDeletaCallData({
81+
orderUid: orderUid,
82+
targetAddress: settlementContract,
83+
data: abi.encodeWithSelector(
84+
ICowSettlement.setPreSignature.selector,
85+
orderUid,
86+
true
87+
)
88+
});
89+
6690
}
6791

6892
}

contracts/safe-integration/src/TradingStrategyModuleV0.sol

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,12 @@ contract TradingStrategyModuleV0 is Module, GuardV0Base {
117117
address tokenOut,
118118
uint256 amountIn,
119119
uint256 minAmountOut
120-
) external returns (bytes memory) {
121-
return _swapAndValidateCowSwap(
120+
) public {
121+
bool success;
122+
bytes memory response;
123+
124+
// Checks for asset manager (msg.sender), receiver, etc.
125+
PresignDeletaCallData memory presignDeletaCallData = _swapAndValidateCowSwap(
122126
settlementContract,
123127
receiver,
124128
appData,
@@ -127,6 +131,20 @@ contract TradingStrategyModuleV0 is Module, GuardV0Base {
127131
amountIn,
128132
minAmountOut
129133
);
134+
// Perform ICowSettlement.setPresigned() call on the behalf of the Safe
135+
(success, response) = execAndReturnData(
136+
presignDeletaCallData.targetAddress,
137+
0,
138+
presignDeletaCallData.data,
139+
Enum.Operation.Call
140+
);
141+
142+
// Bubble up the revert reason
143+
if (!success) {
144+
assembly {
145+
revert(add(response, 0x20), mload(response))
146+
}
147+
}
130148
}
131149

132150
}

0 commit comments

Comments
 (0)