Skip to content
Merged
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
17 changes: 16 additions & 1 deletion EVENT_SCHEMA.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Event Schema
# Event Schema

Events emitted by all Callora contracts for indexers, frontends, and auditors.
All topic/data types refer to Soroban/Stellar XDR values.
Expand All @@ -7,6 +7,21 @@ All topic/data types refer to Soroban/Stellar XDR values.

The `workspace-members-dedup` hardening patch does not introduce event additions, removals, or payload shape changes.

## Change Note (2026-06)

**Event topic centralization (PR: task/event-symbol-catalog).**
All inline `Symbol::new(&env, "...")` event topic literals have been extracted from
`lib.rs` call sites into dedicated `src/events.rs` modules per crate:

- [`contracts/vault/src/events.rs`](contracts/vault/src/events.rs) — 23 topics
- [`contracts/settlement/src/events.rs`](contracts/settlement/src/events.rs) — 8 topics
- [`contracts/revenue_pool/src/events.rs`](contracts/revenue_pool/src/events.rs) — 10 topics

Each module exports one `pub fn event_*(&env) -> Symbol` function per topic and includes
a `#[cfg(test)]` snapshot block asserting byte-level identity to the original literal.
No topic strings were renamed; this refactor is a zero-semantic-change migration.


## Contract: Callora Vault

### `init`
Expand Down
165 changes: 165 additions & 0 deletions contracts/revenue_pool/src/events.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
//! Event topic Symbol constructors for the Callora Revenue Pool contract.
//!
//! This module centralizes all event topic strings into dedicated functions,
//! ensuring byte-identity is preserved and preventing accidental topic name drift
//! across call sites.

use soroban_sdk::{Env, Symbol};

/// Returns the Symbol for the `"init"` event topic.
///
/// Emitted when the revenue pool is first initialized with an admin and USDC token address.
pub fn event_init(env: &Env) -> Symbol {
Symbol::new(env, "init")
}

/// Returns the Symbol for the `"admin_changed"` event topic.
///
/// Emitted during `set_admin` alongside `admin_transfer_started` to record the
/// before/after admin intent explicitly for indexers and audit trails.
pub fn event_admin_changed(env: &Env) -> Symbol {
Symbol::new(env, "admin_changed")
}

/// Returns the Symbol for the `"admin_transfer_started"` event topic.
///
/// Emitted when the current admin nominates a new admin via `set_admin`.
/// The nominated admin must call `claim_admin` to complete the transfer.
pub fn event_admin_transfer_started(env: &Env) -> Symbol {
Symbol::new(env, "admin_transfer_started")
}

/// Returns the Symbol for the `"admin_transfer_completed"` event topic.
///
/// Emitted when the pending admin successfully claims ownership via `claim_admin`,
/// completing the two-step admin handover.
pub fn event_admin_transfer_completed(env: &Env) -> Symbol {
Symbol::new(env, "admin_transfer_completed")
}

/// Returns the Symbol for the `"pause_set"` event topic.
///
/// Emitted by both `pause` (with data `true`) and `unpause` (with data `false`)
/// to signal a change in the pool's pause state.
pub fn event_pause_set(env: &Env) -> Symbol {
Symbol::new(env, "pause_set")
}

/// Returns the Symbol for the `"receive_payment"` event topic.
///
/// Emitted when the admin calls `receive_payment` to log an incoming payment
/// from the vault for indexer alignment.
pub fn event_receive_payment(env: &Env) -> Symbol {
Symbol::new(env, "receive_payment")
}

/// Returns the Symbol for the `"set_max_distribute"` event topic.
///
/// Emitted when the admin updates the per-leg maximum distribute cap.
pub fn event_set_max_distribute(env: &Env) -> Symbol {
Symbol::new(env, "set_max_distribute")
}

/// Returns the Symbol for the `"distribute"` event topic.
///
/// Emitted when the admin distributes USDC to a single developer wallet via `distribute`.
pub fn event_distribute(env: &Env) -> Symbol {
Symbol::new(env, "distribute")
}

