This guide describes the on-chain events emitted by the stellar-did-credit contracts and how off-chain data feeders/indexers can subscribe to and process these events to maintain synchronized off-chain states.
Soroban events are structured as a topic vector and a data payload. By convention, the first topic is a symbol representing the event name.
The identity-oracle, credit-oracle, and revocation-registry contracts emit an Initialized event during their initialize function. The governance contract also emits one — see the Governance section below. In all cases the event is emitted exactly once per contract, immediately after the admin address and target wiring is stored.
- Topic:
[Symbol("Initialized")] - Data:
admin: Address(governance uses(admin: Address, credit_oracle: Address)— see below) - Emitted When: The contract is initialized with an administrator address.
- feeder Action: None (metadata tracking).
- Topic:
[Symbol("Initialized")] - Data:
admin: Address - Emitted When: The contract is initialized with an admin address.
- Note: Emitted exactly once — the
AlreadyInitializederror prevents re-initialization.
- Topic:
[Symbol("DIDAnch")] - Data:
(subject: Address, did_doc_cid: String) - Emitted When: A subject anchors or updates their DID document CID.
- feeder Action: None (metadata tracking).
- Topic:
[Symbol("VCAnch")] - Data:
(issuer: Address, subject: Address, vc_hash: BytesN<32>) - Emitted When: A trusted issuer anchors a new Verifiable Credential for a subject.
- feeder Action: Trigger sync for
subject(fetch new VC count, submitset_vc_count).
- Topic:
[Symbol("RegSet")] - Data:
(previous_registry: Address, new_registry: Address) - Emitted When: The admin updates the revocation registry contract ID on the identity oracle.
- feeder Action: None (configuration tracking). Update local cache of the revocation registry address.
- Topic:
[Symbol("IssReg")]or[Symbol("IssDeReg")] - Data:
issuer: Address - Emitted When: An issuer is registered or deregistered by the admin.
- Topic:
[Symbol("Initialized")] - Data:
admin: Address - Emitted When: The contract is initialized with an admin address.
- Note: Emitted exactly once — the
AlreadyInitializederror prevents re-initialization.
- Topic:
[Symbol("Revoked")] - Data:
(issuer: Address, vc_hash: BytesN<32>) - Emitted When: An issuer revokes a single VC hash.
- feeder Action: Map the
vc_hashto the subject, decrement their VC count, and submitset_vc_countto the credit oracle.
- Topic:
[Symbol("BatchRev")] - Data:
(issuer: Address, count: u32) - Emitted When: An issuer revokes a batch of VC hashes.
- Topic:
[Symbol("OrclSet")] - Data:
(previous_oracle: Address, new_oracle: Address) - Emitted When: The admin updates the identity-oracle contract ID on the credit oracle.
- feeder Action: None (configuration tracking). Update local cache of the identity-oracle address.
- Topic:
[Symbol("Score")] - Data:
(subject: Address, score: u32) - Emitted When: A subject's credit score is recomputed and updated.
- Topic:
[Symbol("FdrReg")]/[Symbol("FdrDeReg")] - Data:
feeder: Address - Emitted When: A feeder is registered or deregistered.
- Topic:
[Symbol("LndReg")]/[Symbol("LndDeReg")] - Data:
lender: Address - Emitted When: A lender is registered or deregistered.
- Topic:
[Symbol("WtProp")] - Data:
(vc_weight: u32, tx_weight: u32, repayment_weight: u32, effective_ledger: u32) - Emitted When: New scoring weights are proposed.
- Topic:
[Symbol("WtApply")] - Data:
(vc_weight: u32, tx_weight: u32, repayment_weight: u32) - Emitted When: Pending or direct weights are applied.
- Topic:
[Symbol("CdSet")] - Data:
(ledgers: u32, admin: Address) - Emitted When: The compute cooldown ledgers value is updated by the admin.
- Topic:
[Symbol("Initialized")] - Data:
(admin: Address, credit_oracle: Address) - Emitted When: The governance contract is initialized with an admin address and the credit-oracle it will govern. The admin address must be passed in by the caller (matches the
initializeparameter);credit_oracleis also passed in at init time and must match the address stored underDataKey::CreditOracle. - Note: The data format differs from the other contracts because governance's
initializesignature includes the credit-oracle target. The identity-oracle address is not currently stored by governance (a specific follow-up to issue #39 would make this consistent — for now governance is the only contract that emits more than just the admin on init). Emitted exactly once — theAlreadyInitializederror prevents re-initialization.
- Topic:
[Symbol("PropCreat"), proposal_id: u64] - Data:
(proposer: Address, expiry_ledger: u32) - Emitted When: A new governance proposal is created.
- Topic:
[Symbol("PropExec"), proposal_id: u64] - Data:
(votes_for: i128, votes_against: i128) - Emitted When: An expired governance proposal is executed.
- Topic:
[Symbol("PropCanc"), proposal_id: u64] - Data:
(canceller: Address, reason: Option<String>) - Emitted When: A governance proposal is cancelled.
Here is a Node.js example using the @stellar/stellar-sdk to subscribe to VCAnch events on the Identity Oracle contract.
import { SorobanRpc, xdr, scValToNative } from "@stellar/stellar-sdk";
const rpcUrl = "https://soroban-testnet.stellar.org";
const server = new SorobanRpc.Server(rpcUrl);
const contractId = "CATORJPJ..."; // Replace with Identity Oracle contract ID
async function pollEvents() {
const currentLedger = await server.getLatestLedger();
const startLedger = currentLedger.sequence - 100; // Start polling from 100 ledgers ago
console.log(`Polling events starting from ledger ${startLedger}...`);
const response = await server.getEvents({
startLedger,
filters: [
{
type: "contract",
contractIds: [contractId],
topics: [
[
xdr.ScVal.scvSymbol("VCAnch").toXDR("base64")
]
]
}
],
limit: 50
});
for (const event of response.events) {
const value = scValToNative(event.value);
// VCAnch value is a tuple/array: [issuer, subject, vc_hash]
const [issuer, subject, vcHash] = value;
console.log(`[VCAnch] Issuer: ${issuer}, Subject: ${subject}, Hash: ${vcHash}`);
// Trigger your feeder sync logic here:
// await syncSubjectVCs(subject);
}
}
pollEvents().catch(console.error);To maintain a real-time credit score, the off-chain feeder performs the following event-driven loops:
- Subscribe to
VCAnchevents onidentity-oracle. - Extract the
subjectaddress from the event payload. - Call
get_active_vc_count(subject)onidentity-oraclevia read-only RPC simulation to get the latest count. - Call
set_vc_count(feeder, subject, count)oncredit-oracle.
- Subscribe to
Revokedevents onrevocation-registry. - Extract the
vc_hash. - Resolve the
subjectaddress associated with thatvc_hash(e.g. from local indexing database). - Call
get_active_vc_count(subject)onidentity-oraclevia read-only RPC simulation to get the decremented count. - Call
set_vc_count(feeder, subject, count)oncredit-oracle.