Skip to main content

Overview

App Data is a flexible metadata system that allows you to attach additional information to CoW Protocol orders. It’s a critical part of order creation, enabling features like developer attribution, analytics tracking, custom hooks, and more.

What is App Data?

App Data is a 32-byte hash (bytes32) stored in every order that points to a JSON document containing metadata. This document follows a standardized schema and can include:
  • Slippage tolerance settings
  • Order classification (market, limit, etc.)
  • UTM tracking parameters for analytics
  • Partner fee information
  • CoW Hooks (pre/post execution callbacks)
  • Custom application-specific data
const order = {
  // ... other order fields
  appData: '0x8e4f5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e', // 32-byte hash
}

App Data Structure

The app data document has this structure:
interface AppDataDoc {
  version: string              // Semantic version (e.g., "1.1.0")
  appCode: string              // Your application identifier
  environment?: string         // "production", "staging", etc.
  metadata: {
    quote?: {
      slippageBips: number     // Slippage in basis points
    }
    orderClass?: {
      orderClass: 'market' | 'limit' | 'liquidity' | 'twap'
    }
    utm?: {
      utmSource?: string       // Traffic source
      utmMedium?: string       // Marketing medium
      utmCampaign?: string     // Campaign name
      utmContent?: string      // Content identifier
      utmTerm?: string         // Keyword term
    }
    hooks?: {
      pre?: CoWHook[]          // Pre-execution hooks
      post?: CoWHook[]         // Post-execution hooks
    }
    partnerFee?: {
      bps: number              // Fee in basis points
      recipient: string        // Fee recipient address
    }
    // ... other metadata
  }
}

Creating App Data

The SDK provides utilities to create and manage app data:

Basic Usage

import { buildAppData } from '@cowprotocol/cow-sdk'

const appDataInfo = await buildAppData({
  appCode: 'MyTradingApp',
  slippageBps: 50,           // 0.5% slippage
  orderClass: 'market',
})

// Returns:
// {
//   doc: { /* full app data document */ },
//   fullAppData: '{ ... }',  // JSON string
//   appDataKeccak256: '0x...' // 32-byte hash to use in order
// }

Using in Orders

const { appDataKeccak256 } = await buildAppData({
  appCode: 'MyApp',
  slippageBps: 50,
  orderClass: 'market',
})

const order = {
  sellToken: '0x...',
  buyToken: '0x...',
  // ... other fields
  appData: appDataKeccak256, // Use the generated hash
}

Default UTM Parameters

The SDK automatically includes UTM parameters for developer attribution:
// Default UTM parameters added by the SDK
const defaultUtm = {
  utmCampaign: 'developer-cohort',
  utmContent: '',
  utmMedium: '[email protected]',  // SDK version
  utmSource: 'cowmunity',
  utmTerm: 'js',
}
These help track SDK usage and ensure developers are recognized for volume they generate.

Overriding UTM Parameters

const appDataInfo = await buildAppData(
  {
    appCode: 'MyApp',
    slippageBps: 50,
    orderClass: 'market',
  },
  {
    metadata: {
      utm: {
        utmSource: 'my-platform',
        utmMedium: 'web-app',
        utmCampaign: 'launch-week',
      },
    },
  }
)
When you provide custom UTM parameters, they completely replace the defaults. The SDK only adds default UTM when none are provided.

Partner Fees

You can specify partner fees to receive a portion of the surplus:
const appDataInfo = await buildAppData({
  appCode: 'MyApp',
  slippageBps: 50,
  orderClass: 'market',
  partnerFee: {
    bps: 25,                                    // 0.25% fee
    recipient: '0x1234...', // Your fee recipient address
  },
})
Partner fees are deducted from the order’s surplus (the difference between executed price and limit price). They don’t affect the minimum output amount users receive.

CoW Hooks

CoW Hooks allow you to execute custom contract calls before and/or after an order executes. This enables advanced use cases like:
  • Approve tokens just-in-time
  • Claim rewards before trading
  • Stake tokens after purchase
  • Execute arbitrary DeFi operations

Hook Structure

interface CoWHook {
  target: string      // Contract address to call
  callData: string    // Encoded function call
  gasLimit: string    // Gas limit for the call
}

Pre-Hooks Example

Executed before the order:
import { encodeFunctionData } from 'viem'

