Skip to main content

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)
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>
params
AppDataParams
App data parameters
document
LatestAppDataDocVersion
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
info
AppDataInfo
Complete app-data information
  • 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
result
object
Validation result

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
version
string
required
Schema version (e.g., ‘1.14.0’)
schema
AppDataSchema
JSON schema definition
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>
appDataHex
string
required
App data hex string (bytes32)
cid
string
IPFS Content Identifier

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>
cid
string
required
IPFS Content Identifier
appDataHex
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>
appDataHex
string
required
App data hex string
document
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.

legacy.uploadMetadataDocToIpfs

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

Creating Order with Metadata

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...' },
      },
    },
  }
)

Retrieving Order Metadata

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

  1. Always validate - Use validateAppDataDoc before using app-data
  2. Use latest version - Let generateAppDataDoc pick the latest schema
  3. Cache app-data - Reuse appDataHex for identical metadata
  4. Include referrer - Track order sources with referrer metadata
  5. Document hooks - Clearly document any hooks for security audits

See Also

Build docs developers (and LLMs) love