diff --git a/apps/onchain/CONTRIBUTING.md b/apps/onchain/CONTRIBUTING.md index 6c4ccee09..71764fece 100644 --- a/apps/onchain/CONTRIBUTING.md +++ b/apps/onchain/CONTRIBUTING.md @@ -13,6 +13,42 @@ Welcome to the on-chain contracts workspace! This document outlines the developm - Constants: `SCREAMING_SNAKE_CASE` (e.g., `MAX_PRIVACY_LEVEL`) - Variables: `snake_case` (e.g., `account_address`) +### Event Versioning + +Every contract event **must** include a `version: u32` field as its first data field +so that backend and data-processing consumers can detect schema changes. + +1. Define a module-level constant in the events module: + ```rust + /// Canonical event version. Bump this when the schema of any event in this + /// module changes so consumers can detect the difference. + pub const EVENT_VERSION: u32 = 1; + ``` + +2. Add `version` as the first field in every event struct: + ```rust + #[contractevent] + pub struct SomeEvent { + /// Schema version for consumer-side migration detection. + pub version: u32, + #[topic] + pub user: Address, + pub amount: i128, + } + ``` + +3. Pass `EVENT_VERSION` when publishing: + ```rust + events::SomeEvent { + version: events::EVENT_VERSION, + user, + amount, + }.publish(&env); + ``` + +4. When changing an event schema (adding, removing, or reordering fields), + **increment** `EVENT_VERSION` so consumers can distinguish the format. + ### Import Order ```rust // 1. External crates diff --git a/apps/onchain/contracts/feature_flags/src/events.rs b/apps/onchain/contracts/feature_flags/src/events.rs index 41873417c..5d69fa291 100644 --- a/apps/onchain/contracts/feature_flags/src/events.rs +++ b/apps/onchain/contracts/feature_flags/src/events.rs @@ -1,8 +1,30 @@ -use soroban_sdk::{contractevent, Address, Symbol}; +use soroban_sdk::{contractevent, Address, Env, Symbol}; + +pub const EVENT_VERSION_INITIALIZED: u32 = 1u32; +pub const EVENT_VERSION_FLAG_SET: u32 = 1u32; +pub const EVENT_VERSION_ADMIN_TRANSFERRED: u32 = 1u32; + +pub mod schema_ids { + use super::*; + + pub fn initialized_v1(env: &Env) -> Symbol { + Symbol::new(env, "sys.initialized.v1") + } + + pub fn flag_set_v1(env: &Env) -> Symbol { + Symbol::new(env, "admin.flag_set.v1") + } + + pub fn admin_transferred_v1(env: &Env) -> Symbol { + Symbol::new(env, "admin.transferred.v1") + } +} #[contractevent] pub struct InitializedEvent { pub admin: Address, + pub version: u32, + pub schema_id: Symbol, } #[contractevent] @@ -11,10 +33,53 @@ pub struct FlagSetEvent { pub key: Symbol, pub enabled: bool, pub toggled_by: Address, + pub version: u32, + pub schema_id: Symbol, } #[contractevent] pub struct AdminTransferredEvent { pub old_admin: Address, pub new_admin: Address, + pub version: u32, + pub schema_id: Symbol, +} + +pub fn publish_initialized(env: &Env, admin: Address) { + InitializedEvent { + admin, + version: EVENT_VERSION_INITIALIZED, + schema_id: schema_ids::initialized_v1(env), + } + .publish(env); +} + +pub fn publish_flag_set( + env: &Env, + key: Symbol, + enabled: bool, + toggled_by: Address, +) { + FlagSetEvent { + key, + enabled, + toggled_by, + version: EVENT_VERSION_FLAG_SET, + schema_id: schema_ids::flag_set_v1(env), + } + .publish(env); +} + +pub fn publish_admin_transferred( + env: &Env, + old_admin: Address, + new_admin: Address, +) { + AdminTransferredEvent { + old_admin, + new_admin, + version: EVENT_VERSION_ADMIN_TRANSFERRED, + schema_id: schema_ids::admin_transferred_v1(env), + } + .publish(env); } diff --git a/apps/onchain/contracts/feature_flags/src/lib.rs b/apps/onchain/contracts/feature_flags/src/lib.rs index 7eadbba83..5922acb99 100644 --- a/apps/onchain/contracts/feature_flags/src/lib.rs +++ b/apps/onchain/contracts/feature_flags/src/lib.rs @@ -47,7 +47,7 @@ impl FeatureFlagsContract { env.storage().instance().set(&DataKey::Admin, &admin); env.storage().instance().set(&DataKey::Paused, &false); - events::InitializedEvent { admin }.publish(&env); + events::publish_initialized(&env, admin); Ok(()) } @@ -83,12 +83,7 @@ impl FeatureFlagsContract { env.storage().instance().set(&DataKey::FlagList, &list); } - events::FlagSetEvent { - key, - enabled, - toggled_by: caller, - } - .publish(&env); + events::publish_flag_set(&env, key, enabled, caller); Ok(()) } @@ -141,11 +136,7 @@ impl FeatureFlagsContract { env.storage().instance().set(&DataKey::Admin, &new_admin); - events::AdminTransferredEvent { - old_admin: current_admin, - new_admin, - } - .publish(&env); + events::publish_admin_transferred(&env, current_admin, new_admin); Ok(()) } diff --git a/apps/onchain/contracts/lumen_token/src/events.rs b/apps/onchain/contracts/lumen_token/src/events.rs index aa8944220..b46492d34 100644 --- a/apps/onchain/contracts/lumen_token/src/events.rs +++ b/apps/onchain/contracts/lumen_token/src/events.rs @@ -1,4 +1,24 @@ -use soroban_sdk::{contractevent, Address, BytesN}; +use soroban_sdk::{contractevent, Address, BytesN, Env, Symbol}; + +pub const EVENT_VERSION_UPGRADED: u32 = 1u32; +pub const EVENT_VERSION_ADMIN_CHANGED: u32 = 1u32; +pub const EVENT_VERSION_BURN: u32 = 1u32; + +pub mod schema_ids { + use super::*; + + pub fn upgraded_v1(env: &Env) -> Symbol { + Symbol::new(env, "admin.upgraded.v1") + } + + pub fn admin_changed_v1(env: &Env) -> Symbol { + Symbol::new(env, "admin.changed.v1") + } + + pub fn burn_v1(env: &Env) -> Symbol { + Symbol::new(env, "token.burned.v1") + } +} /// Emitted when the contract WASM is upgraded to a new hash. #[contractevent] @@ -6,6 +26,8 @@ pub struct UpgradedEvent { #[topic] pub admin: Address, pub new_wasm_hash: BytesN<32>, + pub version: u32, + pub schema_id: Symbol, } /// Emitted when the admin role is transferred to a new address. @@ -14,6 +36,8 @@ pub struct AdminChangedEvent { #[topic] pub old_admin: Address, pub new_admin: Address, + pub version: u32, + pub schema_id: Symbol, } #[contractevent] @@ -21,4 +45,36 @@ pub struct BurnEvent { #[topic] pub from: Address, pub amount: i128, + pub version: u32, + pub schema_id: Symbol, +} + +pub fn publish_upgraded(env: &Env, admin: Address, new_wasm_hash: BytesN<32>) { + UpgradedEvent { + admin, + new_wasm_hash, + version: EVENT_VERSION_UPGRADED, + schema_id: schema_ids::upgraded_v1(env), + } + .publish(env); +} + +pub fn publish_admin_changed(env: &Env, old_admin: Address, new_admin: Address) { + AdminChangedEvent { + old_admin, + new_admin, + version: EVENT_VERSION_ADMIN_CHANGED, + schema_id: schema_ids::admin_changed_v1(env), + } + .publish(env); +} + +pub fn publish_burn(env: &Env, from: Address, amount: i128) { + BurnEvent { + from, + amount, + version: EVENT_VERSION_BURN, + schema_id: schema_ids::burn_v1(env), + } + .publish(env); } diff --git a/apps/onchain/contracts/lumen_token/src/lib.rs b/apps/onchain/contracts/lumen_token/src/lib.rs index 6def1fbf9..8f338248d 100644 --- a/apps/onchain/contracts/lumen_token/src/lib.rs +++ b/apps/onchain/contracts/lumen_token/src/lib.rs @@ -8,7 +8,7 @@ mod metadata; mod storage; mod test; -use events::{AdminChangedEvent, BurnEvent, UpgradedEvent}; +use events; use soroban_sdk::{contract, contractimpl, Address, BytesN, Env, String}; #[contract] @@ -35,11 +35,7 @@ impl LumenToken { let old_admin = admin::read_administrator(&e); old_admin.require_auth(); admin::write_administrator(&e, &new_admin); - AdminChangedEvent { - old_admin, - new_admin, - } - .publish(&e); + events::publish_admin_changed(&e, old_admin, new_admin); } pub fn freeze(e: Env, id: Address) { @@ -87,15 +83,16 @@ impl LumenToken { from.require_auth(); balance::check_not_frozen(&e, &from); balance::spend_balance(&e, from.clone(), amount); - BurnEvent { from, amount }.publish(&e); + events::publish_burn(&e, from, amount); } pub fn burn_from(e: Env, spender: Address, from: Address, amount: i128) { spender.require_auth(); balance::check_not_frozen(&e, &spender); + allowance::spend_allowance(&e, from.clone(), spender, amount); balance::spend_balance(&e, from.clone(), amount); - BurnEvent { from, amount }.publish(&e); + events::publish_burn(&e, from, amount); } pub fn decimals(e: Env) -> u32 { @@ -121,10 +118,6 @@ impl LumenToken { caller.require_auth(); e.deployer() .update_current_contract_wasm(new_wasm_hash.clone()); - UpgradedEvent { - admin: caller, - new_wasm_hash, - } - .publish(&e); + events::publish_upgraded(&e, caller, new_wasm_hash); } } diff --git a/apps/onchain/contracts/matching_pool/src/events.rs b/apps/onchain/contracts/matching_pool/src/events.rs index 09beacf35..fb1a00752 100644 --- a/apps/onchain/contracts/matching_pool/src/events.rs +++ b/apps/onchain/contracts/matching_pool/src/events.rs @@ -1,8 +1,65 @@ -use soroban_sdk::{contractevent, Address, Symbol}; +use soroban_sdk::{contractevent, Address, Env, Symbol}; + +pub const EVENT_VERSION_INITIALIZED: u32 = 1u32; +pub const EVENT_VERSION_ROUND_CREATED: u32 = 1u32; +pub const EVENT_VERSION_POOL_FUNDED: u32 = 1u32; +pub const EVENT_VERSION_PROJECT_APPROVED: u32 = 1u32; +pub const EVENT_VERSION_PROJECT_REMOVED: u32 = 1u32; +pub const EVENT_VERSION_CONTRIBUTION_RECORDED: u32 = 1u32; +pub const EVENT_VERSION_ROUND_FINALIZED: u32 = 1u32; +pub const EVENT_VERSION_MATCH_DISTRIBUTED: u32 = 1u32; +pub const EVENT_VERSION_ALL_MATCHES_DISTRIBUTED: u32 = 1u32; +pub const EVENT_VERSION_ROUND_CAP_UPDATED: u32 = 1u32; + +pub mod schema_ids { + use super::*; + + pub fn initialized_v1(env: &Env) -> Symbol { + Symbol::new(env, "sys.initialized.v1") + } + + pub fn round_created_v1(env: &Env) -> Symbol { + Symbol::new(env, "pool.round_created.v1") + } + + pub fn pool_funded_v1(env: &Env) -> Symbol { + Symbol::new(env, "pool.funded.v1") + } + + pub fn project_approved_v1(env: &Env) -> Symbol { + Symbol::new(env, "pool.project_approved.v1") + } + + pub fn project_removed_v1(env: &Env) -> Symbol { + Symbol::new(env, "pool.project_removed.v1") + } + + pub fn contribution_recorded_v1(env: &Env) -> Symbol { + Symbol::new(env, "pool.contribution_recorded.v1") + } + + pub fn round_finalized_v1(env: &Env) -> Symbol { + Symbol::new(env, "pool.round_finalized.v1") + } + + pub fn match_distributed_v1(env: &Env) -> Symbol { + Symbol::new(env, "pool.match_distributed.v1") + } + + pub fn all_matches_distributed_v1(env: &Env) -> Symbol { + Symbol::new(env, "pool.all_matches_distributed.v1") + } + + pub fn round_cap_updated_v1(env: &Env) -> Symbol { + Symbol::new(env, "pool.cap_updated.v1") + } +} #[contractevent] pub struct InitializedEvent { pub admin: Address, + pub version: u32, + pub schema_id: Symbol, } #[contractevent] @@ -13,6 +70,8 @@ pub struct RoundCreatedEvent { pub name: Symbol, pub start_time: u64, pub end_time: u64, + pub version: u32, + pub schema_id: Symbol, } #[contractevent] @@ -22,6 +81,8 @@ pub struct PoolFundedEvent { #[topic] pub round_id: u64, pub amount: i128, + pub version: u32, + pub schema_id: Symbol, } #[contractevent] @@ -29,6 +90,8 @@ pub struct ProjectApprovedEvent { #[topic] pub round_id: u64, pub project_id: u64, + pub version: u32, + pub schema_id: Symbol, } #[contractevent] @@ -36,6 +99,8 @@ pub struct ProjectRemovedEvent { #[topic] pub round_id: u64, pub project_id: u64, + pub version: u32, + pub schema_id: Symbol, } #[contractevent] @@ -46,6 +111,8 @@ pub struct ContributionRecordedEvent { pub project_id: u64, pub contributor: Address, pub amount: i128, + pub version: u32, + pub schema_id: Symbol, } #[contractevent] @@ -54,6 +121,8 @@ pub struct RoundFinalizedEvent { pub round_id: u64, pub admin: Address, pub finalized_at: u64, + pub version: u32, + pub schema_id: Symbol, } #[contractevent] @@ -62,6 +131,8 @@ pub struct MatchDistributedEvent { pub round_id: u64, pub project_id: u64, pub match_amount: i128, + pub version: u32, + pub schema_id: Symbol, } #[contractevent] @@ -69,6 +140,8 @@ pub struct AllMatchesDistributedEvent { #[topic] pub round_id: u64, pub total_distributed: i128, + pub version: u32, + pub schema_id: Symbol, } #[contractevent] @@ -78,4 +151,151 @@ pub struct RoundCapUpdatedEvent { #[topic] pub round_id: u64, pub cap: i128, + pub version: u32, + pub schema_id: Symbol, +} + +pub fn publish_initialized(env: &Env, admin: Address) { + InitializedEvent { + admin, + version: EVENT_VERSION_INITIALIZED, + schema_id: schema_ids::initialized_v1(env), + } + .publish(env); +} + +pub fn publish_round_created( + env: &Env, + admin: Address, + round_id: u64, + name: Symbol, + start_time: u64, + end_time: u64, +) { + RoundCreatedEvent { + admin, + round_id, + name, + start_time, + end_time, + version: EVENT_VERSION_ROUND_CREATED, + schema_id: schema_ids::round_created_v1(env), + } + .publish(env); +} + +pub fn publish_pool_funded( + env: &Env, + funder: Address, + round_id: u64, + amount: i128, +) { + PoolFundedEvent { + funder, + round_id, + amount, + version: EVENT_VERSION_POOL_FUNDED, + schema_id: schema_ids::pool_funded_v1(env), + } + .publish(env); +} + +pub fn publish_project_approved(env: &Env, round_id: u64, project_id: u64) { + ProjectApprovedEvent { + round_id, + project_id, + version: EVENT_VERSION_PROJECT_APPROVED, + schema_id: schema_ids::project_approved_v1(env), + } + .publish(env); +} + +pub fn publish_project_removed(env: &Env, round_id: u64, project_id: u64) { + ProjectRemovedEvent { + round_id, + project_id, + version: EVENT_VERSION_PROJECT_REMOVED, + schema_id: schema_ids::project_removed_v1(env), + } + .publish(env); +} + +pub fn publish_contribution_recorded( + env: &Env, + round_id: u64, + project_id: u64, + contributor: Address, + amount: i128, +) { + ContributionRecordedEvent { + round_id, + project_id, + contributor, + amount, + version: EVENT_VERSION_CONTRIBUTION_RECORDED, + schema_id: schema_ids::contribution_recorded_v1(env), + } + .publish(env); +} + +pub fn publish_round_finalized( + env: &Env, + round_id: u64, + admin: Address, + finalized_at: u64, +) { + RoundFinalizedEvent { + round_id, + admin, + finalized_at, + version: EVENT_VERSION_ROUND_FINALIZED, + schema_id: schema_ids::round_finalized_v1(env), + } + .publish(env); +} + +pub fn publish_match_distributed( + env: &Env, + round_id: u64, + project_id: u64, + match_amount: i128, +) { + MatchDistributedEvent { + round_id, + project_id, + match_amount, + version: EVENT_VERSION_MATCH_DISTRIBUTED, + schema_id: schema_ids::match_distributed_v1(env), + } + .publish(env); +} + +pub fn publish_all_matches_distributed( + env: &Env, + round_id: u64, + total_distributed: i128, +) { + AllMatchesDistributedEvent { + round_id, + total_distributed, + version: EVENT_VERSION_ALL_MATCHES_DISTRIBUTED, + schema_id: schema_ids::all_matches_distributed_v1(env), + } + .publish(env); +} + +pub fn publish_round_cap_updated( + env: &Env, + admin: Address, + round_id: u64, + cap: i128, +) { + RoundCapUpdatedEvent { + admin, + round_id, + cap, + version: EVENT_VERSION_ROUND_CAP_UPDATED, + schema_id: schema_ids::round_cap_updated_v1(env), + } + .publish(env); } diff --git a/apps/onchain/contracts/matching_pool/src/lib.rs b/apps/onchain/contracts/matching_pool/src/lib.rs index 3a865d9b6..536578317 100644 --- a/apps/onchain/contracts/matching_pool/src/lib.rs +++ b/apps/onchain/contracts/matching_pool/src/lib.rs @@ -61,7 +61,7 @@ impl MatchingPoolContract { env.storage().instance().set(&DataKey::Admin, &admin); env.storage().instance().set(&DataKey::Paused, &false); env.storage().instance().set(&DataKey::NextRoundId, &0u64); - events::InitializedEvent { admin }.publish(&env); + events::publish_initialized(&env, admin); Ok(()) } @@ -112,14 +112,7 @@ impl MatchingPoolContract { env.storage() .instance() .set(&DataKey::NextRoundId, &(round_id + 1)); - events::RoundCreatedEvent { - admin, - round_id, - name, - start_time, - end_time, - } - .publish(&env); + events::publish_round_created(&env, admin, round_id, name, start_time, end_time); Ok(round_id) } @@ -156,12 +149,7 @@ impl MatchingPoolContract { let contract_addr = env.current_contract_address(); TokenClient::new(&env, &round.token_address).transfer(&funder, &contract_addr, &amount); - events::PoolFundedEvent { - funder, - round_id, - amount, - } - .publish(&env); + events::publish_pool_funded(&env, funder, round_id, amount); Ok(()) }) } @@ -204,11 +192,7 @@ impl MatchingPoolContract { &DataKey::ProjectContributorCount(round_id, project_id), &0u32, ); - events::ProjectApprovedEvent { - round_id, - project_id, - } - .publish(&env); + events::publish_project_approved(&env, round_id, project_id); Ok(()) } @@ -237,11 +221,7 @@ impl MatchingPoolContract { return Err(MatchingPoolError::ProjectNotEligible); } env.storage().persistent().set(&eligible_key, &false); - events::ProjectRemovedEvent { - round_id, - project_id, - } - .publish(&env); + events::publish_project_removed(&env, round_id, project_id); Ok(()) } @@ -271,12 +251,7 @@ impl MatchingPoolContract { env.storage() .persistent() .set(&DataKey::RoundCap(round_id), &cap); - events::RoundCapUpdatedEvent { - admin, - round_id, - cap, - } - .publish(&env); + events::publish_round_cap_updated(&env, admin, round_id, cap); Ok(()) } @@ -350,13 +325,7 @@ impl MatchingPoolContract { env.storage() .persistent() .set(&round_total_key, &new_round_total); - events::ContributionRecordedEvent { - round_id, - project_id, - contributor, - amount, - } - .publish(&env); + events::publish_contribution_recorded(&env, round_id, project_id, contributor, amount); Ok(()) } diff --git a/apps/onchain/contracts/vesting-wallet/src/events.rs b/apps/onchain/contracts/vesting-wallet/src/events.rs index 28b3687c6..6c5967bda 100644 --- a/apps/onchain/contracts/vesting-wallet/src/events.rs +++ b/apps/onchain/contracts/vesting-wallet/src/events.rs @@ -1,8 +1,14 @@ use soroban_sdk::{contractevent, Address, BytesN}; +/// Canonical event version. Bump this when the schema of any event in this +/// module changes so consumers can detect the difference. +pub const EVENT_VERSION: u32 = 1; + #[contractevent] #[derive(Clone, Debug, Eq, PartialEq)] pub struct VestingCreatedEvent { + /// Schema version for consumer-side migration detection. + pub version: u32, #[topic] pub beneficiary: Address, pub amount: i128, @@ -13,6 +19,8 @@ pub struct VestingCreatedEvent { #[contractevent] #[derive(Clone, Debug, Eq, PartialEq)] pub struct TokensClaimedEvent { + /// Schema version for consumer-side migration detection. + pub version: u32, #[topic] pub beneficiary: Address, pub amount_claimed: i128, @@ -22,6 +30,8 @@ pub struct TokensClaimedEvent { #[contractevent] #[derive(Clone, Debug, Eq, PartialEq)] pub struct UpgradedEvent { + /// Schema version for consumer-side migration detection. + pub version: u32, #[topic] pub admin: Address, pub new_wasm_hash: BytesN<32>, @@ -31,6 +41,8 @@ pub struct UpgradedEvent { #[contractevent] #[derive(Clone, Debug, Eq, PartialEq)] pub struct AdminChangedEvent { + /// Schema version for consumer-side migration detection. + pub version: u32, #[topic] pub old_admin: Address, pub new_admin: Address, @@ -40,6 +52,8 @@ pub struct AdminChangedEvent { #[contractevent] #[derive(Clone, Debug, Eq, PartialEq)] pub struct DelegateApprovedEvent { + /// Schema version for consumer-side migration detection. + pub version: u32, #[topic] pub beneficiary: Address, pub delegate: Address, @@ -49,6 +63,8 @@ pub struct DelegateApprovedEvent { #[contractevent] #[derive(Clone, Debug, Eq, PartialEq)] pub struct DelegateRevokedEvent { + /// Schema version for consumer-side migration detection. + pub version: u32, #[topic] pub beneficiary: Address, pub delegate: Address, @@ -58,6 +74,8 @@ pub struct DelegateRevokedEvent { #[contractevent] #[derive(Clone, Debug, Eq, PartialEq)] pub struct DelegatedClaimEvent { + /// Schema version for consumer-side migration detection. + pub version: u32, #[topic] pub beneficiary: Address, pub delegate: Address, diff --git a/apps/onchain/contracts/vesting-wallet/src/lib.rs b/apps/onchain/contracts/vesting-wallet/src/lib.rs index 984d53a3b..75e2913d8 100644 --- a/apps/onchain/contracts/vesting-wallet/src/lib.rs +++ b/apps/onchain/contracts/vesting-wallet/src/lib.rs @@ -233,8 +233,8 @@ impl VestingWalletContract { transfer(&env, &token, &admin, &contract_address, &amount); - // Emit VestingCreated event events::VestingCreatedEvent { + version: events::EVENT_VERSION, beneficiary: vesting.beneficiary.clone(), amount: vesting.total_amount, start_time: vesting.start_time, @@ -304,6 +304,7 @@ impl VestingWalletContract { ); events::TokensClaimedEvent { + version: events::EVENT_VERSION, beneficiary: vesting.beneficiary.clone(), amount_claimed: available_amount, remaining, @@ -418,6 +419,7 @@ impl VestingWalletContract { env.deployer() .update_current_contract_wasm(new_wasm_hash.clone()); UpgradedEvent { + version: events::EVENT_VERSION, admin: caller, new_wasm_hash, } @@ -447,6 +449,7 @@ impl VestingWalletContract { .instance() .extend_ttl(LEDGER_THRESHOLD, LEDGER_BUMP); AdminChangedEvent { + version: events::EVENT_VERSION, old_admin: current_admin, new_admin, } @@ -486,6 +489,7 @@ impl VestingWalletContract { } events::DelegateApprovedEvent { + version: events::EVENT_VERSION, beneficiary, delegate, } @@ -527,6 +531,7 @@ impl VestingWalletContract { } events::DelegateRevokedEvent { + version: events::EVENT_VERSION, beneficiary, delegate, } @@ -617,6 +622,7 @@ impl VestingWalletContract { ); events::DelegatedClaimEvent { + version: events::EVENT_VERSION, beneficiary: vesting.beneficiary.clone(), delegate, amount_claimed: available_amount, diff --git a/apps/onchain/contracts/vesting-wallet/test_snapshots/test/test_events_api_direct_publish.1.json b/apps/onchain/contracts/vesting-wallet/test_snapshots/test/test_events_api_direct_publish.1.json index 906a0334b..b8d1b8afb 100644 --- a/apps/onchain/contracts/vesting-wallet/test_snapshots/test/test_events_api_direct_publish.1.json +++ b/apps/onchain/contracts/vesting-wallet/test_snapshots/test/test_events_api_direct_publish.1.json @@ -34,6 +34,14 @@ ], "data": { "map": [ + { + "key": { + "symbol": "version" + }, + "val": { + "u32": 1 + } + }, { "key": { "symbol": "amount" diff --git a/apps/onchain/contracts/vesting-wallet/test_snapshots/test/test_multiple_beneficiaries.1.json b/apps/onchain/contracts/vesting-wallet/test_snapshots/test/test_multiple_beneficiaries.1.json index 9539c5a64..eded9c25b 100644 --- a/apps/onchain/contracts/vesting-wallet/test_snapshots/test/test_multiple_beneficiaries.1.json +++ b/apps/onchain/contracts/vesting-wallet/test_snapshots/test/test_multiple_beneficiaries.1.json @@ -1206,6 +1206,14 @@ ], "data": { "map": [ + { + "key": { + "symbol": "version" + }, + "val": { + "u32": 1 + } + }, { "key": { "symbol": "amount_claimed" diff --git a/apps/onchain/contracts/yield_vault/src/events.rs b/apps/onchain/contracts/yield_vault/src/events.rs index 31aab04e6..9257d186e 100644 --- a/apps/onchain/contracts/yield_vault/src/events.rs +++ b/apps/onchain/contracts/yield_vault/src/events.rs @@ -1,9 +1,15 @@ use soroban_sdk::{contractevent, Address, Symbol}; +/// Canonical event version. Bump this when the schema of any event in this +/// module changes so consumers can detect the difference. +pub const EVENT_VERSION: u32 = 1; + /// Emitted when the vault is initialized. #[contractevent] #[derive(Clone, Debug, Eq, PartialEq)] pub struct VaultInitializedEvent { + /// Schema version for consumer-side migration detection. + pub version: u32, /// The address granted admin privileges. #[topic] pub admin: Address, @@ -15,6 +21,8 @@ pub struct VaultInitializedEvent { #[contractevent] #[derive(Clone, Debug, Eq, PartialEq)] pub struct ProviderRegisteredEvent { + /// Schema version for consumer-side migration detection. + pub version: u32, /// The address of the provider (contract). #[topic] pub address: Address, @@ -31,6 +39,8 @@ pub struct ProviderRegisteredEvent { #[contractevent] #[derive(Clone, Debug, Eq, PartialEq)] pub struct DepositEvent { + /// Schema version for consumer-side migration detection. + pub version: u32, /// The address of the user making the deposit. #[topic] pub user: Address, @@ -45,6 +55,8 @@ pub struct DepositEvent { #[contractevent] #[derive(Clone, Debug, Eq, PartialEq)] pub struct WithdrawEvent { + /// Schema version for consumer-side migration detection. + pub version: u32, /// The address of the user making the withdrawal. #[topic] pub user: Address, @@ -56,6 +68,8 @@ pub struct WithdrawEvent { #[contractevent] #[derive(Clone, Debug, Eq, PartialEq)] pub struct YieldHarvestedEvent { + /// Schema version for consumer-side migration detection. + pub version: u32, /// The unique identifier of the provider from which yield was harvested. #[topic] pub provider_id: u32, diff --git a/apps/onchain/contracts/yield_vault/src/lib.rs b/apps/onchain/contracts/yield_vault/src/lib.rs index 6bd21ebfe..fafbc4983 100644 --- a/apps/onchain/contracts/yield_vault/src/lib.rs +++ b/apps/onchain/contracts/yield_vault/src/lib.rs @@ -31,7 +31,12 @@ impl YieldVaultContract { env.storage().instance().set(&DataKey::ProviderCount, &0u32); env.storage().instance().extend_ttl(100, 100); - events::VaultInitializedEvent { admin, asset }.publish(&env); + events::VaultInitializedEvent { + version: events::EVENT_VERSION, + admin, + asset, + } + .publish(&env); Ok(()) } @@ -79,6 +84,7 @@ impl YieldVaultContract { .set(&DataKey::ProviderCount, &new_count); events::ProviderRegisteredEvent { + version: events::EVENT_VERSION, provider_id, name, address, @@ -167,6 +173,7 @@ impl YieldVaultContract { .set(&DataKey::TotalAUM, &(total_aum + amount)); events::DepositEvent { + version: events::EVENT_VERSION, user: user.clone(), amount, provider_id: best_provider, @@ -284,6 +291,7 @@ impl YieldVaultContract { .set(&DataKey::TotalAUM, &(total_aum - withdrawn)); events::WithdrawEvent { + version: events::EVENT_VERSION, user: user.clone(), amount: withdrawn, } @@ -330,6 +338,7 @@ impl YieldVaultContract { } events::YieldHarvestedEvent { + version: events::EVENT_VERSION, provider_id, yield_earned, }