From 5660e5abfe6a1038814083677f9dd65086e9c4e2 Mon Sep 17 00:00:00 2001 From: JACOB STANLEY Date: Mon, 20 Jul 2026 01:03:41 +0100 Subject: [PATCH 1/2] https://github.com/jaynomyaro/accesslayer-contracts.git --- creator-keys/src/events.rs | 27 +++++++++++++ creator-keys/src/lib.rs | 9 +++++ creator-keys/tests/events.rs | 76 ++++++++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+) diff --git a/creator-keys/src/events.rs b/creator-keys/src/events.rs index c216eecb..318e236a 100644 --- a/creator-keys/src/events.rs +++ b/creator-keys/src/events.rs @@ -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"]; @@ -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 { @@ -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, @@ -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"); diff --git a/creator-keys/src/lib.rs b/creator-keys/src/lib.rs index 4c7070a4..110b976a 100644 --- a/creator-keys/src/lib.rs +++ b/creator-keys/src/lib.rs @@ -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] diff --git a/creator-keys/tests/events.rs b/creator-keys/tests/events.rs index cc2e8e12..01c43796 100644 --- a/creator-keys/tests/events.rs +++ b/creator-keys/tests/events.rs @@ -424,3 +424,79 @@ 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 last = events.last().unwrap(); + + let event_name: Symbol = last + .1 + .get(events::TOPIC_EVENT_NAME_INDEX) + .unwrap() + .into_val(&env); + let event_creator: Address = last + .1 + .get(events::TOPIC_CREATOR_INDEX) + .unwrap() + .into_val(&env); + let payload: events::TtlExtendedEvent = last.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 last = events.last().unwrap(); + + let event_name: Symbol = last + .1 + .get(events::TOPIC_EVENT_NAME_INDEX) + .unwrap() + .into_val(&env); + let event_creator: Address = last + .1 + .get(events::TOPIC_CREATOR_INDEX) + .unwrap() + .into_val(&env); + let payload: events::TtlExtendedEvent = last.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"]); +} From 6ca95f276a1367611f2d5946e66df71b6b7c4ae5 Mon Sep 17 00:00:00 2001 From: JACOB STANLEY Date: Tue, 21 Jul 2026 01:38:31 +0100 Subject: [PATCH 2/2] Fix TTL extension event tests to search for specific event instead of assuming last event --- creator-keys/tests/events.rs | 42 +++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/creator-keys/tests/events.rs b/creator-keys/tests/events.rs index 01c43796..6748da19 100644 --- a/creator-keys/tests/events.rs +++ b/creator-keys/tests/events.rs @@ -436,19 +436,30 @@ fn test_ttl_extended_event_emitted_after_buy() { fixture.buy_key(&buyer, KEY_PRICE); let events = env.events().all(); - let last = events.last().unwrap(); - - let event_name: Symbol = last + 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 = last + let event_creator: Address = ttl_event .1 .get(events::TOPIC_CREATOR_INDEX) .unwrap() .into_val(&env); - let payload: events::TtlExtendedEvent = last.2.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); @@ -472,19 +483,30 @@ fn test_ttl_extended_event_emitted_after_sell() { fixture.sell_key(&seller); let events = env.events().all(); - let last = events.last().unwrap(); - - let event_name: Symbol = last + 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 = last + let event_creator: Address = ttl_event .1 .get(events::TOPIC_CREATOR_INDEX) .unwrap() .into_val(&env); - let payload: events::TtlExtendedEvent = last.2.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);