6

From the following function I can read the frame_system events:

let events = frame_system::Pallet::<T>::read_events_no_consensus();

the above provides me the following example array:

        EventRecord {
            phase: Phase::ApplyExtrinsic(1),
            event: Event::Balances(Event::Withdraw {
                who: d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d(5 GrwvaEF...),
                amount: 125000107
            }),
            topics: []
        },
        EventRecord {
            phase: Phase::ApplyExtrinsic(1),
            event: Event::SubstrateKitties(Event::Created {
                kitty: [177, 185, 56, 80, 240, 56, 24, 57, 112, 17, 186, 6, 89, 221, 25, 98],
                owner: d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d(5 GrwvaEF...)
            }),
            topics: []
        },
        EventRecord {
            phase: Phase::ApplyExtrinsic(1),
            event: Event::System(Event::ExtrinsicSuccess {
                dispatch_info: DispatchInfo {
                    weight: 0,
                    class: DispatchClass::Normal,
                    pays_fee: Pays::Yes
                }
            }),
            topics: []
        }

I would like to be able to parse/match ONLY the Event::SubstrateKitties(Event::Created) events.

I also reviewed the link below and Shawn Tabrizi recommendation related to assert_last_event use cases: https://stackoverflow.com/questions/64199998/converting-between-event-types

However, I still haven't found a proper way to do this. Below is a simplify example of my latest code so far:

        #[pallet::hooks]
        impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
            /// Block finalization
            fn on_finalize(_n: BlockNumberFor<T>) {
                let events = frame_system::Pallet::<T>::read_events_no_consensus();
        
                events.iter().for_each(|EventRecord { event, .. }| {
                    match event {
                        // What should I write here?? => {
                            log::info("SubstrateKitties event: {:?}", event);
                        },
                        _ => {
                            log::info!("Ignoring system events.");
                        },
                    }                   
                });
            }
        }
    ```

3
  • 1
    Please note that it's strongly recommended to NOT read the event records in the runtime. Events can grow very large (think about there were >20mb blocks on Kusama), and the read operation is very expensive. So when you read the event, you essentially read all the events from the storage at once, and iterate over them one by one. Instead, you can create your own vector storage item to track the event you are interested.
    – h4x3rotab
    Commented Mar 23, 2022 at 6:00
  • @h4x3rotab, your are totally right. Thank you for your contribution. Commented Mar 23, 2022 at 21:50
  • Can you read all events for a specific block (perhaps in an offchain worker)? Commented Jan 18, 2023 at 18:20

2 Answers 2

6

The code removed in this PR is exactly what you need to do. Trying to translate it into your case would yield:


#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
    fn on_finalize(_n: BlockNumberFor<T>) {
        let events = frame_system::Pallet::<T>::read_events_no_consensus();

        events.iter().for_each(|EventRecord { event, .. }| {
            // try and convert the event into a local event.
            let local_event = <T as Config>::Event::from(event);
            match local_event.try_into() {
                Ok(ev)
                    // this is an event from this pallet, work with it. 
                },
                _ => {
                    // this is an error from some other pallet
                },
            }
        });
    }
}

Let's now break down what is happening here:

The top level runtime has an outer event, which is this type. As you see, this is just a wrapper for all pallet events. This type implement TryInto<T> where T is individual pallet events. Example for pallet-balances here.

In your code, frame_system::Pallet::<T>::read_events_no_consensus(); returns the outer event type, which you have to iterate and individually try and convert into your local pallet event.

1
  • NB: Ok(ev) has a typo: Ok(ev) => { TIA Commented Mar 21, 2022 at 22:03
4

@kianenigma, thanks for you extended and detailed answer. There was just one small missing piece. I had to setup the TryInto<Event<Self>> to the #[pallet::config] macro or otherwise your will get a is not implemented for pallet::Event<_> error. Thanks for your support. Great job!

    // Configure the pallet by specifying the parameters and types on which it depends.
    #[pallet::config]
    pub trait Config: frame_system::Config {
        /// Because this pallet emits events, it depends on the runtime's definition of an event.
        type Event: From<Event<Self>>
            + IsType<<Self as frame_system::Config>::Event>
            + TryInto<Event<Self>>;

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.