diff --git a/docs/cow-protocol/concepts/order-types/wrappers.md b/docs/cow-protocol/concepts/order-types/wrappers.md new file mode 100644 index 000000000..7d6ebc7a8 --- /dev/null +++ b/docs/cow-protocol/concepts/order-types/wrappers.md @@ -0,0 +1,91 @@ +--- +sidebar_position: 7 +--- + +# Generalized Wrappers + +Generalized wrappers is a new framework to allow custom logic to execute before and/or after order settlement on CoW Protocol. They enable complex DeFi workflows—like flash loans, leveraged positions, and programmatic orders—all while preserving the security and assurances granted by CoW Protocol. + +## What are Wrappers? + +Wrappers are smart contracts that "wrap" the settlement process, executing custom logic surrounding settlement contract. When a solver executes a settlement that includes a wrapper, they call the wrapper contract instead of the settlement contract directly. The wrapper calls the settlement contract on behalf of the solver. + +This mechanism extends CoW Protocol's functionality in a modular way, allowing new features and integrations to be added without modifying the core settlement contract or requiring any changes to solver implementations. + +## Use Cases + +Wrappers enable a wide variety of advanced trading and DeFi operations: + +### Leverage Positions + +By wrapping the execution context of a CoW settlement, protocols implementing leveraged position opening capabilities can be supported. + +As the flagship GW implementation, Euler demonstrates a quintessential implementation of this use case. The wrapper: + +1. Coordinates with Euler's Ethereum Vault Connector (EVC) batch execution system +2. Executes the necessary EVC batch operations before settlement +3. Performs the settlement to acquire the leveraged asset +4. Completes the position setup after settlement + +This enables users to open leveraged positions on Euler through a single CoW Protocol order, with all the complexity handled by the wrapper. Leveraged positions are associated with high-volume trading, so CoW benefits from increased revenue from such integrations. + +### Flash Loan Integration + +Currently, CoW Protocol uses a dedicated `FlashLoanRouter` contract for flash loan functionality. However, this implementation comes with additional implementation effort from both the solvers and the CoW Protocol backend infrastructure. With generalized wrappers, flash loan integration becomes simpler and more flexible. + +### Programmatic Orders + +By introducing a wrapped order type combined with [composable-cow](../../reference/contracts/periphery/composable_cow.md) conditional order generators, it is possible for any account (EOA or smart contract wallet) to authorize delayed orders that can be triggered at a specified time. The wrapper contract provides the signing authentication, the conditional order contract controls the logic for when the order can execute, and the CoW Settlement contract protects the execution of the swap generated by the conditional order. + +### Protocol-Approved Hooks + +Unlike [CoW Hooks](./cow-hooks.mdx), which can revert even if the order is executed successfully, wrappers provide a way to enforce required pre- and post-settlement operations. Since wrappers are protocol-approved through the allowlist authenticator, they can implement critical functionality that must execute: + +- Compliance checks (e.g., OFAC screening) +- Cross chain transfers (pre- or post- transfer) +- Deposit in a vault or other wrapper contract (swap and stake) + +The key difference from hooks is that if a wrapper is required for an order, the settlement cannot proceed without it—making wrappers ideal for transactions requiring robust execution. + +## Considerations + +While wrappers are powerful, there are important considerations to keep in mind: + +### Gas Overhead + +Wrappers add gas overhead to settlement transactions. Benchmarks show that even an empty wrapper (one that does nothing but pass through to settlement) adds approximately 22,272 gas, or about 11.4% overhead. The actual overhead depends on: + +- The complexity of the wrapper's logic +- The size of the settlement data being passed through +- The number of nested wrappers in the chain + +For many use cases, this overhead is acceptable given the functionality unlocked, but it's an important factor in deciding whether to use a wrapper. + +### Requires Protocol Approval + +Wrappers cannot be deployed and used immediately—they must be approved by CoW DAO through the allowlist authenticator. This approval process ensures high-quality wrapper implementations and safety for solvers, but means there's a roadblock for developers looking to extend CoW Protocol. Developers should plan for this approval process when building wrapper-based integrations. + +### On-Chain Protocol Does Not Enforce Execution + +Despite official rules enforcing the execution of wrappers when required by the user, a solver may choose not to execute a wrapper with an order. This means wrappers must be designed defensively: + +- If a wrapper is strictly required, the order should fail to settle without it +- Wrappers should validate all input data and fail in cases where a user's funds could be at risk + +### Encoding Complexity + +To improve gas performance, wrapper data is encoded in a condensed format. Constructing wrapper calldata correctly can be complex, especially when nesting multiple wrappers. While the protocol provides helper contracts like `CowWrapperHelper` to simplify this, developers need to understand the encoding scheme to build robust integrations. + +## Getting Started + +Wrappers are a powerful tool for advanced integrations on CoW Protocol. To start building with wrappers: + +- **For developers**: See the [Integration Guide](../../integrate/wrappers.mdx) for implementation details, code examples, and security guidelines +- **For technical specs**: Consult the [Technical Reference](../../reference/contracts/periphery/wrapper.mdx) for detailed contract documentation and API specifications + +To learn more about wrappers and see example implementations: + +- [Euler Integration Contracts Repository](https://github.com/cowprotocol/euler-integration-contracts) - Contains the `CowWrapper` abstract contract and example implementations +- [Services Repository PR #3700](https://github.com/cowprotocol/services/pull/3700) - Backend integration implementation. Good reference for solvers looking to support wrappers. + +Wrappers represent a significant evolution in CoW Protocol's capabilities, enabling complex DeFi workflows while maintaining security and simplicity for solvers. As more wrappers are developed and approved, they will continue to expand what's possible with intent-based trading. diff --git a/docs/cow-protocol/integrate/wrappers.mdx b/docs/cow-protocol/integrate/wrappers.mdx new file mode 100644 index 000000000..7256dfa35 --- /dev/null +++ b/docs/cow-protocol/integrate/wrappers.mdx @@ -0,0 +1,346 @@ +# Wrapper Integration + +Generalized wrappers enable custom logic to execute surrounding CoW Protocol settlements. This guide covers both using existing wrappers in your orders and building new wrapper contracts. + +**Choose your path:** +- **[Building New Wrappers](#for-wrapper-developers)** - Develop custom wrapper contracts for advanced DeFi workflows +- **[Using Existing Wrappers](#for-order-creators-and-frontend-developers)** - Add wrappers to your orders and integrate into your application +- **[Executing Wrappers as a Solver](#for-solvers)** - Everything solvers need to know to execute wrapper orders + +--- + +## For Wrapper Developers + +This section covers building new wrapper contracts from scratch. + +### Overview + +To build a wrapper, you will: + +1. Inherit from the `CowWrapper` abstract contract +2. Implement `_wrap()` with your pre/post-settlement logic +3. Implement `parseWrapperData()` for input validation +4. Test thoroughly on testnets +5. Submit for allowlist approval by the CoW Protocol team + +Read more on the details of creating a wrapper from [the wrapper contract information page](../reference/contracts/periphery/wrapper.mdx). + +--- + +## For Order Creators and Frontend Developers + +This section shows you how to use existing wrappers in your orders and integrate wrapper support into your application. + +### Adding Wrappers to Orders + +To use a wrapper in your order, add it to the `appData` when creating the order: + +### AppData Structure + +```typescript +interface WrapperCall { + target: string; // Wrapper contract address (must be allowlisted) + data?: string; // Optional hex-encoded wrapper-specific data + is_omittable?: boolean; // Whether solver can skip this wrapper (default: false) +} +``` + +**Fields:** +- **`target`** (required): Address of the allowlisted wrapper contract +- **`data`** (optional): Hex-encoded data specific to the wrapper. Can be empty or omitted if the wrapper doesn't need custom data. +- **`is_omittable`** (optional): Defaults to `false` + - `false` = Solver MUST execute the wrapper with exact data or be slashed + - `true` = Solver MAY skip the wrapper if they find a better solution + +### Example: Using the CoW SDK + +```typescript +import { OrderBookApi, OrderQuoteRequest, OrderCreation } from '@cowprotocol/cow-sdk' + +// Create order with wrapper +const orderCreation: OrderCreation = { + // ... standard order fields (sellToken, buyToken, amounts, etc.) + + appData: { + // ... other appData fields + wrappers: [ + { + target: "0x1234...", // Flash loan wrapper address + data: "0xabcd...", // Encoded flash loan params + is_omittable: false // Must execute + } + ] + } +} + +// Submit order +const orderId = await orderBookApi.sendOrder(orderCreation) +``` + +### Example: Multiple Wrappers (Nested) + +You can chain multiple wrappers in a single order: + +```typescript +appData: { + wrappers: [ + { + target: "0x1111...", // Flash loan wrapper + data: "0xaaaa...", + is_omittable: false + }, + { + target: "0x2222...", // Leverage wrapper + data: "0xbbbb...", + is_omittable: false + } + ] +} +``` + +The wrappers execute in sequence: Wrapper1 → Wrapper2 → Settlement → Wrapper2 (post) → Wrapper1 (post). Note that wrappers from other users' orders may be interspersed, though this should generally not affect the execution of the order. + +### Validating Wrapper Configuration + +Before submitting an order, you can confirm the wrapper encoding is valid using `CowWrapperHelper`: + +```typescript +// On-chain validation (via Ethers/Viem) +const helper = new ethers.Contract(HELPER_ADDRESS, HELPER_ABI, provider) + +const isValid = await helper.verifyAndBuildWrapperData( + ["0x1234...", "0x5678..."], // Wrapper addresses + ["0xabcd...", "0xef01..."] // Wrapper data +) +// Returns encoded wrapperData if valid, reverts if invalid +``` + +This checks: +- All wrappers are allowlisted +- All wrappers use the same settlement contract +- Each wrapper can parse its data successfully + +--- + +## For Solvers + +This section explains how to execute settlements that include wrapper contracts as part of your solver implementation. + +### Detecting Wrapper Orders + +Wrappers are specified in the order's [`appData`](../reference/core/auctions/schema.md) under the `wrappers` field: + +```json +{ + "wrappers": [ + { + "target": "0x1234...", + "data": "0xabcdef...", + "is_omittable": false + } + ] +} +``` + +**Fields:** +- **`target`**: Address of the wrapper contract (treat as opaque - don't parse) +- **`data`**: Wrapper-specific data (treat as opaque - don't parse) +- **`is_omittable`**: Critical flag for solver requirements (see below) + +:::warning +Wrapper addresses and data should be treated as opaque values. Do not attempt to parse or validate them - simply pass them through in your encoding. +::: + +### Solver Requirements + +#### 1. Execute Non-Omittable Wrappers + +Orders with `"is_omittable": false` **MUST** be executed with the specified wrapper. You may be slashed for not doing so. + +If `"is_omittable": true`, you MAY skip the wrapper if you find a better solution without it. + +#### 2. Verify Wrapper Authentication + +All approved wrappers will be approved by the DAO and registered in [`GPv2AllowlistAuthenticator`](../reference/contracts/core/allowlist.md). It is recommended to verify wrappers are allowlisted before including them in settlements. + +#### 3. Include Wrappers in the Solution + +Whether using the CoW Protocol-provided driver or your own, ensure wrapper information flows from the auction to your final settlement transaction. + +### Encoding Wrapper Settlements + +:::info +If you're using the driver provided by CoW Protocol, you can skip this section - the driver handles encoding automatically. +::: + +#### Manual Encoding + +To execute a settlement with wrappers: + +##### 1. Change Transaction Target + +Your transaction should call `wrappedSettle()` on the **first** wrapper (not the settlement contract): + +```typescript +// Without wrapper +tx = { + to: SETTLEMENT_CONTRACT, + data: encodeSettle(...) +} + +// With wrapper +tx = { + to: wrappers[0].target, // First wrapper address + data: encodeWrappedSettle(...) // See below +} +``` + +##### 2. Construct settleData Parameter + +The `settleData` parameter is the **exact** calldata you would send to `GPv2Settlement.settle()`, including the 4-byte function selector: + +```typescript +const settleData = settlementContract.interface.encodeFunctionData("settle", [ + tokens, + clearingPrices, + trades, + interactions +]); +// This is your settleData - unchanged from normal settlement +``` + +##### 3. Encode wrapperData Parameter + +The `wrapperData` combines individual wrapper data with wrapper addresses: + +```typescript +function encodeWrapperData(wrappers: WrapperCall[]): string { + let wrapperData = '0x'; + + for (const [index, wrapper] of wrappers.entries()) { + // Skip first wrapper's address (it's the transaction target) + if (index !== 0) { + // Add wrapper address (20 bytes, without 0x prefix) + wrapperData += wrapper.target.slice(2); + } + + // Encode data length as u16 big-endian (2 bytes) + const dataLength = (wrapper.data.length - 2) / 2; // Remove '0x' and convert to byte length + const lengthHex = dataLength.toString(16).padStart(4, '0'); + wrapperData += lengthHex; + + // Add wrapper data (without 0x prefix) + wrapperData += wrapper.data.slice(2); + } + + return wrapperData; +} +``` + +**Encoding Structure:** +``` +For 2 wrappers: +[length₁(2B)][data₁][address₂(20B)][length₂(2B)][data₂] + +For 3 wrappers: +[length₁(2B)][data₁][address₂(20B)][length₂(2B)][data₂][address₃(20B)][length₃(2B)][data₃] +``` + +##### 4. Call wrappedSettle + +```typescript +const tx = { + to: wrappers[0].target, + data: wrapperContract.interface.encodeFunctionData("wrappedSettle", [ + settleData, // From step 2 + wrapperData // From step 3 + ]) +} +``` + +#### Using CowWrapperHelper + +Alternatively, use the `CowWrapperHelper` contract for encoding and validation: + +```solidity +// On-chain encoding and validation +address[] memory wrapperAddresses = [wrapper1, wrapper2]; +bytes[] memory wrapperDatas = [data1, data2]; + +bytes memory wrapperData = CowWrapperHelper(HELPER).verifyAndBuildWrapperData( + wrapperAddresses, + wrapperDatas +); +``` + +In addition to producing the correct encoded data, the helper also validates: +- All wrappers are allowlisted +- All wrappers use the same settlement contract +- Each wrapper can parse its data + +### Accumulating Wrappers in Solutions + +#### Using CoW-Provided Driver + +If you're using the CoW Protocol-provided driver, wrappers are handled automatically. Just ensure your solver process includes wrapper information in the solution output. + +#### Custom Implementation + +If implementing your own solver: + +1. **Collect wrappers from orders**: As you process orders in an auction, collect all `wrappers` arrays from order appData +2. **Aggregate for batch**: Combine wrappers needed for all orders in the settlement batch +3. **Include in solution**: Add aggregated wrappers to your solution structure +4. **Encode transaction**: Use the encoding algorithm above to construct the final transaction + +**Example (conceptual):** + +```typescript +// Process auction +const solution = { + orders: [...], + wrappers: [] // Collect here +}; + +for (const order of auction.orders) { + if (order.appData.wrappers) { + // Add wrappers needed for this order + solution.wrappers.push(...order.appData.wrappers); + } +} + +// Encode final transaction +const tx = encodeWrapperSettlement(solution); +``` + +### Testing and Validation + +#### Gas Estimation + +Account for wrapper gas overhead in your bids. The easiest way to do this is to simulate the wrapper transaction against an empty settlement. + +### Common Issues + +#### Issue: "Not authorized" error + +**Cause**: Wrapper is not allowlisted in `GPv2AllowlistAuthenticator` + +**Solution**: Verify wrapper is DAO-approved before including in settlement + +#### Issue: Settlement reverts in wrapper + +**Cause**: Incorrect encoding or wrapper-specific validation failure + +**Solution**: +- Verify `settleData` is identical to what you'd send to `GPv2Settlement.settle()` +- Check `wrapperData` encoding follows specification exactly +- Use `CowWrapperHelper` for validation + +### Solver Resources + +#### Documentation +- **[Wrapper Concepts](../concepts/order-types/wrappers.md)** - High-level overview +- **[Contracts Reference](../reference/contracts/periphery/wrapper.mdx)** - Contract specifications +- **[Services PR #3700](https://github.com/cowprotocol/services/pull/3700)** - Reference encoding implementation in production + +--- diff --git a/docs/cow-protocol/reference/contracts/periphery/wrapper.mdx b/docs/cow-protocol/reference/contracts/periphery/wrapper.mdx new file mode 100644 index 000000000..b25b2f840 --- /dev/null +++ b/docs/cow-protocol/reference/contracts/periphery/wrapper.mdx @@ -0,0 +1,705 @@ +--- +sidebar_position: 7 +--- + +# Generalized Wrappers + +Generalized wrappers are smart contracts that enable custom logic to execute before and after CoW Protocol settlement operations. This reference documents the contract interfaces, implementation patterns, and on-chain behavior of the wrapper system. + +## Architecture + +### Execution Flow + +```mermaid +sequenceDiagram + participant Solver + participant Wrapper1 + participant Wrapper2 + participant Settlement + + Solver->>Wrapper1: wrappedSettle(settleData, wrapperData) + activate Wrapper1 + Note over Wrapper1: Pre-settlement logic + Wrapper1->>Wrapper2: _next(remainingData) + activate Wrapper2 + Note over Wrapper2: Pre-settlement logic + Wrapper2->>Settlement: settle(settleData) + activate Settlement + Note over Settlement: Execute trades + Settlement-->>Wrapper2: Return + deactivate Settlement + Note over Wrapper2: Post-settlement logic + Wrapper2-->>Wrapper1: Return + deactivate Wrapper2 + Note over Wrapper1: Post-settlement logic + Wrapper1-->>Solver: Return + deactivate Wrapper1 +``` + +### Key Design Principles + +1. **Abstract**: There are very few limitations on what a wrapper can/can't do around a settlement transaction +2. **Efficient Encoding**: Wrapper-specific data appended to minimize gas overhead +3. **Nested Support**: Multiple wrappers chain by encoding addresses sequentially, allowing CoW orders +4. **Authentication**: Only allowlisted wrappers can call settlement contract + +## Implementation + +Developers looking to integrate using wrappers should copy this all-in-one solidity file into their project as vendored dependency. + +It provides a few base contracts which can serve as the foundation for integration: + +- **`ICowWrapper`**: Core interface all wrappers must implement +- **`CowWrapper`**: Abstract base contract providing security and utilities that all wrappers should use + +```solidity +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity ^0.8; + +/** + * Title: CoW Wrapper all-in-one integration file + * Author: CoW DAO + * This file is completely self-contained (ie no dependencies) and can be portably copied to whatever projects it is needed. + * It contains: + * * CowWrapper -- an abstract base contract which should be inherited by all wrappers + * * ICowWrapper -- the required interface for all wrappers + * * ICowSettlement -- A minimized interface and base structures for CoW Protocol settlement contract. From https://github.com/cowprotocol/contracts/blob/main/src/contracts/GPv2Settlement.sol + * * ICowAuthentication -- The authentication interface used by ICowSettlement. From https://github.com/cowprotocol/contracts/blob/main/src/contracts/interfaces/GPv2Authentication.sol + */ + +/// @title CoW Protocol Authentication Interface +/// @author CoW DAO developers +interface ICowAuthentication { + /// @dev determines whether the provided address is an authenticated solver. + /// @param prospectiveSolver the address of prospective solver. + /// @return true when prospectiveSolver is an authenticated solver, otherwise false. + function isSolver(address prospectiveSolver) external view returns (bool); +} + +/// @title CoW Protocol Settlement Interface +/// @notice Minimal interface for CoW Protocol's settlement contract +/// @dev Used for type-safe calls to the settlement contract's settle function +interface ICowSettlement { + /// @notice Trade data structure matching GPv2Settlement + struct Trade { + uint256 sellTokenIndex; + uint256 buyTokenIndex; + address receiver; + uint256 sellAmount; + uint256 buyAmount; + uint32 validTo; + bytes32 appData; + uint256 feeAmount; + uint256 flags; + uint256 executedAmount; + bytes signature; + } + + /// @notice Interaction data structure for pre/intra/post-settlement actions which are supplied by the solver to complete the user request + struct Interaction { + address target; + uint256 value; + bytes callData; + } + + /// @notice Returns the authentication contract used by the settlement contract. + function authenticator() external view returns (ICowAuthentication); + + /// @notice Returns the address of the vaultRelayer, the target for approvals for funds entering the settlement contract. + function vaultRelayer() external view returns (address); + + /// @notice Returns the domain separator for EIP-712 signing + function domainSeparator() external view returns (bytes32); + + /// @notice Allows for approval of orders by submitting an authorized hash on-chain prior to order execution. + function setPreSignature(bytes calldata orderUid, bool signed) external; + + /// @notice Settles a batch of trades atomically + /// @param tokens Array of token addresses involved in the settlement + /// @param clearingPrices Array of clearing prices for each token + /// @param trades Array of trades to execute + /// @param interactions Array of three interaction arrays (pre, intra, post-settlement) + function settle( + address[] calldata tokens, + uint256[] calldata clearingPrices, + Trade[] calldata trades, + Interaction[][3] calldata interactions + ) external; +} + +/// @title CoW Protocol Wrapper Interface +/// @notice Interface for wrapper contracts that add custom logic around CoW settlements +/// @dev Wrappers can be chained together to compose multiple settlement operations +interface ICowWrapper { + /// @notice A human readable label for this wrapper. Used for display in explorer/analysis UIs + function name() external view returns (string memory); + + /// @notice The settlement contract used by this wrapper + /// @return The CowSettlement contract address + function SETTLEMENT() external view returns (ICowSettlement); + + /// @notice Initiates a wrapped settlement call + /// @dev This is the entry point for wrapped settlements. The wrapper will execute custom logic + /// before calling the next wrapper or settlement contract in the chain. + /// @dev SECURITY: `settleData` is NOT guaranteed to remain unchanged through the wrapper chain. + /// Intermediate wrappers could modify it before passing it along. Do not rely on + /// `settleData` validation for security-critical checks. + /// @param settleData ABI-encoded call to ICowSettlement.settle() containing trade data + /// @param chainedWrapperData Encoded wrapper chain with the following format: + /// Structure: [uint16 len1][bytes data1][address wrapper2][uint16 len2][bytes data2][address wrapper3]... + /// + /// Each wrapper in the chain consists of: + /// - 2 bytes: uint16 length of wrapper-specific data + /// - `length` bytes: wrapper-specific data for this wrapper + /// - 20 bytes: address of next wrapper (omitted for the final wrapper) + /// + /// The final wrapper in the chain omits the next wrapper address and calls SETTLEMENT directly. + /// + /// Example: [0x0005][0xAABBCCDDEE][0x1234...ABCD][0x0003][0x112233] + /// ↑len ↑data ↑next wrapper ↑len ↑data (final, no next address) + /// + function wrappedSettle(bytes calldata settleData, bytes calldata chainedWrapperData) external; + + /// @notice Confirms validity of wrapper-specific data + /// @dev Used by CowWrapperHelpers to validate wrapper data before execution. Reverts if the wrapper data is not valid for some reason. + /// @param wrapperData The wrapper-specific data to parse + function validateWrapperData(bytes calldata wrapperData) external view; +} + +/// @title CoW Protocol Wrapper Base Contract +/// @notice Abstract base contract for creating wrapper contracts around CoW Protocol settlements +/// @dev A wrapper enables custom pre/post-settlement and context-setting logic and can be chained with other wrappers. +/// Wrappers must: +/// - Be approved by the ICowAuthentication contract +/// - Verify the caller is an authenticated solver +/// - Eventually call settle() on the approved ICowSettlement contract +/// - Implement _wrap() for custom logic +abstract contract CowWrapper is ICowWrapper { + /// @notice Thrown when the caller is not an authenticated solver + /// @param unauthorized The address that attempted to call wrappedSettle + error NotASolver(address unauthorized); + + /// @notice Thrown when settle data doesn't contain the correct function selector + /// @param invalidSettleData The invalid settle data that was provided + error InvalidSettleData(bytes invalidSettleData); + + /// @notice The settlement contract + ICowSettlement public immutable SETTLEMENT; + + /// @notice The authentication contract used to verify solvers + /// @dev This is derived from `SETTLEMENT.authenticator()`. + ICowAuthentication public immutable AUTHENTICATOR; + + /// @notice Constructs a new CowWrapper + /// @param settlement_ The ICowSettlement contract to use at the end of the wrapper chain. Also used for wrapper authentication. + constructor(ICowSettlement settlement_) { + SETTLEMENT = settlement_; + AUTHENTICATOR = settlement_.authenticator(); + } + + /// @inheritdoc ICowWrapper + function wrappedSettle(bytes calldata settleData, bytes calldata chainedWrapperData) external { + // Revert if not a valid solver + require(AUTHENTICATOR.isSolver(msg.sender), NotASolver(msg.sender)); + + // Find out how long the next wrapper data is supposed to be + // We use 2 bytes to decode the length of the wrapper data because it allows for up to 64KB of data for each wrapper. + // This should be plenty of length for all identified use-cases of wrappers in the forseeable future. + uint256 nextWrapperDataLen = uint256(uint16(bytes2(chainedWrapperData[0:2]))); + + // Delegate to the wrapper's custom logic + uint256 remainingWrapperDataStart = 2 + nextWrapperDataLen; + _wrap( + settleData, chainedWrapperData[2:remainingWrapperDataStart], chainedWrapperData[remainingWrapperDataStart:] + ); + } + + /// @inheritdoc ICowWrapper + function validateWrapperData(bytes calldata wrapperData) external view virtual; + + /// @notice Internal function containing the wrapper's custom logic + /// @dev Must be implemented by concrete wrapper contracts. Should execute custom logic + /// then eventually call _next() to continue the wrapped settlement chain. + /// @param settleData ABI-encoded call to ICowSettlement.settle() + /// @param wrapperData The wrapper data which should be consumed by this wrapper + /// @param remainingWrapperData The reminder bytes resulting from consuming the current's wrapper data from the original `chainedWrapperData` in the `wrappedSettle` call. This should be passed unaltered to `_next` that will call the settlement function if this remainder is empty, or delegate the settlement to the next wrapper + function _wrap(bytes calldata settleData, bytes calldata wrapperData, bytes calldata remainingWrapperData) + internal + virtual; + + /// @notice Continues the wrapped settlement chain by calling the next wrapper or settlement contract + /// @dev Extracts the next target address from wrapperData and either: + /// - Calls ICowSettlement.settle() directly if no more wrappers remain, or + /// - Calls the next CowWrapper.wrappedSettle() to continue the chain + /// @param settleData ABI-encoded call to ICowSettlement.settle() + /// @param remainingWrapperData Remaining wrapper data starting with the next target address (20 bytes) + function _next(bytes calldata settleData, bytes calldata remainingWrapperData) internal { + if (remainingWrapperData.length == 0) { + // No more wrapper data - we're calling the final settlement contract + // Verify the settle data has the correct function selector + require(bytes4(settleData[:4]) == ICowSettlement.settle.selector, InvalidSettleData(settleData)); + + // Call the settlement contract directly with the settle data + (bool success, bytes memory returnData) = address(SETTLEMENT).call(settleData); + + if (!success) { + // Bubble up the revert reason from the settlement contract + assembly ("memory-safe") { + revert(add(returnData, 0x20), mload(returnData)) + } + } + } else { + // Extract the next wrapper address from the first 20 bytes of wrapperData + address nextWrapper = address(bytes20(remainingWrapperData[:20])); + + // Skip past the address we just read + remainingWrapperData = remainingWrapperData[20:]; + + // More wrapper data remains - call the next wrapper in the chain + CowWrapper(nextWrapper).wrappedSettle(settleData, remainingWrapperData); + } + } +} +``` + +### Quick Start Example + +Here's a minimal wrapper implementation to use as a starting point: + +```solidity +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.0; + +import { CowWrapper } from "./CowWrapper.sol"; + +contract MyWrapper is CowWrapper { + constructor(address settlement) CowWrapper(settlement) {} + + function _wrap(bytes calldata settleData, bytes calldata wrapperData, bytes calldata remainingWrapperData) internal override { + // Your custom pre-settlement logic here + // Example: pull tokens, initiate flash loan, etc. + + // Continue the wrapper chain (calls settlement or next wrapper) + _next(settleData, remainingWrapperData); + + // Your custom post-settlement logic here + // Example: repay flash loan, stake tokens, etc. + } + + function validateWrapperData(bytes calldata wrapperData) + external + view + override + { + // Validate wrapper-specific data + // This must be deterministic - same input always returns same output + // Revert if data is invalid + } +} +``` + +### Wrapper Public Functions + +#### `wrappedSettle` + +Entry point for wrapper execution. Validates caller authentication and delegates to `_wrap()`--where integrators place their custom logic. + +```solidity +function wrappedSettle( + bytes calldata settleData, + bytes calldata chainedWrapperData +) external override { + // Verify caller is an authenticated solver + require(AUTHENTICATOR.isSolver(msg.sender), NotASolver(msg.sender)); + + // Find out how long the next wrapper data is supposed to be + uint256 nextWrapperDataLen = uint256(uint16(bytes2(chainedWrapperData[0:2]))); + + // Delegate to the wrapper's custom logic + uint256 remainingWrapperDataStart = 2 + nextWrapperDataLen; + _wrap( + settleData, + chainedWrapperData[2:remainingWrapperDataStart], + chainedWrapperData[remainingWrapperDataStart:] + ); +} +``` + +**Parameters:** +- `settleData`: Original `GPv2Settlement.settle(...)` calldata +- `chainedWrapperData`: + +#### Chained Wrapper Data Encoding + +Each wrapper in the chain consists of: +- 2 bytes: uint16 length of wrapper-specific data +- `length` bytes: wrapper-specific data for this wrapper +- 20 bytes: address of next wrapper (omitted for the final wrapper) +The final wrapper in the chain omits the next wrapper address and calls SETTLEMENT directly. + +Example: +```text +[0x0005][0xAABBCCDDEE][0x1234...ABCD][0x0003][0x112233] +↑len ↑data ↑next wrapper ↑len ↑data +``` + +Each wrapper in the chain will successively pull off their own data and then call the next wrapper address (or the settlement address if no further wrapper data is supplied). + +### Virtual Functions for integrators + +#### `_wrap` + +Contains custom surrounding settlement logic. **Must** call `_next()` to continue the chain to the settlement contract. + +```solidity +function _wrap(bytes calldata settleData, bytes calldata wrapperData, bytes calldata remainingWrapperData) internal virtual; +``` + +**Implementation Requirements:** +- Parse wrapper-specific data from `wrapperData` as required +- Execute pre-settlement logic +- Call `_next(remainingWrapperData)` to continue chain +- Execute post-settlement logic + +**Example:** + +```solidity +function _wrap(bytes calldata settleData, bytes calldata wrapperData, bytes calldata remainingWrapperData) internal override { + // 1. Parse data + (address token, uint256 amount) = abi.decode(wrapperData, (address, uint256)); + + // 2. Pre-settlement logic. Example, receive tokens from user + IERC20(token).transferFrom(msg.sender, address(this), amount); + + // 3. Continue chain (REQUIRED) + _next(settleData, remainingWrapperData); + + // 4. Post-settlement logic. Example: stake tokens to a contract (for swap and stake) + stakeTokens(token, amount); +} +``` + +### Internal Functions (Provided) + +#### `_next` + +Continues the wrapper chain or calls settlement. Handles all parsing and routing automatically. + +```solidity +function _next(bytes calldata settleData, bytes calldata remainingWrapperData) internal; +``` + +**Behavior:** +- Reads next wrapper address from `remainingWrapperData` +- Determines if more wrappers exist or if settlement is next +- Makes appropriate call with correct data +- Handles wrapper nesting automatically + +**Implementation Details:** +- Extracts next target address (first 20 bytes) +- Separates settle data from remaining wrapper data +- Calls next wrapper via `wrappedSettle()` or settlement via `settle()` + +## Calldata Encoding Specification + +Wrapper data uses an optimized encoding to minimize gas overhead: + +### Structure + +``` +┌─────────────────────┬──────┬──────────┬──────────┬──────┬──────────┐ +│ Settle Calldata │ Len₁ │ Data₁ │ Addr₂ │ Len₂ │ Data₂ │ +│ (to settlement) │(2 B) │ (wrap1) │ (20 B) │(2 B) │ (wrap2) │ +└─────────────────────┴──────┴──────────┴──────────┴──────┴──────────┘ +│<─── settleData ───>│<──────────── wrapperData ─────────────────────>│ +``` + +**Components:** +- **Settle Calldata**: `GPv2Settlement.settle(...)` calldata (function selector + args) +- **Len₁**: 2-byte (uint16) length of wrapper 1's data +- **Data₁**: Wrapper 1's custom data +- **Addr₂**: 20-byte address of wrapper 2 +- **Len₂**: 2-byte (uint16) length of wrapper 2's data +- **Data₂**: Wrapper 2's custom data + +Note: The settlement contract address is derived from the `SETTLEMENT` immutable and is not included in `wrapperData`. + +### Data Consumption + +Each wrapper in the chain: +1. Reads length prefix (2 bytes) +2. Extracts its data (next N bytes) +3. Passes remaining bytes to `_next()` + +The `_next()` function: +1. Reads next address (from appropriate offset) +2. Calls next wrapper or settlement +3. Passes remaining data + +## CowWrapperHelper + +Utility contract for on-chain validation and encoding. + +### Interface + +```solidity +contract CowWrapperHelper { + /// @notice Validate and build wrapper data for multiple wrappers + /// @param wrapperAddresses Array of wrapper contract addresses + /// @param wrapperDatas Array of wrapper-specific data (parallel to addresses) + /// @return wrapperData Encoded wrapper data ready for wrappedSettle call + function verifyAndBuildWrapperData( + address[] calldata wrapperAddresses, + bytes[] calldata wrapperDatas + ) external view returns (bytes memory wrapperData); +} +``` + + +### `CowWrapperHelpers` + +To aid external integrators in encoding and verifying wrapper data, an additional view-only contract is provided. + +See the [CowWrapperHelper contract source code](https://github.com/cowprotocol/euler-integration-contracts/blob/main/src/CowWrapperHelper.sol) for the complete implementation. + + +#### `validateWrapperData` + +Validates wrapper-specific data. Must be deterministic and revert on invalid input. + +```solidity +function validateWrapperData( + bytes calldata wrapperData +) external view override returns (bytes memory); +``` + +**Requirements:** +- **Deterministic**: Same input must always produce same output +- **No time-based logic**: Cannot use `block.timestamp`, `block.number`, etc. +- **No mutable state**: Cannot read external mutable state +- **View function**: Cannot modify state +- **Revert on invalid**: Revert if data is malformed + +## Implementation Guidelines for Integrators + +### Security Requirements + +:::warning Critical Requirements + +#### 1. Use CowWrapper Abstract Contract + +It is strongly recommended to **NOT** implement `ICowWrapper` directly. The `CowWrapper` abstract contract provides: +- Solver authentication checks +- Correct calldata parsing and decoding +- Safe wrapper chain continuation +- Sanity checks for the settlement call + +#### 2. Intermediate Contracts for User Calls + +If allowing user-defined calls, route through intermediate contracts: + +```solidity +// ❌ DANGEROUS +function _wrap(bytes calldata wrapperData) internal override { + (address target, bytes memory data) = abi.decode(wrapperData, (address, bytes)); + target.call(data); // User could call anything they want, including the settlement contract, using the wrapper's authenticated context +} + +// ✅ SAFE +function _wrap(bytes calldata wrapperData) internal override { + (address target, bytes memory data) = abi.decode(wrapperData, (address, bytes)); + HooksTrampoline(TRAMPOLINE).execute(target, data); // Isolated execution +} +``` + +#### 3. Assume All Parameters Are Untrusted + +Settlement data can be modified by nested wrappers, and solvers can supply arbitrary calldata. If it is important for your wrapper to be able to validate the wrapper it is receiving, only trust signature-protected or on-chain validated parameters. + +#### 4. Deterministic Parsing Required + +`validateWrapperData()` must always return same result for same input: + +```solidity +// ❌ NOT DETERMINISTIC +function validateWrapperData(bytes calldata wrapperData) + external view override returns (bytes memory) +{ + uint256 deadline = abi.decode(wrapperData, (uint256)); + require(block.timestamp < deadline, "Expired"); // Changes over time! + return wrapperData; +} + +// ✅ DETERMINISTIC +function validateWrapperData(bytes calldata wrapperData) + external view override returns (bytes memory) +{ + uint256 deadline = abi.decode(wrapperData, (uint256)); + require(deadline > 0, "Invalid deadline"); // Always same result + return wrapperData; +} +``` + +In the example above, your `_wrap` code can always reject deadline past expired instead. + +#### 5. Defensive Design + +Though a solver would be slashed for doing so, there is no hard guarantee wrapper executes even if user specifies it. If wrapper is critical: + +**Option A: Make Order Fail Without Wrapper** +```solidity +// Pull required funds in wrapper +function _wrap(bytes calldata wrapperData) internal override { + vault.withdraw(user, token, amount); // Funds now in wrapper + _next(wrapperData); + // Settlement uses these funds - fails if wrapper didn't execute +} +``` + +**Option B: EIP-1271 Authentication** +```solidity +// Order signature only valid if wrapper executed +function isValidSignature(bytes32 hash, bytes memory signature) + external view returns (bytes4) +{ + require(wrapperExecuted[hash], "Wrapper required"); + return EIP1271_MAGIC; +} +``` + +There may be other similar ways to enforce execution, but these patterns serve as a starting point. + +::: + +### Gas Overhead + +Wrapper execution adds gas overhead to settlements. + +**Benchmark (EmptyWrapper on Ethereum mainnet):** + +| Metric | Value | +|--------|-------| +| With EmptyWrapper | 217,033 gas | +| Without wrapper | 194,761 gas | +| **Overhead** | **22,272 gas (11.4%)** | + +**Scaling Factors:** +- Settlement data size (calldata copying) +- Wrapper logic complexity +- Number of nested wrappers + +**Methodology:** Single Uniswap V3 WETH→USDC trade. See [services PR #3700](https://github.com/cowprotocol/services/pull/3700). + +## Example Implementations + +### EmptyWrapper + +Minimal wrapper demonstrating the pattern: + +```solidity +contract EmptyWrapper is CowWrapper { + constructor(address settlement) CowWrapper(settlement) {} + + function _wrap(bytes calldata settleData, bytes calldata wrapperData, bytes calldata remainingWrapperData) internal override { + // No pre-settlement logic + _next(settleData, remainingWrapperData); // Pass through + // No post-settlement logic + } + + function validateWrapperData(bytes calldata wrapperData) + external + view + override + { + // No validation needed + } +} +``` + +### Flash Loan Wrapper + +Wrapper coordinating with lending protocol: + +```solidity +contract FlashLoanWrapper is CowWrapper { + ILendingPool public immutable lendingPool; + + constructor(address settlement, address _lendingPool) + CowWrapper(settlement) + { + lendingPool = ILendingPool(_lendingPool); + } + + function _wrap(bytes calldata settleData, bytes calldata wrapperData, bytes calldata remainingWrapperData) internal override { + (address asset, uint256 amount) = + abi.decode(wrapperData, (address, uint256)); + + // Store data for use in callback + // (In a real implementation, you would store settleData and remainingWrapperData) + + // Initiate flash loan (calls executeOperation) + lendingPool.flashLoan(address(this), asset, amount, ""); + } + + function executeOperation( + address asset, + uint256 amount, + uint256 premium, + address, + bytes calldata params + ) external returns (bool) { + require(msg.sender == address(lendingPool), "Unauthorized"); + + // Execute settlement with borrowed funds + // (In a real implementation, retrieve stored settleData and remainingWrapperData) + // _next(settleData, remainingWrapperData); + + // Approve repayment + IERC20(asset).approve(address(lendingPool), amount + premium); + return true; + } + + function validateWrapperData(bytes calldata wrapperData) + external + view + override + { + require(wrapperData.length >= 64, "Invalid data length"); + (address asset, uint256 amount) = abi.decode( + wrapperData, + (address, uint256) + ); + require(asset != address(0) && amount > 0, "Invalid parameters"); + } +} +``` + +## Known Limitations + +### Authentication Required + +Wrappers need protocol approval, and are not fully permissionless. + +### Settlement Upgrades + +When settlement contract upgrades, all wrappers must be redeployed. It is recommended to use [Cannon](https://usecannon.com/learn) to make it easy to redeploy your wrappers. + +## Events + +No wrapper-specific events are emitted. Wrapper usage can be identified by: + +1. Transaction target is wrapper address (not settlement) +2. Function signature is `wrappedSettle(bytes,bytes)` (not `settle(...)`) +3. Analyzing transaction trace for wrapper contract calls + +## Resources + +### Documentation +- **[Wrapper Concepts](../../../concepts/order-types/wrappers.md)** - High-level overview and use cases +- **[Integration Guide](../../../integrate/wrappers.mdx)** - Implementation guide for order creators and developers +- **[GPv2Settlement](../core/settlement.md)** - Core settlement contract documentation + +### Code +- **[CowWrapper.sol](https://github.com/cowprotocol/euler-integration-contracts/blob/main/src/CowWrapper.sol)** - Abstract contract and interfaces +- **[EmptyWrapper](https://github.com/cowprotocol/euler-integration-contracts)** - Minimal reference implementation +- **[Euler Integration](https://github.com/cowprotocol/euler-integration-contracts/pull/6)** - Complete production wrapper example