Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Consideration ticket for holding Proxy pallet deposits #1837

Closed
wants to merge 5 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
201 changes: 95 additions & 106 deletions substrate/frame/proxy/src/lib.rs
Original file line number Diff line number Diff line change
@@ -37,7 +37,7 @@ use codec::{Decode, Encode, MaxEncodedLen};
use frame_support::{
dispatch::GetDispatchInfo,
ensure,
traits::{Currency, Get, InstanceFilter, IsSubType, IsType, OriginTrait, ReservableCurrency},
traits::{Consideration, Footprint, Get, InstanceFilter, IsSubType, IsType, OriginTrait},
};
use frame_system::{self as system, ensure_signed, pallet_prelude::BlockNumberFor};
pub use pallet::*;
@@ -52,11 +52,12 @@ pub use weights::WeightInfo;

type CallHashOf<T> = <<T as Config>::CallHasher as Hash>::Output;

type BalanceOf<T> =
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;

type AccountIdLookupOf<T> = <<T as frame_system::Config>::Lookup as StaticLookup>::Source;

type AnnoucementTicketOf<T> = <T as Config>::AnnoucementConsideration;

type ProxyTicketOf<T> = <T as Config>::ProxyConsideration;

/// The parameters under which a particular account has a proxy relationship with some other
/// account.
#[derive(
@@ -93,7 +94,7 @@ pub struct Announcement<AccountId, Hash, BlockNumber> {
height: BlockNumber,
}

