Skip to main content

soroban_sdk/_migrating/
v23_contractevent.rs

1//! [`contractevent`] replaces [`Events::publish`].
2//!
3//! The [`contractevent`] macro provides a type-safe way to define and publish events, and
4//! includes the event into the contract interface specification so that tooling, SDKs, and
5//! generated clients can understand the events published.
6//!
7//! ## Example
8//!
9//! For example, consider the following event publishing code:
10//!
11//! ```
12//! # #![cfg(feature = "testutils")]
13//! # use soroban_sdk::{contract, contractimpl, symbol_short, vec, Env, Address, IntoVal, Map, Symbol, Val, testutils::{Address as _, Events as _}};
14//! #
15//! # #[contract]
16//! # pub struct Contract;
17//! #
18//! # fn main() {
19//! #     let env = Env::default();
20//! #     let id = env.register(Contract, ());
21//! #     let addr = Address::generate(&env);
22//! #     let count = 123u32;
23//! #     env.as_contract(&id, || {
24//! // Define and publish the event:
25//! env.events().publish(
26//!     // Event topics
27//!     (symbol_short!("increment"), &addr),
28//!     // Event data
29//!     Map::<Symbol, Val>::from_array(&env, [
30//!         (symbol_short!("count"), count.into())
31//!     ]),
32//! );
33//! #     });
34//!
35//! // Assert in tests on the published topics and data:
36//! assert_eq!(
37//!     env.events().all(),
38//!     vec![&env,
39//!         (
40//!             id.clone(),
41//!             // Event topics
42//!             (symbol_short!("increment"), &addr).into_val(&env),
43//!             // Event data
44//!             Map::<Symbol, Val>::from_array(&env, [
45//!                 (symbol_short!("count"), count.into())
46//!             ]).into_val(&env),
47//!         ),
48//!     ]
49//! );
50//! # }
51//! ```
52//!
53//! Replace it with the following code using [`contractevent`]:
54//!
55//! ```
56//! # #![cfg(feature = "testutils")]
57//! # use soroban_sdk::{contract, contractevent, contractimpl, symbol_short, vec, Env, Address, IntoVal, Map, Symbol, Val, testutils::{Address as _, Events as _}};
58//! #
59//! # #[contract]
60//! # pub struct Contract;
61//! #
62//! # fn main() {
63//! #     let env = Env::default();
64//! #     let id = env.register(Contract, ());
65//! #     let addr = Address::generate(&env);
66//! #     let count = 123;
67//! #     env.as_contract(&id, || {
68//! // Define the event:
69//! #[contractevent]
70//! pub struct Increment {
71//!   #[topic]
72//!   addr: Address,
73//!   count: u32,
74//! }
75//!
76//! // Publish the event:
77//! Increment {
78//!   addr: addr.clone(),
79//!   count: count,
80//! }.publish(&env);
81//! #     });
82//!
83//! // Assert in tests on the published topics and data:
84//! assert_eq!(
85//!     env.events().all(),
86//!     vec![&env,
87//!         (
88//!             id.clone(),
89//!             // Event topics
90//!             (symbol_short!("increment"), &addr).into_val(&env),
91//!             // Event data
92//!             Map::<Symbol, Val>::from_array(&env, [
93//!                 (symbol_short!("count"), count.into())
94//!             ]).into_val(&env),
95//!         ),
96//!     ]
97//! );
98//! # }
99//! ```
100//!
101//! ## Example: Vec Data
102//!
103//! By default the parameters not marked as `#[topic]`s are collected into a [`Map`] like in the
104//! example above. If transitioning events that publish parameters in a [`Vec`], follow the
105//! this example.
106//!
107//! Consider the following event publishing code:
108//!
109//! ```
110//! # #![cfg(feature = "testutils")]
111//! # use soroban_sdk::{contract, contractimpl, symbol_short, vec, Env, Address, IntoVal, Vec, Symbol, Val, testutils::{Address as _, Events as _}};
112//! #
113//! # #[contract]
114//! # pub struct Contract;
115//! #
116//! # fn main() {
117//! #     let env = Env::default();
118//! #     let id = env.register(Contract, ());
119//! #     let addr = Address::generate(&env);
120//! #     let count = 123u32;
121//! #     env.as_contract(&id, || {
122//! // Define and publish the event:
123//! env.events().publish(
124//!     // Event topics
125//!     (symbol_short!("increment"), &addr),
126//!     // Event data
127//!     Vec::<Val>::from_array(&env, [count.into()]),
128//! );
129//! #     });
130//!
131//! // Assert in tests on the published topics and data:
132//! assert_eq!(
133//!     env.events().all(),
134//!     vec![&env,
135//!         (
136//!             id.clone(),
137//!             // Event topics
138//!             (symbol_short!("increment"), &addr).into_val(&env),
139//!             // Event data
140//!             Vec::<Val>::from_array(&env, [count.into()]).into_val(&env),
141//!         ),
142//!     ]
143//! );
144//! # }
145//! ```
146//!
147//! Replace it with the following code using [`contractevent`]:
148//!
149//! ```
150//! # #![cfg(feature = "testutils")]
151//! # use soroban_sdk::{contract, contractevent, contractimpl, symbol_short, vec, Env, Address, IntoVal, Vec, Symbol, Val, testutils::{Address as _, Events as _}};
152//! #
153//! # #[contract]
154//! # pub struct Contract;
155//! #
156//! # fn main() {
157//! #     let env = Env::default();
158//! #     let id = env.register(Contract, ());
159//! #     let addr = Address::generate(&env);
160//! #     let count = 123;
161//! #     env.as_contract(&id, || {
162//! // Define the event:
163//! #[contractevent(data_format = "vec")]
164//! pub struct Increment {
165//!   #[topic]
166//!   addr: Address,
167//!   count: u32,
168//! }
169//!
170//! // Publish the event:
171//! Increment {
172//!   addr: addr.clone(),
173//!   count: count,
174//! }.publish(&env);
175//! #     });
176//!
177//! // Assert in tests on the published topics and data:
178//! assert_eq!(
179//!     env.events().all(),
180//!     vec![&env,
181//!         (
182//!             id.clone(),
183//!             // Event topics
184//!             (symbol_short!("increment"), &addr).into_val(&env),
185//!             // Event data
186//!             Vec::<Val>::from_array(&env, [count.into()]).into_val(&env),
187//!         ),
188//!     ]
189//! );
190//! # }
191//! ```
192//!
193//! ## Example: Other Data
194//!
195//! If transitioning events that publish some other type directly into the event's data field,
196//! follow the this example.
197//!
198//! Consider the following event publishing code:
199//!
200//! ```
201//! # #![cfg(feature = "testutils")]
202//! # use soroban_sdk::{contract, contractimpl, symbol_short, vec, Env, Address, IntoVal, Vec, Symbol, Val, testutils::{Address as _, Events as _}};
203//! #
204//! # #[contract]
205//! # pub struct Contract;
206//! #
207//! # fn main() {
208//! #     let env = Env::default();
209//! #     let id = env.register(Contract, ());
210//! #     let addr = Address::generate(&env);
211//! #     let count = 123u32;
212//! #     env.as_contract(&id, || {
213//! // Define and publish the event:
214//! env.events().publish(
215//!     // Event topics
216//!     (symbol_short!("increment"), &addr),
217//!     // Event data
218//!     count,
219//! );
220//! #     });
221//!
222//! // Assert in tests on the published topics and data:
223//! assert_eq!(
224//!     env.events().all(),
225//!     vec![&env,
226//!         (
227//!             id.clone(),
228//!             // Event topics
229//!             (symbol_short!("increment"), &addr).into_val(&env),
230//!             // Event data
231//!             count.into(),
232//!         ),
233//!     ]
234//! );
235//! # }
236//! ```
237//!
238//! Replace it with the following code using [`contractevent`]:
239//!
240//! ```
241//! # #![cfg(feature = "testutils")]
242//! # use soroban_sdk::{contract, contractevent, contractimpl, symbol_short, vec, Env, Address, IntoVal, Map, Symbol, Val, testutils::{Address as _, Events as _}};
243//! #
244//! # #[contract]
245//! # pub struct Contract;
246//! #
247//! # fn main() {
248//! #     let env = Env::default();
249//! #     let id = env.register(Contract, ());
250//! #     let addr = Address::generate(&env);
251//! #     let count = 123;
252//! #     env.as_contract(&id, || {
253//! // Define the event:
254//! #[contractevent(data_format = "single-value")]
255//! pub struct Increment {
256//!   #[topic]
257//!   addr: Address,
258//!   count: u32,
259//! }
260//!
261//! // Publish the event:
262//! Increment {
263//!   addr: addr.clone(),
264//!   count: count,
265//! }.publish(&env);
266//! #     });
267//!
268//! // Assert in tests on the published topics and data:
269//! assert_eq!(
270//!     env.events().all(),
271//!     vec![&env,
272//!         (
273//!             id.clone(),
274//!             // Event topics
275//!             (symbol_short!("increment"), &addr).into_val(&env),
276//!             // Event data
277//!             count.into(),
278//!         ),
279//!     ]
280//! );
281//! # }
282//! ```
283//! ## Example: Customising Topics
284//!
285//! By default the topics of an event are made up of a single static topic that is the event's name
286//! converted to snake_case, along with any dynamic topics specified by `#[topic]` on the field.
287//!
288//! ### Custom Static Topic
289//!
290//! The static topic can be changed using the `topics = [...]` option on [`contractevent`]:
291//!
292//! ```
293//! # #![cfg(feature = "testutils")]
294//! # use soroban_sdk::{contract, contractevent, contractimpl, symbol_short, vec, Env, Address, IntoVal, Map, Symbol, Val, testutils::{Address as _, Events as _}};
295//! #
296//! # #[contract]
297//! # pub struct Contract;
298//! #
299//! # fn main() {
300//! #     let env = Env::default();
301//! #     let id = env.register(Contract, ());
302//! #     let addr = Address::generate(&env);
303//! #     let count = 123;
304//! #     env.as_contract(&id, || {
305//! #[contractevent(topics = ["count_chn"])]
306//! pub struct Increment {
307//!   #[topic]
308//!   addr: Address,
309//!   count: u32,
310//! }
311//! #
312//! #         Increment {
313//! #           addr: addr.clone(),
314//! #           count: count,
315//! #         }.publish(&env);
316//! #     });
317//!
318//! // Assert in tests on the published topics and data:
319//! assert_eq!(
320//!     env.events().all(),
321//!     vec![&env,
322//!         (
323//!             id.clone(),
324//!             // Event topics
325//!             (symbol_short!("count_chn"), &addr,).into_val(&env),
326//!             // Event data
327//!             Map::<Symbol, Val>::from_array(&env, [
328//!                 (symbol_short!("count"), count.into())
329//!             ]).into_val(&env),
330//!         ),
331//!     ]
332//! );
333//! # }
334//! ```
335//!
336//! ### Multiple Static Topics
337//!
338//! Multiple static topics can be set using the `topics = [...]` option on [`contractevent`], with
339//! up to two values:
340//!
341//! ```
342//! # #![cfg(feature = "testutils")]
343//! # use soroban_sdk::{contract, contractevent, contractimpl, symbol_short, vec, Env, Address, IntoVal, Map, Symbol, Val, testutils::{Address as _, Events as _}};
344//! #
345//! # #[contract]
346//! # pub struct Contract;
347//! #
348//! # fn main() {
349//! #     let env = Env::default();
350//! #     let id = env.register(Contract, ());
351//! #     let addr = Address::generate(&env);
352//! #     let count = 123;
353//! #     env.as_contract(&id, || {
354//! #[contractevent(topics = ["count", "increment"])]
355//! pub struct Increment {
356//!   #[topic]
357//!   addr: Address,
358//!   count: u32,
359//! }
360//! #
361//! #         Increment {
362//! #           addr: addr.clone(),
363//! #           count: count,
364//! #         }.publish(&env);
365//! #     });
366//!
367//! // Assert in tests on the published topics and data:
368//! assert_eq!(
369//!     env.events().all(),
370//!     vec![&env,
371//!         (
372//!             id.clone(),
373//!             // Event topics
374//!             (symbol_short!("count"), symbol_short!("increment"), &addr,).into_val(&env),
375//!             // Event data
376//!             Map::<Symbol, Val>::from_array(&env, [
377//!                 (symbol_short!("count"), count.into())
378//!             ]).into_val(&env),
379//!         ),
380//!     ]
381//! );
382//! # }
383//! ```
384//!
385//! ### No Static Topics
386//!
387//! Zero static topics can be specified with the following configuration where `topics = []` is
388//! provided to [`contractevent`]:
389//!
390//! ```
391//! # #![cfg(feature = "testutils")]
392//! # use soroban_sdk::{contract, contractevent, contractimpl, symbol_short, vec, Env, Address, IntoVal, Map, Symbol, Val, testutils::{Address as _, Events as _}};
393//! #
394//! # #[contract]
395//! # pub struct Contract;
396//! #
397//! # fn main() {
398//! #     let env = Env::default();
399//! #     let id = env.register(Contract, ());
400//! #     let addr = Address::generate(&env);
401//! #     let count = 123;
402//! #     env.as_contract(&id, || {
403//! #[contractevent(topics = [])]
404//! pub struct Increment {
405//!   #[topic]
406//!   addr: Address,
407//!   count: u32,
408//! }
409//! #
410//! #         Increment {
411//! #           addr: addr.clone(),
412//! #           count: count,
413//! #         }.publish(&env);
414//! #     });
415//!
416//! // Assert in tests on the published topics and data:
417//! assert_eq!(
418//!     env.events().all(),
419//!     vec![&env,
420//!         (
421//!             id.clone(),
422//!             // Event topics
423//!             (&addr,).into_val(&env),
424//!             // Event data
425//!             Map::<Symbol, Val>::from_array(&env, [
426//!                 (symbol_short!("count"), count.into())
427//!             ]).into_val(&env),
428//!         ),
429//!     ]
430//! );
431//! # }
432//! ```
433//!
434//! [`Events::publish`]: crate::events::Events::publish
435//! [`Address`]: crate::MuxedAddress
436//! [`MuxedAddress`]: crate::MuxedAddress
437//! [`contractevent`]: crate::contractevent
438//! [`Map`]: crate::Map