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

Fix/evm fee burning #1390

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
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
59 changes: 56 additions & 3 deletions primitives/src/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,18 @@

use crate::{AccountId, AssetId};

use frame_support::ensure;
use pallet_evm::{AddressMapping, HashedAddressMapping};
use frame_support::{
ensure,
traits::{
fungible::{Balanced, Credit},
tokens::{fungible::Inspect, imbalance::OnUnbalanced},
},
};
use pallet_evm::{AddressMapping, HashedAddressMapping, OnChargeEVMTransaction};
use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
use sp_core::{Hasher, H160, H256};
use sp_core::{Hasher, H160, H256, U256};
use sp_runtime::traits::UniqueSaturatedInto;
use sp_std::marker::PhantomData;

use pallet_assets::AssetsCallback;
Expand Down Expand Up @@ -139,3 +146,49 @@ impl<Address> UnifiedAddress<Address> {
}
}
}

/// Wrapper around the `EvmFungibleAdapter` from the `pallet-evm`.
///
/// While it provides most of the functionality we need,
/// it doesn't allow the tip to be deposited into an arbitrary account.
/// This adapter allows us to do that.
///
/// Two separate `OnUnbalanced` handers are used:
/// - `UOF` for the fee
/// - `OUT` for the tip
pub struct EVMFungibleAdapterWrapper<F, OUF, OUT>(core::marker::PhantomData<(F, OUF, OUT)>);
impl<T, F, OUF, OUT> OnChargeEVMTransaction<T> for EVMFungibleAdapterWrapper<F, OUF, OUT>
where
T: pallet_evm::Config,
F: Balanced<T::AccountId>,
OUF: OnUnbalanced<Credit<T::AccountId, F>>,
OUT: OnUnbalanced<Credit<T::AccountId, F>>,
U256: UniqueSaturatedInto<<F as Inspect<<T as frame_system::Config>::AccountId>>::Balance>,
{
// Kept type as Option to satisfy bound of Default
type LiquidityInfo = Option<Credit<T::AccountId, F>>;

fn withdraw_fee(who: &H160, fee: U256) -> Result<Self::LiquidityInfo, pallet_evm::Error<T>> {
pallet_evm::EVMFungibleAdapter::<F, OUF>::withdraw_fee(who, fee)
}

fn correct_and_deposit_fee(
who: &H160,
corrected_fee: U256,
base_fee: U256,
already_withdrawn: Self::LiquidityInfo,
) -> Self::LiquidityInfo {
<pallet_evm::EVMFungibleAdapter::<F, OUF> as OnChargeEVMTransaction<T>>::correct_and_deposit_fee(
who,
corrected_fee,
base_fee,
already_withdrawn,
)
}

fn pay_priority_fee(tip: Self::LiquidityInfo) {
if let Some(tip) = tip {
OUT::on_unbalanceds(Some(tip).into_iter());
}
}
}
16 changes: 10 additions & 6 deletions runtime/astar/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ use astar_primitives::{
AccountCheck as DappStakingAccountCheck, CycleConfiguration, DAppId, EraNumber,
PeriodNumber, RankedTier, SmartContract, StandardTierSlots,
},
evm::EvmRevertCodeHandler,
evm::{EVMFungibleAdapterWrapper, EvmRevertCodeHandler},
governance::{
CommunityCouncilCollectiveInst, CommunityCouncilMembershipInst, CommunityTreasuryInst,
EnsureRootOrAllMainCouncil, EnsureRootOrAllTechnicalCommittee,
Expand Down Expand Up @@ -474,7 +474,7 @@ impl pallet_inflation::PayoutPerBlock<Credit<AccountId, Balances>> for Inflation
}

fn collators(reward: Credit<AccountId, Balances>) {
ToStakingPot::on_unbalanced(reward);
CollatorRewardPot::on_unbalanced(reward);
}
}

Expand Down Expand Up @@ -614,8 +614,8 @@ parameter_types! {
pub TreasuryAccountId: AccountId = TreasuryPalletId::get().into_account_truncating();
}

