diff --git a/EVENT_SCHEMA.md b/EVENT_SCHEMA.md index 7c22605f..94e87de3 100644 --- a/EVENT_SCHEMA.md +++ b/EVENT_SCHEMA.md @@ -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. @@ -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` diff --git a/contracts/revenue_pool/src/events.rs b/contracts/revenue_pool/src/events.rs new file mode 100644 index 00000000..3a5ef80a --- /dev/null +++ b/contracts/revenue_pool/src/events.rs @@ -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")); + } +} diff --git a/contracts/revenue_pool/src/lib.rs b/contracts/revenue_pool/src/lib.rs index c12e18e1..49706e34 100644 --- a/contracts/revenue_pool/src/lib.rs +++ b/contracts/revenue_pool/src/lib.rs @@ -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. @@ -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, ); } @@ -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) { @@ -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`. @@ -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. @@ -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), ); } @@ -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), ); } @@ -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. @@ -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); } } @@ -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`. @@ -593,6 +593,8 @@ impl RevenuePool { } } +mod events; + #[cfg(test)] mod test; diff --git a/contracts/revenue_pool/src/test.rs b/contracts/revenue_pool/src/test.rs index d81148bf..dfffb994 100644 --- a/contracts/revenue_pool/src/test.rs +++ b/contracts/revenue_pool/src/test.rs @@ -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()); } diff --git a/contracts/settlement/src/events.rs b/contracts/settlement/src/events.rs new file mode 100644 index 00000000..7753ef0a --- /dev/null +++ b/contracts/settlement/src/events.rs @@ -0,0 +1,135 @@ +//! Event topic Symbol constructors for the Callora Settlement 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 `"payment_received"` event topic. +/// +/// Emitted when a payment is received from the vault or admin, crediting +/// either the global pool or a specific developer balance. +pub fn event_payment_received(env: &Env) -> Symbol { + Symbol::new(env, "payment_received") +} + +/// Returns the Symbol for the `"balance_credited"` event topic. +/// +/// Emitted when a developer's balance is incremented — either via +/// `receive_payment` (single) or `batch_receive_payment` (batch). +pub fn event_balance_credited(env: &Env) -> Symbol { + Symbol::new(env, "balance_credited") +} + +/// Returns the Symbol for the `"developer_withdraw"` event topic. +/// +/// Emitted when a developer successfully withdraws their accrued balance +/// as on-ledger USDC. +pub fn event_developer_withdraw(env: &Env) -> Symbol { + Symbol::new(env, "developer_withdraw") +} + +/// Returns the Symbol for the `"daily_withdraw_cap_changed"` event topic. +/// +/// Emitted when the admin sets or updates a developer's daily withdrawal cap. +pub fn event_daily_withdraw_cap_changed(env: &Env) -> Symbol { + Symbol::new(env, "daily_withdraw_cap_changed") +} + +/// Returns the Symbol for the `"admin_nominated"` event topic. +/// +/// Emitted when the current admin nominates a new admin via `set_admin`. +/// The nominated admin must call `accept_admin` to complete the transfer. +pub fn event_admin_nominated(env: &Env) -> Symbol { + Symbol::new(env, "admin_nominated") +} + +/// Returns the Symbol for the `"admin_accepted"` event topic. +/// +/// Emitted when the pending admin accepts the admin role via `accept_admin`, +/// completing the two-step admin handover. +pub fn event_admin_accepted(env: &Env) -> Symbol { + Symbol::new(env, "admin_accepted") +} + +/// Returns the Symbol for the `"vault_proposed"` event topic. +/// +/// Emitted when the admin proposes a new vault address via `propose_vault`. +/// The proposed vault must call `accept_vault` to be activated. +pub fn event_vault_proposed(env: &Env) -> Symbol { + Symbol::new(env, "vault_proposed") +} + +/// Returns the Symbol for the `"vault_accepted"` event topic. +/// +/// Emitted when the proposed vault (or admin) accepts the vault rotation +/// via `accept_vault`, completing the two-step vault update. +pub fn event_vault_accepted(env: &Env) -> Symbol { + Symbol::new(env, "vault_accepted") +} + +#[cfg(test)] +mod tests { + use super::*; + use soroban_sdk::Env; + + /// Snapshot: proves event_payment_received still maps to exactly the bytes for "payment_received". + #[test] + fn test_event_payment_received_bytes() { + let env = Env::default(); + assert_eq!(event_payment_received(&env), Symbol::new(&env, "payment_received")); + } + + /// Snapshot: proves event_balance_credited still maps to exactly the bytes for "balance_credited". + #[test] + fn test_event_balance_credited_bytes() { + let env = Env::default(); + assert_eq!(event_balance_credited(&env), Symbol::new(&env, "balance_credited")); + } + + /// Snapshot: proves event_developer_withdraw still maps to exactly the bytes for "developer_withdraw". + #[test] + fn test_event_developer_withdraw_bytes() { + let env = Env::default(); + assert_eq!(event_developer_withdraw(&env), Symbol::new(&env, "developer_withdraw")); + } + + /// Snapshot: proves event_daily_withdraw_cap_changed still maps to exactly the bytes for "daily_withdraw_cap_changed". + #[test] + fn test_event_daily_withdraw_cap_changed_bytes() { + let env = Env::default(); + assert_eq!( + event_daily_withdraw_cap_changed(&env), + Symbol::new(&env, "daily_withdraw_cap_changed") + ); + } + + /// Snapshot: proves event_admin_nominated still maps to exactly the bytes for "admin_nominated". + #[test] + fn test_event_admin_nominated_bytes() { + let env = Env::default(); + assert_eq!(event_admin_nominated(&env), Symbol::new(&env, "admin_nominated")); + } + + /// Snapshot: proves event_admin_accepted still maps to exactly the bytes for "admin_accepted". + #[test] + fn test_event_admin_accepted_bytes() { + let env = Env::default(); + assert_eq!(event_admin_accepted(&env), Symbol::new(&env, "admin_accepted")); + } + + /// Snapshot: proves event_vault_proposed still maps to exactly the bytes for "vault_proposed". + #[test] + fn test_event_vault_proposed_bytes() { + let env = Env::default(); + assert_eq!(event_vault_proposed(&env), Symbol::new(&env, "vault_proposed")); + } + + /// Snapshot: proves event_vault_accepted still maps to exactly the bytes for "vault_accepted". + #[test] + fn test_event_vault_accepted_bytes() { + let env = Env::default(); + assert_eq!(event_vault_accepted(&env), Symbol::new(&env, "vault_accepted")); + } +} diff --git a/contracts/settlement/src/lib.rs b/contracts/settlement/src/lib.rs index 02b907f7..d48c24be 100644 --- a/contracts/settlement/src/lib.rs +++ b/contracts/settlement/src/lib.rs @@ -247,7 +247,7 @@ impl CalloraSettlement { global_pool.last_updated = env.ledger().timestamp(); inst.set(&StorageKey::GlobalPool, &global_pool); env.events().publish( - (Symbol::new(&env, "payment_received"), caller.clone()), + (events::event_payment_received(&env), caller.clone()), PaymentReceivedEvent { from_vault: caller.clone(), amount, @@ -292,7 +292,7 @@ impl CalloraSettlement { } env.events().publish( - (Symbol::new(&env, "payment_received"), caller.clone()), + (events::event_payment_received(&env), caller.clone()), PaymentReceivedEvent { from_vault: caller.clone(), amount, @@ -301,7 +301,7 @@ impl CalloraSettlement { }, ); env.events().publish( - (Symbol::new(&env, "balance_credited"), dev_address.clone()), + (events::event_balance_credited(&env), dev_address.clone()), BalanceCreditedEvent { developer: dev_address, amount, @@ -376,7 +376,7 @@ impl CalloraSettlement { inst.set(&StorageKey::DeveloperIndex, &index); } env.events().publish( - (Symbol::new(&env, "balance_credited"), dev.clone()), + (events::event_balance_credited(&env), dev.clone()), BalanceCreditedEvent { developer: dev.clone(), amount: amount, @@ -543,7 +543,7 @@ impl CalloraSettlement { .extend_ttl(&StorageKey::WithdrawalToday(developer.clone()), 50000, 50000); env.events().publish( - (Symbol::new(&env, "developer_withdraw"), developer.clone()), + (events::event_developer_withdraw(&env), developer.clone()), DeveloperWithdrawEvent { developer, amount, @@ -577,7 +577,7 @@ impl CalloraSettlement { .extend_ttl(&StorageKey::DailyWithdrawCap(developer.clone()), 50000, 50000); env.events().publish( - (Symbol::new(&env, "daily_withdraw_cap_changed"), caller), + (events::event_daily_withdraw_cap_changed(&env), caller), DailyWithdrawCapChanged { developer, new_cap: cap }, ); } @@ -769,7 +769,7 @@ impl CalloraSettlement { env.events().publish( ( - Symbol::new(&env, "admin_nominated"), + events::event_admin_nominated(&env), current_admin, new_admin, ), @@ -805,7 +805,7 @@ impl CalloraSettlement { inst.remove(&StorageKey::PendingAdmin); env.events() - .publish((Symbol::new(&env, "admin_accepted"), current, pending), ()); + .publish((events::event_admin_accepted(&env), current, pending), ()); } /// Propose a new vault address (admin only). @@ -852,7 +852,7 @@ impl CalloraSettlement { inst.set(&StorageKey::PendingVault, &new_vault); env.events().publish( - (Symbol::new(&env, "vault_proposed"), caller), + (events::event_vault_proposed(&env), caller), VaultProposedEvent { current_vault: old_vault, proposed_vault: new_vault, @@ -889,7 +889,7 @@ impl CalloraSettlement { inst.remove(&StorageKey::PendingVault); env.events().publish( - (Symbol::new(&env, "vault_accepted"), caller.clone()), + (events::event_vault_accepted(&env), caller.clone()), VaultAcceptedEvent { old_vault, new_vault: pending, @@ -908,6 +908,8 @@ impl CalloraSettlement { } } +mod events; + #[cfg(test)] mod test; diff --git a/contracts/vault/src/events.rs b/contracts/vault/src/events.rs new file mode 100644 index 00000000..910f740c --- /dev/null +++ b/contracts/vault/src/events.rs @@ -0,0 +1,368 @@ +//! Event topic Symbol constructors for the Callora Vault 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 vault contract is first initialized with an owner and initial balance. +pub fn event_init(env: &Env) -> Symbol { + Symbol::new(env, "init") +} + +/// Returns the Symbol for the `"admin_nominated"` event topic. +/// +/// Emitted when an owner nominates a new admin. The new admin must call +/// `claim_admin` to complete the transfer. +pub fn event_admin_nominated(env: &Env) -> Symbol { + Symbol::new(env, "admin_nominated") +} + +/// Returns the Symbol for the `"admin_accepted"` event topic. +/// +/// Emitted when a nominated admin claims ownership and completes the admin transfer. +pub fn event_admin_accepted(env: &Env) -> Symbol { + Symbol::new(env, "admin_accepted") +} + +/// Returns the Symbol for the `"set_authorized_caller"` event topic. +/// +/// Emitted when an owner adds a new authorized caller for `deduct` operations. +pub fn event_set_authorized_caller(env: &Env) -> Symbol { + Symbol::new(env, "set_authorized_caller") +} + +/// Returns the Symbol for the `"set_max_deduct"` event topic. +/// +/// Emitted when the owner updates the maximum deductible amount per call. +pub fn event_set_max_deduct(env: &Env) -> Symbol { + Symbol::new(env, "set_max_deduct") +} + +/// Returns the Symbol for the `"vault_paused"` event topic. +/// +/// Emitted when the vault is paused, blocking deposits and deducts. +/// Owner withdrawals and admin distributions remain allowed. +pub fn event_vault_paused(env: &Env) -> Symbol { + Symbol::new(env, "vault_paused") +} + +/// Returns the Symbol for the `"vault_unpaused"` event topic. +/// +/// Emitted when the vault is unpaused, resuming normal operation. +pub fn event_vault_unpaused(env: &Env) -> Symbol { + Symbol::new(env, "vault_unpaused") +} + +/// Returns the Symbol for the `"deposit"` event topic. +/// +/// Emitted when a caller deposits USDC into the vault. +pub fn event_deposit(env: &Env) -> Symbol { + Symbol::new(env, "deposit") +} + +/// Returns the Symbol for the `"deduct"` event topic. +/// +/// Emitted when an authorized caller or admin deducts funds from the vault. +/// Includes an optional request ID for idempotency tracking. +pub fn event_deduct(env: &Env) -> Symbol { + Symbol::new(env, "deduct") +} + +/// Returns the Symbol for the `"ownership_nominated"` event topic. +/// +/// Emitted when the current owner nominates a new owner. +/// The nominee must call `claim_ownership` to complete the transfer. +pub fn event_ownership_nominated(env: &Env) -> Symbol { + Symbol::new(env, "ownership_nominated") +} + +/// Returns the Symbol for the `"ownership_accepted"` event topic. +/// +/// Emitted when a nominated owner accepts and completes the ownership transfer. +pub fn event_ownership_accepted(env: &Env) -> Symbol { + Symbol::new(env, "ownership_accepted") +} + +/// Returns the Symbol for the `"withdraw"` event topic. +/// +/// Emitted when the vault owner withdraws funds from the vault. +pub fn event_withdraw(env: &Env) -> Symbol { + Symbol::new(env, "withdraw") +} + +/// Returns the Symbol for the `"withdraw_to"` event topic. +/// +/// Emitted when the vault owner withdraws funds to a specified recipient address. +pub fn event_withdraw_to(env: &Env) -> Symbol { + Symbol::new(env, "withdraw_to") +} + +/// Returns the Symbol for the `"distribute"` event topic. +/// +/// Emitted when the admin distributes funds to a designated recipient. +pub fn event_distribute(env: &Env) -> Symbol { + Symbol::new(env, "distribute") +} + +/// Returns the Symbol for the `"set_revenue_pool"` event topic. +/// +/// Emitted when the owner configures a revenue pool address for fund settlements. +pub fn event_set_revenue_pool(env: &Env) -> Symbol { + Symbol::new(env, "set_revenue_pool") +} + +/// Returns the Symbol for the `"clear_revenue_pool"` event topic. +/// +/// Emitted when the owner clears the configured revenue pool address. +pub fn event_clear_revenue_pool(env: &Env) -> Symbol { + Symbol::new(env, "clear_revenue_pool") +} + +/// Returns the Symbol for the `"set_settlement"` event topic. +/// +/// Emitted when the admin sets or updates the settlement contract address. +pub fn event_set_settlement(env: &Env) -> Symbol { + Symbol::new(env, "set_settlement") +} + +/// Returns the Symbol for the `"metadata_set"` event topic. +/// +/// Emitted when the admin sets metadata for an offering. +pub fn event_metadata_set(env: &Env) -> Symbol { + Symbol::new(env, "metadata_set") +} + +/// Returns the Symbol for the `"price_set"` event topic. +/// +/// Emitted when the admin sets a price for an offering. +pub fn event_price_set(env: &Env) -> Symbol { + Symbol::new(env, "price_set") +} + +/// Returns the Symbol for the `"price_removed"` event topic. +/// +/// Emitted when the admin removes a price for an offering. +pub fn event_price_removed(env: &Env) -> Symbol { + Symbol::new(env, "price_removed") +} + +/// Returns the Symbol for the `"metadata_updated"` event topic. +/// +/// Emitted when the admin updates metadata for an offering. +pub fn event_metadata_updated(env: &Env) -> Symbol { + Symbol::new(env, "metadata_updated") +} + +/// Returns the Symbol for the `"metadata_removed"` event topic. +/// +/// Emitted when the admin removes metadata for an offering. +pub fn event_metadata_removed(env: &Env) -> Symbol { + Symbol::new(env, "metadata_removed") +} + +/// Returns the Symbol for the `"upgraded"` event topic. +/// +/// Emitted when the vault contract is upgraded to a new WASM hash. +pub fn event_upgraded(env: &Env) -> Symbol { + Symbol::new(env, "upgraded") +} + +/// Returns the Symbol for the `"allowlist_add"` event topic. +/// +/// Emitted when the owner adds an address to the vault deposit allowlist. +pub fn event_allowlist_add(env: &Env) -> Symbol { + Symbol::new(env, "allowlist_add") +} + +/// Returns the Symbol for the `"allowlist_clear"` event topic. +/// +/// Emitted when the owner clears the entire vault deposit allowlist. +pub fn event_allowlist_clear(env: &Env) -> Symbol { + Symbol::new(env, "allowlist_clear") +} + +#[cfg(test)] +mod tests { + use super::*; + + /// Snapshot test: verifies event topic byte identity preservation. + /// If this test fails, a topic was accidentally renamed or changed. + #[test] + fn test_event_init_bytes() { + let env = soroban_sdk::Env::default(); + let sym = event_init(&env); + assert_eq!(sym, Symbol::new(&env, "init")); + } + + #[test] + fn test_event_admin_nominated_bytes() { + let env = soroban_sdk::Env::default(); + let sym = event_admin_nominated(&env); + assert_eq!(sym, Symbol::new(&env, "admin_nominated")); + } + + #[test] + fn test_event_admin_accepted_bytes() { + let env = soroban_sdk::Env::default(); + let sym = event_admin_accepted(&env); + assert_eq!(sym, Symbol::new(&env, "admin_accepted")); + } + + #[test] + fn test_event_set_authorized_caller_bytes() { + let env = soroban_sdk::Env::default(); + let sym = event_set_authorized_caller(&env); + assert_eq!(sym, Symbol::new(&env, "set_authorized_caller")); + } + + #[test] + fn test_event_set_max_deduct_bytes() { + let env = soroban_sdk::Env::default(); + let sym = event_set_max_deduct(&env); + assert_eq!(sym, Symbol::new(&env, "set_max_deduct")); + } + + #[test] + fn test_event_vault_paused_bytes() { + let env = soroban_sdk::Env::default(); + let sym = event_vault_paused(&env); + assert_eq!(sym, Symbol::new(&env, "vault_paused")); + } + + #[test] + fn test_event_vault_unpaused_bytes() { + let env = soroban_sdk::Env::default(); + let sym = event_vault_unpaused(&env); + assert_eq!(sym, Symbol::new(&env, "vault_unpaused")); + } + + #[test] + fn test_event_deposit_bytes() { + let env = soroban_sdk::Env::default(); + let sym = event_deposit(&env); + assert_eq!(sym, Symbol::new(&env, "deposit")); + } + + #[test] + fn test_event_deduct_bytes() { + let env = soroban_sdk::Env::default(); + let sym = event_deduct(&env); + assert_eq!(sym, Symbol::new(&env, "deduct")); + } + + #[test] + fn test_event_ownership_nominated_bytes() { + let env = soroban_sdk::Env::default(); + let sym = event_ownership_nominated(&env); + assert_eq!(sym, Symbol::new(&env, "ownership_nominated")); + } + + #[test] + fn test_event_ownership_accepted_bytes() { + let env = soroban_sdk::Env::default(); + let sym = event_ownership_accepted(&env); + assert_eq!(sym, Symbol::new(&env, "ownership_accepted")); + } + + #[test] + fn test_event_withdraw_bytes() { + let env = soroban_sdk::Env::default(); + let sym = event_withdraw(&env); + assert_eq!(sym, Symbol::new(&env, "withdraw")); + } + + #[test] + fn test_event_withdraw_to_bytes() { + let env = soroban_sdk::Env::default(); + let sym = event_withdraw_to(&env); + assert_eq!(sym, Symbol::new(&env, "withdraw_to")); + } + + #[test] + fn test_event_distribute_bytes() { + let env = soroban_sdk::Env::default(); + let sym = event_distribute(&env); + assert_eq!(sym, Symbol::new(&env, "distribute")); + } + + #[test] + fn test_event_set_revenue_pool_bytes() { + let env = soroban_sdk::Env::default(); + let sym = event_set_revenue_pool(&env); + assert_eq!(sym, Symbol::new(&env, "set_revenue_pool")); + } + + #[test] + fn test_event_clear_revenue_pool_bytes() { + let env = soroban_sdk::Env::default(); + let sym = event_clear_revenue_pool(&env); + assert_eq!(sym, Symbol::new(&env, "clear_revenue_pool")); + } + + #[test] + fn test_event_set_settlement_bytes() { + let env = soroban_sdk::Env::default(); + let sym = event_set_settlement(&env); + assert_eq!(sym, Symbol::new(&env, "set_settlement")); + } + + #[test] + fn test_event_metadata_set_bytes() { + let env = soroban_sdk::Env::default(); + let sym = event_metadata_set(&env); + assert_eq!(sym, Symbol::new(&env, "metadata_set")); + } + + #[test] + fn test_event_price_set_bytes() { + let env = soroban_sdk::Env::default(); + let sym = event_price_set(&env); + assert_eq!(sym, Symbol::new(&env, "price_set")); + } + + #[test] + fn test_event_price_removed_bytes() { + let env = soroban_sdk::Env::default(); + let sym = event_price_removed(&env); + assert_eq!(sym, Symbol::new(&env, "price_removed")); + } + + #[test] + fn test_event_metadata_updated_bytes() { + let env = soroban_sdk::Env::default(); + let sym = event_metadata_updated(&env); + assert_eq!(sym, Symbol::new(&env, "metadata_updated")); + } + + #[test] + fn test_event_metadata_removed_bytes() { + let env = soroban_sdk::Env::default(); + let sym = event_metadata_removed(&env); + assert_eq!(sym, Symbol::new(&env, "metadata_removed")); + } + + #[test] + fn test_event_upgraded_bytes() { + let env = soroban_sdk::Env::default(); + let sym = event_upgraded(&env); + assert_eq!(sym, Symbol::new(&env, "upgraded")); + } + + #[test] + fn test_event_allowlist_add_bytes() { + let env = soroban_sdk::Env::default(); + let sym = event_allowlist_add(&env); + assert_eq!(sym, Symbol::new(&env, "allowlist_add")); + } + + #[test] + fn test_event_allowlist_clear_bytes() { + let env = soroban_sdk::Env::default(); + let sym = event_allowlist_clear(&env); + assert_eq!(sym, Symbol::new(&env, "allowlist_clear")); + } +} diff --git a/contracts/vault/src/lib.rs b/contracts/vault/src/lib.rs index bdd18da6..d792f2dd 100644 --- a/contracts/vault/src/lib.rs +++ b/contracts/vault/src/lib.rs @@ -285,7 +285,7 @@ impl CalloraVault { inst.set(&StorageKey::MaxDeduct, &max_d); inst.extend_ttl(INSTANCE_BUMP_THRESHOLD, INSTANCE_BUMP_AMOUNT); env.events() - .publish((Symbol::new(&env, "init"), owner.clone()), balance); + .publish((events::event_init(&env), owner.clone()), balance); Ok(meta) } @@ -418,7 +418,7 @@ impl CalloraVault { } let mut updated = Vec::new(env); for id in list.iter() { - if id != offering_id { + if id != *offering_id { updated.push_back(id.clone()); } } @@ -451,7 +451,7 @@ impl CalloraVault { .instance() .set(&StorageKey::PendingAdmin, &new_admin); env.events() - .publish((Symbol::new(&env, "admin_nominated"), cur, new_admin), ()); + .publish((events::event_admin_nominated(&env), cur, new_admin), ()); Ok(()) } @@ -466,7 +466,7 @@ impl CalloraVault { env.storage().instance().set(&StorageKey::Admin, &pending); env.storage().instance().remove(&StorageKey::PendingAdmin); env.events() - .publish((Symbol::new(&env, "admin_accepted"), cur, pending), ()); + .publish((events::event_admin_accepted(&env), cur, pending), ()); Ok(()) } @@ -490,7 +490,7 @@ impl CalloraVault { env.storage().instance().set(&StorageKey::MetaKey, &meta); env.events().publish( ( - Symbol::new(&env, "set_authorized_caller"), + events::event_set_authorized_caller(&env), meta.owner.clone(), ), (old, new_caller), @@ -513,7 +513,7 @@ impl CalloraVault { .instance() .set(&StorageKey::MaxDeduct, &max_deduct); env.events().publish( - (Symbol::new(&env, "set_max_deduct"), meta.owner), + (events::event_set_max_deduct(&env), meta.owner), (old, max_deduct), ); Ok(()) @@ -566,7 +566,7 @@ impl CalloraVault { } env.storage().instance().set(&StorageKey::Paused, &true); env.events() - .publish((Symbol::new(&env, "vault_paused"), caller), ()); + .publish((events::event_vault_paused(&env), caller), ()); Ok(()) } @@ -578,7 +578,7 @@ impl CalloraVault { } env.storage().instance().set(&StorageKey::Paused, &false); env.events() - .publish((Symbol::new(&env, "vault_unpaused"), caller), ()); + .publish((events::event_vault_unpaused(&env), caller), ()); Ok(()) } @@ -625,7 +625,7 @@ impl CalloraVault { .instance() .extend_ttl(INSTANCE_BUMP_THRESHOLD, INSTANCE_BUMP_AMOUNT); env.events().publish( - (Symbol::new(&env, "deposit"), caller.clone()), + (events::event_deposit(&env), caller.clone()), (amount, meta.balance), ); @@ -724,7 +724,7 @@ impl CalloraVault { let rid = request_id.unwrap_or(Symbol::new(&env, "")); env.events().publish( - (Symbol::new(&env, "deduct"), caller, rid), + (events::event_deduct(&env), caller, rid), (amount, meta.balance), ); Ok(meta.balance) @@ -829,7 +829,7 @@ impl CalloraVault { for item in items.iter() { let rid = item.request_id.unwrap_or(Symbol::new(&env, "")); env.events().publish( - (Symbol::new(&env, "deduct"), caller.clone(), rid), + (events::event_deduct(&env), caller.clone(), rid), (item.amount, meta.balance), ); } @@ -847,7 +847,7 @@ impl CalloraVault { .set(&StorageKey::PendingOwner, &new_owner); env.events().publish( ( - Symbol::new(&env, "ownership_nominated"), + events::event_ownership_nominated(&env), meta.owner, new_owner, ), @@ -869,7 +869,7 @@ impl CalloraVault { env.storage().instance().set(&StorageKey::MetaKey, &meta); env.storage().instance().remove(&StorageKey::PendingOwner); env.events().publish( - (Symbol::new(&env, "ownership_accepted"), old, meta.owner), + (events::event_ownership_accepted(&env), old, meta.owner), (), ); Ok(()) @@ -901,7 +901,7 @@ impl CalloraVault { .instance() .extend_ttl(INSTANCE_BUMP_THRESHOLD, INSTANCE_BUMP_AMOUNT); env.events().publish( - (Symbol::new(&env, "withdraw"), meta.owner.clone()), + (events::event_withdraw(&env), meta.owner.clone()), (amount, meta.balance), ); Ok(meta.balance) @@ -928,7 +928,7 @@ impl CalloraVault { .instance() .extend_ttl(INSTANCE_BUMP_THRESHOLD, INSTANCE_BUMP_AMOUNT); env.events().publish( - (Symbol::new(&env, "withdraw_to"), meta.owner.clone(), to.clone()), + (events::event_withdraw_to(&env), meta.owner.clone(), to.clone()), (amount, meta.balance), ); Ok(meta.balance) @@ -974,7 +974,7 @@ impl CalloraVault { } // CEI: emit event before external transfer env.events() - .publish((Symbol::new(&env, "distribute"), to.clone()), amount); + .publish((events::event_distribute(&env), to.clone()), amount); usdc.transfer(&env.current_contract_address(), &to, &amount); Ok(()) } @@ -995,12 +995,12 @@ impl CalloraVault { .instance() .set(&StorageKey::RevenuePool, &addr); env.events() - .publish((Symbol::new(&env, "set_revenue_pool"), caller), addr); + .publish((events::event_set_revenue_pool(&env), caller), addr); } None => { env.storage().instance().remove(&StorageKey::RevenuePool); env.events() - .publish((Symbol::new(&env, "clear_revenue_pool"), caller), ()); + .publish((events::event_clear_revenue_pool(&env), caller), ()); } } Ok(()) @@ -1023,7 +1023,7 @@ impl CalloraVault { .instance() .set(&StorageKey::Settlement, &settlement_address); env.events().publish( - (Symbol::new(&env, "set_settlement"), caller), + (events::event_set_settlement(&env), caller), settlement_address, ); Ok(()) @@ -1074,7 +1074,7 @@ impl CalloraVault { .instance() .set(&StorageKey::Metadata(offering_id.clone()), &metadata); env.events().publish( - (Symbol::new(&env, "metadata_set"), offering_id, caller), + (events::event_metadata_set(&env), offering_id, caller), metadata.clone(), ); Ok(metadata) @@ -1108,7 +1108,7 @@ impl CalloraVault { .set(&StorageKey::Price(offering_id.clone()), &price); Self::add_offering_index(&env, &offering_id); env.events().publish( - (Symbol::new(&env, "price_set"), caller, offering_id), + (events::event_price_set(&env), caller, offering_id), price.clone(), ); Ok(()) @@ -1169,7 +1169,7 @@ impl CalloraVault { .remove(&StorageKey::Price(offering_id.clone())); Self::remove_offering_index(&env, &offering_id); env.events().publish( - (Symbol::new(&env, "price_removed"), caller, offering_id), + (events::event_price_removed(&env), caller, offering_id), (), ); Ok(()) @@ -1198,7 +1198,7 @@ impl CalloraVault { .instance() .set(&StorageKey::Metadata(offering_id.clone()), &metadata); env.events().publish( - (Symbol::new(&env, "metadata_updated"), offering_id, caller), + (events::event_metadata_updated(&env), offering_id, caller), (old, metadata.clone()), ); Ok(metadata) @@ -1226,7 +1226,7 @@ impl CalloraVault { .instance() .remove(&StorageKey::Metadata(offering_id.clone())); env.events().publish( - (Symbol::new(&env, "metadata_removed"), offering_id, caller), + (events::event_metadata_removed(&env), offering_id, caller), (), ); Ok(()) @@ -1267,7 +1267,7 @@ impl CalloraVault { // 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`. @@ -1375,7 +1375,7 @@ impl CalloraVault { .instance() .set(&StorageKey::DepositorList, &list); env.events() - .publish((Symbol::new(&env, "allowlist_add"), caller, depositor), ()); + .publish((events::event_allowlist_add(&env), caller, depositor), ()); Ok(()) } @@ -1386,7 +1386,7 @@ impl CalloraVault { .instance() .set(&StorageKey::DepositorList, &Vec::
::new(&env)); env.events() - .publish((Symbol::new(&env, "allowlist_clear"), caller), ()); + .publish((events::event_allowlist_clear(&env), caller), ()); Ok(()) } @@ -1398,6 +1398,8 @@ impl CalloraVault { } } +mod events; + // --------------------------------------------------------------------------- // Test modules // --------------------------------------------------------------------------- diff --git a/contracts/vault/src/test.rs b/contracts/vault/src/test.rs index 57c88236..7c417964 100644 --- a/contracts/vault/src/test.rs +++ b/contracts/vault/src/test.rs @@ -1,7 +1,7 @@ extern crate std; use soroban_sdk::testutils::{Address as _, Events as _}; -use soroban_sdk::{token, Address, Env, IntoVal, String, Symbol, TryFromVal}; +use soroban_sdk::{token, Address, Env, IntoVal, String, Symbol}; use super::*; @@ -2908,7 +2908,7 @@ fn test_set_authorized_caller() { fn set_authorized_caller_non_owner_fails() { let env = Env::default(); let owner = Address::generate(&env); - let non_owner = Address::generate(&env); + let _non_owner = Address::generate(&env); let new_caller = Address::generate(&env); let (_, client) = create_vault(&env); let (usdc, _, _) = create_usdc(&env, &owner); diff --git a/contracts/vault/src/test_views.rs b/contracts/vault/src/test_views.rs index 15b63980..1a07bec6 100644 --- a/contracts/vault/src/test_views.rs +++ b/contracts/vault/src/test_views.rs @@ -360,7 +360,7 @@ fn list_prices_limit_is_capped_at_100() { let env = Env::default(); let (owner, client, _) = setup(&env); for i in 0..105 { - let offering_id = String::from_str(&env, &format!("offer-{}", i)); + let offering_id = String::from_str(&env, &std::format!("offer-{}", i)); client.set_price(&owner, &offering_id, &String::from_str(&env, "1")); } let prices = client.list_prices(&0, &200); @@ -373,7 +373,7 @@ fn remove_price_removes_index_entry() { let (owner, client, _) = setup(&env); let offer = String::from_str(&env, "offer-x"); client.set_price(&owner, &offer, &String::from_str(&env, "500")); - client.remove_price(&owner, &offer).unwrap(); + client.remove_price(&owner, &offer); assert_eq!(client.get_price(&offer), None); assert_eq!(client.list_prices(&0, &10).len(), 0); }