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

feat: Add an ability to use real account energy for proxy #260

Merged
merged 12 commits into from
Apr 9, 2024
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
79 changes: 61 additions & 18 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,14 +453,18 @@ pub mod pallet {
tip,
fallback_info,
),
LiquidityInfo::Energy(paid) => {
LiquidityInfo::Energy(paid, 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 proxy {
IsProxy::Yes(ref account) => account,
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
Loading