1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
// This file is part of Astar.
// Copyright (C) Stake Technologies Pte.Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later
// Astar is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Astar is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Astar. If not, see <http://www.gnu.org/licenses/>.
#![cfg_attr(not(feature = "std"), no_std)]
use frame_support::{
dispatch::GetDispatchInfo,
pallet_prelude::*,
traits::{InstanceFilter, IsType, OriginTrait},
};
use frame_system::pallet_prelude::*;
use sp_runtime::traits::Dispatchable;
use sp_std::prelude::*;
pub use pallet::*;
#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;
#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;
pub mod weights;
pub use weights::WeightInfo;
#[frame_support::pallet]
pub mod pallet {
use super::*;
#[pallet::pallet]
pub struct Pallet<T>(_);
// TODO: The pallet is intentionally very basic. It could be improved to handle more origins, more aliases, etc.
// There could also be different instances, if such approach was needed.
// However, it's supposed to be the simplest solution possible to cover a specific scenario.
// Pallet is stateless and can easily be upgraded in the future.
/// Configuration trait.
#[pallet::config]
pub trait Config: frame_system::Config {
/// The overarching event type.
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
/// The overarching call type.
type RuntimeCall: Parameter
+ Dispatchable<RuntimeOrigin = Self::RuntimeOrigin>
+ GetDispatchInfo
+ From<frame_system::Call<Self>>
+ IsType<<Self as frame_system::Config>::RuntimeCall>;
/// Origin that can act on behalf of the collective.
type CollectiveProxy: EnsureOrigin<<Self as frame_system::Config>::RuntimeOrigin>;
/// Account representing the collective treasury.
type ProxyAccountId: Get<Self::AccountId>;
/// Filter to determine whether a call can be executed or not.
type CallFilter: InstanceFilter<<Self as Config>::RuntimeCall> + Default;
/// Weight info
type WeightInfo: WeightInfo;
}
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// Community proxy call executed successfully.
CollectiveProxyExecuted { result: DispatchResult },
}
#[pallet::call]
impl<T: Config> Pallet<T> {
/// Executes the call on a behalf of an aliased account.
///
/// The `origin` of the call is supposed to be a _collective_ (but can be anything) which can dispatch `call` on behalf of the aliased account.
/// It's essentially a proxy call that can be made by arbitrary origin type.
#[pallet::call_index(0)]
#[pallet::weight({
let di = call.get_dispatch_info();
(T::WeightInfo::execute_call().saturating_add(di.weight), di.class)
})]
pub fn execute_call(
origin: OriginFor<T>,
call: Box<<T as Config>::RuntimeCall>,
) -> DispatchResult {
// Ensure origin is valid.
T::CollectiveProxy::ensure_origin(origin)?;
// Account authentication is ensured by the `CollectiveProxy` origin check.
let mut origin: T::RuntimeOrigin =
frame_system::RawOrigin::Signed(T::ProxyAccountId::get()).into();
// Ensure custom filter is applied.
origin.add_filter(move |c: &<T as frame_system::Config>::RuntimeCall| {
let c = <T as Config>::RuntimeCall::from_ref(c);
T::CallFilter::default().filter(c)
});
// Dispatch the call.
let e = call.dispatch(origin);
Self::deposit_event(Event::CollectiveProxyExecuted {
result: e.map(|_| ()).map_err(|e| e.error),
});
Ok(())
}
}
}