diff --git a/Cargo.toml b/Cargo.toml index 3eb8697f..ceedac85 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ members = [ "frame/collator-selection", "frame/custom-signatures", "frame/dapps-staking", + "frame/pallet-account", "frame/pallet-xcm", "frame/pallet-xvm", "frame/xc-asset-config", diff --git a/frame/pallet-account/Cargo.toml b/frame/pallet-account/Cargo.toml new file mode 100644 index 00000000..0834e1e9 --- /dev/null +++ b/frame/pallet-account/Cargo.toml @@ -0,0 +1,52 @@ +[package] +name = "pallet-account" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true + +[dependencies] +log = { workspace = true } +serde = { workspace = true, optional = true } + +# Substrate +frame-support = { workspace = true } +frame-system = { workspace = true } +parity-scale-codec = { workspace = true } +scale-info = { workspace = true } +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } + +# Benchmarks +frame-benchmarking = { workspace = true, optional = true } +hex-literal = { workspace = true, optional = true } + +[dev-dependencies] +assert_matches = { workspace = true } +hex-literal = { workspace = true } +pallet-balances = { workspace = true, features = ["std"] } + +[features] +default = ["std"] +std = [ + "parity-scale-codec/std", + "frame-benchmarking/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "serde", + "sp-core/std", + "sp-runtime/std", + "sp-std/std", + "sp-io/std", +] +runtime-benchmarks = [ + "hex-literal", + "frame-benchmarking", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", +] +try-runtime = ["frame-support/try-runtime"] diff --git a/frame/pallet-account/src/benchmarking.rs b/frame/pallet-account/src/benchmarking.rs new file mode 100644 index 00000000..a9dad2ef --- /dev/null +++ b/frame/pallet-account/src/benchmarking.rs @@ -0,0 +1,76 @@ +// This file is part of Astar. + +// Copyright (C) 2019-2023 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 . + +use super::*; + +use hex_literal::hex; +use parity_scale_codec::{Decode, Encode}; + +use frame_benchmarking::{benchmarks, whitelisted_caller}; +use frame_support::assert_ok; +use frame_system::{Pallet as System, RawOrigin}; + +const WHITELISTED_D1_NATIVE: [u8; 32] = + hex!["53ebafb5200b910e56654c867e08747f7dbd3695d6c133af24084b23c3253a67"]; + +/// Assert that the last event equals the provided one. +fn assert_last_event(generic_event: ::RuntimeEvent) { + System::::assert_last_event(generic_event.into()); +} + +benchmarks! { + new_origin { + let caller = whitelisted_caller::(); + let origin_kind = NativeAndEVMKind::Native.encode(); + let origin = NativeAndEVM::Native(WHITELISTED_D1_NATIVE.into()).encode(); + }: _(RawOrigin::Signed(caller.clone()), Decode::decode(&mut &origin_kind[..]).unwrap()) + verify { + assert_last_event::( + Event::::NewOrigin { + account: caller, + origin: Decode::decode(&mut &origin[..]).unwrap(), + }.into() + ); + } + + proxy_call { + let caller = whitelisted_caller::(); + let origin_kind = NativeAndEVMKind::Native.encode(); + assert_ok!(Pallet::::new_origin( + RawOrigin::Signed(caller.clone()).into(), + Decode::decode(&mut &origin_kind[..]).unwrap() + )); + + let call = Box::new(frame_system::Call::remark { remark: vec![42u8] }.into()); + let origin = NativeAndEVM::Native(WHITELISTED_D1_NATIVE.into()).encode(); + }: _(RawOrigin::Signed(caller.clone()), 0, call) + verify { + assert_last_event::( + Event::::ProxyCall { + origin: Decode::decode(&mut &origin[..]).unwrap(), + result: Ok(()), + }.into() + ); + } + + impl_benchmark_test_suite!( + Pallet, + crate::mock::ExternalityBuilder::build(), + crate::mock::TestRuntime, + ); +} diff --git a/frame/pallet-account/src/lib.rs b/frame/pallet-account/src/lib.rs new file mode 100644 index 00000000..90c8e1d2 --- /dev/null +++ b/frame/pallet-account/src/lib.rs @@ -0,0 +1,226 @@ +// This file is part of Astar. + +// Copyright (C) 2019-2023 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 . + +//! # Account abstraction pallet +//! +//! ## Overview +//! +//! An accout abstraction pallet makes it possible to derive new blockchain based +//! account for an existing external owned account (seed phrase based). The onchain +//! account could be derived to multiple address spaces: H160 and SS58. For example, +//! it makes possible to predictably interact between substrate native account and +//! EVM smart contracts. +//! +//! ## Interface +//! +//! ### Dispatchable Function +//! +//! * new_origin() - create new origin for account +//! * proxy_call() - make proxy call with derived account as origin +//! + +#![cfg_attr(not(feature = "std"), no_std)] + +pub mod origins; +pub use origins::*; + +pub mod weights; +pub use weights::*; + +pub use pallet::*; + +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmarking; +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; + +#[frame_support::pallet] +#[allow(clippy::module_inception)] +pub mod pallet { + use crate::*; + + use frame_support::pallet_prelude::*; + use frame_support::{ + dispatch::{Dispatchable, GetDispatchInfo}, + traits::{Currency, IsSubType, OnKilledAccount, ReservableCurrency}, + }; + use frame_system::pallet_prelude::*; + use sp_std::prelude::*; + + /// The current storage version. + const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); + + /// Maximum origins that account can hold. + const MAX_ORIGINS: u32 = 50; + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + #[pallet::storage_version(STORAGE_VERSION)] + pub struct Pallet(PhantomData); + + #[pallet::config] + pub trait Config: frame_system::Config { + /// Reservable currency to take origin creation deposit. + type Currency: ReservableCurrency; + /// Origin creation security deposit. + #[pallet::constant] + type SecurityDeposit: Get<>::Balance>; + /// Custom origin type. + type CustomOrigin: Parameter + TryInto + MaxEncodedLen; + /// Parameter that defines different origin options and how to create it. + type CustomOriginKind: Parameter + OriginDeriving; + /// The runtime origin type. + type RuntimeOrigin: From + + From>; + /// The overarching call type. + type RuntimeCall: Parameter + + Dispatchable::RuntimeOrigin> + + GetDispatchInfo + + From> + + IsSubType> + + IsType<::RuntimeCall>; + /// General event type. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// Weight information for extrinsics in this pallet. + type WeightInfo: WeightInfo; + } + + #[pallet::error] + pub enum Error { + /// Origin with given index not registered. + UnregisteredOrigin, + } + + #[pallet::event] + #[pallet::generate_deposit(pub(crate) fn deposit_event)] + pub enum Event { + NewOrigin { + account: T::AccountId, + origin: T::CustomOrigin, + }, + ProxyCall { + origin: T::CustomOrigin, + result: DispatchResult, + }, + } + + #[pallet::origin] + pub type Origin = ::CustomOrigin; + + /// Account origins + #[pallet::storage] + pub type AccountOrigin = + StorageDoubleMap<_, Blake2_128Concat, T::AccountId, Twox64Concat, u32, T::CustomOrigin>; + + /// Account last origin index + #[pallet::storage] + pub type AccountLastOrigin = + StorageMap<_, Twox64Concat, T::AccountId, u32, ValueQuery>; + + impl OnKilledAccount for Pallet { + fn on_killed_account(who: &T::AccountId) { + let _ = >::clear_prefix(who, MAX_ORIGINS, None); + >::remove(who); + } + } + + #[pallet::call] + impl Pallet { + /// Derive new origin for account. + /// + /// The dispatch origin for this call must be _Signed_. + #[pallet::weight( + T::WeightInfo::new_origin() + .saturating_add(T::DbWeight::get().reads_writes(1, 2)) + )] + #[pallet::call_index(0)] + pub fn new_origin( + origin: OriginFor, + origin_kind: T::CustomOriginKind, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + // reserve security deposit + T::Currency::reserve(&who, T::SecurityDeposit::get())?; + + let next_index = AccountLastOrigin::::get(&who); + ensure!( + next_index < MAX_ORIGINS, + "account has maximum origins count" + ); + + let new_origin = origin_kind.derive(&who, next_index); + AccountOrigin::::insert(&who, next_index, new_origin.clone()); + AccountLastOrigin::::insert(&who, next_index + 1); + + Self::deposit_event(Event::NewOrigin { + account: who, + origin: new_origin, + }); + + Ok(()) + } + + /// Dispatch the given `call` from an account that the sender is authorised. + /// + /// The dispatch origin for this call must be _Signed_. + /// + /// Parameters: + /// - `origin_index`: Account origin index for using. + /// - `call`: The call to be made by the `derived` account. + #[pallet::weight({ + let di = call.get_dispatch_info(); + (T::WeightInfo::proxy_call() + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(di.weight), + di.class) + })] + #[pallet::call_index(1)] + pub fn proxy_call( + origin: OriginFor, + #[pallet::compact] origin_index: u32, + call: Box<::RuntimeCall>, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + ensure!( + origin_index < AccountLastOrigin::::get(&who), + Error::::UnregisteredOrigin + ); + + let custom_origin = AccountOrigin::::get(&who, origin_index) + .ok_or(Error::::UnregisteredOrigin)?; + + let e = if let Ok(id) = custom_origin.clone().try_into() { + // in case of native dispatch with system signed origin + call.dispatch(frame_system::RawOrigin::Signed(id).into()) + } else { + // in other case dispatch with custom origin + call.dispatch(custom_origin.clone().into()) + }; + + Self::deposit_event(Event::ProxyCall { + origin: custom_origin, + result: e.map(|_| ()).map_err(|e| e.error), + }); + + Ok(()) + } + } +} diff --git a/frame/pallet-account/src/mock.rs b/frame/pallet-account/src/mock.rs new file mode 100644 index 00000000..6ac0997d --- /dev/null +++ b/frame/pallet-account/src/mock.rs @@ -0,0 +1,154 @@ +// This file is part of Astar. + +// Copyright (C) 2019-2023 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 . + +use crate as pallet_account; + +use frame_support::{ + construct_runtime, parameter_types, sp_io::TestExternalities, weights::Weight, +}; +use hex_literal::hex; +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, IdentityLookup}, + AccountId32, +}; + +pub(crate) type AccountId = AccountId32; +pub(crate) type BlockNumber = u64; +pub(crate) type Balance = u128; + +pub(crate) const ALICE: AccountId = AccountId::new([0u8; 32]); +pub(crate) const BOB: AccountId = AccountId::new([1u8; 32]); + +pub(crate) const ALICE_ED25519: [u8; 32] = + hex!["88dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee"]; + +pub(crate) const ALICE_D1_NATIVE: [u8; 32] = + hex!["9f0e444c69f77a49bd0be89db92c38fe713e0963165cca12faf5712d7657120f"]; +pub(crate) const ALICE_D2_H160: [u8; 20] = hex!["5d2532e641a22a8f5e0a42652fe82dc231fd27f8"]; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +/// Value shouldn't be less than 2 for testing purposes, otherwise we cannot test certain corner cases. +pub(crate) const EXISTENTIAL_DEPOSIT: Balance = 2; + +construct_runtime!( + pub struct TestRuntime + where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system, + Balances: pallet_balances, + Account: pallet_account, + } +); + +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub BlockWeights: frame_system::limits::BlockWeights = + frame_system::limits::BlockWeights::simple_max(Weight::from_ref_time(1024)); +} + +impl frame_system::Config for TestRuntime { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type RuntimeCall = RuntimeCall; + type BlockNumber = BlockNumber; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type DbWeight = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +parameter_types! { + pub const MaxLocks: u32 = 4; + pub const ExistentialDeposit: Balance = EXISTENTIAL_DEPOSIT; +} + +impl pallet_balances::Config for TestRuntime { + type MaxLocks = MaxLocks; + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); +} + +parameter_types! { + pub const SecurityDeposit: Balance = 100; +} + +impl pallet_account::Config for TestRuntime { + type Currency = Balances; + type SecurityDeposit = SecurityDeposit; + type CustomOrigin = super::NativeAndEVM; + type CustomOriginKind = super::NativeAndEVMKind; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); +} + +pub struct ExternalityBuilder; + +impl ExternalityBuilder { + pub fn build() -> TestExternalities { + let mut storage = frame_system::GenesisConfig::default() + .build_storage::() + .unwrap(); + + // This will cause some initial issuance + pallet_balances::GenesisConfig:: { + balances: vec![ + (ALICE, 9000), + (ALICE_ED25519.into(), 1000), + (ALICE_D1_NATIVE.into(), 1000), + (BOB, 800), + ], + } + .assimilate_storage(&mut storage) + .ok(); + + let mut ext = TestExternalities::from(storage); + ext.execute_with(|| System::set_block_number(1)); + ext + } +} diff --git a/frame/pallet-account/src/origins.rs b/frame/pallet-account/src/origins.rs new file mode 100644 index 00000000..63bce39d --- /dev/null +++ b/frame/pallet-account/src/origins.rs @@ -0,0 +1,74 @@ +// This file is part of Astar. + +// Copyright (C) 2019-2023 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 . + +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; +use sp_runtime::{AccountId32, RuntimeDebug}; + +/// Derive new origin. +pub trait OriginDeriving { + /// Derive new origin depend of account and index + fn derive(&self, source: &AccountId, index: u32) -> Origin; +} + +/// Origin that support native and EVM compatible options. +#[derive(PartialEq, Eq, Clone, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)] +pub enum NativeAndEVM { + /// Substrate native origin. + Native(AccountId32), + /// The 20-byte length Ethereum like origin. + H160(sp_core::H160), +} + +impl TryInto for NativeAndEVM { + type Error = (); + fn try_into(self) -> Result { + match self { + NativeAndEVM::Native(a) => Ok(a), + _ => Err(()), + } + } +} + +impl TryInto for NativeAndEVM { + type Error = (); + fn try_into(self) -> Result { + match self { + NativeAndEVM::H160(a) => Ok(a), + _ => Err(()), + } + } +} + +/// Kind for NativeAndEVM origin. +#[derive(PartialEq, Eq, Clone, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)] +pub enum NativeAndEVMKind { + Native, + H160, +} + +impl OriginDeriving for NativeAndEVMKind { + fn derive(&self, source: &AccountId32, index: u32) -> NativeAndEVM { + let salted_source = [source.as_ref(), &index.encode()[..]].concat(); + let derived = sp_io::hashing::blake2_256(&salted_source); + match self { + NativeAndEVMKind::Native => NativeAndEVM::Native(derived.into()), + NativeAndEVMKind::H160 => NativeAndEVM::H160(sp_core::H160::from_slice(&derived[..20])), + } + } +} diff --git a/frame/pallet-account/src/tests.rs b/frame/pallet-account/src/tests.rs new file mode 100644 index 00000000..89b5ade0 --- /dev/null +++ b/frame/pallet-account/src/tests.rs @@ -0,0 +1,145 @@ +// This file is part of Astar. + +// Copyright (C) 2019-2023 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 . + +use super::*; +use assert_matches::assert_matches; +use frame_support::assert_ok; +use mock::*; + +#[test] +pub fn new_origin_works() { + ExternalityBuilder::build().execute_with(|| { + // Create native origin + assert_eq!(Balances::free_balance(&ALICE), 9000); + assert_ok!(Account::new_origin( + RuntimeOrigin::signed(ALICE).into(), + NativeAndEVMKind::Native, + )); + // check that security deposit consumed + assert_eq!(Balances::free_balance(&ALICE), 8900); + // check that origin created + assert_eq!( + AccountOrigin::::get(ALICE, 0), + Some(NativeAndEVM::Native(ALICE_D1_NATIVE.into())), + ); + assert_eq!(AccountOrigin::::get(ALICE, 1), None,); + assert_matches!( + System::events() + .last() + .expect("events expected") + .event + .clone(), + RuntimeEvent::Account(Event::NewOrigin{origin, ..}) + if origin == NativeAndEVM::Native(ALICE_D1_NATIVE.into()) + ); + // Create EVM origin + assert_ok!(Account::new_origin( + RuntimeOrigin::signed(ALICE).into(), + NativeAndEVMKind::H160, + )); + assert_eq!( + AccountOrigin::::get(ALICE, 1), + Some(NativeAndEVM::H160(ALICE_D2_H160.into())), + ); + assert_matches!( + System::events() + .last() + .expect("events expected") + .event + .clone(), + RuntimeEvent::Account(Event::NewOrigin{origin, ..}) + if origin == NativeAndEVM::H160(ALICE_D2_H160.into()) + ); + }) +} + +#[test] +pub fn proxy_call_works() { + ExternalityBuilder::build().execute_with(|| { + let call: RuntimeCall = pallet_balances::Call::transfer { + dest: BOB, + value: 10, + } + .into(); + + // Create native origin + assert_ok!(Account::new_origin( + RuntimeOrigin::signed(ALICE).into(), + NativeAndEVMKind::Native, + )); + + // Make call with native origin + assert_ok!(Account::proxy_call( + RuntimeOrigin::signed(ALICE).into(), + 0, + Box::new(call), + )); + assert_eq!(System::account(BOB).data.free, 810); + assert_matches!( + System::events() + .last() + .expect("events expected") + .event + .clone(), + RuntimeEvent::Account(Event::ProxyCall{origin, ..}) + if origin == NativeAndEVM::Native(ALICE_D1_NATIVE.into()) + ); + assert_matches!( + System::events() + .get(System::events().len() - 2) + .expect("events expected") + .event + .clone(), + RuntimeEvent::Balances(pallet_balances::Event::Transfer{from, ..}) + if from == ALICE_D1_NATIVE.into() + ); + }) +} + +#[test] +pub fn proxy_call_fails() { + ExternalityBuilder::build().execute_with(|| { + let call: RuntimeCall = pallet_balances::Call::transfer { + dest: BOB, + value: 10, + } + .into(); + + // Make call with unknown origin + assert_eq!( + Account::proxy_call( + RuntimeOrigin::signed(ALICE).into(), + 0, + Box::new(call.clone()), + ), + Err(Error::::UnregisteredOrigin.into()) + ); + + // Create native origin + assert_ok!(Account::new_origin( + RuntimeOrigin::signed(ALICE).into(), + NativeAndEVMKind::Native, + )); + + // Make call with native origin + assert_eq!( + Account::proxy_call(RuntimeOrigin::signed(ALICE).into(), 1, Box::new(call),), + Err(Error::::UnregisteredOrigin.into()) + ); + }) +} diff --git a/frame/pallet-account/src/weights.rs b/frame/pallet-account/src/weights.rs new file mode 100644 index 00000000..707ba7a7 --- /dev/null +++ b/frame/pallet-account/src/weights.rs @@ -0,0 +1,35 @@ +// This file is part of Astar. + +// Copyright (C) 2019-2023 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 . + +use frame_support::weights::Weight; + +// TODO: generate weights when pallet comes to testnet + +pub trait WeightInfo { + fn new_origin() -> Weight; + fn proxy_call() -> Weight; +} + +impl WeightInfo for () { + fn new_origin() -> Weight { + Default::default() + } + fn proxy_call() -> Weight { + Default::default() + } +}