Skip to main content

App Data

Overview

App Data is a flexible metadata system enabling developers to attach additional information to CoW Protocol orders. It supports 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. The document can include slippage tolerance, order classification, UTM tracking, partner fees, CoW Hooks, and custom application data.
const order = {
  appData: '0x8e4f5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e',
}

App Data Structure

interface AppDataDoc {
  version: string
  appCode: string
  environment?: string
  metadata: {
    quote?: {
      slippageBips: number
    }
    orderClass?: {
      orderClass: 'market' | 'limit' | 'liquidity' | 'twap'
    }
    utm?: {
      utmSource?: string
      utmMedium?: string
      utmCampaign?: string
      utmContent?: string
      utmTerm?: string
    }
    hooks?: {
      pre?: CoWHook[]
      post?: CoWHook[]
    }
    partnerFee?: {
      bps: number
      recipient: string
    }
  }
}

Creating App Data

Basic Usage

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

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

// Returns:
// {
//   doc: { /* full app data document */ },
//   fullAppData: '{ ... }',
//   appDataKeccak256: '0x...'
// }

Using in Orders

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

const order = {
  sellToken: '0x...',
  buyToken: '0x...',
  appData: appDataKeccak256,
}

Default UTM Parameters

The SDK automatically includes UTM parameters:
const defaultUtm = {
  utmCampaign: 'developer-cohort',
  utmContent: '',
  utmMedium: 'cow-sdk@X.Y.Z',
  utmSource: 'cowmunity',
  utmTerm: 'js',
}

Overriding UTM Parameters

const appDataInfo = await buildAppData(
  {
    appCode: 'MyApp',
    slippageBps: 50,
    orderClass: 'market',
  },
  {
    metadata: {
      utm: {
        utmSource: 'my-platform',
        utmMedium: 'web-app',
        utmCampaign: 'launch-week',
      },
    },
  }
)
Custom UTM parameters completely replace the defaults when provided.

Partner Fees

const appDataInfo = await buildAppData({
  appCode: 'MyApp',
  slippageBps: 50,
  orderClass: 'market',
  partnerFee: {
    bps: 25,
    recipient: '0x1234...',
  },
})
Partner fees are deducted from order surplus and don’t affect minimum user output.

CoW Hooks

CoW Hooks execute custom contract calls before and/or after order execution, enabling token approvals, reward claiming, staking, and arbitrary DeFi operations.

Hook Structure

interface CoWHook {
  target: string
  callData: string
  gasLimit: string
}

Pre-Hooks Example

import { encodeFunctionData } from 'viem'

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

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

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

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

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

Custom Metadata Fields

const appDataInfo = await buildAppData(
  {
    appCode: 'MyApp',
    slippageBps: 50,
    orderClass: 'market',
  },
  {
    environment: 'production',
    metadata: {
      signer: '0x...',
      referrer: {
        address: '0x...',
      },
    },
  }
)

IPFS Integration

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

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

import { MetadataApi } from '@cowprotocol/sdk-app-data'
import { getGlobalAdapter } from '@cowprotocol/cow-sdk'

const metadataApi = new MetadataApi(getGlobalAdapter())

const appDataDoc = await metadataApi.fetchDocFromAppData(
  '0x8e4f5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e'
)

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

Common Patterns

Market Order:
const appData = await buildAppData({
  appCode: 'MyTradingApp',
  slippageBps: 50,
  orderClass: 'market',
})
Limit Order:
const appData = await buildAppData({
  appCode: 'MyTradingApp',
  slippageBps: 0,
  orderClass: 'limit',
})
With Partner Fee:
const appData = await buildAppData({
  appCode: 'MyTradingApp',
  slippageBps: 50,
  orderClass: 'market',
  partnerFee: {
    bps: 10,
    recipient: '0x...',
  },
})
With Custom UTM:
const appData = await buildAppData(
  {
    appCode: 'MyApp',
    slippageBps: 50,
    orderClass: 'market',
  },
  {
    metadata: {
      utm: {
        utmSource: 'twitter',
        utmMedium: 'social',
        utmCampaign: 'launch',
      },
    },
  }
)

Best Practices

  • Use Descriptive App Codes: Choose clear, unique identifiers for analytics
  • Set Appropriate Slippage: Market orders 0.5-1%, limit orders 0%, volatile tokens higher
  • Test Hooks Thoroughly: Always test on testnets before production
  • Cache App Data Hashes: Reuse identical metadata hashes to reduce IPFS uploads

Troubleshooting

App data hash mismatch: Ensure the exact fullAppData JSON string with deterministic formatting is used. The SDK handles this automatically. Hook execution failed: Verify target contract address, callData encoding matches function signature, gasLimit is sufficient, and test hook separately. Custom UTM not appearing: When providing metadata.utm, it replaces all defaults. Set all desired UTM fields explicitly.

Next Steps

Last modified on March 4, 2026