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
10 changes: 10 additions & 0 deletions contract/contracts/hello-world/src/autoshare_logic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ pub fn create_autoshare(
id: id.clone(),
name,
creator: creator.clone(),
priority: NotificationPriority::Standard,
usage_count,
total_usages_paid: usage_count,
members: Vec::new(&env),
Expand Down Expand Up @@ -111,6 +112,7 @@ pub fn create_autoshare(

AutoshareCreated {
creator: creator.clone(),
priority: details.priority,
category: NotificationCategory::Group,
priority: NotificationPriority::Medium,
id: id.clone(),
Expand Down Expand Up @@ -262,6 +264,7 @@ pub fn initialize_admin(env: Env, admin: Address) {
fn publish_authorization_failure(env: &Env, caller: &Address, action: &str) {
AuthorizationFailure {
caller: caller.clone(),
priority: NotificationPriority::High,
category: NotificationCategory::Admin,
priority: NotificationPriority::Critical,
action: String::from_str(env, action),
Expand Down Expand Up @@ -299,6 +302,7 @@ pub fn transfer_admin(env: Env, current_admin: Address, new_admin: Address) -> R
env.storage().persistent().set(&DataKey::Admin, &new_admin);
AdminTransferred {
old_admin: current_admin,
priority: NotificationPriority::High,
category: NotificationCategory::Admin,
priority: NotificationPriority::Critical,
new_admin,
Expand All @@ -324,6 +328,7 @@ pub fn pause(env: Env, admin: Address) -> Result<(), Error> {

env.storage().persistent().set(&pause_key, &true);
ContractPaused {
priority: NotificationPriority::High,
category: NotificationCategory::Admin,
priority: NotificationPriority::High,
}
Expand All @@ -344,6 +349,7 @@ pub fn unpause(env: Env, admin: Address) -> Result<(), Error> {

env.storage().persistent().set(&pause_key, &false);
ContractUnpaused {
priority: NotificationPriority::High,
category: NotificationCategory::Admin,
priority: NotificationPriority::High,
}
Expand Down Expand Up @@ -682,6 +688,7 @@ pub fn update_members(

AutoshareUpdated {
updater: caller,
priority: details.priority,
category: NotificationCategory::Group,
priority: NotificationPriority::Medium,
id: id.clone(),
Expand Down Expand Up @@ -719,6 +726,7 @@ pub fn deactivate_group(env: Env, id: BytesN<32>, caller: Address) -> Result<(),

GroupDeactivated {
creator: caller,
priority: details.priority,
category: NotificationCategory::Group,
priority: NotificationPriority::Low,
id: id.clone(),
Expand Down Expand Up @@ -756,6 +764,7 @@ pub fn activate_group(env: Env, id: BytesN<32>, caller: Address) -> Result<(), E

GroupActivated {
creator: caller,
priority: details.priority,
category: NotificationCategory::Group,
priority: NotificationPriority::Low,
id: id.clone(),
Expand Down Expand Up @@ -805,6 +814,7 @@ pub fn withdraw(
Withdrawal {
token,
recipient,
priority: NotificationPriority::Critical,
category: NotificationCategory::Financial,
priority: NotificationPriority::High,
amount,
Expand Down
28 changes: 28 additions & 0 deletions contract/contracts/hello-world/src/base/events.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
use soroban_sdk::{contractevent, contracttype, Address, BytesN, String};

/// Priority metadata attached to notifications emitted by the contract.
#[contracttype]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum NotificationPriority {
Low = 0,
Standard = 1,
High = 2,
Critical = 3,
}

/// High-level notification category attached to every emitted event.
///
/// Off-chain consumers (listeners, indexers, dashboards) often only care about a
Expand Down Expand Up @@ -62,6 +72,8 @@ pub struct AutoshareCreated {
#[topic]
pub creator: Address,
#[topic]
pub priority: NotificationPriority,
#[topic]
pub category: NotificationCategory,
#[topic]
pub priority: NotificationPriority,
Expand All @@ -72,6 +84,8 @@ pub struct AutoshareCreated {
#[contractevent]
#[derive(Clone)]
pub struct ContractPaused {
#[topic]
pub priority: NotificationPriority,
#[topic]
pub category: NotificationCategory,
#[topic]
Expand All @@ -82,6 +96,8 @@ pub struct ContractPaused {
#[contractevent]
#[derive(Clone)]
pub struct ContractUnpaused {
#[topic]
pub priority: NotificationPriority,
#[topic]
pub category: NotificationCategory,
#[topic]
Expand All @@ -95,6 +111,8 @@ pub struct AutoshareUpdated {
#[topic]
pub updater: Address,
#[topic]
pub priority: NotificationPriority,
#[topic]
pub category: NotificationCategory,
#[topic]
pub priority: NotificationPriority,
Expand All @@ -108,6 +126,8 @@ pub struct GroupDeactivated {
#[topic]
pub creator: Address,
#[topic]
pub priority: NotificationPriority,
#[topic]
pub category: NotificationCategory,
#[topic]
pub priority: NotificationPriority,
Expand All @@ -121,6 +141,8 @@ pub struct GroupActivated {
#[topic]
pub creator: Address,
#[topic]
pub priority: NotificationPriority,
#[topic]
pub category: NotificationCategory,
#[topic]
pub priority: NotificationPriority,
Expand All @@ -134,6 +156,8 @@ pub struct AdminTransferred {
#[topic]
pub old_admin: Address,
#[topic]
pub priority: NotificationPriority,
#[topic]
pub category: NotificationCategory,
#[topic]
pub priority: NotificationPriority,
Expand All @@ -149,6 +173,8 @@ pub struct Withdrawal {
#[topic]
pub recipient: Address,
#[topic]
pub priority: NotificationPriority,
#[topic]
pub category: NotificationCategory,
#[topic]
pub priority: NotificationPriority,
Expand All @@ -162,6 +188,8 @@ pub struct AuthorizationFailure {
#[topic]
pub caller: Address,
#[topic]
pub priority: NotificationPriority,
#[topic]
pub category: NotificationCategory,
#[topic]
pub priority: NotificationPriority,
Expand Down
2 changes: 2 additions & 0 deletions contract/contracts/hello-world/src/base/types.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::base::events::NotificationPriority;
use soroban_sdk::{contracttype, Address, BytesN, String, Vec};

#[contracttype]
Expand All @@ -6,6 +7,7 @@ pub struct AutoShareDetails {
pub id: BytesN<32>,
pub name: String,
pub creator: Address,
pub priority: NotificationPriority,
pub usage_count: u32,
pub total_usages_paid: u32,
pub members: Vec<GroupMember>,
Expand Down
70 changes: 69 additions & 1 deletion contract/contracts/hello-world/src/tests/notification_test.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
//! Tests for notification category metadata attached to emitted events.
//!
//! Every event the contract publishes carries notification metadata so off-chain
//! consumers can route by category and urgency. These tests verify:
//! - each action emits the expected category and priority, and
//! - the change is backward compatible: the event name remains the first topic,
//! the category remains the trailing topic, and payload data is unchanged.
//! Every event the contract publishes now carries a [`NotificationCategory`] as
//! its trailing topic so off-chain consumers can subscribe to / filter by whole
//! categories. These tests verify:
Expand Down Expand Up @@ -58,6 +63,16 @@ fn priority_of(env: &soroban_sdk::Env, event_name: &str) -> Option<NotificationP
NotificationPriority::try_from_val(env, &last).ok()
}

/// Extracts priority metadata for the latest event named `event_name`.
fn priority_of(env: &soroban_sdk::Env, event_name: &str) -> Option<NotificationPriority> {
let topics = topics_of(env, event_name)?;
if topics.len() < 2 {
return None;
}
let priority = topics.get(topics.len() - 2)?;
NotificationPriority::try_from_val(env, &priority).ok()
}

/// Returns the category of the most recently emitted event — i.e. the metadata a
/// streaming consumer would read off the event as it arrives.
///
Expand All @@ -73,6 +88,15 @@ fn latest_category(env: &soroban_sdk::Env) -> Option<NotificationCategory> {
NotificationCategory::try_from_val(env, &category_topic).ok()
}

fn latest_priority(env: &soroban_sdk::Env) -> Option<NotificationPriority> {
let (_addr, topics, _data) = env.events().all().last()?;
if topics.len() < 2 {
return None;
}
let priority = topics.get(topics.len() - 2)?;
NotificationPriority::try_from_val(env, &priority).ok()
}

#[test]
fn test_created_event_has_group_category() {
let test_env = setup_test_env();
Expand All @@ -94,6 +118,28 @@ fn test_created_event_has_group_category() {
);
assert_eq!(
priority_of(&test_env.env, "autoshare_created"),
Some(NotificationPriority::Standard)
);
}

#[test]
fn test_created_group_stores_standard_priority() {
let test_env = setup_test_env();
let client = AutoShareContractClient::new(&test_env.env, &test_env.autoshare_contract);
let creator = test_env.users.get(0).unwrap().clone();
let token = test_env.mock_tokens.get(0).unwrap().clone();

let id = create_test_group(
&test_env.env,
&test_env.autoshare_contract,
&creator,
&Vec::new(&test_env.env),
1,
&token,
);

let details = client.get(&id);
assert_eq!(details.priority, NotificationPriority::Standard);
Some(NotificationPriority::Medium)
);
}
Expand Down Expand Up @@ -127,6 +173,7 @@ fn test_updated_event_has_group_category() {
);
assert_eq!(
priority_of(&test_env.env, "autoshare_updated"),
Some(NotificationPriority::Standard)
Some(NotificationPriority::Medium)
);
}
Expand Down Expand Up @@ -154,6 +201,7 @@ fn test_deactivate_and_activate_events_have_group_category() {
);
assert_eq!(
priority_of(&test_env.env, "group_deactivated"),
Some(NotificationPriority::Standard)
Some(NotificationPriority::Low)
);

Expand All @@ -164,6 +212,7 @@ fn test_deactivate_and_activate_events_have_group_category() {
);
assert_eq!(
priority_of(&test_env.env, "group_activated"),
Some(NotificationPriority::Standard)
Some(NotificationPriority::Low)
);
}
Expand Down Expand Up @@ -207,6 +256,7 @@ fn test_admin_transfer_event_has_admin_category() {
);
assert_eq!(
priority_of(&test_env.env, "admin_transferred"),
Some(NotificationPriority::High)
Some(NotificationPriority::Critical)
);
}
Expand Down Expand Up @@ -236,6 +286,7 @@ fn test_withdrawal_event_has_financial_category() {
);
assert_eq!(
priority_of(&test_env.env, "withdrawal"),
Some(NotificationPriority::Critical)
Some(NotificationPriority::High)
);
}
Expand Down Expand Up @@ -280,6 +331,10 @@ fn test_events_can_be_filtered_by_category() {
latest_category(&test_env.env),
Some(NotificationCategory::Group)
);
assert_eq!(
latest_priority(&test_env.env),
Some(NotificationPriority::Standard)
);
route();

// Admin event -> skipped by this subscriber.
Expand All @@ -288,6 +343,10 @@ fn test_events_can_be_filtered_by_category() {
latest_category(&test_env.env),
Some(NotificationCategory::Admin)
);
assert_eq!(
latest_priority(&test_env.env),
Some(NotificationPriority::High)
);
route();
client.unpause(&test_env.admin);

Expand All @@ -298,6 +357,10 @@ fn test_events_can_be_filtered_by_category() {
latest_category(&test_env.env),
Some(NotificationCategory::Financial)
);
assert_eq!(
latest_priority(&test_env.env),
Some(NotificationPriority::Critical)
);
route();

assert_eq!(processed, 2); // Group + Financial
Expand Down Expand Up @@ -493,6 +556,7 @@ fn test_created_event_backward_compatible_shape() {
);

let topics = topics_of(&test_env.env, "autoshare_created").expect("event emitted");
// [0] event name, [1] creator (unchanged), [2] priority, [3] category.
// [0] event name, [1] creator (unchanged), [2] category (now second-to-last),
// [3] priority (new trailing topic).
assert_eq!(topics.len(), 4);
Expand All @@ -503,8 +567,12 @@ fn test_created_event_backward_compatible_shape() {
let topic_creator = Address::try_from_val(&test_env.env, &topics.get(1).unwrap()).unwrap();
assert_eq!(topic_creator, creator);

let priority =
NotificationPriority::try_from_val(&test_env.env, &topics.get(2).unwrap()).unwrap();
assert_eq!(priority, NotificationPriority::Standard);

let category =
NotificationCategory::try_from_val(&test_env.env, &topics.get(2).unwrap()).unwrap();
NotificationCategory::try_from_val(&test_env.env, &topics.get(3).unwrap()).unwrap();
assert_eq!(category, NotificationCategory::Group);

// The newly added trailing topic is the priority.
Expand Down
Loading