Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions creator-keys/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,9 @@ pub const CREATOR_FEE_RECIPIENT_UPDATED_EVENT_NAME: Symbol = symbol_short!("c_fe
/// Event name for co-creator fee accrual.
pub const CO_CREATOR_FEE_EARNED_EVENT_NAME: Symbol = symbol_short!("co_fee");

/// Event name for creator key TTL extension.
pub const TTL_EXTENDED_EVENT_NAME: Symbol = symbol_short!("ttl_ext");

/// Stable field order for dividend distributed event payloads.
pub const DIVIDEND_DISTRIBUTED_DATA_FIELDS: [&str; 4] =
["creator", "total_amount", "snapshot_supply", "ledger"];
Expand All @@ -190,6 +193,9 @@ pub const DIVIDEND_CLAIMED_DATA_FIELDS: [&str; 3] = ["creator", "claimant", "amo
pub const CO_CREATOR_FEE_EARNED_DATA_FIELDS: [&str; 4] =
["creator_id", "co_creator", "amount", "ledger"];

/// Stable field order for TTL extended event payloads.
pub const TTL_EXTENDED_DATA_FIELDS: [&str; 3] = ["creator", "extend_to", "ledger"];

#[derive(Clone, Debug, Eq, PartialEq)]
#[contracttype]
pub struct DividendDistributedEvent {
Expand Down Expand Up @@ -262,6 +268,22 @@ pub struct CoCreatorFeeEarned {
pub ledger: u32,
}

/// Stable TTL extended event payload for downstream indexers.
///
/// Event shape:
/// - topics: `(TTL_EXTENDED_EVENT_NAME, creator)`
/// - data: `TtlExtendedEvent`
///
/// This event is emitted when a creator's storage entries have their TTL extended
/// after a successful buy, sell, or other operation that keeps the creator active.
#[derive(Clone, Debug, Eq, PartialEq)]
#[contracttype]
pub struct TtlExtendedEvent {
pub creator: Address,
pub extend_to: u32,
pub ledger: u32,
}

pub fn co_creator_fee_earned_topics(
creator_id: &Address,
co_creator: &Address,
Expand All @@ -273,6 +295,11 @@ pub fn co_creator_fee_earned_topics(
)
}

/// Shared TTL extended event topics tuple.
pub fn ttl_extended_topics(creator: &Address) -> (Symbol, Address) {
(TTL_EXTENDED_EVENT_NAME, creator.clone())
}

/// Event name for key transfer.
pub const KEYS_TRANSFERRED_EVENT_NAME: Symbol = symbol_short!("xfer");

Expand Down
9 changes: 9 additions & 0 deletions creator-keys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1274,6 +1274,15 @@ fn extend_creator_ttl(env: &Env, creator: &Address) {
}
}
}

env.events().publish(
events::ttl_extended_topics(creator),
events::TtlExtendedEvent {
creator: creator.clone(),
extend_to,
ledger: current_ledger,
},
);
}

#[contract]
Expand Down
98 changes: 98 additions & 0 deletions creator-keys/tests/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -424,3 +424,101 @@ fn test_sell_key_event_payload_tracks_zero_supply_after_last_sale() {
fn test_sell_key_event_payload_field_order_is_documented() {
assert_eq!(events::SELL_EVENT_DATA_FIELDS, ["supply"]);
}

#[test]
fn test_ttl_extended_event_emitted_after_buy() {
let env = Env::default();
env.mock_all_auths();
let fixture = EventFixture::new(&env);
let buyer = Address::generate(&env);

fixture.register_creator(&env, "alice");
fixture.buy_key(&buyer, KEY_PRICE);

let events = env.events().all();
let ttl_event = events
.iter()
.find(|(_, topics, _)| {
topics
.get(events::TOPIC_EVENT_NAME_INDEX)
.map(|value| {
let sym: Symbol = value.into_val(&env);
sym == events::TTL_EXTENDED_EVENT_NAME
})
.unwrap_or(false)
})
.expect("TTL extended event should be present");

let event_name: Symbol = ttl_event
.1
.get(events::TOPIC_EVENT_NAME_INDEX)
.unwrap()
.into_val(&env);
let event_creator: Address = ttl_event
.1
.get(events::TOPIC_CREATOR_INDEX)
.unwrap()
.into_val(&env);
let payload: events::TtlExtendedEvent = ttl_event.2.into_val(&env);

assert_eq!(event_name, events::TTL_EXTENDED_EVENT_NAME);
assert_eq!(event_creator, fixture.creator);
assert_eq!(payload.creator, fixture.creator);
assert_eq!(payload.ledger, env.ledger().sequence());
assert_eq!(
payload.extend_to,
env.ledger().sequence() + creator_keys::CREATOR_TTL_LEDGERS
);
}

#[test]
fn test_ttl_extended_event_emitted_after_sell() {
let env = Env::default();
env.mock_all_auths();
let fixture = EventFixture::new(&env);
let seller = Address::generate(&env);

fixture.register_creator(&env, "alice");
fixture.buy_key(&seller, KEY_PRICE);
fixture.sell_key(&seller);

let events = env.events().all();
let ttl_event = events
.iter()
.find(|(_, topics, _)| {
topics
.get(events::TOPIC_EVENT_NAME_INDEX)
.map(|value| {
let sym: Symbol = value.into_val(&env);
sym == events::TTL_EXTENDED_EVENT_NAME
})
.unwrap_or(false)
})
.expect("TTL extended event should be present");

let event_name: Symbol = ttl_event
.1
.get(events::TOPIC_EVENT_NAME_INDEX)
.unwrap()
.into_val(&env);
let event_creator: Address = ttl_event
.1
.get(events::TOPIC_CREATOR_INDEX)
.unwrap()
.into_val(&env);
let payload: events::TtlExtendedEvent = ttl_event.2.into_val(&env);

assert_eq!(event_name, events::TTL_EXTENDED_EVENT_NAME);
assert_eq!(event_creator, fixture.creator);
assert_eq!(payload.creator, fixture.creator);
assert_eq!(payload.ledger, env.ledger().sequence());
assert_eq!(
payload.extend_to,
env.ledger().sequence() + creator_keys::CREATOR_TTL_LEDGERS
);
}

#[test]
fn test_ttl_extended_event_payload_field_order_is_documented() {
assert_eq!(events::TTL_EXTENDED_DATA_FIELDS, ["creator", "extend_to", "ledger"]);
}
Loading