Skip to main content

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:


For Bundle Developers

This section covers building new bundle contracts from scratch.

Overview

To build a bundle, you will:

  1. Inherit from the CowWrapper abstract contract
  2. Implement _wrap() with your pre/post-settlement logic
  3. Implement validateWrapperData() for input validation
  4. Test thoroughly on testnets
  5. Review the Implementation Requirements to ensure your bundle meets the acceptance requirements
  6. 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:

  1. Meet the implementation requirements. Review the Implementation Requirements and make sure your bundle satisfies all of them. In particular, ensure your contract uses the CowWrapper abstract contract, that validateWrapperData() is deterministic, and that the bundle is designed defensively.
  2. 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.
  3. 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 contract
  • data (optional): Hex-encoded data specific to the bundle. Can be empty or omitted if the bundle doesn't need custom data.
  • isOmittable (optional): Defaults to false
    • false = Solver MUST execute the bundle with exact data or be slashed
    • true = 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 contract
  • data: Bundle-specific data
  • isOmittable: Whether the target and data must 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.

warning

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

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 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:

  1. Collect bundles from orders: As you process orders in an auction, collect all wrappers arrays from order appData
  2. Aggregate for batch: Combine bundles needed for all orders in the settlement batch
  3. Include in solution: Add aggregated bundles to your solution structure
  4. 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 settleData is identical to what you'd send to GPv2Settlement.settle()
  • Check chainedWrapperData encoding follows specification exactly
  • Use CowWrapperHelpers for validation

Solver Resources

Documentation