diff --git a/Cargo.lock b/Cargo.lock index 9ec07a69e9..d85d16ad43 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2222,6 +2222,7 @@ dependencies = [ "domain-test-primitives", "domain-test-service", "evm-domain-test-runtime", + "frame-system", "futures", "futures-timer", "num-traits", @@ -2828,6 +2829,7 @@ dependencies = [ "pallet-evm-precompile-sha3fips 2.0.0-dev (git+https://github.com/subspace/frontier?rev=74483666645e121c0c5e6616f43fdfd8664ea0d3)", "pallet-evm-precompile-simple 2.0.0-dev (git+https://github.com/subspace/frontier?rev=74483666645e121c0c5e6616f43fdfd8664ea0d3)", "pallet-messenger", + "pallet-operator-rewards", "pallet-sudo", "pallet-timestamp", "pallet-transaction-payment", @@ -2881,6 +2883,7 @@ dependencies = [ "pallet-evm-precompile-sha3fips 2.0.0-dev (git+https://github.com/subspace/frontier?rev=c13d670b25b5506c1c5243f352941dc46c82ffe4)", "pallet-evm-precompile-simple 2.0.0-dev (git+https://github.com/subspace/frontier?rev=c13d670b25b5506c1c5243f352941dc46c82ffe4)", "pallet-messenger", + "pallet-operator-rewards", "pallet-sudo", "pallet-timestamp", "pallet-transaction-payment", @@ -7136,6 +7139,18 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-operator-rewards" +version = "0.1.0" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-rewards" version = "0.1.0" diff --git a/domains/client/domain-operator/Cargo.toml b/domains/client/domain-operator/Cargo.toml index 8b336149fc..5458752d44 100644 --- a/domains/client/domain-operator/Cargo.toml +++ b/domains/client/domain-operator/Cargo.toml @@ -46,6 +46,7 @@ domain-client-message-relayer = { version = "0.1.0", path = "../relayer" } domain-test-service = { version = "0.1.0", path = "../../test/service" } domain-test-primitives = { version = "0.1.0", path = "../../test/primitives" } evm-domain-test-runtime = { version = "0.1.0", path = "../../test/runtime/evm" } +frame-system = { version = "4.0.0-dev", default-features = false, git = "https://github.com/subspace/substrate", rev = "55c157cff49b638a59d81a9f971f0f9a66829c71" } num-traits = "0.2.15" pallet-balances = { version = "4.0.0-dev", git = "https://github.com/subspace/substrate", rev = "55c157cff49b638a59d81a9f971f0f9a66829c71" } pallet-domains = { version = "0.1.0", path = "../../../crates/pallet-domains" } diff --git a/domains/client/domain-operator/src/domain_block_processor.rs b/domains/client/domain-operator/src/domain_block_processor.rs index 47c48d47d5..9b79e374a4 100644 --- a/domains/client/domain-operator/src/domain_block_processor.rs +++ b/domains/client/domain-operator/src/domain_block_processor.rs @@ -352,6 +352,13 @@ where })? }; + // Get the accumulated transaction fee of all transactions included in the block + // and used as the operator reward + let total_rewards = self + .client + .runtime_api() + .block_transaction_fee(header_hash)?; + let execution_receipt = ExecutionReceipt { domain_block_number: header_number, domain_block_hash: header_hash, @@ -363,8 +370,7 @@ where final_state_root: state_root, execution_trace: trace, execution_trace_root: sp_core::H256(trace_root), - // TODO: set proper value - total_rewards: Default::default(), + total_rewards, }; Ok(DomainBlockResult { diff --git a/domains/client/domain-operator/src/tests.rs b/domains/client/domain-operator/src/tests.rs index 81ea27cbfb..4b0ebef1f9 100644 --- a/domains/client/domain-operator/src/tests.rs +++ b/domains/client/domain-operator/src/tests.rs @@ -1816,3 +1816,72 @@ async fn test_restart_domain_operator() { assert_eq!(ferdie.client.info().best_number, 11); assert_eq!(alice.client.info().best_number, 10); } + +#[substrate_test_utils::test(flavor = "multi_thread")] +async fn test_domain_transaction_fee_and_operator_reward() { + let directory = TempDir::new().expect("Must be able to create temporary directory"); + + let mut builder = sc_cli::LoggerBuilder::new(""); + builder.with_colors(false); + let _ = builder.init(); + + let tokio_handle = tokio::runtime::Handle::current(); + + // Start Ferdie + let mut ferdie = MockConsensusNode::run( + tokio_handle.clone(), + Ferdie, + BasePath::new(directory.path().join("ferdie")), + ); + + // Produce 1 consensus block to initialize genesis domain + ferdie.produce_block_with_slot(1.into()).await.unwrap(); + + // Run Alice (a evm domain authority node) + let mut alice = domain_test_service::DomainNodeBuilder::new( + tokio_handle.clone(), + Alice, + BasePath::new(directory.path().join("alice")), + ) + .build_evm_node(Role::Authority, GENESIS_DOMAIN_ID, &mut ferdie) + .await; + + produce_blocks!(ferdie, alice, 3).await.unwrap(); + + // Construct and submit an extrinsic with tip + let pre_alice_free_balance = alice.free_balance(Alice.to_account_id()); + let tip = 123456; + let tx = alice.construct_extrinsic_with_tip( + alice.account_nonce(), + tip, + frame_system::Call::remark { remark: vec![] }, + ); + alice + .send_extrinsic(tx) + .await + .expect("Failed to send extrinsic"); + + // Produce a bundle that contains the just sent extrinsic + let (slot, bundle) = ferdie.produce_slot_and_wait_for_bundle_submission().await; + assert_eq!(bundle.unwrap().extrinsics.len(), 1); + produce_block_with!(ferdie.produce_block_with_slot(slot), alice) + .await + .unwrap(); + let consensus_block_hash = ferdie.client.info().best_hash; + + // Produce one more bundle, this bundle should contains the ER of the previous bundle + let (slot, bundle) = ferdie.produce_slot_and_wait_for_bundle_submission().await; + let receipt = bundle.unwrap().into_receipt(); + assert_eq!(receipt.consensus_block_hash, consensus_block_hash); + produce_block_with!(ferdie.produce_block_with_slot(slot), alice) + .await + .unwrap(); + + // Transaction fee (including the tip) is deducted from alice's account + let alice_free_balance_changes = + pre_alice_free_balance - alice.free_balance(Alice.to_account_id()); + assert!(alice_free_balance_changes >= tip as u128); + + // All the transaction fee is collected as operator reward + assert_eq!(alice_free_balance_changes, receipt.total_rewards); +} diff --git a/domains/pallets/operator-rewards/Cargo.toml b/domains/pallets/operator-rewards/Cargo.toml new file mode 100644 index 0000000000..785aa413e8 --- /dev/null +++ b/domains/pallets/operator-rewards/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "pallet-operator-rewards" +version = "0.1.0" +authors = ["Subspace Labs "] +edition = "2021" +license = "Apache-2.0" +homepage = "https://subspace.network" +repository = "https://github.com/subspace/subspace" +description = "Subspace node pallet for charging and re-distributing domain transaction fees" +include = [ + "/src", + "/Cargo.toml", +] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.6.3", default-features = false, features = ["derive"] } +frame-support = { version = "4.0.0-dev", default-features = false, git = "https://github.com/subspace/substrate", rev = "55c157cff49b638a59d81a9f971f0f9a66829c71" } +frame-system = { version = "4.0.0-dev", default-features = false, git = "https://github.com/subspace/substrate", rev = "55c157cff49b638a59d81a9f971f0f9a66829c71" } +scale-info = { version = "2.7.0", default-features = false, features = ["derive"] } +sp-runtime = { version = "24.0.0", default-features = false, git = "https://github.com/subspace/substrate", rev = "55c157cff49b638a59d81a9f971f0f9a66829c71" } +sp-std = { version = "8.0.0", default-features = false, git = "https://github.com/subspace/substrate", rev = "55c157cff49b638a59d81a9f971f0f9a66829c71" } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "sp-runtime/std", + "sp-std/std", +] diff --git a/domains/pallets/operator-rewards/src/lib.rs b/domains/pallets/operator-rewards/src/lib.rs new file mode 100644 index 0000000000..f0a6eff91b --- /dev/null +++ b/domains/pallets/operator-rewards/src/lib.rs @@ -0,0 +1,75 @@ +// Copyright (C) 2023 Subspace Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Pallet Domain Transaction Fees + +#![cfg_attr(not(feature = "std"), no_std)] + +pub use pallet::*; + +#[frame_support::pallet] +mod pallet { + use codec::{Codec, MaxEncodedLen}; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + use scale_info::TypeInfo; + use sp_runtime::traits::{AtLeast32BitUnsigned, MaybeSerializeDeserialize, Saturating, Zero}; + use sp_runtime::FixedPointOperand; + use sp_std::fmt::Debug; + + #[pallet::config] + pub trait Config: frame_system::Config { + /// The balance of an account. + type Balance: Parameter + + Member + + AtLeast32BitUnsigned + + Codec + + Default + + Copy + + MaybeSerializeDeserialize + + Debug + + MaxEncodedLen + + TypeInfo + + FixedPointOperand; + } + + /// The accumulated rewards of the current block + /// + /// Currently, the only source of rewards is the transaction fees, in the furture it + /// will include the XDM reward. + #[pallet::storage] + #[pallet::getter(fn block_transaction_fee)] + pub(super) type BlockRewards = StorageValue<_, T::Balance, ValueQuery>; + + /// Pallet operator-rewards to store the accumulated rewards of the current block + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(_); + + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_initialize(_now: BlockNumberFor) -> Weight { + BlockRewards::::set(Zero::zero()); + T::DbWeight::get().writes(1) + } + } + + impl Pallet { + pub fn note_transaction_fees(tx_fee: T::Balance) { + let next_block_rewards = BlockRewards::::get().saturating_add(tx_fee); + BlockRewards::::set(next_block_rewards); + } + } +} diff --git a/domains/primitives/runtime/src/lib.rs b/domains/primitives/runtime/src/lib.rs index 7e9b205a5b..0c108eae9d 100644 --- a/domains/primitives/runtime/src/lib.rs +++ b/domains/primitives/runtime/src/lib.rs @@ -195,6 +195,9 @@ sp_api::decl_runtime_apis! { /// Return the extrinsic weight fn extrinsic_weight(ext: &Block::Extrinsic) -> Weight; + + /// The accumulated transaction fee of all transactions included in the block + fn block_transaction_fee() -> Balance; } /// Api that construct inherent extrinsics. diff --git a/domains/runtime/evm/Cargo.toml b/domains/runtime/evm/Cargo.toml index c2f69ccf29..c90fcffa27 100644 --- a/domains/runtime/evm/Cargo.toml +++ b/domains/runtime/evm/Cargo.toml @@ -34,6 +34,7 @@ log = { version = "0.4.19", default-features = false } pallet-balances = { version = "4.0.0-dev", default-features = false, git = "https://github.com/subspace/substrate", rev = "55c157cff49b638a59d81a9f971f0f9a66829c71" } pallet-base-fee = { version = "1.0.0-dev", default-features = false, git = "https://github.com/subspace/frontier", rev = "74483666645e121c0c5e6616f43fdfd8664ea0d3" } pallet-domain-id = { version = "0.1.0", path = "../../pallets/domain-id", default-features = false } +pallet-operator-rewards = { version = "0.1.0", path = "../../pallets/operator-rewards", default-features = false } pallet-ethereum = { version = "4.0.0-dev", default-features = false, git = "https://github.com/subspace/frontier", rev = "74483666645e121c0c5e6616f43fdfd8664ea0d3" } pallet-evm = { version = "6.0.0-dev", default-features = false, git = "https://github.com/subspace/frontier", rev = "74483666645e121c0c5e6616f43fdfd8664ea0d3" } pallet-evm-chain-id = { version = "1.0.0-dev", default-features = false, git = "https://github.com/subspace/frontier", rev = "74483666645e121c0c5e6616f43fdfd8664ea0d3" } @@ -85,6 +86,7 @@ std = [ "pallet-balances/std", "pallet-base-fee/std", "pallet-domain-id/std", + "pallet-operator-rewards/std", "pallet-ethereum/std", "pallet-evm/std", "pallet-evm-chain-id/std", diff --git a/domains/runtime/evm/src/lib.rs b/domains/runtime/evm/src/lib.rs index 6a16d1e665..262c017ffe 100644 --- a/domains/runtime/evm/src/lib.rs +++ b/domains/runtime/evm/src/lib.rs @@ -15,7 +15,9 @@ use domain_runtime_primitives::{MultiAccountId, TryConvertBack, SLOT_DURATION}; use fp_account::EthereumSignature; use fp_self_contained::SelfContainedCall; use frame_support::dispatch::{DispatchClass, GetDispatchInfo}; -use frame_support::traits::{ConstU16, ConstU32, ConstU64, Everything, FindAuthor}; +use frame_support::traits::{ + ConstU16, ConstU32, ConstU64, Currency, Everything, FindAuthor, Imbalance, OnUnbalanced, +}; use frame_support::weights::constants::{ BlockExecutionWeight, ExtrinsicBaseWeight, ParityDbWeight, WEIGHT_REF_TIME_PER_MILLIS, WEIGHT_REF_TIME_PER_SECOND, @@ -323,6 +325,24 @@ impl pallet_balances::Config for Runtime { type MaxHolds = (); } +impl pallet_operator_rewards::Config for Runtime { + type Balance = Balance; +} + +type NegativeImbalance = >::NegativeImbalance; + +/// `ActualPaidFeesHandler` used to collect all the fee in `pallet_operator_rewards` +pub struct ActualPaidFeesHandler; + +impl OnUnbalanced for ActualPaidFeesHandler { + fn on_unbalanceds(fees_then_tips: impl Iterator) { + // Record both actual paid transaction fee and tip in `pallet_operator_rewards` + for fee in fees_then_tips { + OperatorRewards::note_transaction_fees(fee.peek()); + } + } +} + parameter_types! { pub const TransactionByteFee: Balance = 1; pub const OperationalFeeMultiplier: u8 = 5; @@ -330,7 +350,8 @@ parameter_types! { impl pallet_transaction_payment::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type OnChargeTransaction = pallet_transaction_payment::CurrencyAdapter; + type OnChargeTransaction = + pallet_transaction_payment::CurrencyAdapter; type WeightToFee = IdentityFee; type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = (); @@ -453,6 +474,58 @@ parameter_types! { pub WeightPerGas: Weight = Weight::from_parts(WEIGHT_PER_GAS, 0); } +// `BaseFeeHandler` is used to handle the base fee of the evm extrinsic separately, this is necessary +// because `InnerEVMCurrencyAdapter::pallet_operator_rewards` only return the tip of the evm extrinsic. +struct BaseFeeHandler; + +impl OnUnbalanced for BaseFeeHandler { + fn on_nonzero_unbalanced(base_fee: NegativeImbalance) { + // Record the evm transaction base fee + OperatorRewards::note_transaction_fees(base_fee.peek()) + } +} + +type InnerEVMCurrencyAdapter = pallet_evm::EVMCurrencyAdapter; + +// Implementation of [`pallet_transaction_payment::OnChargeTransaction`] that charges evm transaction +// fees from the transaction sender and collect all the fees (including both the base fee and tip) in +// `pallet_operator_rewards` +pub struct EVMCurrencyAdapter; + +impl pallet_evm::OnChargeEVMTransaction for EVMCurrencyAdapter { + type LiquidityInfo = Option; + + fn withdraw_fee( + who: &H160, + fee: U256, + ) -> Result> { + InnerEVMCurrencyAdapter::withdraw_fee(who, fee) + } + + fn correct_and_deposit_fee( + who: &H160, + corrected_fee: U256, + base_fee: U256, + already_withdrawn: Self::LiquidityInfo, + ) -> Self::LiquidityInfo { + let tip = + >::correct_and_deposit_fee(who, corrected_fee, base_fee, already_withdrawn); + if let Some(t) = tip.as_ref() { + // Record the evm transaction tip + OperatorRewards::note_transaction_fees(t.peek()) + } + tip + } + + fn pay_priority_fee(tip: Self::LiquidityInfo) { + >::pay_priority_fee( + tip, + ); + } +} + impl pallet_evm::Config for Runtime { type FeeCalculator = BaseFee; type GasWeightMapping = pallet_evm::FixedGasWeightMapping; @@ -468,7 +541,7 @@ impl pallet_evm::Config for Runtime { type ChainId = EVMChainId; type BlockGasLimit = BlockGasLimit; type Runner = pallet_evm::runner::stack::Runner; - type OnChargeTransaction = (); + type OnChargeTransaction = EVMCurrencyAdapter; type OnCreate = (); type FindAuthor = FindAuthorTruncated; type Timestamp = Timestamp; @@ -550,6 +623,7 @@ construct_runtime!( // domain instance stuff SelfDomainId: pallet_domain_id = 90, + OperatorRewards: pallet_operator_rewards = 91, // Sudo account Sudo: pallet_sudo = 100, @@ -812,6 +886,10 @@ impl_runtime_apis! { fn extrinsic_weight(ext: &::Extrinsic) -> Weight { ext.get_dispatch_info().weight } + + fn block_transaction_fee() -> Balance { + OperatorRewards::block_transaction_fee() + } } impl domain_runtime_primitives::InherentExtrinsicApi for Runtime { diff --git a/domains/test/runtime/evm/Cargo.toml b/domains/test/runtime/evm/Cargo.toml index 7fa7eaf825..d52cd0b9c5 100644 --- a/domains/test/runtime/evm/Cargo.toml +++ b/domains/test/runtime/evm/Cargo.toml @@ -35,6 +35,7 @@ log = { version = "0.4.19", default-features = false } pallet-balances = { version = "4.0.0-dev", default-features = false, git = "https://github.com/subspace/substrate", rev = "55c157cff49b638a59d81a9f971f0f9a66829c71" } pallet-base-fee = { version = "1.0.0-dev", default-features = false, git = "https://github.com/subspace/frontier", rev = "c13d670b25b5506c1c5243f352941dc46c82ffe4" } pallet-domain-id = { version = "0.1.0", path = "../../../pallets/domain-id", default-features = false } +pallet-operator-rewards = { version = "0.1.0", path = "../../../pallets/operator-rewards", default-features = false } pallet-ethereum = { version = "4.0.0-dev", default-features = false, git = "https://github.com/subspace/frontier", rev = "c13d670b25b5506c1c5243f352941dc46c82ffe4" } pallet-evm = { version = "6.0.0-dev", default-features = false, git = "https://github.com/subspace/frontier", rev = "c13d670b25b5506c1c5243f352941dc46c82ffe4" } pallet-evm-chain-id = { version = "1.0.0-dev", default-features = false, git = "https://github.com/subspace/frontier", rev = "c13d670b25b5506c1c5243f352941dc46c82ffe4" } @@ -87,6 +88,7 @@ std = [ "pallet-balances/std", "pallet-base-fee/std", "pallet-domain-id/std", + "pallet-operator-rewards/std", "pallet-ethereum/std", "pallet-evm/std", "pallet-evm-chain-id/std", diff --git a/domains/test/runtime/evm/src/lib.rs b/domains/test/runtime/evm/src/lib.rs index 989d657f83..bdaf9021b2 100644 --- a/domains/test/runtime/evm/src/lib.rs +++ b/domains/test/runtime/evm/src/lib.rs @@ -15,7 +15,9 @@ use domain_runtime_primitives::{MultiAccountId, TryConvertBack, SLOT_DURATION}; use fp_account::EthereumSignature; use fp_self_contained::SelfContainedCall; use frame_support::dispatch::{DispatchClass, GetDispatchInfo}; -use frame_support::traits::{ConstU16, ConstU32, ConstU64, Everything, FindAuthor}; +use frame_support::traits::{ + ConstU16, ConstU32, ConstU64, Currency, Everything, FindAuthor, Imbalance, OnUnbalanced, +}; use frame_support::weights::constants::{ BlockExecutionWeight, ExtrinsicBaseWeight, ParityDbWeight, WEIGHT_REF_TIME_PER_MILLIS, WEIGHT_REF_TIME_PER_SECOND, @@ -323,6 +325,22 @@ impl pallet_balances::Config for Runtime { type MaxHolds = (); } +impl pallet_operator_rewards::Config for Runtime { + type Balance = Balance; +} + +/// `ActualPaidFeesHandler` used to collect all the fee in `pallet_operator_rewards` +pub struct ActualPaidFeesHandler; + +impl OnUnbalanced for ActualPaidFeesHandler { + fn on_unbalanceds(fees_then_tips: impl Iterator) { + // Record both actual paid transaction fee and tip in `pallet_operator_rewards` + for fee in fees_then_tips { + OperatorRewards::note_transaction_fees(fee.peek()); + } + } +} + parameter_types! { pub const TransactionByteFee: Balance = 1; pub const OperationalFeeMultiplier: u8 = 5; @@ -330,7 +348,8 @@ parameter_types! { impl pallet_transaction_payment::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type OnChargeTransaction = pallet_transaction_payment::CurrencyAdapter; + type OnChargeTransaction = + pallet_transaction_payment::CurrencyAdapter; type WeightToFee = IdentityFee; type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = (); @@ -454,6 +473,60 @@ parameter_types! { pub WeightPerGas: Weight = Weight::from_parts(WEIGHT_PER_GAS, 0); } +type NegativeImbalance = >::NegativeImbalance; + +// `BaseFeeHandler` is used to handle the base fee of the evm extrinsic separately, this is necessary +// because `InnerEVMCurrencyAdapter::pallet_operator_rewards` only return the tip of the evm extrinsic. +struct BaseFeeHandler; + +impl OnUnbalanced for BaseFeeHandler { + fn on_nonzero_unbalanced(base_fee: NegativeImbalance) { + // Record the evm transaction base fee + OperatorRewards::note_transaction_fees(base_fee.peek()) + } +} + +type InnerEVMCurrencyAdapter = pallet_evm::EVMCurrencyAdapter; + +// Implementation of [`pallet_transaction_payment::OnChargeTransaction`] that charges evm transaction +// fees from the transaction sender and collect all the fees (including both the base fee and tip) in +// `pallet_operator_rewards` +pub struct EVMCurrencyAdapter; + +impl pallet_evm::OnChargeEVMTransaction for EVMCurrencyAdapter { + type LiquidityInfo = Option; + + fn withdraw_fee( + who: &H160, + fee: U256, + ) -> Result> { + InnerEVMCurrencyAdapter::withdraw_fee(who, fee) + } + + fn correct_and_deposit_fee( + who: &H160, + corrected_fee: U256, + base_fee: U256, + already_withdrawn: Self::LiquidityInfo, + ) -> Self::LiquidityInfo { + let tip = + >::correct_and_deposit_fee(who, corrected_fee, base_fee, already_withdrawn); + if let Some(t) = tip.as_ref() { + // Record the evm transaction tip + OperatorRewards::note_transaction_fees(t.peek()) + } + tip + } + + fn pay_priority_fee(tip: Self::LiquidityInfo) { + >::pay_priority_fee( + tip, + ); + } +} + impl pallet_evm::Config for Runtime { type FeeCalculator = BaseFee; type GasWeightMapping = pallet_evm::FixedGasWeightMapping; @@ -469,7 +542,7 @@ impl pallet_evm::Config for Runtime { type ChainId = EVMChainId; type BlockGasLimit = BlockGasLimit; type Runner = pallet_evm::runner::stack::Runner; - type OnChargeTransaction = (); + type OnChargeTransaction = EVMCurrencyAdapter; type OnCreate = (); type FindAuthor = FindAuthorTruncated; type Timestamp = Timestamp; @@ -551,6 +624,7 @@ construct_runtime!( // domain instance stuff SelfDomainId: pallet_domain_id = 90, + OperatorRewards: pallet_operator_rewards = 91, // Sudo account Sudo: pallet_sudo = 100, @@ -812,6 +886,10 @@ impl_runtime_apis! { fn extrinsic_weight(ext: &::Extrinsic) -> Weight { ext.get_dispatch_info().weight } + + fn block_transaction_fee() -> Balance { + OperatorRewards::block_transaction_fee() + } } impl domain_runtime_primitives::InherentExtrinsicApi for Runtime { diff --git a/domains/test/service/src/domain.rs b/domains/test/service/src/domain.rs index 7ea7ef535e..c4c5104a5f 100644 --- a/domains/test/service/src/domain.rs +++ b/domains/test/service/src/domain.rs @@ -339,6 +339,7 @@ where self.key, false, self.account_nonce(), + 0, ); self.rpc_handlers.send_transaction(extrinsic.into()).await } @@ -349,7 +350,24 @@ where nonce: u32, function: impl Into<::RuntimeCall>, ) -> UncheckedExtrinsicFor { - construct_extrinsic_generic::(&self.client, function, self.key, false, nonce) + construct_extrinsic_generic::(&self.client, function, self.key, false, nonce, 0) + } + + /// Construct an extrinsic with the given transaction tip. + pub fn construct_extrinsic_with_tip( + &mut self, + nonce: u32, + tip: u32, + function: impl Into<::RuntimeCall>, + ) -> UncheckedExtrinsicFor { + construct_extrinsic_generic::( + &self.client, + function, + self.key, + false, + nonce, + tip, + ) } /// Send an extrinsic to this node. diff --git a/domains/test/service/src/lib.rs b/domains/test/service/src/lib.rs index 083a925886..d79715cea4 100644 --- a/domains/test/service/src/lib.rs +++ b/domains/test/service/src/lib.rs @@ -185,6 +185,7 @@ pub fn construct_extrinsic_generic( caller: EcdsaKeyring, immortal: bool, nonce: u32, + tip: u32, ) -> UncheckedExtrinsicFor where Runtime: frame_system::Config @@ -204,7 +205,6 @@ where .checked_next_power_of_two() .map(|c| c / 2) .unwrap_or(2) as u64; - let tip = 0; let extra: SignedExtraFor = ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(),