11#![ no_std]
22
33use soroban_sdk:: {
4- contract, contractimpl, contracttype, Address , Env , IntoVal , String , Symbol , Vec ,
4+ contract, contractimpl, contracttype, Address , BytesN , Env , IntoVal , String , Symbol , Vec ,
55} ;
66
77// Storage keys
@@ -27,6 +27,7 @@ pub struct Event {
2727 pub end_date : u64 ,
2828 pub is_canceled : bool ,
2929 pub ticket_nft_addr : Address ,
30+ pub payment_token : Address ,
3031}
3132
3233#[ contract]
@@ -60,6 +61,7 @@ impl EventManager {
6061 end_date : u64 ,
6162 ticket_price : i128 ,
6263 total_tickets : u128 ,
64+ payment_token : Address ,
6365 ) -> u32 {
6466 // Validate organizer address
6567 organizer. require_auth ( ) ;
@@ -86,13 +88,21 @@ impl EventManager {
8688 end_date,
8789 is_canceled : false ,
8890 ticket_nft_addr : ticket_nft_addr. clone ( ) ,
91+ payment_token,
8992 } ;
9093
9194 // Store event
9295 env. storage ( )
9396 . persistent ( )
9497 . set ( & DataKey :: Event ( event_id) , & event) ;
9598
99+ // Extend TTL for the new event
100+ env. storage ( ) . persistent ( ) . extend_ttl (
101+ & DataKey :: Event ( event_id) ,
102+ 30 * 24 * 60 * 60 / 5 , // threshold (~30 days)
103+ 100 * 24 * 60 * 60 / 5 , // extend_to (~100 days)
104+ ) ;
105+
96106 // Emit event creation event
97107 env. events ( ) . publish (
98108 ( Symbol :: new ( & env, "event_created" ) , ) ,
@@ -189,6 +199,59 @@ impl EventManager {
189199 . set ( & DataKey :: Event ( event_id) , & event) ;
190200 }
191201
202+ /// Purchase a ticket for an event
203+ pub fn purchase_ticket ( env : Env , buyer : Address , event_id : u32 ) {
204+ buyer. require_auth ( ) ;
205+
206+ let mut event: Event = env
207+ . storage ( )
208+ . persistent ( )
209+ . get ( & DataKey :: Event ( event_id) )
210+ . unwrap_or_else ( || panic ! ( "Event not found" ) ) ;
211+
212+ if event. is_canceled {
213+ panic ! ( "Event is canceled" ) ;
214+ }
215+
216+ if event. tickets_sold >= event. total_tickets {
217+ panic ! ( "Event is sold out" ) ;
218+ }
219+
220+ // Handle payment
221+ if event. ticket_price > 0 {
222+ let token_client = soroban_sdk:: token:: Client :: new ( & env, & event. payment_token ) ;
223+ token_client. transfer ( & buyer, & event. organizer , & event. ticket_price ) ;
224+ }
225+
226+ // Mint ticket NFT
227+ env. invoke_contract :: < u128 > (
228+ & event. ticket_nft_addr ,
229+ & Symbol :: new ( & env, "mint_ticket_nft" ) ,
230+ soroban_sdk:: vec![ & env, buyer. into_val( & env) ] ,
231+ ) ;
232+
233+ // Update tickets sold
234+ event. tickets_sold += 1 ;
235+
236+ // Store updated event
237+ env. storage ( )
238+ . persistent ( )
239+ . set ( & DataKey :: Event ( event_id) , & event) ;
240+
241+ // Extend TTL
242+ env. storage ( ) . persistent ( ) . extend_ttl (
243+ & DataKey :: Event ( event_id) ,
244+ 30 * 24 * 60 * 60 / 5 ,
245+ 100 * 24 * 60 * 60 / 5 ,
246+ ) ;
247+
248+ // Emit purchase event
249+ env. events ( ) . publish (
250+ ( Symbol :: new ( & env, "ticket_purchased" ) , ) ,
251+ ( event_id, buyer, event. ticket_nft_addr ) ,
252+ ) ;
253+ }
254+
192255 // ========== Helper Functions ==========
193256
194257 fn validate_event_params (
@@ -236,151 +299,28 @@ impl EventManager {
236299 current
237300 }
238301
239- fn deploy_ticket_nft ( env : & Env , event_id : u32 , theme : String , total_supply : u128 ) -> Address {
302+ fn deploy_ticket_nft (
303+ env : & Env ,
304+ _event_id : u32 ,
305+ _theme : String ,
306+ _total_supply : u128 ,
307+ ) -> Address {
240308 let factory_addr: Address = env
241309 . storage ( )
242310 . instance ( )
243311 . get ( & DataKey :: TicketFactory )
244312 . unwrap_or_else ( || panic ! ( "Ticket factory not initialized" ) ) ;
245-
246- // Call the factory contract to deploy a new NFT contract
247313 // This is a cross-contract call
248- use soroban_sdk:: vec;
249-
250- let nft_addr: Address = env. invoke_contract (
251- & factory_addr,
252- & Symbol :: new ( env, "deploy_ticket_nft" ) ,
253- vec ! [
254- env,
255- event_id. into_val( env) ,
256- theme. into_val( env) ,
257- total_supply. into_val( env) ,
258- ] ,
259- ) ;
260314
315+ let salt = BytesN :: from_array ( & env, & [ 0u8 ; 32 ] ) ;
316+ let mut args = Vec :: new ( & env) ;
317+ args. push_back ( env. current_contract_address ( ) . to_val ( ) ) ;
318+ args. push_back ( salt. to_val ( ) ) ;
319+
320+ let nft_addr: Address =
321+ env. invoke_contract ( & factory_addr, & Symbol :: new ( & env, "deploy_ticket" ) , args) ;
261322 nft_addr
262323 }
263324}
264325
265- #[ cfg( test) ]
266- mod test {
267- use super :: * ;
268- use soroban_sdk:: { testutils:: Address as _, testutils:: Ledger , vec, Env , Symbol } ;
269-
270- #[ contract]
271- pub struct MockFactory ;
272-
273- #[ contractimpl]
274- impl MockFactory {
275- pub fn deploy_ticket_nft (
276- env : Env ,
277- _event_id : u32 ,
278- _theme : String ,
279- _total_supply : u128 ,
280- ) -> Address {
281- Address :: generate ( & env)
282- }
283- }
284-
285- #[ test]
286- fn test_create_event ( ) {
287- let env = Env :: default ( ) ;
288- let contract_id = env. register_contract ( None , EventManager ) ;
289- let client = EventManagerClient :: new ( & env, & contract_id) ;
290-
291- let factory_addr = env. register_contract ( None , MockFactory ) ;
292- let organizer = Address :: generate ( & env) ;
293-
294- // Mock the organizer authorization
295- env. mock_all_auths ( ) ;
296-
297- // Initialize
298- client. initialize ( & factory_addr) ;
299-
300- // Create event
301- let theme = String :: from_str ( & env, "Rust Conference 2026" ) ;
302- let event_type = String :: from_str ( & env, "Conference" ) ;
303- let start_date = env. ledger ( ) . timestamp ( ) + 86400 ; // 1 day from now
304- let end_date = start_date + 86400 ; // 2 days from now
305- let ticket_price = 1000_0000000 ; // 100 XLM (7 decimals)
306- let total_tickets = 500 ;
307-
308- let event_id = client. create_event (
309- & organizer,
310- & theme,
311- & event_type,
312- & start_date,
313- & end_date,
314- & ticket_price,
315- & total_tickets,
316- ) ;
317-
318- assert_eq ! ( event_id, 0 ) ;
319-
320- // Get event
321- let event = client. get_event ( & event_id) ;
322- assert_eq ! ( event. id, 0 ) ;
323- assert_eq ! ( event. organizer, organizer) ;
324- assert_eq ! ( event. total_tickets, total_tickets) ;
325- assert_eq ! ( event. tickets_sold, 0 ) ;
326- assert_eq ! ( event. is_canceled, false ) ;
327- }
328-
329- #[ test]
330- #[ should_panic( expected = "Start date must be in the future" ) ]
331- fn test_create_event_past_date ( ) {
332- let env = Env :: default ( ) ;
333- let contract_id = env. register_contract ( None , EventManager ) ;
334- let client = EventManagerClient :: new ( & env, & contract_id) ;
335-
336- let factory_addr = env. register_contract ( None , MockFactory ) ;
337- let organizer = Address :: generate ( & env) ;
338-
339- env. mock_all_auths ( ) ;
340- env. ledger ( ) . set_timestamp ( 1000 ) ;
341- client. initialize ( & factory_addr) ;
342-
343- let theme = String :: from_str ( & env, "Past Event" ) ;
344- let event_type = String :: from_str ( & env, "Conference" ) ;
345- let start_date = env. ledger ( ) . timestamp ( ) . saturating_sub ( 1 ) ; // Past date
346- let end_date = start_date. saturating_add ( 86400 ) ;
347-
348- client. create_event (
349- & organizer,
350- & theme,
351- & event_type,
352- & start_date,
353- & end_date,
354- & 1000_0000000 ,
355- & 100 ,
356- ) ;
357- }
358-
359- #[ test]
360- fn test_cancel_event ( ) {
361- let env = Env :: default ( ) ;
362- let contract_id = env. register_contract ( None , EventManager ) ;
363- let client = EventManagerClient :: new ( & env, & contract_id) ;
364-
365- let factory_addr = env. register_contract ( None , MockFactory ) ;
366- let organizer = Address :: generate ( & env) ;
367-
368- env. mock_all_auths ( ) ;
369- client. initialize ( & factory_addr) ;
370-
371- let event_id = client. create_event (
372- & organizer,
373- & String :: from_str ( & env, "Event" ) ,
374- & String :: from_str ( & env, "Type" ) ,
375- & ( env. ledger ( ) . timestamp ( ) + 86400 ) ,
376- & ( env. ledger ( ) . timestamp ( ) + 172800 ) ,
377- & 1000_0000000 ,
378- & 100 ,
379- ) ;
380-
381- client. cancel_event ( & event_id) ;
382-
383- let event = client. get_event ( & event_id) ;
384- assert_eq ! ( event. is_canceled, true ) ;
385- }
386- }
326+ mod test;
0 commit comments