Skip to main content

Verification Pattern

BDS responses can include enough metadata for the consumer to verify that the API payload matches the snapshot CID finalized by the Powerloom protocol.

This is the core trust property of the market. The API is convenient, but it is not meant to be the root of trust.

Verification object

For supported routes such as the all-trades snapshot and SSE stream, BDS returns a verification object alongside the data:

{
"epoch": 24785718,
"snapshot": { "...trade data..." },
"verification": {
"cid": "bafkreib6h6au6hyv3hnx3wmbysu4lcoouah2yicst3nwnwndpcw2yxxrta",
"epochId": 24785718,
"projectId": "allTradesSnapshot:0x26c44e5CcEB7Fe69Cffc933838CF40286b2dc01a:mainnet-BDS_MAINNET_UNISWAPV3-ETH",
"protocolState": "0x1d0e010Ff11b781CA1dE34BD25a0037203e25E2a",
"dataMarket": "0x26c44e5CcEB7Fe69Cffc933838CF40286b2dc01a"
}
}

What the check proves

The consumer can call:

ProtocolState.maxSnapshotsCid(dataMarket, projectId, epochId)

If the returned CID matches verification.cid, the API served the same finalized content reference recorded by the protocol for that project and epoch.

This proves:

  • the response maps back to finalized protocol state,
  • the CID the API returned is not arbitrary,
  • and the operator is not asking the consumer to trust a private database as the source of truth.

Contract pair for the live BDS market

ContractAddress
ProtocolState0x1d0e010Ff11b781CA1dE34BD25a0037203e25E2a
DataMarket0x26c44e5CcEB7Fe69Cffc933838CF40286b2dc01a

RPC reads for verification should go to the Powerloom anchor chain where ProtocolState is deployed, not to Ethereum mainnet.

Python example

from web3 import Web3

PROTOCOL_STATE_ABI = [
{
"name": "maxSnapshotsCid",
"type": "function",
"inputs": [
{"name": "dataMarket", "type": "address"},
{"name": "projectId", "type": "string"},
{"name": "epochId", "type": "uint256"},
],
"outputs": [
{"name": "", "type": "string"},
{"name": "", "type": "uint8"},
],
"stateMutability": "view",
}
]

w3 = Web3(Web3.HTTPProvider("https://rpc-v2.powerloom.network"))
contract = w3.eth.contract(
address=Web3.to_checksum_address("0x1d0e010Ff11b781CA1dE34BD25a0037203e25E2a"),
abi=PROTOCOL_STATE_ABI,
)

verification = {
"cid": "bafkrei...",
"epochId": 24785718,
"projectId": "allTradesSnapshot:0x26c44e5CcEB7Fe69Cffc933838CF40286b2dc01a:mainnet-BDS_MAINNET_UNISWAPV3-ETH",
"dataMarket": "0x26c44e5CcEB7Fe69Cffc933838CF40286b2dc01a",
}

on_chain_cid, status = contract.functions.maxSnapshotsCid(
verification["dataMarket"],
verification["projectId"],
verification["epochId"],
).call()

assert on_chain_cid == verification["cid"]
print("verified", status)

TypeScript example

import { ethers } from "ethers";

const abi = [
"function maxSnapshotsCid(address dataMarket, string projectId, uint256 epochId) view returns (string, uint8)",
];

const provider = new ethers.JsonRpcProvider("https://rpc-v2.powerloom.network");
const contract = new ethers.Contract(
"0x1d0e010Ff11b781CA1dE34BD25a0037203e25E2a",
abi,
provider,
);

const verification = {
cid: "bafkrei...",
epochId: 24785718,
projectId:
"allTradesSnapshot:0x26c44e5CcEB7Fe69Cffc933838CF40286b2dc01a:mainnet-BDS_MAINNET_UNISWAPV3-ETH",
dataMarket: "0x26c44e5CcEB7Fe69Cffc933838CF40286b2dc01a",
};

const [onChainCid, status] = await contract.maxSnapshotsCid(
verification.dataMarket,
verification.projectId,
verification.epochId,
);

if (onChainCid !== verification.cid) {
throw new Error("verification mismatch");
}

console.log("verified", status);

Operational note

lastFinalizedSnapshot is not the right read for payload verification. It gives you the latest finalized epoch for a project, not the finalized CID for a specific epoch. For provenance checks, the canonical read is maxSnapshotsCid(...).