From f7c512377f77a1923be9263aecc10772067894ce Mon Sep 17 00:00:00 2001 From: whiteghost0001 Date: Thu, 26 Mar 2026 19:29:10 +0100 Subject: [PATCH] feat: implement create_event storage logic - Add EventMetrics struct with tickets_sold field (zero-initialized) - Add StorageKey::Event(BytesN<32>) and StorageKey::EventMetrics(BytesN<32>) - Update EventDetails: id changed to BytesN<32>, added token field - Implement create_event to persist EventDetails and EventMetrics in storage - Add state logic test verifying all fields saved correctly in env.storage() --- contract/contract/src/base/types.rs | 34 ++++++++- contract/contract/src/crowdfunding.rs | 38 +++++++--- contract/contract/test/create_event_test.rs | 82 ++++++++++++++++++--- 3 files changed, 130 insertions(+), 24 deletions(-) diff --git a/contract/contract/src/base/types.rs b/contract/contract/src/base/types.rs index 6a163f2..fc4dcbe 100644 --- a/contract/contract/src/base/types.rs +++ b/contract/contract/src/base/types.rs @@ -144,12 +144,32 @@ pub enum EventStatus { #[contracttype] #[derive(Clone, Debug, PartialEq, Eq)] pub struct EventDetails { - pub id: u64, + pub id: BytesN<32>, pub title: String, pub creator: Address, pub ticket_price: i128, pub max_attendees: u32, pub deadline: u64, + pub token: Address, +} + +#[contracttype] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct EventMetrics { + pub tickets_sold: u32, +} + +impl Default for EventMetrics { + fn default() -> Self { + Self::new() + } +} + +impl EventMetrics { + /// Creates zero-initialized metrics for a new event. + pub fn new() -> Self { + Self { tickets_sold: 0 } + } } /// Represents the type of a ticket. @@ -290,6 +310,10 @@ pub enum StorageKey { EventPlatformFees(u64), // Track if someone bought a ticket UserTicket(u64, Address), + // Event details keyed by event id + Event(BytesN<32>), + // Per-event metrics (tickets sold, etc.) + EventMetrics(BytesN<32>), } #[cfg(test)] @@ -462,18 +486,22 @@ mod tests { use soroban_sdk::testutils::Address as _; let env = Env::default(); let creator = soroban_sdk::Address::generate(&env); + let token = soroban_sdk::Address::generate(&env); + let id = soroban_sdk::BytesN::from_array(&env, &[1u8; 32]); let event = EventDetails { - id: 1, + id: id.clone(), title: String::from_str(&env, "Nevo Launch"), creator: creator.clone(), ticket_price: 500, max_attendees: 100, deadline: 1_700_000_000, + token: token.clone(), }; - assert_eq!(event.id, 1); + assert_eq!(event.id, id); assert_eq!(event.ticket_price, 500); assert_eq!(event.max_attendees, 100); assert_eq!(event.deadline, 1_700_000_000); assert_eq!(event.creator, creator); + assert_eq!(event.token, token); } } diff --git a/contract/contract/src/crowdfunding.rs b/contract/contract/src/crowdfunding.rs index f67974a..10062d1 100644 --- a/contract/contract/src/crowdfunding.rs +++ b/contract/contract/src/crowdfunding.rs @@ -9,9 +9,9 @@ use crate::base::{ }, types::{ CampaignDetails, CampaignLifecycleStatus, CampaignMetrics, Contribution, - EmergencyWithdrawal, MultiSigConfig, PoolConfig, PoolContribution, PoolMetadata, - PoolMetrics, PoolState, StorageKey, MAX_DESCRIPTION_LENGTH, MAX_HASH_LENGTH, - MAX_STRING_LENGTH, MAX_URL_LENGTH, + EmergencyWithdrawal, EventDetails, EventMetrics, MultiSigConfig, PoolConfig, + PoolContribution, PoolMetadata, PoolMetrics, PoolState, StorageKey, + MAX_DESCRIPTION_LENGTH, MAX_HASH_LENGTH, MAX_STRING_LENGTH, MAX_URL_LENGTH, }, }; use crate::interfaces::crowdfunding::CrowdfundingTrait; @@ -1922,16 +1922,34 @@ impl SecondCrowdfundingTrait for CrowdfundingContract { fn create_event( env: Env, - _id: BytesN<32>, + id: BytesN<32>, title: String, - _creator: Address, - _ticket_price: i128, - _max_attendees: u32, - _deadline: u64, - _token: Address, + creator: Address, + ticket_price: i128, + max_attendees: u32, + deadline: u64, + token: Address, ) -> Result<(), SecondCrowdfundingError> { Self::validate_string_length(&title)?; - let _ = env; + + let details = EventDetails { + id: id.clone(), + title, + creator, + ticket_price, + max_attendees, + deadline, + token, + }; + + env.storage() + .instance() + .set(&StorageKey::Event(id.clone()), &details); + + env.storage() + .instance() + .set(&StorageKey::EventMetrics(id), &EventMetrics::new()); + Ok(()) } } diff --git a/contract/contract/test/create_event_test.rs b/contract/contract/test/create_event_test.rs index d9a6cd3..7e5e5e7 100644 --- a/contract/contract/test/create_event_test.rs +++ b/contract/contract/test/create_event_test.rs @@ -3,7 +3,11 @@ use soroban_sdk::{testutils::Address as _, Address, BytesN, Env, String}; use crate::{ - base::errors::SecondCrowdfundingError, crowdfunding::CrowdfundingContract, + base::{ + errors::SecondCrowdfundingError, + types::{EventDetails, EventMetrics, StorageKey}, + }, + crowdfunding::CrowdfundingContract, interfaces::second_crowdfunding::SecondCrowdfundingTrait, }; @@ -14,6 +18,7 @@ fn string_of_len(env: &Env, len: usize) -> String { #[test] fn test_create_event_success_path_returns_ok_for_valid_titles() { let env = Env::default(); + let contract_id = env.register(CrowdfundingContract, ()); let creator = Address::generate(&env); let token = Address::generate(&env); @@ -25,16 +30,18 @@ fn test_create_event_success_path_returns_ok_for_valid_titles() { let id = BytesN::from_array(&env, &[(index + 1) as u8; 32]); let title = string_of_len(&env, title_len); - let result = ::create_event( - env.clone(), - id, - title, - creator.clone(), - 100, - 500, - base_deadline + index as u64, - token.clone(), - ); + let result = env.as_contract(&contract_id, || { + ::create_event( + env.clone(), + id, + title, + creator.clone(), + 100, + 500, + base_deadline + index as u64, + token.clone(), + ) + }); assert_eq!( result, @@ -43,3 +50,56 @@ fn test_create_event_success_path_returns_ok_for_valid_titles() { ); } } + +#[test] +fn test_create_event_stores_event_details_and_initializes_metrics() { + let env = Env::default(); + let contract_id = env.register(CrowdfundingContract, ()); + + let creator = Address::generate(&env); + let token = Address::generate(&env); + let id = BytesN::from_array(&env, &[42u8; 32]); + let title = String::from_str(&env, "Soroban Hackathon"); + let ticket_price: i128 = 250; + let max_attendees: u32 = 100; + let deadline: u64 = env.ledger().timestamp() + 7 * 86_400; + + env.as_contract(&contract_id, || { + let result = ::create_event( + env.clone(), + id.clone(), + title.clone(), + creator.clone(), + ticket_price, + max_attendees, + deadline, + token.clone(), + ); + + assert_eq!(result, Ok(()), "create_event should return Ok"); + + // Verify EventDetails stored correctly + let stored_details: EventDetails = env + .storage() + .instance() + .get(&StorageKey::Event(id.clone())) + .expect("EventDetails should be stored"); + + assert_eq!(stored_details.id, id, "id mismatch"); + assert_eq!(stored_details.title, title, "title mismatch"); + assert_eq!(stored_details.creator, creator, "creator mismatch"); + assert_eq!(stored_details.ticket_price, ticket_price, "ticket_price mismatch"); + assert_eq!(stored_details.max_attendees, max_attendees, "max_attendees mismatch"); + assert_eq!(stored_details.deadline, deadline, "deadline mismatch"); + assert_eq!(stored_details.token, token, "token mismatch"); + + // Verify EventMetrics initialized with 0 tickets sold + let stored_metrics: EventMetrics = env + .storage() + .instance() + .get(&StorageKey::EventMetrics(id.clone())) + .expect("EventMetrics should be stored"); + + assert_eq!(stored_metrics.tickets_sold, 0, "tickets_sold should be initialized to 0"); + }); +}