Skip to content

Commit e3f4970

Browse files
committed
feat(creator-earnings): emit events for primary state changes
Add InitializedEvent, AuthorizedAddedEvent, and DepositEvent structs. Emit events at the end of initialize(), add_authorized(), and deposit() alongside the existing WithdrawEvent in withdraw(). Add tests verifying each new event is emitted with correct data. Closes #942
1 parent 5c21688 commit e3f4970

2 files changed

Lines changed: 178 additions & 4 deletions

File tree

contract/contracts/creator-earnings/src/lib.rs

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,30 @@ pub enum Error {
4040
InvalidAmount = 5,
4141
}
4242

43-
/// -------- Events (INLINE) --------
43+
/// -------- Events --------
44+
45+
#[contracttype]
46+
#[derive(Clone, Debug, Eq, PartialEq)]
47+
pub struct InitializedEvent {
48+
pub admin: Address,
49+
pub token: Address,
50+
}
51+
52+
#[contracttype]
53+
#[derive(Clone, Debug, Eq, PartialEq)]
54+
pub struct AuthorizedAddedEvent {
55+
pub depositor: Address,
56+
}
57+
58+
#[contracttype]
59+
#[derive(Clone, Debug, Eq, PartialEq)]
60+
pub struct DepositEvent {
61+
pub from: Address,
62+
pub creator: Address,
63+
pub amount: i128,
64+
pub token: Address,
65+
}
66+
4467
#[contracttype]
4568
#[derive(Clone, Debug, Eq, PartialEq)]
4669
pub struct WithdrawEvent {
@@ -50,6 +73,9 @@ pub struct WithdrawEvent {
5073
}
5174

5275
/// Avoid magic strings
76+
const INITIALIZED_EVENT: &str = "initialized";
77+
const AUTHORIZED_ADDED_EVENT: &str = "authorized_added";
78+
const DEPOSIT_EVENT: &str = "deposit";
5379
const WITHDRAW_EVENT: &str = "withdraw";
5480

5581
#[contract]
@@ -69,6 +95,14 @@ impl CreatorEarnings {
6995
env.storage()
7096
.instance()
7197
.set(&DataKey::Token, &token_address);
98+
99+
env.events().publish(
100+
(Symbol::new(&env, INITIALIZED_EVENT),),
101+
InitializedEvent {
102+
admin,
103+
token: token_address,
104+
},
105+
);
72106
}
73107

74108
/// Add authorized depositor contract (admin only)
@@ -78,7 +112,12 @@ impl CreatorEarnings {
78112

79113
env.storage()
80114
.instance()
81-
.set(&DataKey::AuthorizedDepositor(contract), &true);
115+
.set(&DataKey::AuthorizedDepositor(contract.clone()), &true);
116+
117+
env.events().publish(
118+
(Symbol::new(&env, AUTHORIZED_ADDED_EVENT),),
119+
AuthorizedAddedEvent { depositor: contract },
120+
);
82121
}
83122

84123
/// Deposit earnings for creator
@@ -102,6 +141,16 @@ impl CreatorEarnings {
102141
env.storage()
103142
.instance()
104143
.set(&DataKey::Balance(creator.clone()), &new_balance);
144+
145+
env.events().publish(
146+
(Symbol::new(&env, DEPOSIT_EVENT),),
147+
DepositEvent {
148+
from,
149+
creator,
150+
amount,
151+
token: token_address,
152+
},
153+
);
105154
}
106155

107156
/// Get creator balance
@@ -112,7 +161,7 @@ impl CreatorEarnings {
112161
.unwrap_or(0)
113162
}
114163

115-
/// Withdraw earnings (WITH EVENT)
164+
/// Withdraw earnings
116165
pub fn withdraw(env: Env, creator: Address, amount: i128) {
117166
if amount <= 0 {
118167
panic_with_error!(&env, Error::InvalidAmount);
@@ -141,7 +190,6 @@ impl CreatorEarnings {
141190
.instance()
142191
.set(&DataKey::Balance(creator.clone()), &new_balance);
143192

144-
// ✅ Typed event emission
145193
env.events().publish(
146194
(Symbol::new(&env, WITHDRAW_EVENT),),
147195
WithdrawEvent {

contract/contracts/creator-earnings/src/test.rs

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,3 +240,129 @@ fn withdraw_failed_emits_no_event() {
240240
assert_eq!(client.balance(&creator), 500);
241241
assert!(env.events().all().len() >= events_before);
242242
}
243+
244+
// -------- Event tests for issue #942: emit events for primary state changes --------
245+
246+
#[test]
247+
fn initialize_emits_event() {
248+
let env = Env::default();
249+
env.mock_all_auths();
250+
251+
let admin = Address::generate(&env);
252+
let token_admin = Address::generate(&env);
253+
#[allow(deprecated)]
254+
let token_id = env.register_stellar_asset_contract(token_admin.clone());
255+
256+
let contract_id = env.register_contract(None, CreatorEarnings);
257+
let client = CreatorEarningsClient::new(&env, &contract_id);
258+
259+
client.initialize(&admin, &token_id);
260+
261+
let all_events = env.events().all();
262+
let mut init_event: Option<(
263+
Address,
264+
soroban_sdk::Vec<soroban_sdk::Val>,
265+
soroban_sdk::Val,
266+
)> = None;
267+
for i in 0..all_events.len() {
268+
let evt = all_events.get(i).unwrap();
269+
let (id, topics, _) = &evt;
270+
if *id != client.address {
271+
continue;
272+
}
273+
let t0: Option<Symbol> = topics.get(0).and_then(|v| v.try_into_val(&env).ok());
274+
if t0 == Some(Symbol::new(&env, "initialized")) {
275+
init_event = Some(evt);
276+
break;
277+
}
278+
}
279+
280+
let event = init_event.expect("initialized event not emitted");
281+
let data: InitializedEvent = event.2.try_into_val(&env).unwrap();
282+
assert_eq!(data.admin, admin);
283+
assert_eq!(data.token, token_id);
284+
}
285+
286+
#[test]
287+
fn add_authorized_emits_event() {
288+
let env = Env::default();
289+
env.mock_all_auths();
290+
291+
let admin = Address::generate(&env);
292+
let token_admin = Address::generate(&env);
293+
#[allow(deprecated)]
294+
let token_id = env.register_stellar_asset_contract(token_admin.clone());
295+
296+
let contract_id = env.register_contract(None, CreatorEarnings);
297+
let client = CreatorEarningsClient::new(&env, &contract_id);
298+
299+
client.initialize(&admin, &token_id);
300+
301+
let depositor = Address::generate(&env);
302+
client.add_authorized(&depositor);
303+
304+
let all_events = env.events().all();
305+
let mut auth_event: Option<(
306+
Address,
307+
soroban_sdk::Vec<soroban_sdk::Val>,
308+
soroban_sdk::Val,
309+
)> = None;
310+
for i in 0..all_events.len() {
311+
let evt = all_events.get(i).unwrap();
312+
let (id, topics, _) = &evt;
313+
if *id != client.address {
314+
continue;
315+
}
316+
let t0: Option<Symbol> = topics.get(0).and_then(|v| v.try_into_val(&env).ok());
317+
if t0 == Some(Symbol::new(&env, "authorized_added")) {
318+
auth_event = Some(evt);
319+
break;
320+
}
321+
}
322+
323+
let event = auth_event.expect("authorized_added event not emitted");
324+
let data: AuthorizedAddedEvent = event.2.try_into_val(&env).unwrap();
325+
assert_eq!(data.depositor, depositor);
326+
}
327+
328+
#[test]
329+
fn deposit_emits_event() {
330+
let env = Env::default();
331+
332+
let (_admin, creator, depositor, client, _, _) = setup(&env);
333+
334+
let token_address: Address = env.as_contract(&client.address, || {
335+
env.storage()
336+
.instance()
337+
.get(&DataKey::Token)
338+
.expect("token not set")
339+
});
340+
341+
client.deposit(&depositor, &creator, &300);
342+
343+
let all_events = env.events().all();
344+
let mut dep_event: Option<(
345+
Address,
346+
soroban_sdk::Vec<soroban_sdk::Val>,
347+
soroban_sdk::Val,
348+
)> = None;
349+
for i in 0..all_events.len() {
350+
let evt = all_events.get(i).unwrap();
351+
let (id, topics, _) = &evt;
352+
if *id != client.address {
353+
continue;
354+
}
355+
let t0: Option<Symbol> = topics.get(0).and_then(|v| v.try_into_val(&env).ok());
356+
if t0 == Some(Symbol::new(&env, "deposit")) {
357+
dep_event = Some(evt);
358+
break;
359+
}
360+
}
361+
362+
let event = dep_event.expect("deposit event not emitted");
363+
let data: DepositEvent = event.2.try_into_val(&env).unwrap();
364+
assert_eq!(data.from, depositor);
365+
assert_eq!(data.creator, creator);
366+
assert_eq!(data.amount, 300);
367+
assert_eq!(data.token, token_address);
368+
}

0 commit comments

Comments
 (0)