@@ -45,6 +45,17 @@ pub struct SwapCancelledEvent {
4545 pub canceller : Address ,
4646}
4747
48+ // ── Events ────────────────────────────────────────────────────────────────────
49+
50+ /// Payload published when a key is successfully revealed and the swap completes.
51+ /// Topic: `key_revld` (symbol_short, max 9 chars) — used by off-chain indexers.
52+ #[ contracttype]
53+ #[ derive( Clone , Debug , PartialEq ) ]
54+ pub struct KeyRevealedEvent {
55+ pub swap_id : u64 ,
56+ pub decryption_key : BytesN < 32 > ,
57+ }
58+
4859// ── Contract ──────────────────────────────────────────────────────────────────
4960
5061#[ contract]
@@ -114,8 +125,10 @@ impl AtomicSwap {
114125 env. storage ( ) . persistent ( ) . set ( & DataKey :: Swap ( swap_id) , & swap) ;
115126 }
116127
117- /// Seller reveals the decryption key; escrowed payment releases to seller.
118- pub fn reveal_key ( env : Env , swap_id : u64 , _decryption_key : BytesN < 32 > ) {
128+ /// Seller reveals the decryption key; payment releases.
129+ /// Emits a `key_revld` event on success so external systems can detect
130+ /// when the key becomes available.
131+ pub fn reveal_key ( env : Env , swap_id : u64 , decryption_key : BytesN < 32 > ) {
119132 let mut swap: SwapRecord = env
120133 . storage ( )
121134 . persistent ( )
@@ -131,6 +144,12 @@ impl AtomicSwap {
131144
132145 swap. status = SwapStatus :: Completed ;
133146 env. storage ( ) . persistent ( ) . set ( & DataKey :: Swap ( swap_id) , & swap) ;
147+
148+ // Emit event — only reached after successful state transition.
149+ env. events ( ) . publish (
150+ ( symbol_short ! ( "key_revld" ) , ) ,
151+ KeyRevealedEvent { swap_id, decryption_key } ,
152+ ) ;
134153 }
135154
136155 /// Cancel a swap (invalid key or timeout). Emits a `swap_cancelled` event
@@ -463,3 +482,92 @@ mod tests {
463482 assert_eq ! ( env. events( ) . all( ) . len( ) , 0 ) ;
464483 }
465484}
485+
486+ // ── Tests ─────────────────────────────────────────────────────────────────────
487+
488+ #[ cfg( test) ]
489+ mod tests {
490+ use super :: * ;
491+ use soroban_sdk:: {
492+ testutils:: { Address as _, BytesN as _, Events } ,
493+ vec, Env , IntoVal ,
494+ } ;
495+
496+ fn setup ( ) -> ( Env , Address ) {
497+ let env = Env :: default ( ) ;
498+ env. mock_all_auths ( ) ;
499+ let contract_id = env. register_contract ( None , AtomicSwap ) ;
500+ ( env, contract_id)
501+ }
502+
503+ /// Bring a swap to Accepted state and return its ID + the key used.
504+ fn accepted_swap ( env : & Env , client : & AtomicSwapClient ) -> ( u64 , BytesN < 32 > ) {
505+ let buyer = Address :: generate ( env) ;
506+ let swap_id = client. initiate_swap ( & 1u64 , & 1000_i128 , & buyer) ;
507+ client. accept_swap ( & swap_id) ;
508+ let key = BytesN :: random ( env) ;
509+ ( swap_id, key)
510+ }
511+
512+ #[ test]
513+ fn test_reveal_key_emits_event_with_correct_values ( ) {
514+ let ( env, contract_id) = setup ( ) ;
515+ let client = AtomicSwapClient :: new ( & env, & contract_id) ;
516+ let ( swap_id, key) = accepted_swap ( & env, & client) ;
517+
518+ client. reveal_key ( & swap_id, & key) ;
519+
520+ // State must be Completed
521+ assert_eq ! ( client. get_swap( & swap_id) . status, SwapStatus :: Completed ) ;
522+
523+ // Exactly one event emitted
524+ let events = env. events ( ) . all ( ) ;
525+ assert_eq ! ( events. len( ) , 1 ) ;
526+
527+ // Topic is correct
528+ let ( _, topics, data) = events. get ( 0 ) . unwrap ( ) ;
529+ assert_eq ! ( topics, vec![ & env, symbol_short!( "key_revld" ) . into_val( & env) ] ) ;
530+
531+ // Payload fields match exactly
532+ let payload: KeyRevealedEvent = data. into_val ( & env) ;
533+ assert_eq ! ( payload. swap_id, swap_id) ;
534+ assert_eq ! ( payload. decryption_key, key) ;
535+ }
536+
537+ #[ test]
538+ #[ should_panic( expected = "swap not accepted" ) ]
539+ fn test_reveal_key_on_pending_swap_fails_no_event ( ) {
540+ let ( env, contract_id) = setup ( ) ;
541+ let client = AtomicSwapClient :: new ( & env, & contract_id) ;
542+ let buyer = Address :: generate ( & env) ;
543+ let swap_id = client. initiate_swap ( & 1u64 , & 1000_i128 , & buyer) ;
544+ let key = BytesN :: random ( & env) ;
545+
546+ // Swap is still Pending — must panic before event fires
547+ client. reveal_key ( & swap_id, & key) ;
548+ }
549+
550+ #[ test]
551+ #[ should_panic( expected = "swap not accepted" ) ]
552+ fn test_reveal_key_on_completed_swap_fails_no_event ( ) {
553+ let ( env, contract_id) = setup ( ) ;
554+ let client = AtomicSwapClient :: new ( & env, & contract_id) ;
555+ let ( swap_id, key) = accepted_swap ( & env, & client) ;
556+
557+ client. reveal_key ( & swap_id, & key) ;
558+ // Second call on an already-Completed swap — must panic
559+ client. reveal_key ( & swap_id, & key) ;
560+ }
561+
562+ #[ test]
563+ fn test_no_event_emitted_on_normal_completion_without_reveal ( ) {
564+ let ( env, contract_id) = setup ( ) ;
565+ let client = AtomicSwapClient :: new ( & env, & contract_id) ;
566+ let buyer = Address :: generate ( & env) ;
567+ let swap_id = client. initiate_swap ( & 1u64 , & 1000_i128 , & buyer) ;
568+ client. accept_swap ( & swap_id) ;
569+
570+ // No reveal_key called — events list must be empty
571+ assert_eq ! ( env. events( ) . all( ) . len( ) , 0 ) ;
572+ }
573+ }
0 commit comments