Developers
Blueprint SDK Event Listeners
Custom Listeners

To create an event listener, begin by defining a struct or enum which will listen to events:

use gadget_sdk::event_listener::EventListener;
use async_trait::async_trait;
 
/// Define a simple event listener that ticks every MS milliseconds.
pub struct Ticker<const MSEC: usize> {
    additional_delay: u64,
}

Then, define a context for the event listener:

#[derive(Copy, Clone)]
pub struct MyContext {
    pub additional_delay: u64,
}

Next, implement EventListener for Ticker:

/// Implement the [`EventListener`] trait for the Ticker struct. [`EventListener`] has two type parameters:
///
/// - T: is the type of the event that the listener listens for. This can be anything that is Send + Sync + 'static.
/// In this case, we are using [`Instant`], which is a timestamp.
///
/// - Ctx: is the context type that the listener receives when constructed. This can be anything that is Send + Sync + 'static,
/// with the special requirement that it is the *first* listed additional parameter in the [`job`] macro (i.e., a parameter not
/// in params(...)). In this case, we are using [`MyContext`].
#[async_trait]
impl<const MSEC: usize> EventListener<Instant, MyContext> for Ticker<MSEC> {
    /// Use a reference to the context, [`MyContext`], to construct the listener
    async fn new(context: &MyContext) -> Result<Self, Error>
    where
        Self: Sized,
    {
        Ok(Self { additional_delay: context.additional_delay })
    }
 
    /// Implement the logic that looks for events. In this case, we have a simple stream
    /// that returns after MS milliseconds.
    async fn next_event(&mut self) -> Option<Instant> {
        tokio::time::sleep(tokio::time::Duration::from_millis(MSEC as u64 + self.additional_delay)).await;
        Some(Instant::now())
    }
 
    /// After next_event is called, the event gets passed here. This is where you would implement
    /// listener-specific logic.
    async fn handle_event(&mut self, _event: Instant) -> Result<(), Error> {
        Ok(())
    }
}

Finally, register the event listener inside the job macro using event_listener:

#[job(
    id = 0,
    params(x),
    result(_),
    event_listener(listener = Ticker::<6000>), // <-- Register the event listener here
)]
pub fn hello_event_listener(
    x: u64,
    context: MyContext, // <-- The context type must be the first additional parameter
) -> Result<u64, Infallible> {
    Ok(x.saturating_pow(2u32))
}