Overview
The app_data module provides tools for creating, encoding, and managing app data for CoW Protocol orders. App data allows you to attach metadata to orders, including referrer information, partner fees, and custom graffiti.
AppDataDoc
Creates and manages app data documents that can be hashed or converted to IPFS CIDs.
Constructor
from cowdao_cowpy.app_data import AppDataDoc
app_data = AppDataDoc(
app_data_doc={"appCode": "my-app"},
app_data_doc_string=""
)
app_data_doc
Dict[str, Any]
default:"{}"
Dictionary containing app data fields. Merged with default app data structure. Common fields:
appCode: Your application identifier
metadata: Nested metadata object
version: App data schema version
Pre-serialized app data string. If provided, takes precedence over app_data_doc.
Methods
to_string
def to_string(self) -> str:
"""Serialize app data to deterministic JSON string."""
Returns a deterministically ordered JSON string of the app data.
app_data = AppDataDoc({"appCode": "my-app"})
json_string = app_data.to_string()
print(json_string) # {"appCode":"my-app",...}
to_hex
def to_hex(self) -> str:
"""Generate keccak256 hash of the app data."""
Returns the keccak256 hash of the serialized app data as a hex string with ‘0x’ prefix.
app_data_hash = app_data.to_hex()
print(app_data_hash) # "0xabc123..."
to_cid
def to_cid(self) -> str:
"""Convert app data to IPFS CID."""
Returns the IPFS Content Identifier (CID) for the app data hash.
cid = app_data.to_cid()
print(cid) # "f01..."
Example
from cowdao_cowpy.app_data import AppDataDoc
# Create app data with custom metadata
app_data = AppDataDoc({
"appCode": "my-trading-bot",
"metadata": {
"referrer": {
"address": "0x1234567890123456789012345678901234567890"
},
"utm": {
"utmSource": "my-app",
"utmContent": "Custom message"
}
}
})
# Get different representations
json_str = app_data.to_string()
app_data_hash = app_data.to_hex()
ipfs_cid = app_data.to_cid()
print(f"Hash: {app_data_hash}")
print(f"CID: {ipfs_cid}")
AppDataHex
Converts app data hashes to IPFS CIDs and retrieves documents from IPFS.
Constructor
from cowdao_cowpy.app_data import AppDataHex
app_data_hex = AppDataHex("abc123def456...") # Without 0x prefix
The app data hash as a hex string (without ‘0x’ prefix).
Methods
to_cid
def to_cid(self) -> str:
"""Convert app data hex to IPFS CID."""
Returns the IPFS CID (v1, base16 encoded with keccak-256 hash).
app_data_hex = AppDataHex("971c41b97f59534448ab833b0d83f755a4bc5c29f92b01776faa3699fcb0eeae")
cid = app_data_hex.to_cid()
to_doc
async def to_doc(self, ipfs_uri: str = DEFAULT_IPFS_READ_URI) -> Dict[str, Any]:
"""Fetch the app data document from IPFS."""
ipfs_uri
str
default:"DEFAULT_IPFS_READ_URI"
The IPFS gateway URI. Defaults to https://cloudflare-ipfs.com/ipfs.
Returns the app data document as a dictionary.
import asyncio
from cowdao_cowpy.app_data import AppDataHex
async def fetch_app_data():
app_data_hex = AppDataHex("971c41b97f59534448ab833b0d83f755a4bc5c29f92b01776faa3699fcb0eeae")
doc = await app_data_hex.to_doc()
print(doc)
asyncio.run(fetch_app_data())
AppDataCid
Converts IPFS CIDs to app data hashes and retrieves documents.
Constructor
from cowdao_cowpy.app_data import AppDataCid
app_data_cid = AppDataCid("f01...") # IPFS CID
The IPFS Content Identifier (CID).
Methods
to_hex
def to_hex(self) -> str:
"""Extract the app data hash from the CID."""
Returns the app data hash with ‘0x’ prefix.
from cowdao_cowpy.app_data import AppDataCid
app_data_cid = AppDataCid("f01551b1b971c41b97f59534448ab833b0d83f755a4bc5c29f92b01776faa3699fcb0eeae")
hex_hash = app_data_cid.to_hex()
print(hex_hash) # "0x971c41b9..."
to_doc
async def to_doc(self, ipfs_uri: str = DEFAULT_IPFS_READ_URI) -> Dict[str, Any]:
"""Fetch the app data document from IPFS."""
ipfs_uri
str
default:"DEFAULT_IPFS_READ_URI"
The IPFS gateway URI.
Returns the app data document.
Utility Functions
generate_app_data
Generate app data with common parameters like referrer address and partner fees.
from cowdao_cowpy.app_data.utils import generate_app_data, PartnerFee
app_data = generate_app_data(
app_code="my-app",
partner_fee=PartnerFee(bps=50, recipient="0x..."),
referrer_address="0x...",
graffiti="My custom message"
)
Your application code/identifier.
Partner fee configuration with bps (basis points) and recipient address.
Referrer’s Ethereum address.
Custom graffiti message to include in metadata.
Returns a CreateAppData object containing:
app_data_object: The serialized app data
app_data_hash: The keccak256 hash
from cowdao_cowpy.app_data.utils import generate_app_data, PartnerFee
app_data = generate_app_data(
app_code="my-dex-aggregator",
partner_fee=PartnerFee(
bps=50, # 0.5% fee
recipient="0x1234567890123456789012345678901234567890"
),
referrer_address="0xabcdef1234567890123456789012345678901234",
graffiti="Powered by My DEX"
)
print(f"App Data Hash: {app_data.app_data_hash.root}")
build_and_post_app_data
Build app data and upload it to the CoW Protocol orderbook API.
from cowdao_cowpy.app_data.utils import build_and_post_app_data
from cowdao_cowpy.order_book.api import OrderBookApi
app_data_hash = build_and_post_app_data(
orderbook=orderbook_api,
app_code="my-app",
referrer_address="0x...",
partner_fee=PartnerFee(bps=50, recipient="0x..."),
graffiti="Custom message"
)
OrderBookApi instance for the target network.
Partner fee configuration.
Returns the app data hash if upload succeeds, or hash from cache if already exists.
build_all_app_codes
Build and post app data across all supported chains.
from cowdao_cowpy.app_data.utils import build_all_app_codes
from cowdao_cowpy.common.api.api_base import Envs
app_data_hash = build_all_app_codes(
env=Envs.PROD,
app_code="my-app",
graffiti="My message",
referrer_address="0x...",
partner_fee=None
)
Environment: Envs.PROD or Envs.STAGING.
Returns the app data hash after uploading to all chains.
stringify_deterministic
Serialize a dictionary to deterministic JSON (sorted keys).
from cowdao_cowpy.app_data.utils import stringify_deterministic
json_str = stringify_deterministic({"b": 2, "a": 1})
print(json_str) # '{"a":1,"b":2}'
Extract the hash digest from an IPFS CID.
from cowdao_cowpy.app_data.utils import extract_digest
hex_hash = extract_digest("f01551b1b971c41b97f59534448ab833b0d83f755a4bc5c29f92b01776faa3699fcb0eeae")
print(hex_hash) # "0x971c41b9..."
fetch_doc_from_cid
Fetch a document from IPFS using a CID.
from cowdao_cowpy.app_data.utils import fetch_doc_from_cid
import asyncio
async def fetch():
doc = await fetch_doc_from_cid("f01551b1b...", ipfs_uri="https://cloudflare-ipfs.com/ipfs")
return doc
doc = asyncio.run(fetch())
Data Classes
PartnerFee
@dataclass
class PartnerFee:
bps: int # Basis points (100 = 1%)
recipient: str # Recipient address
Used to specify partner fee configuration.
fee = PartnerFee(bps=50, recipient="0x1234567890123456789012345678901234567890")
QuoteFee
@dataclass
class QuoteFee:
slippageBips: int = 1
version: str = "0.2.0"
Configuration for quote fee parameters.
CreateAppData
@dataclass
class CreateAppData:
app_data_object: AppDataObject
app_data_hash: AppDataHash
Return type from generate_app_data() containing both the serialized object and its hash.
Constants
DEFAULT_APP_DATA_DOC
The default app data structure used when creating new app data:
DEFAULT_APP_DATA_DOC = {
"appCode": "cowdao_cowpy",
"metadata": {
"hooks": {
"version": "0.1.0",
},
"utm": {
"utmSource": "cowmunity",
"utmMedium": "cow-py@{version}",
"utmCampaign": "developer-cohort",
"utmContent": "🥩📢🌀🐮 'MÖØ'",
"utmTerm": "python",
},
},
"version": "1.3.0",
}
DEFAULT_IPFS_READ_URI
Default IPFS gateway URI: https://cloudflare-ipfs.com/ipfs
Can be overridden via IPFS_READ_URI environment variable.
DEFAULT_APP_CODE
Default application code: "cowdao_cowpy"
DEFAULT_GRAFFITI
Default graffiti message: "🥩📢🌀🐮 'MÖØ'"
LATEST_APP_DATA_VERSION
Current app data schema version: "1.3.0"
Error Handling
class MetaDataError(Exception):
pass
Raised when there are issues with app data encoding, decoding, or CID conversion.
from cowdao_cowpy.app_data.consts import MetaDataError
try:
app_data_hex = AppDataHex("invalid")
cid = app_data_hex.to_cid()
except MetaDataError as e:
print(f"App data error: {e}")
Complete Example
import asyncio
from cowdao_cowpy.app_data import AppDataDoc, AppDataHex, AppDataCid
from cowdao_cowpy.app_data.utils import (
generate_app_data,
PartnerFee,
build_and_post_app_data
)
from cowdao_cowpy.order_book.api import OrderBookApi
from cowdao_cowpy.order_book.config import OrderBookAPIConfigFactory
from cowdao_cowpy.common.config import SupportedChainId
async def main():
# Create custom app data
app_data = AppDataDoc({
"appCode": "my-trading-app",
"metadata": {
"referrer": {
"address": "0x1234567890123456789012345678901234567890"
}
}
})
# Get different formats
app_data_hash = app_data.to_hex()
app_data_cid = app_data.to_cid()
print(f"Hash: {app_data_hash}")
print(f"CID: {app_data_cid}")
# Generate app data with partner fee
generated = generate_app_data(
app_code="my-app",
partner_fee=PartnerFee(bps=50, recipient="0xabcd..."),
graffiti="Powered by My App"
)
# Upload to orderbook
orderbook = OrderBookApi(
OrderBookAPIConfigFactory.get_config("prod", SupportedChainId.MAINNET)
)
uploaded_hash = build_and_post_app_data(
orderbook=orderbook,
app_code="my-app",
graffiti="Custom message"
)
print(f"Uploaded hash: {uploaded_hash}")
# Convert back from hash to document
app_data_hex = AppDataHex(uploaded_hash[2:]) # Remove 0x prefix
doc = await app_data_hex.to_doc()
print(f"Retrieved doc: {doc}")
asyncio.run(main())