// Example: Approve token before trading
const approveCallData = encodeFunctionData({
  abi: ERC20_ABI,
  functionName: 'approve',
  args: [SPENDER_ADDRESS, MAX_UINT256],
})

const appDataInfo = await buildAppData(
  {
    appCode: 'MyApp',
    slippageBps: 50,
    orderClass: 'market',
  },
  {
    metadata: {
      hooks: {
        pre: [
          {
            target: TOKEN_ADDRESS,
            callData: approveCallData,
            gasLimit: '50000',
          },
        ],
      },
    },
  }
)

Post-Hooks Example

Executed after the order:
// Example: Stake received tokens
const stakeCallData = encodeFunctionData({
  abi: STAKING_ABI,
  functionName: 'stake',
  args: [AMOUNT],
})

const appDataInfo = await buildAppData(
  {
    appCode: 'MyApp',
    slippageBps: 50,
    orderClass: 'market',
  },
  {
    metadata: {
      hooks: {
        post: [
          {
            target: STAKING_CONTRACT,
            callData: stakeCallData,
            gasLimit: '100000',
          },
        ],
      },
    },
  }
)
Hook Limitations:
  • Hooks execute in the same transaction as the order
  • Failed hooks cause the entire transaction to revert
  • Set appropriate gas limits to avoid out-of-gas errors
  • Only use trusted contracts as hook targets

Advanced Usage

Merging App Data

You can merge additional metadata into existing app data:
import { mergeAppDataDoc } from '@cowprotocol/cow-sdk'

const existingDoc = await buildAppData({
  appCode: 'MyApp',
  slippageBps: 50,
  orderClass: 'market',
})

// Merge additional data
const mergedAppData = await mergeAppDataDoc(
  existingDoc.doc,
  {
    metadata: {
      referrer: {
        address: '0x...',
      },
    },
  }
)

Custom Metadata Fields

You can add custom fields to the metadata:
const appDataInfo = await buildAppData(
  {
    appCode: 'MyApp',
    slippageBps: 50,
    orderClass: 'market',
  },
  {
    environment: 'production',
    metadata: {
      // Custom fields
      signer: '0x...',
      referrer: {
        address: '0x...',
      },
    },
  }
)

IPFS Integration

App data documents are stored on IPFS for decentralized access:
// The appDataKeccak256 points to IPFS content
const { fullAppData, appDataKeccak256 } = await buildAppData({
  appCode: 'MyApp',
  slippageBps: 50,
  orderClass: 'market',
})

// The hash is the keccak256 of the full JSON document
import { keccak256, toUtf8Bytes } from 'ethers'
const computedHash = keccak256(toUtf8Bytes(fullAppData))
// computedHash === appDataKeccak256
The CoW Protocol API automatically uploads app data to IPFS when you submit orders.

Retrieving App Data

Fetch app data from an existing order:
import { MetadataApi } from '@cowprotocol/sdk-app-data'
import { getGlobalAdapter } from '@cowprotocol/cow-sdk'

const metadataApi = new MetadataApi(getGlobalAdapter())

// Fetch by app data hash
const appDataDoc = await metadataApi.fetchDocFromAppData(
  '0x8e4f5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e'
)

console.log(appDataDoc.metadata.quote?.slippageBips)

Common Patterns

const appData = await buildAppData({
  appCode: 'MyTradingApp',
  slippageBps: 50,
  orderClass: 'market',
})

Best Practices

Use Descriptive App Codes

Choose clear, unique appCode values to identify your application in analytics.

Set Appropriate Slippage

Market orders: 0.5-1%. Limit orders: 0%. Volatile tokens: higher values.

Test Hooks Thoroughly

Always test hooks on testnets before production. Failed hooks revert the entire order.

Cache App Data Hashes

For identical metadata, reuse the same app data hash to reduce IPFS uploads.

Troubleshooting

Ensure you’re using the exact fullAppData JSON string (deterministic formatting) when computing the hash. The SDK handles this automatically.
  • Check that the target contract address is correct
  • Verify the callData encoding matches the function signature
  • Ensure the gasLimit is sufficient
  • Test the hook call separately before including in an order
If you provide a metadata.utm object, it completely replaces defaults. Make sure you’re setting all desired UTM fields.

Next Steps

Order Types

Learn about different order types

Trading SDK

Start building with the SDK

Hooks Guide

Advanced hooks implementation

Build docs developers (and LLMs) love