Atomic Bundle Integration
Atomic Bundles (formerly known as Generalized Wrappers) enable custom logic to execute surrounding CoW Protocol settlements. This guide covers both using existing bundles in your orders and building new bundle contracts.
Choose your path:
- Building New Bundles - Develop custom bundle contracts for advanced DeFi workflows
- Using Existing Bundles - Add bundles to your orders and integrate into your application
- Executing Bundles as a Solver - Everything solvers need to know to execute bundle orders
For Bundle Developers
This section covers building new bundle contracts from scratch.
Overview
To build a bundle, you will:
- Inherit from the
CowWrapperabstract contract - Implement
_wrap()with your pre/post-settlement logic - Implement
validateWrapperData()for input validation - Test thoroughly on testnets
- Review the Implementation Requirements to ensure your bundle meets the acceptance requirements
- Submit for allowlist approval by the CoW Protocol team
Building a bundle is a significant undertaking. An insecure bundle would put user and protocol funds at risk. Make sure you understand CoW Protocol well before starting. A contract audit by a reputable firm is mandatory before a bundle is considered for approval. Read more on the details of creating a bundle from the bundle contract information page.
Getting Whitelisted on Staging
Before a bundle can be used in production it must be approved by CoW DAO, but you can get it whitelisted on the staging environment much earlier to test end-to-end against real infrastructure.
To get whitelisted on staging:
- Meet the implementation requirements. Review the Implementation Requirements and make sure your bundle satisfies all of them. In particular, ensure your contract uses the
CowWrapperabstract contract, thatvalidateWrapperData()is deterministic, and that the bundle is designed defensively. - Reach out to the CoW Protocol team. Contact the team (e.g. via the CoW Protocol Discord) to request staging allowlist access. Share your contract which is verified and deployed on Etherscan and a brief description of the intended use case.
- Deploy to a supported testnet. The team will review your contract and, if it meets the requirements, add it to the staging allowlist so you can test your full integration.
Note that staging whitelisting is not a substitute for the full DAO approval process required for production. Plan for the DAO governance timeline when building towards a mainnet launch.
For Order Creators and Frontend Developers
This section shows you how to use existing bundles in your orders and integrate bundle support into your application.
Adding Bundles to Orders
To use a bundle in your order, add it to the appData when creating the order:
AppData Structure
interface WrapperCall {
target: string; // Bundle contract address (must be allowlisted)
data?: string; // Optional hex-encoded bundle-specific data
isOmittable?: boolean; // Whether solver can skip this bundle (default: false)
}
Fields:
target(required): Address of the allowlisted bundle contractdata(optional): Hex-encoded data specific to the bundle. Can be empty or omitted if the bundle doesn't need custom data.isOmittable(optional): Defaults tofalsefalse= Solver MUST execute the bundle with exact data or be slashedtrue= Solver MAY skip the bundle if they find a better solution
Example: Using the CoW SDK
import { OrderBookApi, OrderQuoteRequest, OrderCreation } from '@cowprotocol/cow-sdk'
// Create order with bundle
const orderCreation: OrderCreation = {
// ... standard order fields (sellToken, buyToken, amounts, etc.)
appData: {
// ... other appData fields
wrappers: [
{
target: "0x1234...", // Flash loan bundle address
data: "0xabcd...", // Encoded flash loan params
isOmittable: false // Must execute
}
]
}
}
// Submit order
const orderId = await orderBookApi.sendOrder(orderCreation)
Example: Multiple Bundles (Nested)
You can chain multiple bundles in a single order:
appData: {
wrappers: [
{
target: "0x1111...", // Flash loan bundle
data: "0xaaaa...",
isOmittable: false
},
{
target: "0x2222...", // Leverage bundle
data: "0xbbbb...",
isOmittable: false
}
]
}
The bundles execute in sequence: Bundle1 → Bundle2 → Settlement → Bundle2 (post) → Bundle1 (post). Note that bundles from other users' orders may be interspersed, though this should generally not affect the execution of the order.
Validating Bundle Configuration
Before submitting an order, you can confirm the bundle encoding is valid using CowWrapperHelpers.
// On-chain validation (via Ethers/Viem)
const helper = new ethers.Contract(HELPER_ADDRESS, HELPER_ABI, provider)
const isValid = await helper.verifyAndBuildWrapperData([
{
target: '0xSomeAddress',
data: '0xSomeData'
},
{
target: '0xSomeAddress2',
data: '0x'
}
])
// Returns encoded chainedWrapperData if valid, reverts if invalid
This checks:
- All bundles are allowlisted
- Each bundle can parse its data successfully
See the periphery contract reference for deployment addresses.
For Solvers
This section explains how to execute settlements that include bundle contracts as part of your solver implementation.
Detecting Bundle Orders
Bundles are specified in the order's appData under the wrappers field:
{
"wrappers": [
{
"target": "0x1234...",
"data": "0xabcdef...",
"isOmittable": false
}
]
}
Fields:
target: Address of the bundle contractdata: Bundle-specific dataisOmittable: Whether thetargetanddatamust be executed unmodified as part of the order execution
target may be any address and data is arbitrary bytes that will be passed to the target. There is generally no need for decoding the bundle data since the execution process uses the same opaque format on each bundle.
Solver Requirements
1. Execute Non-Omittable Bundles
Orders with "isOmittable": false MUST be executed with the specified bundle. You may be slashed for not doing so.
If "isOmittable": true, you MAY skip the bundle if you find a better solution without it.
2. Verify Bundle Authentication and Simulate
All approved bundles will be approved by the DAO and registered in GPv2AllowlistAuthenticator. It is advised to verify this is the case before attempting to process a bundle order.
Additionally, bundles define additional operations that may revert, so it is strongly recommended to simulate the settlement transaction while including the bundle call to verify its viability. The call trace can also be inspected for any unusual behavior.
The bundle will be directly called from the solver address and will mediate the settlement. Since the data is user-supplied, it may be malicious.
3. Include Bundles in the Solution
Whether using the CoW Protocol-provided driver or your own, ensure bundle information flows from the auction to your final settlement transaction. This data should be passed unmodified from the orders in the auction to the final Solution submitted to the API.
Encoding Bundle Settlements
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 bundles:
1. Change Transaction Target
Your transaction should call wrappedSettle() on the first bundle (not the settlement contract):
// Without bundle
tx = {
to: SETTLEMENT_CONTRACT,
data: encodeSettle(...)
}
// With bundle
tx = {
to: wrappers[0].target, // First bundle 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:
const settleData = settlementContract.interface.encodeFunctionData("settle", [
tokens,
clearingPrices,
trades,
interactions
]);
// This is your settleData - unchanged from normal settlement
3. Encode the chainedWrapperData Parameter
Build chainedWrapperData according to the Calldata Encoding Specification.
4. Call wrappedSettle
const tx = {
// The `chainedWrapperData` does not include the first bundle to call,
// so it is supplied here as the _target_ of the settlement call.
to: wrappers[0].target,
data: wrapperContract.interface.encodeFunctionData("wrappedSettle", [
settleData, // From step 2
chainedWrapperData // From step 3
])
}
Using CowWrapperHelpers
CowWrapperHelpers is a read-only periphery contract that can be called to validate many important points prior to execution. See the periphery contract documentation.
Accumulating Bundles in Solutions
Using CoW-Provided Driver
If you're using the CoW Protocol-provided driver, bundles are handled automatically. Just ensure your solver process includes bundle information in the solution output.
Custom Implementation
If implementing your own solver:
- Collect bundles from orders: As you process orders in an auction, collect all
wrappersarrays from order appData - Aggregate for batch: Combine bundles needed for all orders in the settlement batch
- Include in solution: Add aggregated bundles to your solution structure
- Encode transaction: Use the encoding algorithm above to construct the final transaction
Example (conceptual):
// Process auction
const solution = {
orders: [...],
wrappers: [] // Collect here
};
for (const order of auction.orders) {
if (order.appData.wrappers) {
// Add bundles needed for this order
solution.wrappers.push(...order.appData.wrappers);
}
}
// Encode final transaction using the manual encoding steps described above:
// 1. Set tx target to solution.wrappers[0].target
// 2. Build settleData from your normal GPv2Settlement.settle() calldata
// 3. Build chainedWrapperData per the Calldata Encoding Specification in the reference docs
// 4. Call wrappedSettle(settleData, chainedWrapperData) on the first bundle
const tx = {
to: solution.wrappers[0].target,
data: wrapperContract.interface.encodeFunctionData("wrappedSettle", [
settleData,
chainedWrapperData
])
};
Testing and Validation
Gas Estimation
Account for bundle gas overhead in your bids — see Gas Overhead for benchmarks and scaling factors. The easiest way to estimate overhead for a specific bundle is to simulate the bundle transaction against an empty settlement.
Common Issues
Issue: "Not authorized" error
Cause: Bundle is not allowlisted in GPv2AllowlistAuthenticator
Solution: Verify bundle is DAO-approved before including in settlement
Issue: Settlement reverts in bundle
Cause: Incorrect encoding or bundle-specific validation failure
Solution:
- Verify
settleDatais identical to what you'd send toGPv2Settlement.settle() - Check
chainedWrapperDataencoding follows specification exactly - Use
CowWrapperHelpersfor validation
Solver Resources
Documentation
- Bundle Concepts - High-level overview
- Contracts Reference - Contract specifications
- Services PR #3700 - Reference encoding implementation in production