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()
+ }
+}