pub struct ToStakingPot;
impl OnUnbalanced<Credit<AccountId, Balances>> for ToStakingPot {
pub struct CollatorRewardPot;
impl OnUnbalanced<Credit<AccountId, Balances>> for CollatorRewardPot {
fn on_nonzero_unbalanced(amount: Credit<AccountId, Balances>) {
let staking_pot = PotId::get().into_account_truncating();
let _ = Balances::resolve(&staking_pot, amount);
Expand Down Expand Up @@ -838,9 +838,13 @@ impl OnUnbalanced<Credit<AccountId, Balances>> for DealWithFees {
drop(to_burn);

// pay fees to collator
<ToStakingPot as OnUnbalanced<_>>::on_unbalanced(collator);
<CollatorRewardPot as OnUnbalanced<_>>::on_unbalanced(collator);
}
}

fn on_unbalanced(amount: Credit<AccountId, Balances>) {
Self::on_unbalanceds(Some(amount).into_iter());
}
}

impl pallet_transaction_payment::Config for Runtime {
Expand Down Expand Up @@ -946,7 +950,7 @@ impl pallet_evm::Config for Runtime {
type PrecompilesType = Precompiles;
type PrecompilesValue = PrecompilesValue;
type ChainId = ChainId;
type OnChargeTransaction = pallet_evm::EVMFungibleAdapter<Balances, ToStakingPot>;
type OnChargeTransaction = EVMFungibleAdapterWrapper<Balances, DealWithFees, CollatorRewardPot>;
type BlockGasLimit = BlockGasLimit;
type Timestamp = Timestamp;
type OnCreate = ();
Expand Down
16 changes: 10 additions & 6 deletions runtime/shibuya/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ use astar_primitives::{
AccountCheck as DappStakingAccountCheck, CycleConfiguration, DAppId, EraNumber,
PeriodNumber, RankedTier, SmartContract, StandardTierSlots,
},
evm::{EvmRevertCodeHandler, HashedDefaultMappings},
evm::{EVMFungibleAdapterWrapper, EvmRevertCodeHandler, HashedDefaultMappings},
governance::{
CommunityCouncilCollectiveInst, CommunityCouncilMembershipInst, CommunityTreasuryInst,
EnsureRootOrAllMainCouncil, EnsureRootOrAllTechnicalCommittee,
Expand Down Expand Up @@ -497,7 +497,7 @@ impl pallet_inflation::PayoutPerBlock<Credit<AccountId, Balances>> for Inflation
}

fn collators(reward: Credit<AccountId, Balances>) {
ToStakingPot::on_unbalanced(reward);
CollatorRewardPot::on_unbalanced(reward);
}
}

Expand Down Expand Up @@ -638,8 +638,8 @@ parameter_types! {
pub TreasuryAccountId: AccountId = TreasuryPalletId::get().into_account_truncating();
}

pub struct ToStakingPot;
impl OnUnbalanced<Credit<AccountId, Balances>> for ToStakingPot {
pub struct CollatorRewardPot;
impl OnUnbalanced<Credit<AccountId, Balances>> for CollatorRewardPot {
fn on_nonzero_unbalanced(amount: Credit<AccountId, Balances>) {
let staking_pot = PotId::get().into_account_truncating();
let _ = Balances::resolve(&staking_pot, amount);
Expand Down Expand Up @@ -834,9 +834,13 @@ impl OnUnbalanced<Credit<AccountId, Balances>> for DealWithFees {
drop(to_burn);

// pay fees to collator
<ToStakingPot as OnUnbalanced<_>>::on_unbalanced(collator);
<CollatorRewardPot as OnUnbalanced<_>>::on_unbalanced(collator);
}
}

fn on_unbalanced(amount: Credit<AccountId, Balances>) {
Self::on_unbalanceds(Some(amount).into_iter());
}
}

impl pallet_transaction_payment::Config for Runtime {
Expand Down Expand Up @@ -947,7 +951,7 @@ impl pallet_evm::Config for Runtime {
// Ethereum-compatible chain_id:
// * Shibuya: 81
type ChainId = EVMChainId;
type OnChargeTransaction = pallet_evm::EVMFungibleAdapter<Balances, ToStakingPot>;
type OnChargeTransaction = EVMFungibleAdapterWrapper<Balances, DealWithFees, CollatorRewardPot>;
type BlockGasLimit = BlockGasLimit;
type Timestamp = Timestamp;
type OnCreate = ();
Expand Down
16 changes: 10 additions & 6 deletions runtime/shiden/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ use astar_primitives::{
AccountCheck as DappStakingAccountCheck, CycleConfiguration, DAppId, EraNumber,
PeriodNumber, RankedTier, SmartContract, TierSlots as TierSlotsFunc,
},
evm::EvmRevertCodeHandler,
evm::{EVMFungibleAdapterWrapper, EvmRevertCodeHandler},
governance::OracleMembershipInst,
oracle::{CurrencyAmount, CurrencyId, DummyCombineData, Price},
xcm::AssetLocationIdConverter,
Expand Down Expand Up @@ -462,7 +462,7 @@ impl pallet_inflation::PayoutPerBlock<Credit<AccountId, Balances>> for Inflation
}

fn collators(reward: Credit<AccountId, Balances>) {
ToStakingPot::on_unbalanced(reward);
CollatorRewardPot::on_unbalanced(reward);
}
}

Expand Down Expand Up @@ -601,8 +601,8 @@ parameter_types! {
pub TreasuryAccountId: AccountId = TreasuryPalletId::get().into_account_truncating();
}

pub struct ToStakingPot;
impl OnUnbalanced<Credit<AccountId, Balances>> for ToStakingPot {
pub struct CollatorRewardPot;
impl OnUnbalanced<Credit<AccountId, Balances>> for CollatorRewardPot {
fn on_nonzero_unbalanced(amount: Credit<AccountId, Balances>) {
let staking_pot = PotId::get().into_account_truncating();
let _ = Balances::resolve(&staking_pot, amount);
Expand Down Expand Up @@ -813,9 +813,13 @@ impl OnUnbalanced<Credit<AccountId, Balances>> for DealWithFees {
drop(to_burn);

// pay fees to collator
<ToStakingPot as OnUnbalanced<_>>::on_unbalanced(collator);
<CollatorRewardPot as OnUnbalanced<_>>::on_unbalanced(collator);
}
}

fn on_unbalanced(amount: Credit<AccountId, Balances>) {
Self::on_unbalanceds(Some(amount).into_iter());
}
}

impl pallet_transaction_payment::Config for Runtime {
Expand Down Expand Up @@ -920,7 +924,7 @@ impl pallet_evm::Config for Runtime {
type PrecompilesType = Precompiles;
type PrecompilesValue = PrecompilesValue;
type ChainId = ChainId;
type OnChargeTransaction = pallet_evm::EVMFungibleAdapter<Balances, ToStakingPot>;
type OnChargeTransaction = EVMFungibleAdapterWrapper<Balances, DealWithFees, CollatorRewardPot>;
type BlockGasLimit = BlockGasLimit;
type Timestamp = Timestamp;
type OnCreate = ();
Expand Down
86 changes: 86 additions & 0 deletions tests/integration/src/fees.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// This file is part of Astar.

// Copyright (C) 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 <http://www.gnu.org/licenses/>.

use crate::setup::*;

use frame_support::traits::Currency;
use pallet_evm::{AddressMapping, OnChargeEVMTransaction};
use sp_core::{H160, U256};
use sp_runtime::traits::AccountIdConversion;

#[test]
fn evm_fees_work() {
new_test_ext().execute_with(|| {
let address = H160::repeat_byte(0xbe);
let mapped_address =
<Runtime as pallet_evm::Config>::AddressMapping::into_account_id(address);
Balances::make_free_balance_be(&mapped_address, 1_000_000_000_000_000);

type EvmFeeHandler = <Runtime as pallet_evm::Config>::OnChargeTransaction;

// 0. Define init values
let (base_fee, tip, init_fee) = (500, 100, 1000);
let corrected_fee = base_fee + tip;

let pot_account = PotId::get().into_account_truncating();
let init_reward_pot = Balances::free_balance(&pot_account);
let init_total_issuance = Balances::total_issuance();

// 1. Withdraw some init fee
let result = <EvmFeeHandler as OnChargeEVMTransaction<Runtime>>::withdraw_fee(
&address,
U256::from(init_fee),
);
let already_withdrawn = result.expect("Account is funded, must succeed.");

// 2. Correct the charged fee
let calculated_tip =
<EvmFeeHandler as OnChargeEVMTransaction<Runtime>>::correct_and_deposit_fee(
&address,
U256::from(corrected_fee),
U256::from(base_fee),
already_withdrawn,
);
assert!(calculated_tip.is_some());

// The expectation is that 20% of the fee was deposited into the reward pot, and the rest was burned.
assert_eq!(
init_reward_pot + base_fee / 5,
Balances::free_balance(&pot_account)
);
assert_eq!(
init_total_issuance - base_fee / 5 * 4,
Balances::total_issuance()
);

// 3. Deposit the tip
let issuance = Balances::total_issuance();
let pot = Balances::free_balance(&pot_account);
<EvmFeeHandler as OnChargeEVMTransaction<Runtime>>::pay_priority_fee(calculated_tip);
assert_eq!(
issuance,
Balances::total_issuance(),
"Total issuance should not change since tip isn't burned."
);
assert_eq!(
pot + tip,
Balances::free_balance(&pot_account),
"Pot should increase by the tip amount."
);
})
}
3 changes: 3 additions & 0 deletions tests/integration/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,6 @@ mod governance;

#[cfg(any(feature = "shibuya", feature = "shiden", feature = "astar"))]
mod xcm_api;

#[cfg(any(feature = "shibuya", feature = "shiden", feature = "astar"))]
mod fees;
Loading