Every event in the PAX operational ledger is hash-chained, batched into a Merkle tree, and anchored to external tamper-evident storage. This page exposes the verifier surface so any auditor, investor, or third-party developer can independently reconstruct the chain and confirm inclusion for any fill — without contacting PAX support and without trusting the PAX API.
Refreshes on page load from GET /v1/audit/status. If you see counts, the chain is running.
Three layers of tamper evidence, each independently verifiable by third parties:
All six endpoints are public, read-only, and rate-limited per source IP. Response envelope is the canonical {data, meta} shape with trace_id for support debugging.
| Endpoint | Purpose |
|---|---|
GET /v1/audit/head | Latest event in the hash chain: {id, prev_hash, entry_hash, actor, action, target, created_at_ms} + total event count. |
GET /v1/audit/root | Current chain-tip root hash + when it was written + the anchor chain that receives daily commits. |
GET /v1/audit/batches?limit=25 | Recent chain events with their entry_hash — useful for scanning without following every event. |
GET /v1/audit/proof/{event_id} | Full event body + prev_hash + entry_hash — verify by replaying sha256(prev_hash || canonical(entry)). Merkle-tree sibling proof coming. |
GET /v1/audit/health | Chain status: total events, latest timestamp, lag_ms (staleness gauge), anchor-chain identity. |
GET /v1/audit/anchor | On-chain anchor contract config: chain, contract address, cadence, last-anchored timestamp. |
Enter an event seq below. The page will fetch the event + its Merkle inclusion proof + the batch's merkle_root, then verify the proof entirely in your browser (WebCrypto SHA-256, OpenZeppelin sorted-pair). The result is computed client-side — no need to trust the PAX response.
The verify algorithm is 5 lines. Everything below runs locally against the public endpoints — you never need to trust PAX to compute the answer.
// npm i @predictasiax/api
import { PaxClient, AuditResource } from '@predictasiax/api';
const pax = new PaxClient({ apiKey: 'anonymous' }); // audit endpoints are no-auth
const seq = 110841;
const proof = await pax.audit.getProof(seq);
// Node crypto (sync)
const ok = AuditResource.verifyProof(
proof.event_hash,
proof.batch.merkle_root,
proof.merkle_proof,
);
// Browser / edge (WebCrypto async)
const okAsync = await AuditResource.verifyProofAsync(
proof.event_hash,
proof.batch.merkle_root,
proof.merkle_proof,
);
console.log('Verified locally:', ok); // true
# pip install pax-api
from pax_api import PaxClient
pax = PaxClient(api_key="anonymous") # audit endpoints are no-auth
seq = 110841
proof = pax.audit_proof(seq)["data"]
ok = PaxClient.verify_merkle_proof(
leaf=proof["event_hash"],
root=proof["batch"]["merkle_root"],
proof=proof["merkle_proof"],
)
print("Verified locally:", ok) # True
# 1. Fetch the event + its Merkle proof
curl -s https://api.predictasiax.com/v1/audit/proof/110841 | jq
# 2. Verify in shell (OpenZeppelin sorted-pair sha256)
verify() {
local h=$1 root=$2 shift 2
local proof=("$@")
for s in "${proof[@]}"; do
if [[ "$h" < "$s" ]]; then pair="${h}${s}"; else pair="${s}${h}"; fi
h=$(printf '%s' "$pair" | shasum -a 256 | cut -c1-64)
done
[[ "$h" == "$root" ]] && echo OK || echo MISMATCH
}
// The Merkle algorithm is bit-identical to OpenZeppelin's MerkleProof —
// the proofs returned by /v1/audit/proof/{seq} work on-chain without change.
import { MerkleProof } from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
function verifyPaxEvent(
bytes32 leaf, // event_hash from /v1/audit/proof/{seq}
bytes32 root, // merkle_root from same response
bytes32[] calldata proof // merkle_proof array
) public pure returns (bool) {
return MerkleProof.verify(proof, root, leaf);
}
event_log. If your app routed the trade, the event's payload carries your execution_builder_id — publicly verifiable via /v1/audit/events/{seq}.computeFeeSplit in apps/api/src/index.ts) writes idempotent financial_event rows keyed by fee_split:<trade_id>:<event_type>. The same trade cannot be double-attributed even under retry storms.POST /v1/fees/estimate endpoint and the fill-time settlement path both call the same computeFeeSplit function with the same PAF_BASE_BPS / PAF_FLOOR_BPS / PAF_CEILING_BPS constants. The estimate returned before your trade uses identical math to the settlement — divergence would require the API to run two different builds simultaneously, which the deploy pipeline forbids.api.predictasiax.com were compromised, historical anchors let auditors detect after-the-fact rewrites of past events.seq, the computed hash, and the expected hash. Every legitimate discrepancy report is investigated within 24h.