Skip to content

Commit 97357a6

Browse files
committed
feat(contract): emit events for primary state changes in creator-earnings
Add InitializedEvent, AuthorizedAddedEvent, and DepositEvent typed event structs. Emit events at the end of initialize(), add_authorized(), and deposit() alongside the existing WithdrawEvent in withdraw(). Add unit tests verifying each new event is emitted with correct data. Also applies cargo fmt fixes to pre-existing formatting violations in content-access, content-likes, creator-earnings, subscription, test-consumer, and treasury. Closes #942
1 parent 5c21688 commit 97357a6

13 files changed

Lines changed: 278 additions & 45 deletions

File tree

contract/contracts/content-access/src/lib.rs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,10 @@ impl ContentAccess {
127127

128128
// Check if already unlocked (idempotent) – but re-check expiry.
129129
let access_key = DataKey::Access(buyer.clone(), creator.clone(), content_id);
130-
if let Some(existing) = env.storage().instance().get::<DataKey, Purchase>(&access_key)
130+
if let Some(existing) = env
131+
.storage()
132+
.instance()
133+
.get::<DataKey, Purchase>(&access_key)
131134
{
132135
// If the existing purchase is still valid, treat as no-op.
133136
if existing.expiry > current_seq {
@@ -166,7 +169,11 @@ impl ContentAccess {
166169
/// Check if buyer has valid (non-expired) access to content.
167170
pub fn has_access(env: Env, buyer: Address, creator: Address, content_id: u64) -> bool {
168171
let access_key = DataKey::Access(buyer, creator, content_id);
169-
if let Some(purchase) = env.storage().instance().get::<DataKey, Purchase>(&access_key) {
172+
if let Some(purchase) = env
173+
.storage()
174+
.instance()
175+
.get::<DataKey, Purchase>(&access_key)
176+
{
170177
let current_seq: u64 = env.ledger().sequence() as u64;
171178
purchase.expiry > current_seq
172179
} else {
@@ -853,7 +860,10 @@ mod test {
853860
assert!(client.has_access(&buyer, &creator, &1));
854861
// verify_access should not panic (we test this by not expecting an error)
855862
let verify_result = client.try_verify_access(&buyer, &creator, &1);
856-
assert!(verify_result.is_ok(), "verify_access should succeed when has_access is true");
863+
assert!(
864+
verify_result.is_ok(),
865+
"verify_access should succeed when has_access is true"
866+
);
857867
}
858868

859869
/// Invariant: If verify_access succeeds, has_access should return true.
@@ -879,7 +889,10 @@ mod test {
879889
client.unlock_content(&buyer, &creator, &1, &NO_EXPIRY);
880890
assert!(client.has_access(&buyer, &creator, &1));
881891
let verify_result = client.try_verify_access(&buyer, &creator, &1);
882-
assert!(verify_result.is_ok(), "verify_access should succeed after unlock");
892+
assert!(
893+
verify_result.is_ok(),
894+
"verify_access should succeed after unlock"
895+
);
883896
}
884897

885898
/// Invariant: Price set by creator should be retrievable.

contract/contracts/content-access/src/tests/event_tests.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
use crate::{
2-
events::{
3-
AdminTransferredEvent, ContentPriceSetEvent, MaxPriceClearedEvent, MaxPriceSetEvent,
4-
},
2+
events::{AdminTransferredEvent, ContentPriceSetEvent, MaxPriceClearedEvent, MaxPriceSetEvent},
53
ContentAccess, ContentAccessClient,
64
};
75
use soroban_sdk::{

contract/contracts/content-access/src/tests/init_admin_tests.rs

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,7 @@
1313

1414
use crate::{ContentAccess, ContentAccessClient, Error};
1515
use soroban_sdk::{
16-
testutils::Address as _,
17-
xdr::SorobanAuthorizationEntry,
18-
Address, Env, Error as SorobanError,
16+
testutils::Address as _, xdr::SorobanAuthorizationEntry, Address, Env, Error as SorobanError,
1917
};
2018

2119
const EMPTY_AUTHS: &[SorobanAuthorizationEntry] = &[];
@@ -62,7 +60,11 @@ fn initialize_stores_admin() {
6260

6361
client.initialize(&admin, &token_id);
6462

65-
assert_eq!(client.admin(), admin, "admin() must return the initialized admin");
63+
assert_eq!(
64+
client.admin(),
65+
admin,
66+
"admin() must return the initialized admin"
67+
);
6668
}
6769

6870
/// initialize stores the token address; set_content_price succeeds after init.
@@ -136,7 +138,10 @@ fn admin_view_panics_when_uninitialized() {
136138
let client = ContentAccessClient::new(&env, &contract_id);
137139

138140
let result = client.try_admin();
139-
assert!(result.is_err(), "admin() must fail on uninitialized contract");
141+
assert!(
142+
result.is_err(),
143+
"admin() must fail on uninitialized contract"
144+
);
140145
}
141146

142147
// ── set_admin ─────────────────────────────────────────────────────────────────
@@ -152,7 +157,8 @@ fn set_admin_transfers_admin_role() {
152157
client.set_admin(&new_admin);
153158

154159
assert_eq!(
155-
client.admin(), new_admin,
160+
client.admin(),
161+
new_admin,
156162
"admin() must return new admin after set_admin"
157163
);
158164
}
@@ -246,7 +252,11 @@ fn set_max_price_zero_clears_cap() {
246252
assert_eq!(client.get_max_price(), Some(500_000));
247253

248254
client.set_max_price(&0);
249-
assert_eq!(client.get_max_price(), None, "cap must be removed after set_max_price(0)");
255+
assert_eq!(
256+
client.get_max_price(),
257+
None,
258+
"cap must be removed after set_max_price(0)"
259+
);
250260
}
251261

252262
/// Non-admin cannot call set_max_price.
@@ -264,7 +274,10 @@ fn set_max_price_rejected_for_non_admin() {
264274

265275
env.set_auths(EMPTY_AUTHS);
266276
let result = client.try_set_max_price(&500_000);
267-
assert!(result.is_err(), "set_max_price must fail without admin auth");
277+
assert!(
278+
result.is_err(),
279+
"set_max_price must fail without admin auth"
280+
);
268281
}
269282

270283
/// Prices above max_price are rejected when cap is configured.

contract/contracts/content-access/src/tests/unauthorized_tests.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,13 @@ fn setup(env: &Env) -> (ContentAccessClient<'_>, Address, Address) {
3030
(client, admin, token_address)
3131
}
3232

33-
fn mock_rogue_auth(env: &Env, rogue: &Address, contract: &Address, fn_name: &'static str, args: soroban_sdk::Vec<Val>) {
33+
fn mock_rogue_auth(
34+
env: &Env,
35+
rogue: &Address,
36+
contract: &Address,
37+
fn_name: &'static str,
38+
args: soroban_sdk::Vec<Val>,
39+
) {
3440
env.mock_auths(&[MockAuth {
3541
address: rogue,
3642
invoke: &MockAuthInvoke {
@@ -163,7 +169,9 @@ fn initialize_reverts_if_already_initialized() {
163169
let second_admin = Address::generate(&env);
164170

165171
env.mock_all_auths();
166-
assert!(client.try_initialize(&second_admin, &token_address).is_err());
172+
assert!(client
173+
.try_initialize(&second_admin, &token_address)
174+
.is_err());
167175
}
168176

169177
#[test]

contract/contracts/content-likes/src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,10 @@ mod test {
584584

585585
// Verify events were published
586586
let events = env.events().all();
587-
assert!(events.len() >= 2, "Expected at least 2 events (like and unlike)");
587+
assert!(
588+
events.len() >= 2,
589+
"Expected at least 2 events (like and unlike)"
590+
);
588591
}
589592

590593
#[test]

contract/contracts/content-likes/tests/contract_integration.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,10 @@ fn test_error_unlike_without_like() {
6363

6464
// Try to unlike without ever liking — should fail with NotLiked error (code 1)
6565
let result = client.try_unlike(&user, &content_id);
66-
assert!(result.is_err(), "Expected unlike without prior like to fail with NotLiked error");
66+
assert!(
67+
result.is_err(),
68+
"Expected unlike without prior like to fail with NotLiked error"
69+
);
6770
}
6871

6972
/// Test multiple users liking the same content.

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

Lines changed: 54 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,14 @@ 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 {
120+
depositor: contract,
121+
},
122+
);
82123
}
83124

84125
/// Deposit earnings for creator
@@ -102,6 +143,16 @@ impl CreatorEarnings {
102143
env.storage()
103144
.instance()
104145
.set(&DataKey::Balance(creator.clone()), &new_balance);
146+
147+
env.events().publish(
148+
(Symbol::new(&env, DEPOSIT_EVENT),),
149+
DepositEvent {
150+
from,
151+
creator,
152+
amount,
153+
token: token_address,
154+
},
155+
);
105156
}
106157

107158
/// Get creator balance
@@ -112,7 +163,7 @@ impl CreatorEarnings {
112163
.unwrap_or(0)
113164
}
114165

115-
/// Withdraw earnings (WITH EVENT)
166+
/// Withdraw earnings
116167
pub fn withdraw(env: Env, creator: Address, amount: i128) {
117168
if amount <= 0 {
118169
panic_with_error!(&env, Error::InvalidAmount);
@@ -141,7 +192,6 @@ impl CreatorEarnings {
141192
.instance()
142193
.set(&DataKey::Balance(creator.clone()), &new_balance);
143194

144-
// ✅ Typed event emission
145195
env.events().publish(
146196
(Symbol::new(&env, WITHDRAW_EVENT),),
147197
WithdrawEvent {

0 commit comments

Comments
 (0)