Skip to content

Commit 68fa25e

Browse files
Samuel1-onamanoahLinks
authored andcommitted
feat(event_manager): implement ticket purchase flow and upgrade SDK #24
1 parent 861898a commit 68fa25e

3 files changed

Lines changed: 222 additions & 224 deletions

File tree

soroban-contract/contracts/event_manager/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ edition = "2021"
77
crate-type = ["cdylib"]
88

99
[dependencies]
10-
soroban-sdk = "21.7.0"
10+
soroban-sdk = { workspace = true }
1111

1212
[dev-dependencies]
13-
soroban-sdk = { version = "21.7.0", features = ["testutils"] }
13+
soroban-sdk = { workspace = true, features = ["testutils"] }
1414

1515
[profile.release]
1616
opt-level = "z"

soroban-contract/contracts/event_manager/src/lib.rs

Lines changed: 78 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#![no_std]
22

33
use 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

Comments
 (0)