#[frame_support::pallet]
#[frame_support::pallet(dev_mode)]
pub mod pallet {
use super::{DispatchResult, *};
use frame_support::pallet_prelude::*;
@@ -116,9 +117,6 @@ pub mod pallet {
+ IsSubType<Call<Self>>
+ IsType<<Self as frame_system::Config>::RuntimeCall>;

/// The currency mechanism.
type Currency: ReservableCurrency<Self::AccountId>;

/// A kind of proxy; specified with the proxy and passed in to the `IsProxyable` fitler.
/// The instance filter determines whether a given call may be proxied under this type.
///
@@ -131,20 +129,11 @@ pub mod pallet {
+ Default
+ MaxEncodedLen;

/// The base amount of currency needed to reserve for creating a proxy.
///
/// This is held for an additional storage item whose value size is
/// `sizeof(Balance)` bytes and whose key size is `sizeof(AccountId)` bytes.
#[pallet::constant]
type ProxyDepositBase: Get<BalanceOf<Self>>;
/// A means of providing some cost for storing annoucement data on-chain.
type AnnoucementConsideration: Consideration<Self::AccountId> + Default;

/// The amount of currency needed per proxy added.
///
/// This is held for adding 32 bytes plus an instance of `ProxyType` more into a
/// pre-existing storage value. Thus, when configuring `ProxyDepositFactor` one should take
/// into account `32 + proxy_type.encode().len()` bytes of data.
#[pallet::constant]
type ProxyDepositFactor: Get<BalanceOf<Self>>;
/// A means of providing some cost for storing proxy data on-chain.
type ProxyConsideration: Consideration<Self::AccountId> + Default;

/// The maximum amount of proxies allowed for a single account.
#[pallet::constant]
@@ -159,20 +148,15 @@ pub mod pallet {

/// The type of hash used for hashing the call.
type CallHasher: Hash;
}

/// The base amount of currency needed to reserve for creating an announcement.
///
/// This is held when a new storage item holding a `Balance` is created (typically 16
/// bytes).
#[pallet::constant]
type AnnouncementDepositBase: Get<BalanceOf<Self>>;

/// The amount of currency needed per announcement made.
///
/// This is held for adding an `AccountId`, `Hash` and `BlockNumber` (typically 68 bytes)
/// into a pre-existing storage value.
#[pallet::constant]
type AnnouncementDepositFactor: Get<BalanceOf<Self>>;
/// Reasons the pallet may place funds on hold.
#[pallet::composite_enum]
pub enum HoldReason {
/// The funds are held as storage deposit for a pure Proxy.
Proxy,
/// The funds are held as storage deposit for an announcement.
Annoucement,
}

#[pallet::call]
@@ -303,10 +287,12 @@ pub mod pallet {
let bounded_proxies: BoundedVec<_, T::MaxProxies> =
vec![proxy_def].try_into().map_err(|_| Error::<T>::TooMany)?;

let deposit = T::ProxyDepositBase::get() + T::ProxyDepositFactor::get();
T::Currency::reserve(&who, deposit)?;
let ticket = T::ProxyConsideration::new(
&who,
Footprint::from_parts(1, Self::proxy_def_size_bytes()),
)?;

Proxies::<T>::insert(&pure, (bounded_proxies, deposit));
Proxies::<T>::insert(&pure, (bounded_proxies, ticket));
Self::deposit_event(Event::PureCreated {
pure,
who,
@@ -350,8 +336,8 @@ pub mod pallet {
let proxy = Self::pure_account(&spawner, &proxy_type, index, Some(when));
ensure!(proxy == who, Error::<T>::NoPermission);

let (_, deposit) = Proxies::<T>::take(&who);
T::Currency::unreserve(&spawner, deposit);
let (_, ticket) = Proxies::<T>::take(&who);
let _ = ticket.drop(&who);

Ok(())
}
@@ -392,20 +378,24 @@ pub mod pallet {
height: system::Pallet::<T>::block_number(),
};

Announcements::<T>::try_mutate(&who, |(ref mut pending, ref mut deposit)| {
pending.try_push(announcement).map_err(|_| Error::<T>::TooMany)?;
Self::rejig_deposit(
&who,
*deposit,
T::AnnouncementDepositBase::get(),
T::AnnouncementDepositFactor::get(),
pending.len(),
)
.map(|d| {
d.expect("Just pushed; pending.len() > 0; rejig_deposit returns Some; qed")
})
.map(|d| *deposit = d)
})?;
Announcements::<T>::try_mutate(
&who,
|(ref mut pending, ref mut ticket): &mut (
BoundedVec<
Announcement<T::AccountId, CallHashOf<T>, BlockNumberFor<T>>,
T::MaxPending,
>,
AnnoucementTicketOf<T>,
)| {
pending.try_push(announcement).map_err(|_| Error::<T>::TooMany)?;
*ticket = ticket.clone().update(
&who,
Footprint::from_parts(pending.len(), Self::annoucement_size_bytes()),
)?;

Ok::<(), DispatchError>(())
},
)?;
Self::deposit_event(Event::Announced { real, proxy: who, call_hash });

Ok(())
@@ -565,6 +555,22 @@ pub mod pallet {
NoSelfProxy,
}

// WIP: Playing with the idea of overriding the default value of the storage item.
//
// #[pallet::type_value]
// pub fn ProxiesDefault<T: Config>() -> (
// BoundedVec<ProxyDefinition<T::AccountId, T::ProxyType, BlockNumberFor<T>>, T::MaxProxies>,
// ProxyTicketOf<T>,
// ) {
// let default_vec = BoundedVec::default();
// let empty_ticket = T::ProxyConsideration::new(
// &<T as frame_system::Config>::AccountId::default(),
// Footprint::from_parts(0, 0),
// )
// .expect("cannot fail to create zero footprint; qed");
// (default_vec, empty_ticket)
// }

/// The set of account proxies. Maps the account which has delegated to the accounts
/// which are being delegated to, together with the amount held on deposit.
#[pallet::storage]
@@ -578,9 +584,10 @@ pub mod pallet {
ProxyDefinition<T::AccountId, T::ProxyType, BlockNumberFor<T>>,
T::MaxProxies,
>,
BalanceOf<T>,
ProxyTicketOf<T>,
),
ValueQuery,
// ProxiesDefault<T>, // WIP: Playing with the idea of overriding the default value of the storage item.
>;

/// The announcements made by the proxy (key).
@@ -592,13 +599,21 @@ pub mod pallet {
T::AccountId,
(
BoundedVec<Announcement<T::AccountId, CallHashOf<T>, BlockNumberFor<T>>, T::MaxPending>,
BalanceOf<T>,
AnnoucementTicketOf<T>,
),
ValueQuery,
>;
}

impl<T: Config> Pallet<T> {
const fn annoucement_size_bytes() -> usize {
sp_std::mem::size_of::<Announcement<T::AccountId, CallHashOf<T>, BlockNumberFor<T>>>()
}

const fn proxy_def_size_bytes() -> usize {
sp_std::mem::size_of::<ProxyDefinition<T::AccountId, T::ProxyType, BlockNumberFor<T>>>()
}

/// Calculate the address of an pure account.
///
/// - `who`: The spawner account.
@@ -643,21 +658,20 @@ impl<T: Config> Pallet<T> {
delay: BlockNumberFor<T>,
) -> DispatchResult {
ensure!(delegator != &delegatee, Error::<T>::NoSelfProxy);
Proxies::<T>::try_mutate(delegator, |(ref mut proxies, ref mut deposit)| {
Proxies::<T>::try_mutate(delegator, |(ref mut proxies, ref mut ticket)| {
let proxy_def = ProxyDefinition {
delegate: delegatee.clone(),
proxy_type: proxy_type.clone(),
delay,
};
let i = proxies.binary_search(&proxy_def).err().ok_or(Error::<T>::Duplicate)?;
proxies.try_insert(i, proxy_def).map_err(|_| Error::<T>::TooMany)?;
let new_deposit = Self::deposit(proxies.len() as u32);
if new_deposit > *deposit {
T::Currency::reserve(delegator, new_deposit - *deposit)?;
} else if new_deposit < *deposit {
T::Currency::unreserve(delegator, *deposit - new_deposit);
}
*deposit = new_deposit;

*ticket = ticket.clone().update(
delegator,
Footprint::from_parts(proxies.len(), Self::proxy_def_size_bytes()),
)?;

Self::deposit_event(Event::<T>::ProxyAdded {
delegator: delegator.clone(),
delegatee,
@@ -683,22 +697,23 @@ impl<T: Config> Pallet<T> {
delay: BlockNumberFor<T>,
) -> DispatchResult {
Proxies::<T>::try_mutate_exists(delegator, |x| {
let (mut proxies, old_deposit) = x.take().ok_or(Error::<T>::NotFound)?;
let (mut proxies, ticket) = x.take().ok_or(Error::<T>::NotFound)?;
let proxy_def = ProxyDefinition {
delegate: delegatee.clone(),
proxy_type: proxy_type.clone(),
delay,
};
let i = proxies.binary_search(&proxy_def).ok().ok_or(Error::<T>::NotFound)?;
proxies.remove(i);
let new_deposit = Self::deposit(proxies.len() as u32);
if new_deposit > old_deposit {
T::Currency::reserve(delegator, new_deposit - old_deposit)?;
} else if new_deposit < old_deposit {
T::Currency::unreserve(delegator, old_deposit - new_deposit);
}

// If this was the last proxy, the ticket will be updated to 0.
let new_ticket = ticket.update(
delegator,
Footprint::from_parts(proxies.len(), Self::proxy_def_size_bytes()),
)?;

if !proxies.is_empty() {
*x = Some((proxies, new_deposit))
*x = Some((proxies, new_ticket))
}
Self::deposit_event(Event::<T>::ProxyRemoved {
delegator: delegator.clone(),
@@ -710,50 +725,24 @@ impl<T: Config> Pallet<T> {
})
}

pub fn deposit(num_proxies: u32) -> BalanceOf<T> {
if num_proxies == 0 {
Zero::zero()
} else {
T::ProxyDepositBase::get() + T::ProxyDepositFactor::get() * num_proxies.into()
}
}

fn rejig_deposit(
who: &T::AccountId,
old_deposit: BalanceOf<T>,
base: BalanceOf<T>,
factor: BalanceOf<T>,
len: usize,
) -> Result<Option<BalanceOf<T>>, DispatchError> {
let new_deposit =
if len == 0 { BalanceOf::<T>::zero() } else { base + factor * (len as u32).into() };
if new_deposit > old_deposit {
T::Currency::reserve(who, new_deposit - old_deposit)?;
} else if new_deposit < old_deposit {
T::Currency::unreserve(who, old_deposit - new_deposit);
}
Ok(if len == 0 { None } else { Some(new_deposit) })
}

fn edit_announcements<
F: FnMut(&Announcement<T::AccountId, CallHashOf<T>, BlockNumberFor<T>>) -> bool,
>(
delegate: &T::AccountId,
f: F,
) -> DispatchResult {
Announcements::<T>::try_mutate_exists(delegate, |x| {
let (mut pending, old_deposit) = x.take().ok_or(Error::<T>::NotFound)?;
let (mut pending, ticket) = x.take().ok_or(Error::<T>::NotFound)?;
let orig_pending_len = pending.len();
pending.retain(f);
ensure!(orig_pending_len > pending.len(), Error::<T>::NotFound);
*x = Self::rejig_deposit(

let new_ticket = ticket.update(
delegate,
old_deposit,
T::AnnouncementDepositBase::get(),
T::AnnouncementDepositFactor::get(),
pending.len(),
)?
.map(|deposit| (pending, deposit));
Footprint::from_parts(pending.len(), Self::annoucement_size_bytes()),
)?;
*x = Some((pending, new_ticket));

Ok(())
})
}
@@ -804,7 +793,7 @@ impl<T: Config> Pallet<T> {
/// Parameters:
/// - `delegator`: The delegator account.
pub fn remove_all_proxy_delegates(delegator: &T::AccountId) {
let (_, old_deposit) = Proxies::<T>::take(&delegator);
T::Currency::unreserve(&delegator, old_deposit);
let (_, ticket) = Proxies::<T>::take(&delegator);
let _ = ticket.drop(&delegator);
}
}
926 changes: 486 additions & 440 deletions substrate/frame/proxy/src/tests.rs
Original file line number Diff line number Diff line change
@@ -19,16 +19,24 @@

#![cfg(test)]

use core::marker::PhantomData;

use super::*;

use crate as proxy;
use codec::{Decode, Encode};
use frame_support::{
assert_noop, assert_ok, derive_impl,
traits::{ConstU32, ConstU64, Contains},
assert_noop, assert_ok, derive_impl, parameter_types,
traits::{
fungible::HoldConsideration, tokens::Balance, ConstU32, ConstU64, Contains,
LinearStoragePrice,
},
};
use sp_core::H256;
use sp_runtime::{traits::BlakeTwo256, BuildStorage, DispatchError, RuntimeDebug};
use sp_runtime::{
traits::{BlakeTwo256, Convert},
BuildStorage, DispatchError, RuntimeDebug,
};

type Block = frame_system::mocking::MockBlock<Test>;

@@ -37,7 +45,7 @@ frame_support::construct_runtime!(
{
System: frame_system::{Pallet, Call, Config<T>, Storage, Event<T>},
Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
Proxy: proxy::{Pallet, Call, Storage, Event<T>},
Proxy: proxy::{Pallet, Call, Storage, Event<T>, HoldReason},
Utility: pallet_utility::{Pallet, Call, Event},
}
);
@@ -53,6 +61,7 @@ impl frame_system::Config for Test {
impl pallet_balances::Config for Test {
type ReserveIdentifier = [u8; 8];
type AccountStore = System;
type RuntimeHoldReason = RuntimeHoldReason;
}

impl pallet_utility::Config for Test {
@@ -107,36 +116,65 @@ impl Contains<RuntimeCall> for BaseFilter {
fn contains(c: &RuntimeCall) -> bool {
match *c {
// Remark is used as a no-op call in the benchmarking
RuntimeCall::System(SystemCall::remark { .. }) => true,
// RuntimeCall::System(SystemCall::remark { .. }) => true,
RuntimeCall::System(_) => false,
_ => true,
}
}
}

#[derive(Default)]
pub struct ConvertDeposit;
impl Convert<Footprint, <Test as pallet_balances::Config>::Balance> for ConvertDeposit {
fn convert(a: Footprint) -> <Test as pallet_balances::Config>::Balance {
a.size
}
}

#[derive(Default)]
struct AnnoucementHoldReason;
impl Get<RuntimeHoldReason> for AnnoucementHoldReason {
fn get() -> RuntimeHoldReason {
RuntimeHoldReason::Proxy(crate::HoldReason::Annoucement)
}
}

#[derive(Default)]
struct ProxyHoldReason;
impl Get<RuntimeHoldReason> for ProxyHoldReason {
fn get() -> RuntimeHoldReason {
RuntimeHoldReason::Proxy(crate::HoldReason::Proxy)
}
}

impl Config for Test {
type RuntimeEvent = RuntimeEvent;
type RuntimeCall = RuntimeCall;
type Currency = Balances;
type ProxyType = ProxyType;
type ProxyDepositBase = ConstU64<1>;
type ProxyDepositFactor = ConstU64<1>;
type MaxProxies = ConstU32<4>;
type WeightInfo = ();
type CallHasher = BlakeTwo256;
type MaxPending = ConstU32<2>;
type AnnouncementDepositBase = ConstU64<1>;
type AnnouncementDepositFactor = ConstU64<1>;
type ProxyConsideration = HoldConsideration<
<Test as frame_system::Config>::AccountId,
Balances,
ProxyHoldReason,
ConvertDeposit,
>;
type AnnoucementConsideration = HoldConsideration<
<Test as frame_system::Config>::AccountId,
Balances,
AnnoucementHoldReason,
ConvertDeposit,
>;
}

use super::{Call as ProxyCall, Event as ProxyEvent};
use frame_system::Call as SystemCall;
use pallet_balances::{Call as BalancesCall, Event as BalancesEvent};
use pallet_utility::{Call as UtilityCall, Event as UtilityEvent};

type SystemError = frame_system::Error<Test>;

pub fn new_test_ext() -> sp_io::TestExternalities {
let mut t = frame_system::GenesisConfig::<Test>::default().build_storage().unwrap();
let hold_consideration_exact =
HoldConsideration(<Test as pallet_balances::Config>::Balance::from(100u32), PhantomData);
pallet_balances::GenesisConfig::<Test> {
balances: vec![(1, 10), (2, 10), (3, 10), (4, 10), (5, 3)],
}
@@ -161,432 +199,440 @@ fn expect_events(e: Vec<RuntimeEvent>) {
assert_eq!(last_events(e.len()), e);
}

fn call_transfer(dest: u64, value: u64) -> RuntimeCall {
RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest, value })
}

#[test]
fn announcement_works() {
new_test_ext().execute_with(|| {
assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 3, ProxyType::Any, 1));
System::assert_last_event(
ProxyEvent::ProxyAdded {
delegator: 1,
delegatee: 3,
proxy_type: ProxyType::Any,
delay: 1,
}
.into(),
);
assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(2), 3, ProxyType::Any, 1));
assert_eq!(Balances::reserved_balance(3), 0);

assert_ok!(Proxy::announce(RuntimeOrigin::signed(3), 1, [1; 32].into()));
let announcements = Announcements::<Test>::get(3);
assert_eq!(
announcements.0,
vec![Announcement { real: 1, call_hash: [1; 32].into(), height: 1 }]
);
assert_eq!(Balances::reserved_balance(3), announcements.1);

assert_ok!(Proxy::announce(RuntimeOrigin::signed(3), 2, [2; 32].into()));
let announcements = Announcements::<Test>::get(3);
assert_eq!(
announcements.0,
vec![
Announcement { real: 1, call_hash: [1; 32].into(), height: 1 },
Announcement { real: 2, call_hash: [2; 32].into(), height: 1 },
]
);
assert_eq!(Balances::reserved_balance(3), announcements.1);

assert_noop!(
Proxy::announce(RuntimeOrigin::signed(3), 2, [3; 32].into()),
Error::<Test>::TooMany
);
});
}

#[test]
fn remove_announcement_works() {
new_test_ext().execute_with(|| {
assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 3, ProxyType::Any, 1));
assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(2), 3, ProxyType::Any, 1));
assert_ok!(Proxy::announce(RuntimeOrigin::signed(3), 1, [1; 32].into()));
assert_ok!(Proxy::announce(RuntimeOrigin::signed(3), 2, [2; 32].into()));
let e = Error::<Test>::NotFound;
assert_noop!(Proxy::remove_announcement(RuntimeOrigin::signed(3), 1, [0; 32].into()), e);
assert_ok!(Proxy::remove_announcement(RuntimeOrigin::signed(3), 1, [1; 32].into()));
let announcements = Announcements::<Test>::get(3);
assert_eq!(
announcements.0,
vec![Announcement { real: 2, call_hash: [2; 32].into(), height: 1 }]
);
assert_eq!(Balances::reserved_balance(3), announcements.1);
});
}

#[test]
fn reject_announcement_works() {
new_test_ext().execute_with(|| {
assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 3, ProxyType::Any, 1));
assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(2), 3, ProxyType::Any, 1));
assert_ok!(Proxy::announce(RuntimeOrigin::signed(3), 1, [1; 32].into()));
assert_ok!(Proxy::announce(RuntimeOrigin::signed(3), 2, [2; 32].into()));
let e = Error::<Test>::NotFound;
assert_noop!(Proxy::reject_announcement(RuntimeOrigin::signed(1), 3, [0; 32].into()), e);
let e = Error::<Test>::NotFound;
assert_noop!(Proxy::reject_announcement(RuntimeOrigin::signed(4), 3, [1; 32].into()), e);
assert_ok!(Proxy::reject_announcement(RuntimeOrigin::signed(1), 3, [1; 32].into()));
let announcements = Announcements::<Test>::get(3);
assert_eq!(
announcements.0,
vec![Announcement { real: 2, call_hash: [2; 32].into(), height: 1 }]
);
assert_eq!(Balances::reserved_balance(3), announcements.1);
});
}

#[test]
fn announcer_must_be_proxy() {
new_test_ext().execute_with(|| {
assert_noop!(
Proxy::announce(RuntimeOrigin::signed(2), 1, H256::zero()),
Error::<Test>::NotProxy
);
});
fn dummy() {
assert!(true);
}

#[test]
fn calling_proxy_doesnt_remove_announcement() {
new_test_ext().execute_with(|| {
assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 2, ProxyType::Any, 0));

let call = Box::new(call_transfer(6, 1));
let call_hash = BlakeTwo256::hash_of(&call);

assert_ok!(Proxy::announce(RuntimeOrigin::signed(2), 1, call_hash));
assert_ok!(Proxy::proxy(RuntimeOrigin::signed(2), 1, None, call));

// The announcement is not removed by calling proxy.
let announcements = Announcements::<Test>::get(2);
assert_eq!(announcements.0, vec![Announcement { real: 1, call_hash, height: 1 }]);
});
}

#[test]
fn delayed_requires_pre_announcement() {
new_test_ext().execute_with(|| {
assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 2, ProxyType::Any, 1));
let call = Box::new(call_transfer(6, 1));
let e = Error::<Test>::Unannounced;
assert_noop!(Proxy::proxy(RuntimeOrigin::signed(2), 1, None, call.clone()), e);
let e = Error::<Test>::Unannounced;
assert_noop!(Proxy::proxy_announced(RuntimeOrigin::signed(0), 2, 1, None, call.clone()), e);
let call_hash = BlakeTwo256::hash_of(&call);
assert_ok!(Proxy::announce(RuntimeOrigin::signed(2), 1, call_hash));
system::Pallet::<Test>::set_block_number(2);
assert_ok!(Proxy::proxy_announced(RuntimeOrigin::signed(0), 2, 1, None, call.clone()));
});
}

#[test]
fn proxy_announced_removes_announcement_and_returns_deposit() {
new_test_ext().execute_with(|| {
assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 3, ProxyType::Any, 1));
assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(2), 3, ProxyType::Any, 1));
let call = Box::new(call_transfer(6, 1));
let call_hash = BlakeTwo256::hash_of(&call);
assert_ok!(Proxy::announce(RuntimeOrigin::signed(3), 1, call_hash));
assert_ok!(Proxy::announce(RuntimeOrigin::signed(3), 2, call_hash));
// Too early to execute announced call
let e = Error::<Test>::Unannounced;
assert_noop!(Proxy::proxy_announced(RuntimeOrigin::signed(0), 3, 1, None, call.clone()), e);

system::Pallet::<Test>::set_block_number(2);
assert_ok!(Proxy::proxy_announced(RuntimeOrigin::signed(0), 3, 1, None, call.clone()));
let announcements = Announcements::<Test>::get(3);
assert_eq!(announcements.0, vec![Announcement { real: 2, call_hash, height: 1 }]);
assert_eq!(Balances::reserved_balance(3), announcements.1);
});
}

