soroban_sdk/events.rs
1//! Events contains types for publishing contract events.
2use core::fmt::Debug;
3
4#[cfg(doc)]
5use crate::{contracttype, Bytes, Map};
6use crate::{env::internal, unwrap::UnwrapInfallible, Env, IntoVal, Val, Vec};
7
8// TODO: consolidate with host::events::TOPIC_BYTES_LENGTH_LIMIT
9const TOPIC_BYTES_LENGTH_LIMIT: u32 = 32;
10
11/// Events publishes events for the currently executing contract.
12///
13/// ```
14/// use soroban_sdk::Env;
15///
16/// # use soroban_sdk::{contract, contractimpl, vec, map, Val, BytesN};
17/// #
18/// # #[contract]
19/// # pub struct Contract;
20/// #
21/// # #[contractimpl]
22/// # impl Contract {
23/// # pub fn f(env: Env) {
24/// let event = env.events();
25/// let data = map![&env, (1u32, 2u32)];
26/// // topics can be represented with tuple up to a certain length
27/// let topics0 = ();
28/// let topics1 = (0u32,);
29/// let topics2 = (0u32, 1u32);
30/// let topics3 = (0u32, 1u32, 2u32);
31/// let topics4 = (0u32, 1u32, 2u32, 3u32);
32/// // topics can also be represented with a `Vec` with no length limit
33/// let topics5 = vec![&env, 4u32, 5u32, 6u32, 7u32, 8u32];
34/// event.publish(topics0, data.clone());
35/// event.publish(topics1, data.clone());
36/// event.publish(topics2, data.clone());
37/// event.publish(topics3, data.clone());
38/// event.publish(topics4, data.clone());
39/// event.publish(topics5, data.clone());
40/// # }
41/// # }
42///
43/// # #[cfg(feature = "testutils")]
44/// # fn main() {
45/// # let env = Env::default();
46/// # let contract_id = env.register(Contract, ());
47/// # ContractClient::new(&env, &contract_id).f();
48/// # }
49/// # #[cfg(not(feature = "testutils"))]
50/// # fn main() { }
51/// ```
52#[derive(Clone)]
53pub struct Events(Env);
54
55impl Debug for Events {
56 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
57 write!(f, "Events")
58 }
59}
60
61pub trait Topics: IntoVal<Env, Vec<Val>> {}
62
63impl<T> Topics for Vec<T> {}
64
65impl Events {
66 #[inline(always)]
67 pub(crate) fn env(&self) -> &Env {
68 &self.0
69 }
70
71 #[inline(always)]
72 pub(crate) fn new(env: &Env) -> Events {
73 Events(env.clone())
74 }
75
76 /// Publish an event.
77 ///
78 /// Event data is specified in `data`. Data may be any value or
79 /// type, including types defined by contracts using [contracttype].
80 ///
81 /// Event topics must not contain:
82 ///
83 /// - [Vec]
84 /// - [Map]
85 /// - [Bytes]/[BytesN][crate::BytesN] longer than 32 bytes
86 /// - [contracttype]
87 #[inline(always)]
88 pub fn publish<T, D>(&self, topics: T, data: D)
89 where
90 T: Topics,
91 D: IntoVal<Env, Val>,
92 {
93 let env = self.env();
94 internal::Env::contract_event(env, topics.into_val(env).to_object(), data.into_val(env))
95 .unwrap_infallible();
96 }
97}
98
99#[cfg(any(test, feature = "testutils"))]
100use crate::{testutils, xdr, Address, TryIntoVal};
101
102#[cfg(any(test, feature = "testutils"))]
103#[cfg_attr(feature = "docs", doc(cfg(feature = "testutils")))]
104impl testutils::Events for Events {
105 fn all(&self) -> Vec<(crate::Address, Vec<Val>, Val)> {
106 let env = self.env();
107 let mut vec = Vec::new(env);
108 self.env()
109 .host()
110 .get_events()
111 .unwrap()
112 .0
113 .into_iter()
114 .for_each(|e| {
115 if let xdr::ContractEvent {
116 type_: xdr::ContractEventType::Contract,
117 contract_id: Some(contract_id),
118 body: xdr::ContractEventBody::V0(xdr::ContractEventV0 { topics, data }),
119 ..
120 } = e.event
121 {
122 vec.push_back((
123 Address::from_contract_id(env, contract_id.0),
124 topics.try_into_val(env).unwrap(),
125 data.try_into_val(env).unwrap(),
126 ))
127 }
128 });
129 vec
130 }
131}