Skip to content

Commit fa7d2b7

Browse files
authored
feat: euler working poc (#1)
implements the bulk code expected to be required for an Ethereum Vault Connector integration with CoWSwap through a GPv2Wrapper integration. The code here is based on an original PoC developed by @anxolin https://github.com/anxolin/evk-periphery . Compared to the PoC, it has these major changes: * bring it into our own repo and trim the dependencies as much as possible * modify the overall flow so that it is easier for backend changes: * (in pre-hook): call `setEvcCalls`, which sets transient data for use in the next call * (as main op): solver calls `settle()` function (same as GPv2Settlement signature) on the EVC wraper contract * (within settle): call pre-hooks with EVC (ex borrow) * (within settle): EVC calls `internalSettle()` which calls the actual `GPv2Settlement` `settle()` * (within settle): call post-hooks with EVC (ex. return) * tighten the security (ex. only the EVC can call `internalSettle`) <img width="720" height="383" alt="image" src="https://github.com/user-attachments/assets/fa88861f-10d4-4f39-95c7-a9e5adb4f6e2" /> To be done in separate PRs: * analyze gas usage and make sure its in line with our expectations --------- Co-authored-by: Kaze <Kaze Cow>
1 parent ec200a4 commit fa7d2b7

File tree

11 files changed

+872
-63
lines changed

11 files changed

+872
-63
lines changed

.github/workflows/test.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ on:
77

88
env:
99
FOUNDRY_PROFILE: ci
10+
FORK_RPC_URL: ${{ secrets.RPC_URL_1 }}
1011

1112
jobs:
1213
check:
@@ -31,7 +32,7 @@ jobs:
3132

3233
- name: Run Forge build
3334
run: |
34-
forge build --sizes
35+
forge build
3536
id: build
3637

3738
- name: Run Forge tests

.gitmodules

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
[submodule "lib/cow"]
55
path = lib/cow
66
url = https://github.com/cowprotocol/contracts
7+
branch = feat/wrapper
78
[submodule "lib/euler-vault-kit"]
89
path = lib/euler-vault-kit
910
url = https://github.com/euler-xyz/euler-vault-kit

foundry.lock

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
{
22
"lib/cow": {
3-
"rev": "39d7f4d68e37d14adeaf3c0caca30ea5c1a2fad9"
3+
"branch": {
4+
"name": "feat/wrapper",
5+
"rev": "1e8127f476f8fef7758cf25033a0010d325dba8d"
6+
}
47
},
58
"lib/euler-vault-kit": {
69
"rev": "5b98b42048ba11ae82fb62dfec06d1010c8e41e6"
710
},
11+
"lib/evc": {
12+
"rev": "34bb788288a0eb0fbba06bc370cb8ca3dd42614e"
13+
},
814
"lib/forge-std": {
9-
"tag": {
10-
"name": "v1.10.0",
11-
"rev": "8bbcf6e3f8f62f419e5429a0bd89331c85c37824"
12-
}
15+
"rev": "0768d9c08c085c79bb31d88683a78770764fec49"
1316
}
1417
}

foundry.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22
src = "src"
33
out = "out"
44
libs = ["lib"]
5+
optimize = true
56

67
# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options

script/Counter.s.sol

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

src/Counter.sol

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

src/CowEvcWrapper.sol

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// SPDX-License-Identifier: GPL-3.0-or-later
2+
pragma solidity ^0.8;
3+
4+
import {IEVC} from "evc/EthereumVaultConnector.sol";
5+
import {IGPv2Authentication} from "./vendor/interfaces/IGPv2Authentication.sol";
6+
7+
import {GPv2Signing, IERC20, GPv2Trade} from "cow/mixins/GPv2Signing.sol";
8+
import {GPv2Wrapper,GPv2Interaction} from "cow/GPv2Wrapper.sol";
9+
10+
/// @title CowEvcWrapper
11+
/// @notice A wrapper around the EVC that allows for settlement operations
12+
contract CowEvcWrapper is GPv2Wrapper, GPv2Signing {
13+
IEVC public immutable EVC;
14+
15+
/// @notice 0 = not executing, 1 = wrappedSettle() called and not yet internal settle, 2 = evcInternalSettle() called
16+
uint256 public transient settleState;
17+
18+
error Unauthorized(address msgSender);
19+
error NoReentrancy();
20+
error MultiplePossibleReceivers(
21+
address resolvedVault, address resolvedSender, address secondVault, address secondSender
22+
);
23+
24+
error NotEVCSettlement();
25+
26+
constructor(address _evc, address payable _settlement) GPv2Wrapper(_settlement) {
27+
EVC = IEVC(_evc);
28+
}
29+
30+
struct ResolvedValues {
31+
address vault;
32+
address sender;
33+
uint256 minAmount;
34+
}
35+
36+
/// @notice Implementation of GPv2Wrapper._wrap - executes EVC operations around settlement
37+
/// @param tokens Tokens involved in settlement
38+
/// @param clearingPrices Clearing prices for settlement
39+
/// @param trades Trade data for settlement
40+
/// @param interactions Interaction data for settlement
41+
/// @param wrapperData Additional data for the wrapper (unused in this implementation)
42+
function _wrap(
43+
IERC20[] calldata tokens,
44+
uint256[] calldata clearingPrices,
45+
GPv2Trade.Data[] calldata trades,
46+
GPv2Interaction.Data[][3] calldata interactions,
47+
bytes calldata wrapperData
48+
) internal override {
49+
// prevent reentrancy: there is no reason why we would want to allow it here
50+
if (settleState != 0) {
51+
revert NoReentrancy();
52+
}
53+
settleState = 1;
54+
55+
// Decode wrapperData into pre and post settlement actions
56+
(IEVC.BatchItem[] memory preSettlementItems, IEVC.BatchItem[] memory postSettlementItems) =
57+
abi.decode(wrapperData, (IEVC.BatchItem[], IEVC.BatchItem[]));
58+
IEVC.BatchItem[] memory items = new IEVC.BatchItem[](preSettlementItems.length + postSettlementItems.length + 1);
59+
60+
// Copy pre-settlement items
61+
for (uint256 i = 0; i < preSettlementItems.length; i++) {
62+
items[i] = preSettlementItems[i];
63+
}
64+
65+
// Add settlement call to wrapper - use _internalSettle from GPv2Wrapper
66+
items[preSettlementItems.length] = IEVC.BatchItem({
67+
onBehalfOfAccount: msg.sender,
68+
targetContract: address(this),
69+
value: 0,
70+
data: abi.encodeCall(this.evcInternalSettle, (tokens, clearingPrices, trades, interactions))
71+
});
72+
73+
// Copy post-settlement items
74+
for (uint256 i = 0; i < postSettlementItems.length; i++) {
75+
items[preSettlementItems.length + 1 + i] = postSettlementItems[i];
76+
}
77+
78+
// Execute all items in a single batch
79+
EVC.batch(items);
80+
settleState = 0;
81+
}
82+
83+
/// @notice Internal settlement function called by EVC
84+
/// @param tokens Tokens involved in settlement
85+
/// @param clearingPrices Clearing prices for settlement
86+
/// @param trades Trade data for settlement
87+
/// @param interactions Interaction data for settlement
88+
function evcInternalSettle(
89+
IERC20[] calldata tokens,
90+
uint256[] calldata clearingPrices,
91+
GPv2Trade.Data[] calldata trades,
92+
GPv2Interaction.Data[][3] calldata interactions
93+
) external payable {
94+
if (msg.sender != address(EVC)) {
95+
revert Unauthorized(msg.sender);
96+
}
97+
98+
if (settleState != 1) {
99+
// origSender will be address(0) here which indiates that internal settle was called when it shouldn't be (outside of wrappedSettle call)
100+
revert Unauthorized(address(0));
101+
}
102+
103+
settleState = 2;
104+
105+
// Use GPv2Wrapper's _internalSettle to call the settlement contract
106+
_internalSettle(tokens, clearingPrices, trades, interactions);
107+
}
108+
}

test/Counter.t.sol

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

0 commit comments

Comments
 (0)