Skip to content

Commit

Permalink
feat: Add an ability to use real account energy for proxy (#260)
Browse files Browse the repository at this point in the history
* Add an ability to use real account energy for proxy

* Update spec_version to 43

* Fix mocks for energy pallet

* Add tests for a new feature

---------

Co-authored-by: Alex Siman <[email protected]>
Co-authored-by: Oleh Mell <[email protected]>
  • Loading branch information
3 people authored Apr 9, 2024
1 parent dcbbc7f commit cb8489a
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 29 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions pallets/energy/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ scale-info = { version = "2.2.0", default-features = false, features = ["derive"
frame-benchmarking = { optional = true, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false }
frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false }
frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false }
pallet-proxy = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false }
pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false }
sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false }
Expand All @@ -45,6 +46,7 @@ std = [
"frame-system/std",
"pallet-balances/std",
"pallet-transaction-payment/std",
"pallet-proxy/std",
"sp-runtime/std",
"sp-std/std",
]
Expand Down
81 changes: 62 additions & 19 deletions pallets/energy/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,15 @@ pub mod weights;
#[frame_support::pallet]
pub mod pallet {
use frame_support::{
dispatch::GetDispatchInfo,
pallet_prelude::*,
traits::{tokens::Balance, Currency, ExistenceRequirement, WithdrawReasons},
traits::{tokens::Balance, Currency, ExistenceRequirement, WithdrawReasons, IsSubType},
};
use frame_system::pallet_prelude::*;
use pallet_transaction_payment::OnChargeTransaction;
use sp_runtime::{
traits::{
CheckedAdd, CheckedSub, DispatchInfoOf, PostDispatchInfoOf, Saturating, StaticLookup,
CheckedAdd, CheckedSub, Dispatchable, DispatchInfoOf, PostDispatchInfoOf, Saturating, StaticLookup,
Zero,
},
ArithmeticError, FixedI64, FixedPointNumber, FixedPointOperand,
Expand All @@ -45,10 +46,18 @@ pub mod pallet {
pub(crate) type BalanceOf<T> = <T as Config>::Balance;

#[pallet::config]
pub trait Config: frame_system::Config + pallet_transaction_payment::Config {
pub trait Config: frame_system::Config + pallet_transaction_payment::Config + pallet_proxy::Config {
/// The overarching event type.
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;

/// The overarching call type.
type RuntimeCall: Parameter
+ Dispatchable<RuntimeOrigin = Self::RuntimeOrigin>
+ GetDispatchInfo
+ From<pallet_proxy::Call<Self>>
+ IsSubType<pallet_proxy::Call<Self>>
+ IsType<<Self as frame_system::Config>::RuntimeCall>;

/// The currency type.
type Currency: Currency<Self::AccountId, Balance = Self::Balance>;

Expand Down Expand Up @@ -166,13 +175,13 @@ pub mod pallet {
let caller = ensure_signed(origin)?;
let target = T::Lookup::lookup(target)?;

let caller_balance = T::Currency::free_balance(&caller);
let caller_balance = <T as Config>::Currency::free_balance(&caller);
let caller_balance_after_burn =
caller_balance.checked_sub(&burn_amount).ok_or(Error::<T>::NotEnoughBalance)?;

let withdraw_reason = WithdrawReasons::all();

T::Currency::ensure_can_withdraw(
<T as Config>::Currency::ensure_can_withdraw(
&caller,
burn_amount,
withdraw_reason,
Expand All @@ -192,7 +201,7 @@ pub mod pallet {

Self::ensure_can_capture_energy(&target, captured_energy_amount)?;

let _ = T::Currency::withdraw(
let _ = <T as Config>::Currency::withdraw(
&caller,
burn_amount,
withdraw_reason,
Expand Down Expand Up @@ -329,12 +338,18 @@ pub mod pallet {
}
}

/// Keeps track of whether transaction was paid using proxy's real account energy.
pub enum IsProxy<AccountId> {
Yes(AccountId),
No,
}

/// Keeps track of how the user paid for the transaction.
pub enum LiquidityInfo<T: Config> {
/// Nothing have been paid.
Nothing,
/// Transaction have been paid using energy.
Energy(BalanceOf<T>),
Energy(BalanceOf<T>, IsProxy<T::AccountId>),
/// Transaction have been paid using the native method.
Native(<T::NativeOnChargeTransaction as OnChargeTransaction<T>>::LiquidityInfo),
}
Expand All @@ -351,8 +366,8 @@ pub mod pallet {

fn withdraw_fee(
who: &T::AccountId,
call: &T::RuntimeCall,
dispatch_info: &DispatchInfoOf<T::RuntimeCall>,
call: &<T as frame_system::Config>::RuntimeCall,
dispatch_info: &DispatchInfoOf<<T as frame_system::Config>::RuntimeCall>,
fee: Self::Balance,
tip: Self::Balance,
) -> Result<Self::LiquidityInfo, TransactionValidityError> {
Expand All @@ -362,9 +377,26 @@ pub mod pallet {

let fee_without_tip = fee.saturating_sub(tip);
let energy_fee = Self::native_token_to_energy(fee_without_tip);

let maybe_proxy_call = <T as Config>::RuntimeCall::from_ref(call).is_sub_type();

let mut is_who_a_proxy = false;
let energy_provider = match maybe_proxy_call {
Some(pallet_proxy::Call::proxy { real, .. }) => {
let real_account = T::Lookup::lookup(real.clone())?;
is_who_a_proxy = pallet_proxy::Pallet::<T>::find_proxy(&real_account, who, None).is_ok();

if is_who_a_proxy {
real_account
} else {
who.clone()
}
}
_ => who.clone(),
};

// if we don't have enough energy then fallback to paying with native token.
if Self::energy_balance(&who) < energy_fee {
if Self::energy_balance(&energy_provider) < energy_fee {
return T::NativeOnChargeTransaction::withdraw_fee(
who,
call,
Expand All @@ -377,7 +409,7 @@ pub mod pallet {

if !tip.is_zero() {
// TODO: maybe do something with tip?
let _ = T::Currency::withdraw(
let _ = <T as Config>::Currency::withdraw(
who,
tip,
WithdrawReasons::TIP,
Expand All @@ -386,19 +418,26 @@ pub mod pallet {
.map_err(|_| -> InvalidTransaction { InvalidTransaction::Payment })?;
}

match Self::ensure_can_consume_energy(who, energy_fee) {
match Self::ensure_can_consume_energy(&energy_provider, energy_fee) {
Ok(()) => {
Self::consume_energy(who, energy_fee);
Ok(LiquidityInfo::Energy(energy_fee))
Self::consume_energy(&energy_provider, energy_fee);
Ok(LiquidityInfo::Energy(
energy_fee,
if is_who_a_proxy {
IsProxy::Yes(energy_provider)
} else {
IsProxy::No
},
))
},
Err(_) => Err(InvalidTransaction::Payment.into()),
}
}

fn correct_and_deposit_fee(
who: &T::AccountId,
dispatch_info: &DispatchInfoOf<T::RuntimeCall>,
post_info: &PostDispatchInfoOf<T::RuntimeCall>,
dispatch_info: &DispatchInfoOf<<T as frame_system::Config>::RuntimeCall>,
post_info: &PostDispatchInfoOf<<T as frame_system::Config>::RuntimeCall>,
corrected_fee: Self::Balance,
tip: Self::Balance,
already_withdrawn: Self::LiquidityInfo,
Expand All @@ -414,17 +453,21 @@ pub mod pallet {
tip,
fallback_info,
),
LiquidityInfo::Energy(paid) => {
LiquidityInfo::Energy(paid, maybe_proxy) => {
let corrected_fee_without_tip = corrected_fee.saturating_sub(tip);
let corrected_energy_fee =
Self::native_token_to_energy(corrected_fee_without_tip);

let refund_amount = paid.saturating_sub(corrected_energy_fee);
let refund_destination = match maybe_proxy {
IsProxy::Yes(ref energy_provider) => energy_provider,
IsProxy::No => who,
};

Self::capture_energy(who, refund_amount);
Self::capture_energy(refund_destination, refund_amount);

Ok(())
},
}
}
}
}
Expand Down
43 changes: 36 additions & 7 deletions pallets/energy/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
// Full notice is available at https://github.com/dappforce/subsocial-parachain/blob/main/COPYRIGHT
// Full license is available at https://github.com/dappforce/subsocial-parachain/blob/main/LICENSE

use codec::Decode;
use scale_info::TypeInfo;
use codec::{Decode, Encode, MaxEncodedLen};
use frame_support::{
dispatch::{RawOrigin, DispatchInfo},
pallet_prelude::{DispatchClass, Pays, Weight},
Expand All @@ -15,17 +16,14 @@ use frame_support::{
WeightToFeePolynomial,
},
};
use frame_support::traits::InstanceFilter;
use pallet_balances::NegativeImbalance;
use pallet_transaction_payment::{CurrencyAdapter, OnChargeTransaction};
use smallvec::smallvec;
use sp_core::H256;
use sp_io::TestExternalities;
use sp_runtime::{
testing::Header,
traits::{BlakeTwo256, DispatchInfoOf, IdentityLookup, One, PostDispatchInfoOf},
transaction_validity::TransactionValidityError,
FixedI64, Perbill,
};
use sp_runtime::{testing::Header, traits::{BlakeTwo256, DispatchInfoOf, IdentityLookup, One, PostDispatchInfoOf}, transaction_validity::TransactionValidityError, FixedI64, Perbill, RuntimeDebug};
use sp_runtime::traits::{ConstU32, ConstU64};
use sp_std::{
cell::RefCell,
convert::{TryFrom, TryInto},
Expand All @@ -52,6 +50,7 @@ frame_support::construct_runtime!(
Balances: pallet_balances,
TransactionPayment: pallet_transaction_payment,
Energy: pallet_energy,
Proxy: pallet_proxy,
}
);

Expand Down Expand Up @@ -108,6 +107,35 @@ impl pallet_balances::Config for Test {
type ReserveIdentifier = ();
}

#[derive(Encode, Decode, Clone, Eq, PartialEq, Ord, PartialOrd, TypeInfo, MaxEncodedLen, RuntimeDebug, Default)]
pub enum MockProxyType {
#[default]
Any,
}

impl InstanceFilter<RuntimeCall> for MockProxyType {
fn filter(&self, _call: &RuntimeCall) -> bool {
match self {
Self::Any => true,
}
}
}

impl pallet_proxy::Config for Test {
type RuntimeEvent = RuntimeEvent;
type RuntimeCall = RuntimeCall;
type Currency = Balances;
type ProxyType = MockProxyType;
type ProxyDepositBase = ConstU64<0>;
type ProxyDepositFactor = ConstU64<0>;
type MaxProxies = ConstU32<10>;
type WeightInfo = ();
type MaxPending = ConstU32<0>;
type CallHasher = BlakeTwo256;
type AnnouncementDepositBase = ConstU64<0>;
type AnnouncementDepositFactor = ConstU64<0>;
}

/// It returns the input weight as the result.
///
/// Equals to: f(x) = x
Expand Down Expand Up @@ -217,6 +245,7 @@ where

impl pallet_energy::Config for Test {
type RuntimeEvent = RuntimeEvent;
type RuntimeCall = RuntimeCall;
type Currency = Balances;
type Balance = <Test as pallet_balances::Config>::Balance;
type DefaultValueCoefficient = ValueCoefficient;
Expand Down
Loading

0 comments on commit cb8489a

Please sign in to comment.