Record type registry
Every credential carries a recordType number. Ids 0 to 31 are the built-in types every issuer may use (Transcript, Degree, and so on). An institution that needs something else (a workshop certificate, a micro-credential) registers its own type in RecordTypeRegistry, and the credential registry refuses any id that is neither built in nor an active custom type of that issuer.
Id ranges
uint32 public constant LAST_BUILT_IN_TYPE = 31;
uint32 public constant FIRST_CUSTOM_TYPE = 1000;
uint256 public constant MAX_NAME_BYTES = 64;
uint256 public constant MAX_DESCRIPTION_BYTES = 256;
| Range | Meaning | Issuance |
|---|---|---|
| 0 to 31 | Built-in types, same meaning for every issuer | Always allowed |
| 32 to 999 | Reserved for future built-in types | Always refused (RecordTypeNotAllowed) |
| 1000 and up | Custom types, numbered per issuer from 1000 in registration order | Allowed only for the issuer that registered the id, and only while it is active |
Because custom ids are per issuer, id 1000 of one university and id 1000 of another are different types. A verifier always reads a custom name with typeOf(issuer, id).
An early design record (ADR-0038) described 0 to 999 as built in. The contract and the app use 0 to 31 as built in and keep 32 to 999 reserved. These pages follow the code.
Built-in names
The app holds the 32 built-in names in src/types/records.ts (RECORD_TYPE_NAMES, index equals id); the contract stores no names for them.
| Id | Name | Id | Name | Id | Name | Id | Name |
|---|---|---|---|---|---|---|---|
| 0 | Transcript | 8 | National ID | 16 | Transfer Certificate | 24 | Circular |
| 1 | Degree | 9 | Passport Copy | 17 | Bills | 25 | News |
| 2 | Marksheet | 10 | Character Certificate | 18 | Fee Receipt | 26 | Recommendation Letter |
| 3 | Diploma | 11 | Entrance Results | 19 | Scholarship Letter | 27 | Internship Certificate |
| 4 | Certificate | 12 | Admit Card | 20 | Loan Document | 28 | Experience Letter |
| 5 | Provisional Certificate | 13 | Counseling Letter | 21 | Hostel Clearance | 29 | Bonafide Certificate |
| 6 | Birth Certificate | 14 | Seat Allotment Letter | 22 | Routine | 30 | No Objection Certificate |
| 7 | Citizenship | 15 | Migration Certificate | 23 | Notice | 31 | Other |
The list matches the 32 values of the v0.2 RecordType enum, so credentials of both generations use the same numbers for built-in types.
Storage in words
| Mapping | Meaning |
|---|---|
_counts[issuer] | How many custom types the issuer has registered. The next id is 1000 + count |
_types[issuer][id] | RecordTypeInfo {name, description, active} |
_nameTaken[issuer][keccak256(name)] | Names already used by that issuer, kept even after a type is switched off |
The contract inherits only ERC165. It has no admin.
Functions
| Function | Who may call | Checks | Effects | Event | Reverts |
|---|---|---|---|---|---|
registerType(string name, string description) uint32 | An active issuer | Not paused; isActiveIssuer(sender); name 1 to 64 bytes; description at most 256 bytes; name not used before by this issuer | Assigns id 1000 + count, stores the type as active | RecordTypeRegistered(issuer, id, name, description) | SystemPaused, NotIssuer, EmptyTypeName, TypeNameTooLong, TypeDescriptionTooLong, TypeNameInUse |
setTypeActive(uint32 id, bool active) | The issuer that owns the id; a retired key is refused | The type exists in the caller's namespace | Changes the flag; does nothing (and emits nothing) if unchanged | RecordTypeActiveChanged(issuer, id, active) | NotIssuer, UnknownRecordType |
typeOf(address issuer, uint32 id) RecordTypeInfo | Anyone (view) | The custom type exists | UnknownRecordType | ||
typeCount(address issuer) uint32 | Anyone (view) | ||||
isTypeAllowed(address issuer, uint32 id) bool | Anyone (view); called by the credential registry at issuance | True for 0 to 31, or for an existing active custom type of that issuer | |||
contractVersion() string, supportsInterface(bytes4) bool | Anyone | "0.5.0", ERC-165 |
Rules that follow from the code:
- Ids are never reused, and a switched-off type keeps its name reserved for that issuer. Old credentials therefore keep a stable meaning.
- Switching a type off stops new issuance under it but leaves existing credentials untouched; their name stays readable through
typeOf. setTypeActivekeeps working while the set is paused. A removed issuer (no name) can still switch its types; a retired key cannot.- Built-in ids cannot be passed to
setTypeActiveortypeOf; both revertUnknownRecordType. - Custom types belong to the key that registered them. After a key retirement the successor starts with no custom types and registers its own.
How issuance uses it
Reserved ids 32 to 999 fall through to the second question, and since custom ids start at 1000 they never exist, so they are always refused.
How the app uses it
/records/typeslists the issuer's types and offers registration and switching (src/services/domain/record-type-service.ts:registerType,setTypeActive,typeOf). The list is rebuilt fromRecordTypeRegisteredandRecordTypeActiveChangedevents, because the contract returns no lists.src/lib/record-types.tsholds the shared helpers: it classifies an id as built-in, reserved or custom and validates a new name and description before they are sent (it strips hidden characters).- The issue form, the holder inbox and the verify page show the type name, and mark custom ones as issuer-defined.
The full flow is in Record types.
Gas
From blockchain/gas-snapshot.json: registerType 150,590; setTypeActive 32,070.
Source files
blockchain/contracts/v03/RecordTypeRegistry.solblockchain/contracts/v03/interfaces/IRecordTypeRegistry.solsrc/services/domain/record-type-service.ts,src/lib/record-types.ts,src/types/records.ts
Tests
blockchain/test/v03/RecordTypeRegistry.test.ts: reserved ranges, per-issuer numbering from 1000, names per issuer, ids never reused, removed issuers, bounds, owner-only switching, no event when unchanged,typeOfandisTypeAllowedblockchain/test/v03/CredentialRecordTypes.test.ts: issuance with built-in, active custom, reserved, unregistered, foreign and inactive idsblockchain/test/v03/KeyLineage.test.ts: a retired key cannot register or change a type