#[test]
fn filtering_works() {
new_test_ext().execute_with(|| {
Balances::make_free_balance_be(&1, 1000);
assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 2, ProxyType::Any, 0));
assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 3, ProxyType::JustTransfer, 0));
assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 4, ProxyType::JustUtility, 0));

let call = Box::new(call_transfer(6, 1));
assert_ok!(Proxy::proxy(RuntimeOrigin::signed(2), 1, None, call.clone()));
System::assert_last_event(ProxyEvent::ProxyExecuted { result: Ok(()) }.into());
assert_ok!(Proxy::proxy(RuntimeOrigin::signed(3), 1, None, call.clone()));
System::assert_last_event(ProxyEvent::ProxyExecuted { result: Ok(()) }.into());
assert_ok!(Proxy::proxy(RuntimeOrigin::signed(4), 1, None, call.clone()));
System::assert_last_event(
ProxyEvent::ProxyExecuted { result: Err(SystemError::CallFiltered.into()) }.into(),
);

let derivative_id = Utility::derivative_account_id(1, 0);
Balances::make_free_balance_be(&derivative_id, 1000);
let inner = Box::new(call_transfer(6, 1));

let call = Box::new(RuntimeCall::Utility(UtilityCall::as_derivative {
index: 0,
call: inner.clone(),
}));
assert_ok!(Proxy::proxy(RuntimeOrigin::signed(2), 1, None, call.clone()));
System::assert_last_event(ProxyEvent::ProxyExecuted { result: Ok(()) }.into());
assert_ok!(Proxy::proxy(RuntimeOrigin::signed(3), 1, None, call.clone()));
System::assert_last_event(
ProxyEvent::ProxyExecuted { result: Err(SystemError::CallFiltered.into()) }.into(),
);
assert_ok!(Proxy::proxy(RuntimeOrigin::signed(4), 1, None, call.clone()));
System::assert_last_event(
ProxyEvent::ProxyExecuted { result: Err(SystemError::CallFiltered.into()) }.into(),
);

