Overview
The MetadataApi class provides utilities for working with CoW Protocol order metadata (app-data). It handles schema validation, document generation, IPFS hashing, and conversion between different metadata formats.
Installation
npm install @cowprotocol/sdk-app-data
# or
pnpm add @cowprotocol/sdk-app-data
# or
yarn add @cowprotocol/sdk-app-data
Constructor
new MetadataApi ( adapter ?: AbstractProviderAdapter )
Provider adapter (ViemAdapter, EthersV5Adapter, or EthersV6Adapter)
Basic Setup
import { MetadataApi } from '@cowprotocol/sdk-app-data'
import { EthersV6Adapter } from '@cowprotocol/sdk-ethers-v6-adapter'
import { JsonRpcProvider , Wallet } from 'ethers'
const provider = new JsonRpcProvider ( 'YOUR_RPC_URL' )
const wallet = new Wallet ( 'YOUR_PRIVATE_KEY' , provider )
const adapter = new EthersV6Adapter ({ provider , signer: wallet })
const metadataApi = new MetadataApi ( adapter )
Core Methods
generateAppDataDoc
Generate an app-data document using the latest schema version.
async generateAppDataDoc (
params ?: AppDataParams
): Promise < LatestAppDataDocVersion >
App data parameters Show AppDataParams properties
Environment name (e.g., ‘production’, ‘staging’)
Order metadata Quote metadata with slippage tolerance
orderClass
{ orderClass: 'market' | 'limit' | 'liquidity' }
Order classification
Pre/post interaction hooks partnerFee
{ bps: number, recipient: string }
Partner fee configuration
Generated app-data document with latest version
Example
const appDataDoc = await metadataApi . generateAppDataDoc ({
appCode: 'My Trading App' ,
environment: 'production' ,
metadata: {
referrer: {
address: '0x123...' ,
},
quote: {
slippageBips: 50 , // 0.5%
},
orderClass: {
orderClass: 'market' ,
},
},
})
console . log ( 'App data document:' , appDataDoc )
// {
// version: '1.14.0',
// appCode: 'My Trading App',
// environment: 'production',
// metadata: { ... }
// }
getAppDataInfo
Calculate app-data information including CID, hex, and content.
async getAppDataInfo (
appData : AnyAppDataDocVersion | string
): Promise < AppDataInfo >
appData
AnyAppDataDocVersion | string
required
App data document object or JSON string
Complete app-data information Show AppDataInfo properties
Hex string used in order’s appData field (bytes32)
Full JSON content (pre-image of appDataHex)
appDataContent - The exact string that gets hashed (keccak256) to produce appDataHex
appDataHex - The bytes32 value used in CoW Protocol orders
cid - IPFS identifier for finding the document
Example
const appDataDoc = await metadataApi . generateAppDataDoc ({
appCode: 'My App' ,
metadata: {
quote: { slippageBips: 50 },
},
})
const { appDataHex , appDataContent , cid } = await metadataApi . getAppDataInfo (
appDataDoc
)
console . log ( 'App data hex:' , appDataHex )
// '0x1234567890abcdef...'
console . log ( 'IPFS CID:' , cid )
// 'QmX...'
console . log ( 'Content:' , appDataContent )
// '{"version":"1.14.0","appCode":"My App",...}'
validateAppDataDoc
Validate an app-data document against its schema.
async validateAppDataDoc (
doc : AnyAppDataDocVersion
): Promise < { success : boolean ; errors ?: string } >
doc
AnyAppDataDocVersion
required
App data document to validate
Validation result Whether validation passed
Error description if validation failed
Example
const doc = {
version: '1.14.0' ,
appCode: 'My App' ,
metadata: {},
}
const result = await metadataApi . validateAppDataDoc ( doc )
if ( result . success ) {
console . log ( 'Document is valid' )
} else {
console . error ( 'Validation errors:' , result . errors )
}
getAppDataSchema
Retrieve app-data schema definition by version.
getAppDataSchema ( version : string ): AppDataSchema
Schema version (e.g., ‘1.14.0’)
Throws an error if the version doesn’t exist.
Example
const schema = metadataApi . getAppDataSchema ( '1.14.0' )
console . log ( 'Schema:' , schema )
// Use for custom validation
import Ajv from 'ajv'
const ajv = new Ajv ()
const validate = ajv . compile ( schema )
const isValid = validate ( myAppDataDoc )
Conversion Methods
appDataHexToCid
Convert app-data hex to IPFS CID.
async appDataHexToCid (
appDataHex : string
): Promise < string >
App data hex string (bytes32)
Example
const cid = await metadataApi . appDataHexToCid (
'0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'
)
console . log ( 'CID:' , cid )
// 'QmX...'
cidToAppDataHex
Convert IPFS CID to app-data hex.
async cidToAppDataHex (
cid : string
): Promise < string >
App data hex string (bytes32)
Example
const appDataHex = await metadataApi . cidToAppDataHex ( 'QmX...' )
console . log ( 'App data hex:' , appDataHex )
// '0x1234...'
fetchDocFromAppDataHex
Fetch app-data document from IPFS using app-data hex.
async fetchDocFromAppDataHex (
appDataHex : string
): Promise < AnyAppDataDocVersion >
Retrieved app-data document
Requires the document to be uploaded to IPFS.
Example
try {
const doc = await metadataApi . fetchDocFromAppDataHex (
'0x1234567890abcdef...'
)
console . log ( 'Retrieved document:' , doc )
} catch ( error ) {
console . error ( 'Document not found in IPFS:' , error )
}
Legacy Methods
The legacy property provides deprecated methods for backward compatibility.
legacy.fetchDocFromCid
metadataApi . legacy . fetchDocFromCid ( cid : string ): Promise < AnyAppDataDocVersion >
Deprecated. Use fetchDocFromAppDataHex instead.
metadataApi . legacy . uploadMetadataDocToIpfs (
appData : AnyAppDataDocVersion
): Promise < string >
Deprecated. IPFS upload functionality is no longer actively maintained.
legacy.appDataToCid
metadataApi . legacy . appDataToCid (
appData : AnyAppDataDocVersion | string
): Promise < AppDataInfo >
Deprecated. Use getAppDataInfo instead.
legacy.appDataHexToCid
metadataApi . legacy . appDataHexToCid (
appDataHex : string
): Promise < string >
Deprecated. Uses old IPFS CID hashing algorithm.
legacy.fetchDocFromAppDataHex
metadataApi . legacy . fetchDocFromAppDataHex (
appDataHex : string
): Promise < AnyAppDataDocVersion >
Deprecated. Uses old IPFS CID format.
Common Use Cases
import { TradingSdk } from '@cowprotocol/sdk-trading'
import { MetadataApi } from '@cowprotocol/sdk-app-data'
import { OrderKind } from '@cowprotocol/sdk-order-book'
const metadataApi = new MetadataApi ( adapter )
const sdk = new TradingSdk (
{ chainId: 1 , appCode: 'My App' },
{},
adapter
)
// 1. Generate metadata
const appDataDoc = await metadataApi . generateAppDataDoc ({
appCode: 'My Trading App' ,
metadata: {
referrer: { address: '0xReferrer...' },
quote: { slippageBips: 50 },
orderClass: { orderClass: 'market' },
},
})
// 2. Get app-data info
const { appDataHex } = await metadataApi . getAppDataInfo ( appDataDoc )
// 3. Create order with app-data
const { orderId } = await sdk . postSwapOrder (
{
kind: OrderKind . SELL ,
sellToken: '0xSellToken...' ,
sellTokenDecimals: 18 ,
buyToken: '0xBuyToken...' ,
buyTokenDecimals: 18 ,
amount: '1000000000000000000' ,
},
{
// Pass app-data params directly
appData: {
appCode: 'My Trading App' ,
metadata: {
referrer: { address: '0xReferrer...' },
quote: { slippageBips: 50 },
orderClass: { orderClass: 'market' },
},
},
}
)
console . log ( 'Order created with metadata:' , orderId )
Adding Hooks to Orders
const appDataDoc = await metadataApi . generateAppDataDoc ({
appCode: 'My App' ,
metadata: {
hooks: {
version: 1 ,
pre: [
{
target: '0xHookContract...' ,
callData: '0x70a08231000000000000000000000000...' ,
gasLimit: 21000 ,
},
],
post: [
{
target: '0xAnotherContract...' ,
callData: '0xa9059cbb000000000000000000000000...' ,
gasLimit: 50000 ,
},
],
},
},
})
const { appDataHex } = await metadataApi . getAppDataInfo ( appDataDoc )
console . log ( 'App data with hooks:' , appDataHex )
Partner Fee Integration
const appDataDoc = await metadataApi . generateAppDataDoc ({
appCode: 'Partner App' ,
metadata: {
partnerFee: {
bps: 10 , // 0.1% fee
recipient: '0xPartnerFeeRecipient...' ,
},
},
})
const { appDataHex } = await metadataApi . getAppDataInfo ( appDataDoc )
// Use in order
const { orderId } = await sdk . postSwapOrder (
tradeParams ,
{
appData: {
appCode: 'Partner App' ,
metadata: {
partnerFee: { bps: 10 , recipient: '0xPartner...' },
},
},
}
)
import { OrderBookApi } from '@cowprotocol/sdk-order-book'
const orderBookApi = new OrderBookApi ({ chainId: 1 })
const metadataApi = new MetadataApi ( adapter )
// Get order
const order = await orderBookApi . getOrder ( orderUid )
// Fetch metadata from app-data
const appDataDoc = await metadataApi . fetchDocFromAppDataHex (
order . appData
)
console . log ( 'Order metadata:' , appDataDoc . metadata )
console . log ( 'App code:' , appDataDoc . appCode )
Schema Versions
The SDK supports multiple app-data schema versions. The latest version is automatically used.
import {
LATEST_APP_DATA_VERSION ,
LATEST_QUOTE_METADATA_VERSION ,
LATEST_REFERRER_METADATA_VERSION ,
} from '@cowprotocol/sdk-app-data'
console . log ( 'Latest app-data version:' , LATEST_APP_DATA_VERSION )
// '1.14.0'
Available Schemas
v0.1.0 - Initial schema
v0.2.0 - Added quote metadata
v0.3.0 - Added referrer support
v0.4.0 - Added order class
…
v1.14.0 - Latest (includes hooks, partner fees, etc.)
Type Definitions
import { v1_14_0 } from '@cowprotocol/sdk-app-data'
function createAppData (
appCode : v1_14_0 . AppCode ,
metadata : v1_14_0 . Metadata
) : v1_14_0 . AppDataRootSchema {
return {
version: '1.14.0' ,
appCode ,
metadata ,
}
}
Error Handling
try {
const { appDataHex } = await metadataApi . getAppDataInfo ( appDataDoc )
} catch ( error ) {
if ( error . message . includes ( 'Invalid appData' )) {
console . error ( 'App data validation failed:' , error . message )
} else if ( error . message . includes ( 'calculate appDataHex' )) {
console . error ( 'Failed to calculate hash:' , error . message )
} else {
console . error ( 'Unexpected error:' , error )
}
}
Best Practices
Always validate - Use validateAppDataDoc before using app-data
Use latest version - Let generateAppDataDoc pick the latest schema
Cache app-data - Reuse appDataHex for identical metadata
Include referrer - Track order sources with referrer metadata
Document hooks - Clearly document any hooks for security audits
See Also