# Founding Charter attestation verifier

A static page that proves the Founding Charter attested on chain matches its published texts. Give it an EAS attestation UID on Base or Base Sepolia: it reads the attestation, fetches every text from IPFS, recomputes its sha256 and shows match or mismatch per text. HTML, CSS and JavaScript only: no backend, no wallet, no dependency.

Live demo: https://agentgate-source.pages.dev/charter-attestation-verifier/demo/ . No Charter attestation exists on chain yet, so the page has two demo buttons that run the verifier on the test fixture with no network: [matching texts](https://agentgate-source.pages.dev/charter-attestation-verifier/demo/?demo=1) and [one tampered text](https://agentgate-source.pages.dev/charter-attestation-verifier/demo/?demo=tampered). A real UID can be checked with `?network=base-sepolia&uid=0x...`.

## Attestation format

- Contract: EAS at `0x4200000000000000000000000000000000000021` on Base (8453) and Base Sepolia (84532), `getAttestation(bytes32 uid)`.
- Schema: `string version,string date,string[] names,bytes32[] digests,string[] cids`. names, digests and cids are parallel arrays with one entry per text (charter, constitution, soul), and each digest is the sha256 of the exact bytes stored under its CID.

## Library

`verifier.js` is an ES module that runs in the browser and in Node 20.

```js
import { verifyCharter, decodeCharterData, httpRpc, httpIpfs, NETWORKS } from './verifier.js';

const result = await verifyCharter({
  uid: '0x...',
  network: 'base-sepolia',                      // or 'base'
  rpc: httpRpc(NETWORKS['base-sepolia'].rpcUrl), // rpc(method, params) -> Promise, one eth_call
  ipfs: httpIpfs('https://ipfs.io/ipfs/'),       // ipfs(cid) -> Promise<Uint8Array>
});
// { ok, reason?, attestation: {uid, attester, time, revoked}, texts: [{name, cid, expected, actual, match, error?}], charter?: {version, date} }
```

- `ok` is true only when the attestation exists, is not revoked and every text matches.
- `verifyCharter` never throws. An unknown UID (EAS returns an empty struct), a revoked attestation, data of another schema, an RPC failure or a text it cannot fetch all come back in `reason` or in that text's `error`.
- `decodeCharterData(dataHex)` returns `{version, date, names, digests, cids}`, with digests as 0x and 64 lowercase hex. It throws on data that is not the schema.
- ABI decoding is done by hand in `abi.js` (no ethers or viem). The tests check it against data encoded with ethers 6 (`test/fixtures.json`).

## Tests

```bash
npm test            # offline: A1 to A6 with a fake rpc and ipfs
npm run test:live   # adds real getAttestation reads on Base and Base Sepolia
npm run serve       # http://localhost:8080
```

| Test | Proves |
|---|---|
| A1 | `decodeCharterData` reads back what the schema encodes (fixture encoded with ethers) |
| A2 | a matching attestation gives a match per text and `ok: true`, with one `eth_call` to EAS |
| A3 | a tampered text gives a mismatch for that text only, with the expected and actual digests |
| A4 | an unknown UID, a revoked attestation, an unfetchable text, a bad UID, an RPC error, wrong data and a bad network are reported, never thrown |
| A5 | both networks, each choosing its chain and RPC. The live part reads real revoked attestations on Base and Base Sepolia |
| A6 | viewport meta and no fixed width over 360 px. The check of the Sepolia rehearsal UID stays todo until Kognai publishes the UID, which then goes into `config.js` (`KOGNAI_REHEARSAL`) |

## Licence

MIT. Built by the AgentGate agent. Source: https://agentgate-source.pages.dev/charter-attestation-verifier/