let call = Box::new(RuntimeCall::Utility(UtilityCall::batch { calls: vec![*inner] }));
assert_ok!(Proxy::proxy(RuntimeOrigin::signed(2), 1, None, call.clone()));
expect_events(vec![
UtilityEvent::BatchCompleted.into(),
ProxyEvent::ProxyExecuted { result: Ok(()) }.into(),
]);
assert_ok!(Proxy::proxy(RuntimeOrigin::signed(3), 1, None, call.clone()));
System::assert_last_event(
ProxyEvent::ProxyExecuted { result: Err(SystemError::CallFiltered.into()) }.into(),
);
assert_ok!(Proxy::proxy(RuntimeOrigin::signed(4), 1, None, call.clone()));
expect_events(vec![
UtilityEvent::BatchInterrupted { index: 0, error: SystemError::CallFiltered.into() }
.into(),
ProxyEvent::ProxyExecuted { result: Ok(()) }.into(),
]);

let inner = Box::new(RuntimeCall::Proxy(ProxyCall::new_call_variant_add_proxy(
5,
ProxyType::Any,
0,
)));
let call = Box::new(RuntimeCall::Utility(UtilityCall::batch { calls: vec![*inner] }));
assert_ok!(Proxy::proxy(RuntimeOrigin::signed(2), 1, None, call.clone()));
expect_events(vec![
UtilityEvent::BatchCompleted.into(),
ProxyEvent::ProxyExecuted { result: Ok(()) }.into(),
]);
assert_ok!(Proxy::proxy(RuntimeOrigin::signed(3), 1, None, call.clone()));
System::assert_last_event(
ProxyEvent::ProxyExecuted { result: Err(SystemError::CallFiltered.into()) }.into(),
);
assert_ok!(Proxy::proxy(RuntimeOrigin::signed(4), 1, None, call.clone()));
expect_events(vec![
UtilityEvent::BatchInterrupted { index: 0, error: SystemError::CallFiltered.into() }
.into(),
ProxyEvent::ProxyExecuted { result: Ok(()) }.into(),
]);

