A school issues diplomas. An employer wants to check one. Today that means asking the school — by email, by phone, through a verification service that charges for the privilege — and trusting the answer. The school may be slow, closed, defunct, or, occasionally, lying on behalf of an alumnus. Build the version where the school’s attestation is public, permanent, and checkable by anyone against the school’s known address, without the school being involved in the check.
Why the chain is load-bearing
An issuer writes an attestation — “this address holds credential X, issued on this date, described by this document” — to a contract, signed by the issuer’s key. From that moment, anyone can verify it: read the contract, check the issuer’s address against the issuer’s published identity, check the document hash against the document they were handed. The issuer cannot later deny having issued it. Nobody can forge one without the issuer’s key. Nobody can delete it. And the check works if the issuer is offline, or gone.
What is still trusted: the issuer, entirely, for the truth of the claim. The chain proves the school said it; it does not prove the student passed. And the issuer keeps the power to revoke — which is a feature, and a trust relationship, and belongs in the roles table. Your design also has to answer how a verifier knows which address is really the school’s; that question has no purely on-chain answer, and pretending it does is the common mistake.
Actors
| Role | Does |
|---|---|
| Issuer | Issues credentials to holders, revokes them |
| Holder | Owns credentials, proves ownership to verifiers |
| Verifier | Checks a credential’s existence, issuer, status, and content hash — with no account and no permission |
| Registry admin, if any | Decides who counts as an issuer — and this is the sensitive role |
| Deployer | Deploys, and then — ideally — has no further power |
Core features
- Issuer registry. A set of addresses recognised as issuers, each with a name or a pointer to an identity document. How addresses get into and out of this set is the central trust decision of the subject: a single admin, a multi-signature, a vote among existing issuers, or open registration with reputation left to the verifier. Pick one, defend it.
- Issue. An issuer creates a credential for a holder address: a type (diploma, certificate, membership), an issuance date, an optional expiry, and a content hash that commits to the full document held off chain. The credential is non-transferable — a diploma that can be sold is not a diploma. Whether you model it as a soulbound token, a record in a mapping, or an attestation in the style of EAS is a design choice with trade-offs to explain.
- Revoke. The issuer of a credential — and only that issuer — can revoke it, with a reason code. Revocation is an event and a state, not a deletion: a verifier can see that a credential existed and was revoked, which is different from never having existed.
- Verify. A public function, callable by anyone with no transaction —
a
view— that answers: does this credential exist, who issued it, is it currently valid, what is its content hash. The frontend has a verifier page that needs no wallet. - Prove ownership. A holder demonstrates to a verifier that they control the address a credential was issued to, by signing a verifier-chosen message. The frontend does this without a transaction.
- A holder’s credentials are listable, from events or from an enumeration, on a page the holder can share.
Extensions
- Off-chain attestations with on-chain revocation: the issuer signs the credential (EIP-712) and hands it to the holder; only revocations touch the chain. Issuance becomes free and private; verification checks the signature and then the revocation registry. Explain what is gained and what is lost.
- Privacy: the chain holds only a hash; the holder decides what to reveal and to whom. Go further with selective disclosure of fields via a Merkle tree of claims.
- Expiry and renewal, with the frontend showing validity windows.
- Issuer governance: issuers admitted and removed by vote of existing issuers, with a timelock.
- Documents on IPFS, the CID committed on chain, the frontend resolving and displaying them, with the hash checked client-side.
- An indexer offering search by holder, issuer, type, and status.
Traps
- Transferable credentials. If you build on ERC-721 and forget to block transfers, the first thing the jury will do is transfer one.
- Personal data on chain. Names, birth dates, grades, in plain text, on a public ledger, forever. Do not. Hashes and pointers only, and think about what a hash of a small, guessable document reveals.
- “The admin is trusted.” An issuer registry controlled by one key makes that key the root of all trust in the system. It may be the right design for a prototype — but the roles table must say, in so many words, that whoever holds that key can make anyone an issuer of anything.
- Verification that needs a wallet. A verifier is an employer with a browser. If your verify page starts with “connect wallet”, you have misunderstood who the user is.
- Revocation by deletion. Removing the record erases the evidence that it existed. Keep the record, flip the state.
- Assuming the issuer’s address is self-evidently the school’s. It is not. Say how a verifier learns it — a DNS record, a published page, an ENS name, the registry’s metadata — and what happens if that channel is compromised.
What the demo should show
Three profiles — issuer, holder, and a verifier with no wallet connected — on Base Sepolia. The issuer issues a credential to the holder; the event appears on the explorer. The verifier opens the verification page, pastes the credential’s identifier or the holder’s address, and sees: issued by whom, when, valid, content hash matching the document the holder provides. The holder proves control of the address by signing a challenge from the verifier’s page. Then the issuer revokes; the verifier refreshes and sees the revocation, with its reason and date. Then the failures: a non-issuer tries to issue and is reverted; a different issuer tries to revoke a credential it did not issue and is reverted; the holder tries to transfer the credential and is reverted. Each with a readable reason.