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...' ,
},
},
}
)
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
Market Order
Limit Order
With Partner Fee
With Custom UTM
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