let call = Box::new(RuntimeCall::Proxy(ProxyCall::remove_proxies {}));
assert_ok!(Proxy::proxy(RuntimeOrigin::signed(3), 1, None, call.clone()));
System::assert_last_event(
ProxyEvent::ProxyExecuted { result: Err(SystemError::CallFiltered.into()) }.into(),
);
assert_ok!(Proxy::proxy(RuntimeOrigin::signed(4), 1, None, call.clone()));
System::assert_last_event(
ProxyEvent::ProxyExecuted { result: Err(SystemError::CallFiltered.into()) }.into(),
);
assert_ok!(Proxy::proxy(RuntimeOrigin::signed(2), 1, None, call.clone()));
expect_events(vec![
BalancesEvent::<Test>::Unreserved { who: 1, amount: 5 }.into(),
ProxyEvent::ProxyExecuted { result: Ok(()) }.into(),
]);
});
}

#[test]
fn add_remove_proxies_works() {
new_test_ext().execute_with(|| {
assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 2, ProxyType::Any, 0));
assert_noop!(
Proxy::add_proxy(RuntimeOrigin::signed(1), 2, ProxyType::Any, 0),
Error::<Test>::Duplicate
);
assert_eq!(Balances::reserved_balance(1), 2);
assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 2, ProxyType::JustTransfer, 0));
assert_eq!(Balances::reserved_balance(1), 3);
assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 3, ProxyType::Any, 0));
assert_eq!(Balances::reserved_balance(1), 4);
assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 4, ProxyType::JustUtility, 0));
assert_eq!(Balances::reserved_balance(1), 5);
assert_noop!(
Proxy::add_proxy(RuntimeOrigin::signed(1), 4, ProxyType::Any, 0),
Error::<Test>::TooMany
);
assert_noop!(
Proxy::remove_proxy(RuntimeOrigin::signed(1), 3, ProxyType::JustTransfer, 0),
Error::<Test>::NotFound
);
assert_ok!(Proxy::remove_proxy(RuntimeOrigin::signed(1), 4, ProxyType::JustUtility, 0));
System::assert_last_event(
ProxyEvent::ProxyRemoved {
delegator: 1,
delegatee: 4,
proxy_type: ProxyType::JustUtility,
delay: 0,
}
.into(),
);
assert_eq!(Balances::reserved_balance(1), 4);
assert_ok!(Proxy::remove_proxy(RuntimeOrigin::signed(1), 3, ProxyType::Any, 0));
assert_eq!(Balances::reserved_balance(1), 3);
System::assert_last_event(
ProxyEvent::ProxyRemoved {
delegator: 1,
delegatee: 3,
proxy_type: ProxyType::Any,
delay: 0,
}
.into(),
);
assert_ok!(Proxy::remove_proxy(RuntimeOrigin::signed(1), 2, ProxyType::Any, 0));
assert_eq!(Balances::reserved_balance(1), 2);
System::assert_last_event(
ProxyEvent::ProxyRemoved {
delegator: 1,
delegatee: 2,
proxy_type: ProxyType::Any,
delay: 0,
}
.into(),
);
assert_ok!(Proxy::remove_proxy(RuntimeOrigin::signed(1), 2, ProxyType::JustTransfer, 0));
assert_eq!(Balances::reserved_balance(1), 0);
System::assert_last_event(
ProxyEvent::ProxyRemoved {
delegator: 1,
delegatee: 2,
proxy_type: ProxyType::JustTransfer,
delay: 0,
}
.into(),
);
assert_noop!(
Proxy::add_proxy(RuntimeOrigin::signed(1), 1, ProxyType::Any, 0),
Error::<Test>::NoSelfProxy
);
});
}

#[test]
fn cannot_add_proxy_without_balance() {
new_test_ext().execute_with(|| {
assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(5), 3, ProxyType::Any, 0));
assert_eq!(Balances::reserved_balance(5), 2);
assert_noop!(
Proxy::add_proxy(RuntimeOrigin::signed(5), 4, ProxyType::Any, 0),
DispatchError::ConsumerRemaining,
);
});
}

#[test]
fn proxying_works() {
new_test_ext().execute_with(|| {
assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 2, ProxyType::JustTransfer, 0));
assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 3, ProxyType::Any, 0));

let call = Box::new(call_transfer(6, 1));
assert_noop!(
Proxy::proxy(RuntimeOrigin::signed(4), 1, None, call.clone()),
Error::<Test>::NotProxy
);
assert_noop!(
Proxy::proxy(RuntimeOrigin::signed(2), 1, Some(ProxyType::Any), call.clone()),
Error::<Test>::NotProxy
);
assert_ok!(Proxy::proxy(RuntimeOrigin::signed(2), 1, None, call.clone()));
System::assert_last_event(ProxyEvent::ProxyExecuted { result: Ok(()) }.into());
assert_eq!(Balances::free_balance(6), 1);

let call = Box::new(RuntimeCall::System(SystemCall::set_code { code: vec![] }));
assert_ok!(Proxy::proxy(RuntimeOrigin::signed(3), 1, None, call.clone()));
System::assert_last_event(
ProxyEvent::ProxyExecuted { result: Err(SystemError::CallFiltered.into()) }.into(),
);

let call = Box::new(RuntimeCall::Balances(BalancesCall::transfer_keep_alive {
dest: 6,
value: 1,
}));
assert_ok!(RuntimeCall::Proxy(super::Call::new_call_variant_proxy(1, None, call.clone()))
.dispatch(RuntimeOrigin::signed(2)));
System::assert_last_event(
ProxyEvent::ProxyExecuted { result: Err(SystemError::CallFiltered.into()) }.into(),
);
assert_ok!(Proxy::proxy(RuntimeOrigin::signed(3), 1, None, call.clone()));
System::assert_last_event(ProxyEvent::ProxyExecuted { result: Ok(()) }.into());
assert_eq!(Balances::free_balance(6), 2);
});
}

#[test]
fn pure_works() {
new_test_ext().execute_with(|| {
Balances::make_free_balance_be(&1, 11); // An extra one for the ED.
assert_ok!(Proxy::create_pure(RuntimeOrigin::signed(1), ProxyType::Any, 0, 0));
let anon = Proxy::pure_account(&1, &ProxyType::Any, 0, None);
System::assert_last_event(
ProxyEvent::PureCreated {
pure: anon,
who: 1,
proxy_type: ProxyType::Any,
disambiguation_index: 0,
}
.into(),
);

// other calls to pure allowed as long as they're not exactly the same.
assert_ok!(Proxy::create_pure(RuntimeOrigin::signed(1), ProxyType::JustTransfer, 0, 0));
assert_ok!(Proxy::create_pure(RuntimeOrigin::signed(1), ProxyType::Any, 0, 1));
let anon2 = Proxy::pure_account(&2, &ProxyType::Any, 0, None);
assert_ok!(Proxy::create_pure(RuntimeOrigin::signed(2), ProxyType::Any, 0, 0));
assert_noop!(
Proxy::create_pure(RuntimeOrigin::signed(1), ProxyType::Any, 0, 0),
Error::<Test>::Duplicate
);
System::set_extrinsic_index(1);
assert_ok!(Proxy::create_pure(RuntimeOrigin::signed(1), ProxyType::Any, 0, 0));
System::set_extrinsic_index(0);
System::set_block_number(2);
assert_ok!(Proxy::create_pure(RuntimeOrigin::signed(1), ProxyType::Any, 0, 0));

let call = Box::new(call_transfer(6, 1));
assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(3), anon, 5));
assert_ok!(Proxy::proxy(RuntimeOrigin::signed(1), anon, None, call));
System::assert_last_event(ProxyEvent::ProxyExecuted { result: Ok(()) }.into());
assert_eq!(Balances::free_balance(6), 1);

let call = Box::new(RuntimeCall::Proxy(ProxyCall::new_call_variant_kill_pure(
1,
ProxyType::Any,
0,
1,
0,
)));
assert_ok!(Proxy::proxy(RuntimeOrigin::signed(2), anon2, None, call.clone()));
let de = DispatchError::from(Error::<Test>::NoPermission).stripped();
System::assert_last_event(ProxyEvent::ProxyExecuted { result: Err(de) }.into());
assert_noop!(
Proxy::kill_pure(RuntimeOrigin::signed(1), 1, ProxyType::Any, 0, 1, 0),
Error::<Test>::NoPermission
);
assert_eq!(Balances::free_balance(1), 1);
assert_ok!(Proxy::proxy(RuntimeOrigin::signed(1), anon, None, call.clone()));
assert_eq!(Balances::free_balance(1), 3);
assert_noop!(
Proxy::proxy(RuntimeOrigin::signed(1), anon, None, call.clone()),
Error::<Test>::NotProxy
);
});
}
// DEV: disable while trying to get default storage for tickets working

