pallet_hookpoints/lib.rs
1// Copyright (C) Deep Ink Ventures GmbH
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! HookPoint Pallet
16//!
17//! The HookPoint pallet provides a mechanism to manage, register, and execute hook points in the runtime.
18//! These hook points act as predefined points in code execution where custom logic (in the form of
19//! callbacks) can be injected, allowing for extensibility and custom behaviors.
20//!
21//! The primary components of this pallet are:
22//! - `GlobalCallbacks`: A storage map that holds global callbacks registered for a specific owner.
23//! These callbacks act as default behavior when no specific callback is registered for an event.
24//! - `SpecificCallbacks`: A double map storage that holds specific callbacks for particular events.
25//! These callbacks take precedence over global callbacks.
26//! - Registration Functions: Allows users to register global or specific callbacks.
27//! - Execution Function: Allows the execution of a hook point, invoking the associated callback.
28//!
29//! This pallet interacts closely with the `pallet_contracts` to manage and execute contracts as callbacks.
30
31#![cfg_attr(not(feature = "std"), no_std)]
32use sp_std::prelude::*;
33
34pub use pallet::*;
35
36#[cfg(test)]
37mod mock;
38
39#[cfg(test)]
40mod tests;
41
42#[cfg(feature = "runtime-benchmarks")]
43mod benchmarking;
44
45mod functions;
46mod builder;
47pub mod weights;
48use weights::WeightInfo;
49
50#[frame_support::pallet]
51pub mod pallet {
52
53 use super::*;
54 use frame_support::pallet_prelude::*;
55 use frame_system::pallet_prelude::*;
56
57 #[pallet::pallet]
58 pub struct Pallet<T>(_);
59
60 #[pallet::config]
61 pub trait Config: frame_system::Config + pallet_contracts::Config {
62
63 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
64
65 /// Maximum callback identifier length
66 #[pallet::constant]
67 type MaxLengthId: Get<u32>;
68 type WeightInfo: WeightInfo;
69 }
70
71 #[pallet::storage]
72 #[pallet::getter(fn callbacks)]
73 pub type GlobalCallbacks<T: Config> =
74 StorageMap<Hasher = Blake2_128Concat, Key = T::AccountId, Value = T::AccountId>;
75
76 #[pallet::storage]
77 #[pallet::getter(fn specific_callbacks)]
78 pub type SpecificCallbacks<T: Config> = StorageDoubleMap<
79 Hasher1 = Blake2_128Concat,
80 Key1 = T::AccountId,
81 Hasher2 = Blake2_128Concat,
82 Key2 = BoundedVec<u8, T::MaxLengthId>,
83 Value = T::AccountId,
84 >;
85
86 #[pallet::event]
87 #[pallet::generate_deposit(pub(super) fn deposit_event)]
88 pub enum Event<T: Config> {
89 /// Event documentation should end with an array that provides descriptive names for event
90 /// parameters. [something, who]
91 GlobalCallbackRegistered {
92 who: T::AccountId,
93 },
94 SpecificCallbackRegistered {
95 who: T::AccountId,
96 },
97 }
98
99 // Errors inform users that something went wrong.
100 #[pallet::error]
101 pub enum Error<T> {
102 /// id is too long
103 IdTooLong,
104 }
105
106 #[pallet::call]
107 impl<T: Config> Pallet<T> {
108 /// Register a global ink! callback for an address.
109 ///
110 /// This contract is used for each callback within the substrate system.
111 /// It allows a user to set a default callback contract that will be triggered
112 /// for any hook point unless a specific callback is set.
113 ///
114 /// - `contract`: The contract address to use as the endpoint for all callbacks.
115 #[pallet::call_index(0)]
116 #[pallet::weight(<T as Config>::WeightInfo::register_global_callback())]
117 pub fn register_global_callback(
118 origin: OriginFor<T>,
119 contract: T::AccountId,
120 ) -> DispatchResult {
121 let who = ensure_signed(origin)?;
122 GlobalCallbacks::<T>::insert(&who, contract);
123 Self::deposit_event(Event::GlobalCallbackRegistered { who });
124 Ok(())
125 }
126
127 /// Register a specific ink! callback for an address.
128 ///
129 /// Allows a user to set a callback for a specific hook point.
130 /// This will take precedence over the global callback for the given hook point.
131 ///
132 /// - `contract`: Contract address to use as the endpoint for this specific callback.
133 /// - `id`: Identifier for this registration.
134 #[pallet::call_index(1)]
135 #[pallet::weight(<T as Config>::WeightInfo::register_specific_callback())]
136 pub fn register_specific_callback(
137 origin: OriginFor<T>,
138 contract: T::AccountId,
139 id: Vec<u8>,
140 ) -> DispatchResult {
141 let who = ensure_signed(origin)?;
142 let id: BoundedVec<_, _> = id.try_into().map_err(|_| Error::<T>::IdTooLong)?;
143 SpecificCallbacks::<T>::insert(&who, id, contract);
144 Self::deposit_event(Event::SpecificCallbackRegistered { who });
145 Ok(())
146 }
147 }
148}