> ## Documentation Index
> Fetch the complete documentation index at: https://docs.presschain.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Read Chain State

> Use eth_call and event logs to build a read-only PressChain feature before adding transactions.

# Read chain state

The best first PressChain feature is a read-only one. It proves your network configuration, ABI handling and identifier discipline without involving user signing.

A typical product starts by resolving a Capsule ID from a route and reading the corresponding contract state.

## Preserve bytes32 IDs

Capsule IDs are canonical `bytes32` values in V3.

```ts theme={null}
export function normalizeBytes32(value: string): `0x${string}` {
  if (!/^0x[0-9a-fA-F]{64}$/.test(value)) {
    throw new Error("Invalid bytes32 value");
  }
  return value.toLowerCase() as `0x${string}`;
}
```

Do not convert these values into JavaScript numbers. Preserve them as hex strings through routing, API serialization and storage.

## Read through a contract library

With an EVM library, a read-only provider can call contract view functions without a signer. The exact deployment address must come from the active V3 deployment manifest.

```ts theme={null}
import { JsonRpcProvider, Contract } from "ethers";

const provider = new JsonRpcProvider("https://rpc.presschain.io", 77117002);

const evidenceAbi = [
  "function getEvidenceIds(bytes32 capsuleId) view returns (uint256[])",
  "function getEvidence(uint256 evidenceId) view returns (tuple(uint256 evidenceId,bytes32 capsuleId,uint8 evidenceType,string label,string uri,bytes32 contentHash,string mimeType,string metadataURI,string coverUrl,address submittedBy,bool isPrimary,bool active,uint64 createdAt))",
  "function getEvidenceTally(uint256 evidenceId) view returns (tuple(uint256 supportCount,uint256 rejectCount,uint256 participationCount,uint8 status,bool finalized))"
];

const evidence = new Contract(EVIDENCE_REGISTRY_ADDRESS, evidenceAbi, provider);
const ids = await evidence.getEvidenceIds(capsuleId);
```

The functions above are present in the active V3 evidence registry. The address placeholder is intentional. Use the approved deployment manifest rather than copying an address from an old client.

## Build a useful read model

Do not force your UI to understand raw tuple positions. Normalize contract output into an application model while preserving source coordinates.

```ts theme={null}
const records = await Promise.all(
  ids.map(async (id: bigint) => {
    const [item, tally] = await Promise.all([
      evidence.getEvidence(id),
      evidence.getEvidenceTally(id),
    ]);

    return {
      id: id.toString(),
      capsuleId: item.capsuleId,
      label: item.label,
      uri: item.uri,
      contentHash: item.contentHash,
      coverUrl: item.coverUrl || null,
      active: item.active,
      tally: {
        support: tally.supportCount.toString(),
        reject: tally.rejectCount.toString(),
        participation: tally.participationCount.toString(),
        status: Number(tally.status),
        finalized: tally.finalized,
      },
    };
  })
);
```

For high-volume applications, this direct pattern eventually becomes too chatty. That is where an indexer is useful. The chain remains authoritative, while the projection packages common queries efficiently.

## Verify referenced artifacts

An evidence item contains both a URI and a content hash. Fetching the URI is only half the job. A serious application should hash the retrieved bytes and compare them with the commitment before displaying a “verified artifact” state.

That distinction lets the interface say something precise:

* available and hash-matched
* available but hash-mismatched
* temporarily unavailable
* intentionally inactive in protocol state

Those states are more informative than a single green check mark.

## Use events for discovery

Contract calls are good for a known Capsule. Events are better when an indexer needs to discover all changes over time. Store block number, block hash, transaction hash and log index beside each projected record.

If replaying the same block range creates duplicates, the indexer is not ready for production. Use deterministic event identity and upsert behavior.

## Next step

Once reads are stable, add a user-authorized flow with [Build with PressKey](/quickstart/presskey-app). Keep the read provider independent so your application can still display public state when PressKey is locked or not installed.