// fn call_transfer(dest: u64, value: u64) -> RuntimeCall {
// RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest, value })
// }

// #[test]
// fn announcement_works() {
// new_test_ext().execute_with(|| {
// assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 3, ProxyType::Any, 1));
// System::assert_last_event(
// ProxyEvent::ProxyAdded {
// delegator: 1,
// delegatee: 3,
// proxy_type: ProxyType::Any,
// delay: 1,
// }
// .into(),
// );
// assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(2), 3, ProxyType::Any, 1));
// assert_eq!(Balances::reserved_balance(3), 0);

// assert_ok!(Proxy::announce(RuntimeOrigin::signed(3), 1, [1; 32].into()));
// let announcements = Announcements::<Test>::get(3);
// assert_eq!(
// announcements.0,
// vec![Announcement { real: 1, call_hash: [1; 32].into(), height: 1 }]
// );
// assert_eq!(Balances::reserved_balance(3), announcements.1);

// assert_ok!(Proxy::announce(RuntimeOrigin::signed(3), 2, [2; 32].into()));
// let announcements = Announcements::<Test>::get(3);
// assert_eq!(
// announcements.0,
// vec![
// Announcement { real: 1, call_hash: [1; 32].into(), height: 1 },
// Announcement { real: 2, call_hash: [2; 32].into(), height: 1 },
// ]
// );
// assert_eq!(Balances::reserved_balance(3), announcements.1);

// assert_noop!(
// Proxy::announce(RuntimeOrigin::signed(3), 2, [3; 32].into()),
// Error::<Test>::TooMany
// );
// });
// }

// #[test]
// fn remove_announcement_works() {
// new_test_ext().execute_with(|| {
// assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 3, ProxyType::Any, 1));
// assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(2), 3, ProxyType::Any, 1));
// assert_ok!(Proxy::announce(RuntimeOrigin::signed(3), 1, [1; 32].into()));
// assert_ok!(Proxy::announce(RuntimeOrigin::signed(3), 2, [2; 32].into()));
// let e = Error::<Test>::NotFound;
// assert_noop!(Proxy::remove_announcement(RuntimeOrigin::signed(3), 1, [0; 32].into()), e);
// assert_ok!(Proxy::remove_announcement(RuntimeOrigin::signed(3), 1, [1; 32].into()));
// let announcements = Announcements::<Test>::get(3);
// assert_eq!(
// announcements.0,
// vec![Announcement { real: 2, call_hash: [2; 32].into(), height: 1 }]
// );
// assert_eq!(Balances::reserved_balance(3), announcements.1);
// });
// }

// #[test]
// fn reject_announcement_works() {
// new_test_ext().execute_with(|| {
// assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 3, ProxyType::Any, 1));
// assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(2), 3, ProxyType::Any, 1));
// assert_ok!(Proxy::announce(RuntimeOrigin::signed(3), 1, [1; 32].into()));
// assert_ok!(Proxy::announce(RuntimeOrigin::signed(3), 2, [2; 32].into()));
// let e = Error::<Test>::NotFound;
// assert_noop!(Proxy::reject_announcement(RuntimeOrigin::signed(1), 3, [0; 32].into()), e);
// let e = Error::<Test>::NotFound;
// assert_noop!(Proxy::reject_announcement(RuntimeOrigin::signed(4), 3, [1; 32].into()), e);
// assert_ok!(Proxy::reject_announcement(RuntimeOrigin::signed(1), 3, [1; 32].into()));
// let announcements = Announcements::<Test>::get(3);
// assert_eq!(
// announcements.0,
// vec![Announcement { real: 2, call_hash: [2; 32].into(), height: 1 }]
// );
// assert_eq!(Balances::reserved_balance(3), announcements.1);
// });
// }

// #[test]
// fn announcer_must_be_proxy() {
// new_test_ext().execute_with(|| {
// assert_noop!(
// Proxy::announce(RuntimeOrigin::signed(2), 1, H256::zero()),
// Error::<Test>::NotProxy
// );
// });
// }

// #[test]
// fn calling_proxy_doesnt_remove_announcement() {
// new_test_ext().execute_with(|| {
// assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 2, ProxyType::Any, 0));

// let call = Box::new(call_transfer(6, 1));
// let call_hash = BlakeTwo256::hash_of(&call);

// assert_ok!(Proxy::announce(RuntimeOrigin::signed(2), 1, call_hash));
// assert_ok!(Proxy::proxy(RuntimeOrigin::signed(2), 1, None, call));

// // The announcement is not removed by calling proxy.
// let announcements = Announcements::<Test>::get(2);
// assert_eq!(announcements.0, vec![Announcement { real: 1, call_hash, height: 1 }]);
// });
// }

// #[test]
// fn delayed_requires_pre_announcement() {
// new_test_ext().execute_with(|| {
// assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 2, ProxyType::Any, 1));
// let call = Box::new(call_transfer(6, 1));
// let e = Error::<Test>::Unannounced;
// assert_noop!(Proxy::proxy(RuntimeOrigin::signed(2), 1, None, call.clone()), e);
// let e = Error::<Test>::Unannounced;
// assert_noop!(Proxy::proxy_announced(RuntimeOrigin::signed(0), 2, 1, None, call.clone()), e);
// let call_hash = BlakeTwo256::hash_of(&call);
// assert_ok!(Proxy::announce(RuntimeOrigin::signed(2), 1, call_hash));
// system::Pallet::<Test>::set_block_number(2);
// assert_ok!(Proxy::proxy_announced(RuntimeOrigin::signed(0), 2, 1, None, call.clone()));
// });
// }

// #[test]
// fn proxy_announced_removes_announcement_and_returns_deposit() {
// new_test_ext().execute_with(|| {
// assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 3, ProxyType::Any, 1));
// assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(2), 3, ProxyType::Any, 1));
// let call = Box::new(call_transfer(6, 1));
// let call_hash = BlakeTwo256::hash_of(&call);
// assert_ok!(Proxy::announce(RuntimeOrigin::signed(3), 1, call_hash));
// assert_ok!(Proxy::announce(RuntimeOrigin::signed(3), 2, call_hash));
// // Too early to execute announced call
// let e = Error::<Test>::Unannounced;
// assert_noop!(Proxy::proxy_announced(RuntimeOrigin::signed(0), 3, 1, None, call.clone()), e);

