Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ jobs:
cd stellar-lend
cargo fmt --all -- --check

- name: Check event schema compliance
run: |
cd stellar-lend
python3 scripts/check_event_schemas.py

- name: Run clippy
run: |
cd stellar-lend
Expand Down
9 changes: 9 additions & 0 deletions docs/event-indexing.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

This guide documents the event surfaces that exist in the current StellarLend workspace and how to consume them safely.

The canonical event compatibility rules live in
[`stellar-lend/docs/event-schema.md`](../stellar-lend/docs/event-schema.md), with the
machine-readable schema in
[`stellar-lend/docs/event-schema.v1.json`](../stellar-lend/docs/event-schema.v1.json).
CI runs `stellar-lend/scripts/check_event_schemas.py` to keep `#[contractevent]` definitions,
legacy manual topics, and the schema version constants in sync.

It is written around the code that exists today:

- `stellar-lend/contracts/hello-world` is the main protocol contract currently used by the API.
Expand All @@ -26,6 +33,8 @@ For this repo there are two emission styles:

Important decoding notes:

- Decoded rows should include `_schema_version: 1` and `_event_topic` metadata when persisted by
indexers. The prototype `indexing_system` annotates parsed payloads with these fields.
- `Option<Address>` means the native asset can appear as `None`.
- Amounts are raw integer amounts in the token's smallest unit.
- Risk parameters and reserve factors are basis points unless the source struct says otherwise.
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"deploy:verify": "bash scripts/verify-deployment.sh",
"deploy:rollback": "bash scripts/rollback.sh",
"verify:contract": "bash scripts/verify-contract.sh",
"contracts:event-schema:check": "python stellar-lend/scripts/check_event_schemas.py",
"mutation:api": "npm --workspace api run mutation:test",
"mutation:api:check": "npm --workspace api run mutation:check"
},
Expand Down
2 changes: 1 addition & 1 deletion stellar-lend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ members = [
"contracts/institutional-wallet",
"contracts/migration-hub",
]
exclude = ["fuzz"]
exclude = ["fuzz", "indexing_system"]

[workspace.dependencies]
soroban-sdk = "23.4.1"
Expand Down
27 changes: 27 additions & 0 deletions stellar-lend/contracts/common/src/event_schema.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//! Shared event schema constants for StellarLend contracts and indexers.
//!
//! Version 1 is intentionally additive: existing event topics and payloads remain stable while
//! new documentation, indexer metadata, and CI checks describe the canonical schema.

use soroban_sdk::contracttype;

pub const EVENT_SCHEMA_VERSION: u32 = 1;
pub const EVENT_SCHEMA_VERSION_FIELD: &str = "_schema_version";
pub const EVENT_TOPIC_FIELD: &str = "_event_topic";
pub const MAX_SOROBAN_EVENT_TOPICS: u32 = 4;

#[contracttype]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u32)]
pub enum EventSchemaVersion {
V1 = 1,
}

#[contracttype]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u32)]
pub enum EventPayloadFormat {
ContractEvent = 1,
SingleValue = 2,
LegacyTuple = 3,
}
1 change: 1 addition & 0 deletions stellar-lend/contracts/common/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#![no_std]
#![allow(deprecated)]
pub mod cache;
pub mod event_schema;
pub mod events;
pub mod message_bus;
pub mod shared_types;
Expand Down
41 changes: 41 additions & 0 deletions stellar-lend/docs/event-schema.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# StellarLend Event Schema v1

Schema Version: 1

This document defines the event compatibility rules for the StellarLend Soroban contracts. The
machine-readable companion file is `event-schema.v1.json`.

## Compatibility Rules

- Existing event topics are append-only. Do not rename or remove a topic without adding a migration note.
- New typed events should use `#[contractevent]` and lower snake case topic names.
- Manual `env.events().publish(...)` calls are allowed only for legacy compatibility and must be listed in the schema JSON.
- Event topics must fit Soroban's four-topic limit, including static topics and `#[topic]` fields.
- Field names must use lower snake case and payload fields should stay append-only.
- Amounts are raw token units. Percentages use basis points where `10000` is `100%`.
- Indexers should persist raw topics and decoded payloads, then annotate decoded rows with `_schema_version` and `_event_topic`.

## Common Field Semantics

| Field | Meaning |
| --- | --- |
| `user` | End-user account that initiated or owns a position-changing action. |
| `actor` | Authorized protocol account that performed an administrative action. |
| `caller` | Authenticated account that invoked an administrative or governance entrypoint. |
| `asset` | Soroban token contract address; `None` represents native asset where supported. |
| `amount` | Raw integer amount in the asset's smallest unit. |
| `fee` | Raw integer fee in the asset's smallest unit. |
| `timestamp` | Ledger timestamp in seconds. |
| `*_bps` | Basis points; `10000` is `100%`. |
Comment on lines +20 to +29

## Versioning

Version 1 is additive. It standardizes documentation, CI validation, and indexer metadata without
changing existing runtime event topics or payload shapes.

For a future breaking change:

1. Add a new `event-schema.vN.json` file.
2. Keep the old schema available for historical decoding.
3. Add an explicit migration section to `docs/event-indexing.md`.
4. Update indexers to decode both versions during the migration window.
140 changes: 140 additions & 0 deletions stellar-lend/docs/event-schema.v1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
{
"schema_version": 1,
"topic_style": "lower_snake_case",
"contracts": [
{
"name": "hello-world",
"source_globs": ["contracts/hello-world/src/**/*.rs"],
"active": true
},
{
"name": "lending",
"source_globs": ["contracts/lending/src/**/*.rs"],
"active": true
},
{
"name": "amm",
"source_globs": ["contracts/amm/src/**/*.rs"],
"active": true
},
{
"name": "bridge",
"source_globs": ["contracts/bridge/src/**/*.rs"],
"active": true
},
{
"name": "stablecoin",
"source_globs": ["contracts/stablecoin/src/**/*.rs"],
"active": true
},
{
"name": "common",
"source_globs": ["contracts/common/src/**/*.rs"],
"active": true
}
],
"allow_overloaded_topics": [
{
"contract": "lending",
"topic": "deposit_event",
"reason": "Vault deposits and borrow-collateral deposits predate this schema and are distinguished by payload shape."
}
],
"legacy_manual_events": [
{
"contract": "hello-world",
"topic": "bridge",
"format": "legacy_tuple"
},
{
"contract": "hello-world",
"topic": "mon_hlth",
"format": "legacy_tuple"
},
{
"contract": "hello-world",
"topic": "mon_init",
"format": "legacy_tuple"
},
{
"contract": "hello-world",
"topic": "mon_perf",
"format": "legacy_tuple"
},
{
"contract": "hello-world",
"topic": "mon_sec",
"format": "legacy_tuple"
},
{
"contract": "hello-world",
"topic": "rep_add",
"format": "legacy_tuple"
},
{
"contract": "hello-world",
"topic": "rep_del",
"format": "legacy_tuple"
},
{
"contract": "lending",
"topic": "bad_debt",
"format": "legacy_tuple"
},
{
"contract": "lending",
"topic": "bad_debt_recovered",
"format": "legacy_tuple"
},
{
"contract": "lending",
"topic": "yield_compounded",
"format": "legacy_tuple"
},
{
"contract": "lending",
"topic": "yield_deposit",
"format": "legacy_tuple"
},
{
"contract": "stablecoin",
"topic": "stablecoin_initialized",
"format": "legacy_tuple"
},
{
"contract": "stablecoin",
"topic": "shutdown_set",
"format": "legacy_tuple"
},
{
"contract": "stablecoin",
"topic": "reserve_ratio_set",
"format": "legacy_tuple"
},
{
"contract": "stablecoin",
"topic": "collateral_deposited",
"format": "legacy_tuple"
},
{
"contract": "stablecoin",
"topic": "stablecoin_minted",
"format": "legacy_tuple"
},
{
"contract": "stablecoin",
"topic": "stablecoin_redeemed",
"format": "legacy_tuple"
}
],
"field_semantics": {
"user": "End-user account that initiated or owns the position-changing operation.",
"actor": "Authorized protocol account that performed an administrative operation.",
"caller": "Authenticated account that invoked an administrative or governance entrypoint.",
"asset": "Soroban token contract address; null/None represents the native asset where supported.",
"amount": "Raw integer amount in the asset's smallest unit.",
"fee": "Raw integer fee amount in the asset's smallest unit.",
"timestamp": "Ledger timestamp in seconds.",
"bps": "Basis points, where 10000 is 100%."
}
}
18 changes: 11 additions & 7 deletions stellar-lend/indexing_system/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub mod parallel_indexer;
pub mod parser;
pub mod query;
pub mod repository;
pub mod schema;

#[cfg(test)]
pub mod tests;
Expand All @@ -20,10 +21,16 @@ pub use metrics::{IndexingMetrics, MetricsSnapshot};
pub use models::{
CreateEvent, Event, EventQuery, EventStats, EventUpdate, IndexingMetadata, UpdateType,
};
pub use parallel_indexer::{BlockProcessor, BlockRangeResult, BlockRangeTask, ParallelIndexer, StateManager};
pub use parallel_indexer::{
BlockProcessor, BlockRangeResult, BlockRangeTask, ParallelIndexer, StateManager,
};
pub use parser::{create_erc20_abi, EventParser};
pub use query::QueryService;
pub use repository::EventRepository;
pub use schema::{
annotate_event_payload, load_standard_event_schema, normalize_event_topic, EventSchemaDocument,
STANDARD_EVENT_SCHEMA_VERSION,
};

pub fn init_tracing() {
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
Expand Down Expand Up @@ -77,12 +84,9 @@ pub async fn run_migrations(database_url: &str) -> IndexerResult<()> {
///
/// This is the main initialization function that should be called
/// to set up the entire indexing system.
pub async fn initialize_system(config: &Config) -> IndexerResult<(
EventRepository,
CacheService,
QueryService,
IndexerService,
)> {
pub async fn initialize_system(
config: &Config,
) -> IndexerResult<(EventRepository, CacheService, QueryService, IndexerService)> {
info!("Initializing indexing system...");

// Run database migrations first
Expand Down
2 changes: 2 additions & 0 deletions stellar-lend/indexing_system/src/parser.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/// Event parser for decoding smart contract events
use crate::error::{IndexerError, IndexerResult};
use crate::models::CreateEvent;
use crate::schema::annotate_event_payload;
use ethers::abi::{Abi, Event as AbiEvent, RawLog};
use ethers::prelude::*;
use serde_json::Value;
Expand Down Expand Up @@ -100,6 +101,7 @@ impl EventParser {
let value = self.token_to_json(&param.value)?;
event_data.insert(param.name, value);
}
annotate_event_payload(&mut event_data, &event_def.name);

Ok(Some(CreateEvent {
contract_address,
Expand Down
Loading