/// Returns the Symbol for the `"batch_distribute"` event topic.
///
/// Emitted once per payment leg during a `batch_distribute` call, after all
/// validation has passed.
pub fn event_batch_distribute(env: &Env) -> Symbol {
Symbol::new(env, "batch_distribute")
}

/// Returns the Symbol for the `"upgraded"` event topic.
///
/// Emitted when the admin upgrades the contract to a new WASM hash via `upgrade`.
pub fn event_upgraded(env: &Env) -> Symbol {
Symbol::new(env, "upgraded")
}

#[cfg(test)]
mod tests {
use super::*;
use soroban_sdk::Env;

/// Snapshot: proves event_init still maps to exactly the bytes for "init".
#[test]
fn test_event_init_bytes() {
let env = Env::default();
assert_eq!(event_init(&env), Symbol::new(&env, "init"));
}

/// Snapshot: proves event_admin_changed still maps to exactly the bytes for "admin_changed".
#[test]
fn test_event_admin_changed_bytes() {
let env = Env::default();
assert_eq!(event_admin_changed(&env), Symbol::new(&env, "admin_changed"));
}

/// Snapshot: proves event_admin_transfer_started still maps to exactly the bytes for "admin_transfer_started".
#[test]
fn test_event_admin_transfer_started_bytes() {
let env = Env::default();
assert_eq!(
event_admin_transfer_started(&env),
Symbol::new(&env, "admin_transfer_started")
);
}

/// Snapshot: proves event_admin_transfer_completed still maps to exactly the bytes for "admin_transfer_completed".
#[test]
fn test_event_admin_transfer_completed_bytes() {
let env = Env::default();
assert_eq!(
event_admin_transfer_completed(&env),
Symbol::new(&env, "admin_transfer_completed")
);
}

/// Snapshot: proves event_pause_set still maps to exactly the bytes for "pause_set".
#[test]
fn test_event_pause_set_bytes() {
let env = Env::default();
assert_eq!(event_pause_set(&env), Symbol::new(&env, "pause_set"));
}

/// Snapshot: proves event_receive_payment still maps to exactly the bytes for "receive_payment".
#[test]
fn test_event_receive_payment_bytes() {
let env = Env::default();
assert_eq!(event_receive_payment(&env), Symbol::new(&env, "receive_payment"));
}

/// Snapshot: proves event_set_max_distribute still maps to exactly the bytes for "set_max_distribute".
#[test]
fn test_event_set_max_distribute_bytes() {
let env = Env::default();
assert_eq!(event_set_max_distribute(&env), Symbol::new(&env, "set_max_distribute"));
}

/// Snapshot: proves event_distribute still maps to exactly the bytes for "distribute".
#[test]
fn test_event_distribute_bytes() {
let env = Env::default();
assert_eq!(event_distribute(&env), Symbol::new(&env, "distribute"));
}

/// Snapshot: proves event_batch_distribute still maps to exactly the bytes for "batch_distribute".
#[test]
fn test_event_batch_distribute_bytes() {
let env = Env::default();
assert_eq!(event_batch_distribute(&env), Symbol::new(&env, "batch_distribute"));
}

/// Snapshot: proves event_upgraded still maps to exactly the bytes for "upgraded".
#[test]
fn test_event_upgraded_bytes() {
let env = Env::default();
assert_eq!(event_upgraded(&env), Symbol::new(&env, "upgraded"));
}
}
24 changes: 13 additions & 11 deletions contracts/revenue_pool/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ impl RevenuePool {
inst.extend_ttl(LIFETIME_THRESHOLD, BUMP_AMOUNT);

env.events()
.publish((Symbol::new(&env, "init"), admin), usdc_token);
.publish((events::event_init(&env), admin), usdc_token);
}