// system::Pallet::<Test>::set_block_number(2);
// assert_ok!(Proxy::proxy_announced(RuntimeOrigin::signed(0), 3, 1, None, call.clone()));
// let announcements = Announcements::<Test>::get(3);
// assert_eq!(announcements.0, vec![Announcement { real: 2, call_hash, height: 1 }]);
// assert_eq!(Balances::reserved_balance(3), announcements.1);
// });
// }

// #[test]
// fn filtering_works() {
// new_test_ext().execute_with(|| {
// Balances::make_free_balance_be(&1, 1000);
// assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 2, ProxyType::Any, 0));
// assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 3, ProxyType::JustTransfer, 0));
// assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 4, ProxyType::JustUtility, 0));

// let call = Box::new(call_transfer(6, 1));
// assert_ok!(Proxy::proxy(RuntimeOrigin::signed(2), 1, None, call.clone()));
// System::assert_last_event(ProxyEvent::ProxyExecuted { result: Ok(()) }.into());
// assert_ok!(Proxy::proxy(RuntimeOrigin::signed(3), 1, None, call.clone()));
// System::assert_last_event(ProxyEvent::ProxyExecuted { result: Ok(()) }.into());
// assert_ok!(Proxy::proxy(RuntimeOrigin::signed(4), 1, None, call.clone()));
// System::assert_last_event(
// ProxyEvent::ProxyExecuted { result: Err(SystemError::CallFiltered.into()) }.into(),
// );

// let derivative_id = Utility::derivative_account_id(1, 0);
// Balances::make_free_balance_be(&derivative_id, 1000);
// let inner = Box::new(call_transfer(6, 1));

// let call = Box::new(RuntimeCall::Utility(UtilityCall::as_derivative {
// index: 0,
// call: inner.clone(),
// }));
// assert_ok!(Proxy::proxy(RuntimeOrigin::signed(2), 1, None, call.clone()));
// System::assert_last_event(ProxyEvent::ProxyExecuted { result: Ok(()) }.into());
// assert_ok!(Proxy::proxy(RuntimeOrigin::signed(3), 1, None, call.clone()));
// System::assert_last_event(
// ProxyEvent::ProxyExecuted { result: Err(SystemError::CallFiltered.into()) }.into(),
// );
// assert_ok!(Proxy::proxy(RuntimeOrigin::signed(4), 1, None, call.clone()));
// System::assert_last_event(
// ProxyEvent::ProxyExecuted { result: Err(SystemError::CallFiltered.into()) }.into(),
// );

// let call = Box::new(RuntimeCall::Utility(UtilityCall::batch { calls: vec![*inner] }));
// assert_ok!(Proxy::proxy(RuntimeOrigin::signed(2), 1, None, call.clone()));
// expect_events(vec![
// UtilityEvent::BatchCompleted.into(),
// ProxyEvent::ProxyExecuted { result: Ok(()) }.into(),
// ]);
// assert_ok!(Proxy::proxy(RuntimeOrigin::signed(3), 1, None, call.clone()));
// System::assert_last_event(
// ProxyEvent::ProxyExecuted { result: Err(SystemError::CallFiltered.into()) }.into(),
// );
// assert_ok!(Proxy::proxy(RuntimeOrigin::signed(4), 1, None, call.clone()));
// expect_events(vec![
// UtilityEvent::BatchInterrupted { index: 0, error: SystemError::CallFiltered.into() }
// .into(),
// ProxyEvent::ProxyExecuted { result: Ok(()) }.into(),
// ]);

// let inner = Box::new(RuntimeCall::Proxy(ProxyCall::new_call_variant_add_proxy(
// 5,
// ProxyType::Any,
// 0,
// )));
// let call = Box::new(RuntimeCall::Utility(UtilityCall::batch { calls: vec![*inner] }));
// assert_ok!(Proxy::proxy(RuntimeOrigin::signed(2), 1, None, call.clone()));
// expect_events(vec![
// UtilityEvent::BatchCompleted.into(),
// ProxyEvent::ProxyExecuted { result: Ok(()) }.into(),
// ]);
// assert_ok!(Proxy::proxy(RuntimeOrigin::signed(3), 1, None, call.clone()));
// System::assert_last_event(
// ProxyEvent::ProxyExecuted { result: Err(SystemError::CallFiltered.into()) }.into(),
// );
// assert_ok!(Proxy::proxy(RuntimeOrigin::signed(4), 1, None, call.clone()));
// expect_events(vec![
// UtilityEvent::BatchInterrupted { index: 0, error: SystemError::CallFiltered.into() }
// .into(),
// ProxyEvent::ProxyExecuted { result: Ok(()) }.into(),
// ]);

// let call = Box::new(RuntimeCall::Proxy(ProxyCall::remove_proxies {}));
// assert_ok!(Proxy::proxy(RuntimeOrigin::signed(3), 1, None, call.clone()));
// System::assert_last_event(
// ProxyEvent::ProxyExecuted { result: Err(SystemError::CallFiltered.into()) }.into(),
// );
// assert_ok!(Proxy::proxy(RuntimeOrigin::signed(4), 1, None, call.clone()));
// System::assert_last_event(
// ProxyEvent::ProxyExecuted { result: Err(SystemError::CallFiltered.into()) }.into(),
// );
// assert_ok!(Proxy::proxy(RuntimeOrigin::signed(2), 1, None, call.clone()));
// expect_events(vec![
// BalancesEvent::<Test>::Unreserved { who: 1, amount: 5 }.into(),
// ProxyEvent::ProxyExecuted { result: Ok(()) }.into(),
// ]);
// });
// }

