|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +This repository contains **Euler-CoW Protocol integration contracts** that enable leveraged position management (opening/closing) through CoW Protocol settlements combined with Ethereum Vault Connector (EVC) operations. The contracts act as "wrappers" that coordinate complex multi-step DeFi operations atomically. |
| 8 | + |
| 9 | +### Core Architecture |
| 10 | + |
| 11 | +**Wrapper Pattern**: The codebase uses a chaining wrapper pattern where solvers can execute wrapped settlements that perform custom logic before/during/after CoW Protocol settlements. |
| 12 | + |
| 13 | +- `CowWrapper.sol`: Base abstract contract providing the wrapper framework |
| 14 | + - Validates callers are authenticated solvers |
| 15 | + - Implements `wrappedSettle()` entry point |
| 16 | + - Provides `_internalSettle()` for continuing the settlement chain |
| 17 | + - Wrappers can be chained: Wrapper1 → Wrapper2 → Settlement |
| 18 | + |
| 19 | +- `CowWrapperHelpers.sol`: Helper utilities for wrapper data parsing and validation |
| 20 | +- The CowWrapper is designed to support reentrancy. Additionally, the CowWrapper is designed with gas efficiency in mind, so we only check if the previous contract was part of the trusted wrapper chain. Furthermore, any wrappers that will ever be approved exist will use `CowWrapper.sol` as a base, so its not possible to inject a unauthorized wrapper into the chain without it getting ultimately rejected by the time the settlement contract is reached. |
| 21 | + |
| 22 | +**Specialized Wrappers**: Two production wrappers implement specific EVC + CoW Protocol workflows: |
| 23 | + |
| 24 | +1. **`CowEvcOpenPositionWrapper.sol`**: Opens leveraged positions |
| 25 | + - Enables collateral vault |
| 26 | + - Enables controller (borrow vault) |
| 27 | + - Deposits collateral |
| 28 | + - Borrows assets |
| 29 | + - Executes CoW settlement to swap borrowed assets → collateral |
| 30 | + - All operations are atomic within EVC batch |
| 31 | + |
| 32 | +2. **`CowEvcClosePositionWrapper.sol`**: Closes leveraged positions |
| 33 | + - Executes CoW settlement to swap collateral → repayment assets |
| 34 | + - Repays debt to borrow vault |
| 35 | + - Returns excess assets to user |
| 36 | + - Disables collateral if full repayment |
| 37 | + - All operations are atomic within EVC batch |
| 38 | + |
| 39 | +**Authorization Mechanisms**: Both wrappers support two authorization modes: |
| 40 | +- **EVC Permit**: One-time permit signature for specific operation |
| 41 | +- **Pre-Approved Hashes** (`PreApprovedHashes.sol`): Users pre-approve operation hashes on-chain (useful for EIP-7702 wallet interactions) |
| 42 | + |
| 43 | +### Key Dependencies |
| 44 | + |
| 45 | +- **Euler Vault Kit** (`lib/euler-vault-kit`): ERC4626 vault implementation with borrowing |
| 46 | +- **Ethereum Vault Connector (EVC)** (`lib/evc`): Batch transaction coordinator with account checking |
| 47 | +- **CoW Protocol** (`lib/cow`): DEX aggregator settlement contracts and order libraries |
| 48 | + |
| 49 | +## Development Commands |
| 50 | + |
| 51 | +### Build |
| 52 | +```bash |
| 53 | +forge build --deny notes |
| 54 | +``` |
| 55 | + |
| 56 | +### Test |
| 57 | +```bash |
| 58 | +# Run all tests (requires FORK_RPC_URL environment variable) |
| 59 | +forge test |
| 60 | + |
| 61 | +# Run specific test file |
| 62 | +forge test --match-path test/CowEvcOpenPositionWrapper.t.sol |
| 63 | + |
| 64 | +# Run specific test function |
| 65 | +forge test --match-test test_OpenPosition |
| 66 | + |
| 67 | +# Run with verbose output |
| 68 | +forge test -vvv |
| 69 | +``` |
| 70 | + |
| 71 | +**Important**: Tests require mainnet fork. Set `FORK_RPC_URL` environment variable to a mainnet RPC endpoint. |
| 72 | + |
| 73 | +### Format |
| 74 | +```bash |
| 75 | +forge fmt |
| 76 | +``` |
| 77 | + |
| 78 | +### Gas Snapshots |
| 79 | +```bash |
| 80 | +forge snapshot |
| 81 | +``` |
| 82 | + |
| 83 | +## Testing Architecture |
| 84 | + |
| 85 | +**Base Test Contract**: `test/helpers/CowBaseTest.sol` |
| 86 | +- Sets up mainnet fork at block 22546006 |
| 87 | +- Configures CoW Protocol settlement and authenticator |
| 88 | +- Deploys test solver contract |
| 89 | +- Sets up test vaults (eSUSDS, eWETH) and tokens |
| 90 | +- Provides helper functions for creating settlement data structures |
| 91 | + |
| 92 | +**Test Helpers**: |
| 93 | +- `MilkSwap.sol`: Simple test DEX for simulating swaps in settlements |
| 94 | +- `GPv2OrderHelper.sol`: Utilities for constructing CoW Protocol orders |
| 95 | +- `SignerECDSA.sol`: ECDSA signature utilities for tests |
| 96 | +- `EmptyWrapper.sol`: Minimal wrapper for testing wrapper chaining |
| 97 | + |
| 98 | +## Important Implementation Details |
| 99 | + |
| 100 | +### Security Considerations |
| 101 | + |
| 102 | +- It is generally assumed that the `solvers` (aka, an address for which `CowAuthentication.isSolver()` returns true) is a trusted actor within the system. Only in the case that a solver could steal an entire user's deposit or funds, or steal funds beyond what the user specified as their minimum out/minimum buy amount, assume there is incentive for a solver to provide the best rate/user outcome possible. To be clear, a solver cannot steal funds simply by setting arbitrary `clearingPrices` (as documented a bit later). |
| 103 | + - For a solver to be able to steal an entire user's deposit or funds, they must be able to withdraw the users token to an address of their choosing or otherwise in their control (therefore, a "nuisance" transfer between two wallets that the user effectively owns does not count). |
| 104 | +- If a user takes on debt, that debt position must be sufficiently collateralized above a set collateralization ratio higher than liquidation ratio before the EVC batch transaction concludes. If it is not, the transaction reverts and nothing can happen. Therefore, there is no risk of undercollateralization to the system due to a user opening a position because the transaction would revert. |
| 105 | +- anyone can call the `EVC.batch()` function to initialize a batched call through the EVC. This call is allowed to be reentrant. Therefore, simply checking that a caller is the `address(EVC)` doesn't really offer any added security benefit. |
| 106 | +- The parameters supplied by a solver to the settlement contract are all indirectly bounded from within the settlement contract by ceratin restrictions: |
| 107 | + - `tokens` -- this is a mapping used by the settlement contract to save on gas. If a token used by an order is missing, it will fail to pass signature checks. |
| 108 | + - `clearingPrices` -- these define prices to go with the previously defined `tokens`. These clearing prices are set by the solver and determine exactly how many tokens come out of a trade. **However, if a clearingPrice is lower than any of a user's limit price in `trades`, the transaction will revert. Therefore, it is not possible for a user to steal a users funds simply by setting clearingPrices to an arbitrary value.** There is incentive to provide the best clearingPrice because an auction is held off-chain by CoW Protocol and only the best overall rate outcome is selected. |
| 109 | + - `trades` -- List of orders to fulfill. All of the data inside this structure is effectively signed by the user and cannot be altered by solvers, other than adding or removing signed orders. |
| 110 | + - `interactions` -- Solvers use this to specify operations that should be executed from within the settlement contract. This could include swaps, pre-hooks, post-hooks, etc. This is completely controlled by the solver. |
| 111 | + |
| 112 | +- Please consider any potential security vulnerabilities resulting from potential flawed assumptions of the above from any contracts outside this repo, including the Ethereum Vault Connector (EVC), Settlement Contract, or Euler Vaults, out of scope. |
| 113 | + |
| 114 | +### Wrapper Data Format |
| 115 | +Wrapper data is passed as a calldata slice with format: |
| 116 | +``` |
| 117 | +[wrapper-specific-params][signature][next-wrapper-address (20 bytes)][remaining-wrapper-data] |
| 118 | +``` |
| 119 | + |
| 120 | +The `parseWrapperData()` function must consume its portion and return the remainder. |
| 121 | + |
| 122 | +### EVC Integration |
| 123 | +Both wrappers execute operations within EVC batches to ensure atomicity and proper account health checks. The flow is: |
| 124 | +1. Wrapper validates authorization (permit or pre-approved hash) |
| 125 | +2. Build EVC.BatchItem[] array with all operations |
| 126 | +3. Call `EVC.batch()` - EVC ensures account is healthy at end |
| 127 | + |
| 128 | +### Settlement Execution Context |
| 129 | +- Wrappers use `evcInternalSettle()` as internal callback from EVC batch |
| 130 | +- This function can only be called by EVC during batch execution |
| 131 | +- Uses transient storage (`depth`, `settleCalls`) to prevent reentrancy |
| 132 | + |
| 133 | +### Authentication |
| 134 | +- Only authenticated CoW Protocol solvers can call `wrappedSettle()` |
| 135 | +- Authentication checked via `AUTHENTICATOR.isSolver(msg.sender)` |
| 136 | +- Wrappers themselves can be added to solver allowlist for testing |
| 137 | + |
| 138 | +## Foundry Configuration |
| 139 | + |
| 140 | +- Compiler optimization: enabled |
| 141 | +- IR compilation: enabled (`via_ir = true`) |
| 142 | +- Source directory: `src/` |
| 143 | +- Test directory: `test/` |
| 144 | +- Library dependencies managed via git submodules |
| 145 | + |
| 146 | +## Coding Style |
| 147 | + |
| 148 | +### Error Handling |
| 149 | +**Always use `require()` with custom errors instead of `if () { revert }`**. This pattern is used consistently throughout the codebase: |
| 150 | + |
| 151 | +```solidity |
| 152 | +// ✅ Preferred |
| 153 | +require(msg.sender == address(EVC), Unauthorized(msg.sender)); |
| 154 | +require(depth > 0 && settleCalls == 0, Unauthorized(address(0))); |
| 155 | +
|
| 156 | +// ❌ Avoid |
| 157 | +if (msg.sender != address(EVC)) { |
| 158 | + revert Unauthorized(msg.sender); |
| 159 | +} |
| 160 | +``` |
| 161 | + |
| 162 | +This approach is more concise and maintains consistency with the existing codebase style. |
| 163 | + |
| 164 | +## Remappings |
| 165 | + |
| 166 | +Key import remappings: |
| 167 | +- `cow/` → CoW Protocol contracts (`lib/cow/src/contracts`) |
| 168 | +- `evc/` → Ethereum Vault Connector (`lib/euler-vault-kit/lib/ethereum-vault-connector/src/`) |
| 169 | +- `euler-vault-kit/` → Euler vault implementation |
| 170 | +- `openzeppelin/` → OpenZeppelin contracts (via EVC dependency) |
| 171 | + |
| 172 | + |
| 173 | +## When Giving PR feedback |
| 174 | +* do not re-suggest or address feedback after it has already been given, either by you or other contributors who have commented. |
| 175 | +* be careful not to use too many inline comments. If there are already inline comments on the same line that you want to comment on, or if the inline comment is about something that has already been suggested, don't comment. |
0 commit comments