/// Return the current admin address.
Expand Down Expand Up @@ -125,12 +125,12 @@ impl RevenuePool {

// Emit explicit before/after admin intent for indexers and audit trails.
env.events().publish(
(Symbol::new(&env, "admin_changed"), current.clone()),
(events::event_admin_changed(&env), current.clone()),
(current.clone(), new_admin.clone()),
);

env.events().publish(
(Symbol::new(&env, "admin_transfer_started"), current),
(events::event_admin_transfer_started(&env), current),
new_admin,
);
}
Expand Down Expand Up @@ -177,7 +177,7 @@ impl RevenuePool {
inst.extend_ttl(LIFETIME_THRESHOLD, BUMP_AMOUNT);

env.events()
.publish((Symbol::new(&env, "admin_transfer_completed"), pending), ());
.publish((events::event_admin_transfer_completed(&env), pending), ());
}

fn require_not_paused(env: &Env) {
Expand Down Expand Up @@ -212,7 +212,7 @@ impl RevenuePool {
.instance()
.set(&Symbol::new(&env, PAUSED_KEY), &true);
env.events()
.publish((Symbol::new(&env, "pause_set"), caller), true);
.publish((events::event_pause_set(&env), caller), true);
}

/// Unpause the revenue pool, restoring `distribute` and `batch_distribute`.
Expand All @@ -236,7 +236,7 @@ impl RevenuePool {
.instance()
.set(&Symbol::new(&env, PAUSED_KEY), &false);
env.events()
.publish((Symbol::new(&env, "pause_set"), caller), false);
.publish((events::event_pause_set(&env), caller), false);
}

/// Return `true` if the revenue pool is currently paused, `false` otherwise.
Expand Down Expand Up @@ -278,7 +278,7 @@ impl RevenuePool {
panic!("unauthorized: caller is not admin");
}
env.events().publish(
(Symbol::new(&env, "receive_payment"), caller),
(events::event_receive_payment(&env), caller),
(amount, from_vault),
);
}
Expand Down Expand Up @@ -309,7 +309,7 @@ impl RevenuePool {
.instance()
.set(&Symbol::new(&env, MAX_DISTRIBUTE_KEY), &max_distribute);
env.events().publish(
(Symbol::new(&env, "set_max_distribute"), admin),
(events::event_set_max_distribute(&env), admin),
(old_max, max_distribute),
);
}
Expand Down Expand Up @@ -382,7 +382,7 @@ impl RevenuePool {

usdc.transfer(&contract_address, &to, &amount);
env.events()
.publish((Symbol::new(&env, "distribute"), to), amount);
.publish((events::event_distribute(&env), to), amount);
}

/// Distribute USDC from this contract to multiple developer wallets in one atomic transaction.
Expand Down Expand Up @@ -531,7 +531,7 @@ impl RevenuePool {

// Emit one event per leg reflecting the final transferred amount.
env.events()
.publish((Symbol::new(&env, "batch_distribute"), to), amount);
.publish((events::event_batch_distribute(&env), to), amount);
}
}

Expand Down Expand Up @@ -579,7 +579,7 @@ impl RevenuePool {

// Emit an event for indexers / audit logs.
env.events()
.publish((Symbol::new(&env, "upgraded"), admin), new_wasm_hash);
.publish((events::event_upgraded(&env), admin), new_wasm_hash);
}

/// Read the stored contract version (WASM hash) as last set by `upgrade`.
Expand All @@ -593,6 +593,8 @@ impl RevenuePool {
}
}

mod events;

#[cfg(test)]
mod test;

Expand Down
2 changes: 1 addition & 1 deletion contracts/revenue_pool/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ fn create_usdc<'a>(
client.pause(&admin);
assert!(client.is_paused());

let result = std::panic::catch_unwind(|| client.distribute(&admin, &developer, &100));
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| client.distribute(&admin, &developer, &100)));
assert!(result.is_err());
}

Expand Down
Loading
Loading