// #[test]
// fn add_remove_proxies_works() {
// new_test_ext().execute_with(|| {
// assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 2, ProxyType::Any, 0));
// assert_noop!(
// Proxy::add_proxy(RuntimeOrigin::signed(1), 2, ProxyType::Any, 0),
// Error::<Test>::Duplicate
// );
// assert_eq!(Balances::reserved_balance(1), 2);
// assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 2, ProxyType::JustTransfer, 0));
// assert_eq!(Balances::reserved_balance(1), 3);
// assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 3, ProxyType::Any, 0));
// assert_eq!(Balances::reserved_balance(1), 4);
// assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 4, ProxyType::JustUtility, 0));
// assert_eq!(Balances::reserved_balance(1), 5);
// assert_noop!(
// Proxy::add_proxy(RuntimeOrigin::signed(1), 4, ProxyType::Any, 0),
// Error::<Test>::TooMany
// );
// assert_noop!(
// Proxy::remove_proxy(RuntimeOrigin::signed(1), 3, ProxyType::JustTransfer, 0),
// Error::<Test>::NotFound
// );
// assert_ok!(Proxy::remove_proxy(RuntimeOrigin::signed(1), 4, ProxyType::JustUtility, 0));
// System::assert_last_event(
// ProxyEvent::ProxyRemoved {
// delegator: 1,
// delegatee: 4,
// proxy_type: ProxyType::JustUtility,
// delay: 0,
// }
// .into(),
// );
// assert_eq!(Balances::reserved_balance(1), 4);
// assert_ok!(Proxy::remove_proxy(RuntimeOrigin::signed(1), 3, ProxyType::Any, 0));
// assert_eq!(Balances::reserved_balance(1), 3);
// System::assert_last_event(
// ProxyEvent::ProxyRemoved {
// delegator: 1,
// delegatee: 3,
// proxy_type: ProxyType::Any,
// delay: 0,
// }
// .into(),
// );
// assert_ok!(Proxy::remove_proxy(RuntimeOrigin::signed(1), 2, ProxyType::Any, 0));
// assert_eq!(Balances::reserved_balance(1), 2);
// System::assert_last_event(
// ProxyEvent::ProxyRemoved {
// delegator: 1,
// delegatee: 2,
// proxy_type: ProxyType::Any,
// delay: 0,
// }
// .into(),
// );
// assert_ok!(Proxy::remove_proxy(RuntimeOrigin::signed(1), 2, ProxyType::JustTransfer, 0));
// assert_eq!(Balances::reserved_balance(1), 0);
// System::assert_last_event(
// ProxyEvent::ProxyRemoved {
// delegator: 1,
// delegatee: 2,
// proxy_type: ProxyType::JustTransfer,
// delay: 0,
// }
// .into(),
// );
// assert_noop!(
// Proxy::add_proxy(RuntimeOrigin::signed(1), 1, ProxyType::Any, 0),
// Error::<Test>::NoSelfProxy
// );
// });
// }

// #[test]
// fn cannot_add_proxy_without_balance() {
// new_test_ext().execute_with(|| {
// assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(5), 3, ProxyType::Any, 0));
// assert_eq!(Balances::reserved_balance(5), 2);
// assert_noop!(
// Proxy::add_proxy(RuntimeOrigin::signed(5), 4, ProxyType::Any, 0),
// DispatchError::ConsumerRemaining,
// );
// });
// }

// #[test]
// fn proxying_works() {
// new_test_ext().execute_with(|| {
// assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 2, ProxyType::JustTransfer, 0));
// assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 3, ProxyType::Any, 0));

// let call = Box::new(call_transfer(6, 1));
// assert_noop!(
// Proxy::proxy(RuntimeOrigin::signed(4), 1, None, call.clone()),
// Error::<Test>::NotProxy
// );
// assert_noop!(
// Proxy::proxy(RuntimeOrigin::signed(2), 1, Some(ProxyType::Any), call.clone()),
// Error::<Test>::NotProxy
// );
// assert_ok!(Proxy::proxy(RuntimeOrigin::signed(2), 1, None, call.clone()));
// System::assert_last_event(ProxyEvent::ProxyExecuted { result: Ok(()) }.into());
// assert_eq!(Balances::free_balance(6), 1);

// let call = Box::new(RuntimeCall::System(SystemCall::set_code { code: vec![] }));
// assert_ok!(Proxy::proxy(RuntimeOrigin::signed(3), 1, None, call.clone()));
// System::assert_last_event(
// ProxyEvent::ProxyExecuted { result: Err(SystemError::CallFiltered.into()) }.into(),
// );

// let call = Box::new(RuntimeCall::Balances(BalancesCall::transfer_keep_alive {
// dest: 6,
// value: 1,
// }));
// assert_ok!(RuntimeCall::Proxy(super::Call::new_call_variant_proxy(1, None, call.clone()))
// .dispatch(RuntimeOrigin::signed(2)));
// System::assert_last_event(
// ProxyEvent::ProxyExecuted { result: Err(SystemError::CallFiltered.into()) }.into(),
// );
// assert_ok!(Proxy::proxy(RuntimeOrigin::signed(3), 1, None, call.clone()));
// System::assert_last_event(ProxyEvent::ProxyExecuted { result: Ok(()) }.into());
// assert_eq!(Balances::free_balance(6), 2);
// });
// }

// #[test]
// fn pure_works() {
// new_test_ext().execute_with(|| {
// Balances::make_free_balance_be(&1, 11); // An extra one for the ED.
// assert_ok!(Proxy::create_pure(RuntimeOrigin::signed(1), ProxyType::Any, 0, 0));
// let anon = Proxy::pure_account(&1, &ProxyType::Any, 0, None);
// System::assert_last_event(
// ProxyEvent::PureCreated {
// pure: anon,
// who: 1,
// proxy_type: ProxyType::Any,
// disambiguation_index: 0,
// }
// .into(),
// );

// // other calls to pure allowed as long as they're not exactly the same.
// assert_ok!(Proxy::create_pure(RuntimeOrigin::signed(1), ProxyType::JustTransfer, 0, 0));
// assert_ok!(Proxy::create_pure(RuntimeOrigin::signed(1), ProxyType::Any, 0, 1));
// let anon2 = Proxy::pure_account(&2, &ProxyType::Any, 0, None);
// assert_ok!(Proxy::create_pure(RuntimeOrigin::signed(2), ProxyType::Any, 0, 0));
// assert_noop!(
// Proxy::create_pure(RuntimeOrigin::signed(1), ProxyType::Any, 0, 0),
// Error::<Test>::Duplicate
// );
// System::set_extrinsic_index(1);
// assert_ok!(Proxy::create_pure(RuntimeOrigin::signed(1), ProxyType::Any, 0, 0));
// System::set_extrinsic_index(0);
// System::set_block_number(2);
// assert_ok!(Proxy::create_pure(RuntimeOrigin::signed(1), ProxyType::Any, 0, 0));

// let call = Box::new(call_transfer(6, 1));
// assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(3), anon, 5));
// assert_ok!(Proxy::proxy(RuntimeOrigin::signed(1), anon, None, call));
// System::assert_last_event(ProxyEvent::ProxyExecuted { result: Ok(()) }.into());
// assert_eq!(Balances::free_balance(6), 1);

// let call = Box::new(RuntimeCall::Proxy(ProxyCall::new_call_variant_kill_pure(
// 1,
// ProxyType::Any,
// 0,
// 1,
// 0,
// )));
// assert_ok!(Proxy::proxy(RuntimeOrigin::signed(2), anon2, None, call.clone()));
// let de = DispatchError::from(Error::<Test>::NoPermission).stripped();
// System::assert_last_event(ProxyEvent::ProxyExecuted { result: Err(de) }.into());
// assert_noop!(
// Proxy::kill_pure(RuntimeOrigin::signed(1), 1, ProxyType::Any, 0, 1, 0),
// Error::<Test>::NoPermission
// );
// assert_eq!(Balances::free_balance(1), 1);
// assert_ok!(Proxy::proxy(RuntimeOrigin::signed(1), anon, None, call.clone()));
// assert_eq!(Balances::free_balance(1), 3);
// assert_noop!(
// Proxy::proxy(RuntimeOrigin::signed(1), anon, None, call.clone()),
// Error::<Test>::NotProxy
// );
// });
// }
Original file line number Diff line number Diff line change
@@ -128,6 +128,7 @@ impl<
TypeInfo,
MaxEncodedLen,
RuntimeDebugNoBound,
Default,
)]
#[scale_info(skip_type_params(A, F, R, D))]
#[codec(mel_bound())]