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