This document defines the stable API contract for the AnchorKit smart contract, including error codes, event schemas, and method signatures.
All error codes are in the range 100-120 and are guaranteed to remain stable across contract versions.
| Code | Name | Description | When It Occurs |
|---|---|---|---|
| 100 | AlreadyInitialized |
Contract has already been initialized | Attempting to call initialize() more than once |
| 101 | NotInitialized |
Contract has not been initialized yet | Calling methods that require initialization before initialize() is called |
| 102 | UnauthorizedAttestor |
Caller is not a registered attestor | Attempting to submit an attestation without being registered as an attestor |
| 103 | AttestorAlreadyRegistered |
Attestor is already registered | Attempting to register an attestor that is already registered |
| 104 | AttestorNotRegistered |
Attestor is not registered | Attempting to revoke an attestor that is not registered |
| 105 | ReplayAttack |
Attestation with this hash has already been submitted | Attempting to submit an attestation with a payload hash that has already been used |
| 106 | InvalidTimestamp |
Timestamp is invalid (zero or in the future) | Submitting an attestation with a timestamp of 0 |
| 107 | AttestationNotFound |
Attestation with the given ID was not found | Querying an attestation ID that doesn't exist |
| 108 | InvalidPublicKey |
Public key format is invalid | Reserved for future use with signature verification |
Error codes 100-120 are reserved for AnchorKit and will never be reassigned. New errors will be added to the end of the range, ensuring backward compatibility.
All public methods return Result<T, Error> where T is the return type and Error is the error enum defined above.
Initializes the contract with an admin address. Can only be called once.
Returns:
Ok(())on successErr(Error::AlreadyInitialized)if already initialized
Registers a new attestor. Only callable by admin.
Returns:
Ok(())on successErr(Error::NotInitialized)if contract not initializedErr(Error::AttestorAlreadyRegistered)if attestor already registered
Revokes an attestor. Only callable by admin.
Returns:
Ok(())on successErr(Error::NotInitialized)if contract not initializedErr(Error::AttestorNotRegistered)if attestor not registered
submit_attestation(env: Env, issuer: Address, subject: Address, timestamp: u64, payload_hash: BytesN<32>, signature: Bytes) -> Result<u64, Error>
Submits an attestation. Must be signed by a registered attestor.
Returns:
Ok(attestation_id)on successErr(Error::InvalidTimestamp)if timestamp is 0Err(Error::UnauthorizedAttestor)if issuer is not registeredErr(Error::ReplayAttack)if payload hash already used
Retrieves an attestation by ID.
Returns:
Ok(attestation)on successErr(Error::AttestationNotFound)if ID doesn't exist
Gets the admin address.
Returns:
Ok(admin_address)on successErr(Error::NotInitialized)if contract not initialized
Checks if an address is a registered attestor. This method never fails.
Returns:
trueif registeredfalseif not registered
All events follow a standardized format to minimize gas costs while maintaining queryability.
Events use the following structure:
(Topic, ID/SubjectAddr, Data)
Where:
- Topic: A tuple of symbols identifying the event type
- ID/SubjectAddr: The primary identifier (attestation ID or subject address)
- Data: Minimal data structure containing only essential information
Topic: ("attestor", "added", attestor_address)
Data:
struct AttestorAddedData {} // Empty for gas efficiencyExample:
Topic: ("attestor", "added", GABC...XYZ)
Data: {}
Topic: ("attestor", "removed", attestor_address)
Data:
struct AttestorRemovedData {} // Empty for gas efficiencyExample:
Topic: ("attestor", "removed", GABC...XYZ)
Data: {}
Topic: ("attest", "recorded", attestation_id, subject_address)
Data:
struct AttestationRecordedData {
pub timestamp: u64,
pub payload_hash: BytesN<32>,
}Example:
Topic: ("attest", "recorded", 42, GDEF...ABC)
Data: {
timestamp: 1234567890,
payload_hash: 0x1234...5678
}
Events are designed for efficient querying:
- Filter by event type using the first topic symbol
- Filter by subject address using the address in the topic
- Filter by attestation ID using the ID in the topic
- Access timestamp and payload hash from the data payload
Frontend applications should handle error codes in the 100-120 range:
const ERROR_CODES = {
100: 'Contract already initialized',
101: 'Contract not initialized',
102: 'Unauthorized attestor',
103: 'Attestor already registered',
104: 'Attestor not registered',
105: 'Replay attack detected',
106: 'Invalid timestamp',
107: 'Attestation not found',
108: 'Invalid public key'
};
function parseError(error) {
const code = error.code;
if (code >= 100 && code <= 120) {
return ERROR_CODES[code] || 'Unknown error';
}
return 'Unexpected error';
}Frontend applications can listen for events:
// Listen for attestor additions
contract.on('attestor:added', (attestorAddress) => {
console.log(`Attestor added: ${attestorAddress}`);
});
// Listen for attestations
contract.on('attest:recorded', (attestationId, subjectAddress, data) => {
console.log(`Attestation ${attestationId} recorded for ${subjectAddress}`);
console.log(`Timestamp: ${data.timestamp}`);
console.log(`Payload hash: ${data.payload_hash}`);
});This API specification follows semantic versioning:
- Major version: Breaking changes to error codes or event schemas
- Minor version: New error codes or events added
- Patch version: Documentation updates
Current version: 1.0.0
- Error codes 100-120 will never be reassigned
- Event topic structures will remain stable
- Event data structures may only add optional fields
- Method signatures will not change in breaking ways
Any breaking changes will result in a new major version and a migration guide.