A Soroban smart contract for immutably logging financial transactions on the Stellar network, providing a publicly verifiable audit trail. Built with the Soroban SDK.
AuditLedger acts as an append-only log for financial and operational events. Each entry is sealed with a timestamp, event type (Symbol), and submitter address (Address), producing a tamper-evident historical record that any party can independently verify. Configurable global and per-event logging limits prevent state bloat while maintaining a complete, ordered history.
- Immutable Event Logging — Every event is recorded on-chain with a standardized
Eventstruct, creating a permanent audit trail. - Configurable Logging Limits — Separate global and per-event-type caps (
u32) prevent contract state spam. Caps can be set to any value (including0to freeze logging) or removed entirely viaremove_event_cap. - Public Verifiability — Anyone can enumerate and verify the full log history or filter by event type — no trusted intermediary required.
- Metadata Standardization — Events carry opaque
Bytesmetadata, encouraging off-chain consumers to adopt a consistent schema. - Boundary-Safe Validation — Contract logic is hardened against edge cases: zero-maximum configurations, equal min/max value ranges, empty metadata, and cap removal.
| Component | Description |
|---|---|
| Global Log Registry | Sequential array of all events, capped by global_max_logs. |
| Per-Event Sub-Ledgers | Namespaced event types (Symbol), each with an optional independent maximum log limit. |
| Cap Gates | event_cap_set boolean gates per-event enforcement — caps are opt-in and can be removed via remove_event_cap. |
pub struct Event {
pub index: u32,
pub timestamp: u64,
pub event_type: Symbol,
pub submitter: Address,
pub metadata: Bytes,
}fn initialize(env: Env, owner: Address, global_max_logs: u32);
fn log_event(env: Env, submitter: Address, event_type: Symbol, metadata: Bytes) -> u32;
fn log_events(env: Env, events: Vec<(Address, Symbol, Bytes)>) -> Vec<u32>;fn total_events(env: Env) -> u32;
fn get_event(env: Env, id: BytesN<32>) -> Event;
fn event_count(env: Env, event_type: Symbol) -> u32;
fn get_event_by_type(env: Env, event_type: Symbol, type_index: u32) -> Event;fn set_global_max_logs(env: Env, caller: Address, new_max: u32);
fn set_event_max_logs(env: Env, caller: Address, event_type: Symbol, new_max: u32);
fn remove_event_cap(env: Env, caller: Address, event_type: Symbol);
fn transfer_ownership(env: Env, caller: Address, new_owner: Address);# Build
cargo build
# Run all tests
cargo test
# Format
cargo fmt
# Lint
cargo clippy- Rust toolchain (install via rustup)
- WASM target:
rustup target add wasm32-unknown-unknown - Soroban CLI:
cargo install soroban-cli --features opt - Docker & Docker Compose (for local infrastructure)
- Node.js 20+ (for UI and metrics exporter)
The fastest way to iterate on the contract locally:
# 1. Build and test in one cycle
cargo build && cargo test
# 2. Run a single test to narrow down issues
cargo test test_log_event
# 3. Format and lint before committing
cargo fmt --check && cargo clippy -- -D warnings
# 4. Build the WASM binary for size checks
cargo build --target wasm32-unknown-unknown --release
ls -lh target/wasm32-unknown-unknown/release/audit_ledger.wasmcargo build --target wasm32-unknown-unknown --releaseUsing the deploy script (recommended):
# Set your secret key (never commit this)
export SOROBAN_SECRET_KEY="<your_secret_key>"
# Run the deployment script
./scripts/deploy_testnet.shThe script validates required environment variables, builds the WASM binary, and deploys it to Stellar testnet. See scripts/deploy_testnet.sh for details.
Using the Soroban CLI directly:
soroban contract deploy \
--wasm target/wasm32-unknown-unknown/release/audit_ledger.wasm \
--source <secret_key> \
--network testnetThe contract must only be initialized once. Repeated calls to initialize() will revert with AlreadyInitialized.
soroban contract invoke \
--id <contract_id> \
--source <owner_secret> \
--network testnet \
-- \
initialize \
--owner <owner_address> \
--global_max_logs 100000Run the full monitoring and UI stack locally:
# Copy and configure environment variables
cp .env.example .env
# Start all services
docker compose up --build- UI: http://localhost:3001
- Grafana: http://localhost:3000
- Prometheus metrics: http://localhost:9090
Copy .env.example to .env and configure the required variables:
| Variable | Required | Default | Description |
|---|---|---|---|
CONTRACT_ID |
Yes | — | Deployed contract ID |
RPC_URL |
No | https://soroban-testnet.stellar.org |
Soroban RPC endpoint |
NETWORK |
No | testnet |
Stellar network passphrase |
SCRAPE_INTERVAL_MS |
No | 15000 |
Metrics exporter poll interval |
EVENT_TYPES |
No | payment,refund,transfer |
Event types to track |
GRAFANA_PASSWORD |
No | admin |
Grafana admin password |
| Test File | Count | Description |
|---|---|---|
src/test.rs |
22 | Logging, queries, governance, ownership transfers, cap management, event emission, empty metadata, access control, boundary conditions |
Boundary tests include: zero global/event max logs, setting max equal to current count, removing caps after zero-lock, mixed multi-type limits, and panic-on-nonexistent access.
Contributions are organized into Wave Issues with point values:
| Difficulty | Points | Example Task |
|---|---|---|
| High | 200 | Implement global vs. per-event logging limits to prevent contract state spam. |
| Medium | 150 | Write edge-case tests validating boundary conditions (e.g., zero maximum logs, equal min/max values). |
| Trivial | 100 | Standardize the metadata structure for all logged events. |
- Claim an issue or submit a proposal.
- Fork the repo and implement the feature/fix.
- Open a pull request with tests and documentation.
- Earn points redeemable for rewards.
Every push and pull request triggers a GitHub Actions workflow that:
- Installs the Rust toolchain via
dtolnay/rust-toolchain - Checks formatting with
cargo fmt --check - Lints with
cargo clippy - Builds with
cargo build - Runs the full test suite with
cargo test