1
// Copyright 2024 Moonbeam foundation
2
// This file is part of Moonbeam.
3

            
4
// Moonbeam is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8

            
9
// Moonbeam is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13

            
14
// You should have received a copy of the GNU General Public License
15
// along with Moonbeam.  If not, see <http://www.gnu.org/licenses/>.
16

            
17
use core::marker::PhantomData;
18
use cumulus_primitives_core::Weight;
19
use frame_support::pallet_prelude::TypedGet;
20
use frame_support::traits::fungible::Credit;
21
use frame_support::traits::tokens::imbalance::ResolveTo;
22
use frame_support::traits::OnUnbalanced;
23
use frame_support::traits::{Get, Imbalance};
24
use frame_support::weights::ConstantMultiplier;
25
use moonbeam_core_primitives::Balance;
26
use pallet_treasury::TreasuryAccountId;
27
use sp_runtime::{Perbill, SaturatedConversion};
28

            
29
/// Type alias for converting reference time weight to fee using a constant multiplier.
30
///
31
/// This maps computational weight (ref_time) to a fee amount by multiplying
32
/// the weight by a constant factor `M`.
33
pub type RefTimeToFee<M> = ConstantMultiplier<Balance, M>;
34

            
35
/// Type alias for converting proof size weight to fee using a constant multiplier.
36
///
37
/// This maps the proof size (PoV size) component of weight to a fee amount
38
/// by multiplying by a constant factor `M`.
39
pub struct ProofSizeToFee<M>(PhantomData<M>);
40

            
41
impl<M> frame_support::weights::WeightToFee for ProofSizeToFee<M>
42
where
43
	M: Get<Balance>,
44
{
45
	type Balance = Balance;
46

            
47
67
	fn weight_to_fee(weight: &Weight) -> Self::Balance {
48
67
		Self::Balance::saturated_from(weight.proof_size()).saturating_mul(M::get())
49
67
	}
50
}
51

            
52
/// Combines reference time and proof size fees, charging by the more scarce resource.
53
///
54
/// This struct implements `WeightToFee` by computing fees for both the ref_time and
55
/// proof_size components of a `Weight`, then returning the maximum of the two.
56
/// This ensures transactions are charged based on whichever resource they consume
57
/// more of relative to block limits.
58
pub struct WeightToFee<RefTimeToFee, ProofSizeToFee>(PhantomData<(RefTimeToFee, ProofSizeToFee)>);
59
impl<
60
		RefTimeToFee: frame_support::weights::WeightToFee<Balance = Balance>,
61
		ProofSizeToFee: frame_support::weights::WeightToFee<Balance = Balance>,
62
	> frame_support::weights::WeightToFee for WeightToFee<RefTimeToFee, ProofSizeToFee>
63
{
64
	type Balance = Balance;
65

            
66
67
	fn weight_to_fee(weight: &Weight) -> Self::Balance {
67
		// Take the maximum instead of the sum to charge by the more scarce resource.
68
67
		RefTimeToFee::weight_to_fee(weight).max(ProofSizeToFee::weight_to_fee(weight))
69
67
	}
70
}
71

            
72
/// Deal with substrate based fees and tip. This should be used with pallet_transaction_payment.
73
pub struct DealWithSubstrateFeesAndTip<R, FeesTreasuryProportion>(
74
	sp_std::marker::PhantomData<(R, FeesTreasuryProportion)>,
75
);
76
impl<R, FeesTreasuryProportion> DealWithSubstrateFeesAndTip<R, FeesTreasuryProportion>
77
where
78
	R: pallet_balances::Config + pallet_treasury::Config + pallet_author_inherent::Config,
79
	pallet_author_inherent::Pallet<R>: Get<R::AccountId>,
80
	FeesTreasuryProportion: Get<Perbill>,
81
{
82
3
	fn deal_with_fees(amount: Credit<R::AccountId, pallet_balances::Pallet<R>>) {
83
		// Balances pallet automatically burns dropped Credits by decreasing
84
		// total_supply accordingly
85
3
		let treasury_proportion = FeesTreasuryProportion::get();
86
3
		let treasury_part = treasury_proportion.deconstruct();
87
3
		let burn_part = Perbill::one().deconstruct() - treasury_part;
88
3
		let (_, to_treasury) = amount.ration(burn_part, treasury_part);
89
3
		ResolveTo::<TreasuryAccountId<R>, pallet_balances::Pallet<R>>::on_unbalanced(to_treasury);
90
3
	}
91

            
92
3
	fn deal_with_tip(amount: Credit<R::AccountId, pallet_balances::Pallet<R>>) {
93
3
		ResolveTo::<BlockAuthorAccountId<R>, pallet_balances::Pallet<R>>::on_unbalanced(amount);
94
3
	}
95
}
96

            
97
impl<R, FeesTreasuryProportion> OnUnbalanced<Credit<R::AccountId, pallet_balances::Pallet<R>>>
98
	for DealWithSubstrateFeesAndTip<R, FeesTreasuryProportion>
99
where
100
	R: pallet_balances::Config + pallet_treasury::Config + pallet_author_inherent::Config,
101
	pallet_author_inherent::Pallet<R>: Get<R::AccountId>,
102
	FeesTreasuryProportion: Get<Perbill>,
103
{
104
3
	fn on_unbalanceds(
105
3
		mut fees_then_tips: impl Iterator<Item = Credit<R::AccountId, pallet_balances::Pallet<R>>>,
106
3
	) {
107
3
		if let Some(fees) = fees_then_tips.next() {
108
3
			Self::deal_with_fees(fees);
109
3
			if let Some(tip) = fees_then_tips.next() {
110
3
				Self::deal_with_tip(tip);
111
3
			}
112
		}
113
3
	}
114
}
115

            
116
/// Deal with ethereum based fees. To handle tips/priority fees, use DealWithEthereumPriorityFees.
117
pub struct DealWithEthereumBaseFees<R, FeesTreasuryProportion>(
118
	sp_std::marker::PhantomData<(R, FeesTreasuryProportion)>,
119
);
120
impl<R, FeesTreasuryProportion> OnUnbalanced<Credit<R::AccountId, pallet_balances::Pallet<R>>>
121
	for DealWithEthereumBaseFees<R, FeesTreasuryProportion>
122
where
123
	R: pallet_balances::Config + pallet_treasury::Config,
124
	FeesTreasuryProportion: Get<Perbill>,
125
{
126
190
	fn on_nonzero_unbalanced(amount: Credit<R::AccountId, pallet_balances::Pallet<R>>) {
127
		// Balances pallet automatically burns dropped Credits by decreasing
128
		// total_supply accordingly
129
190
		let treasury_proportion = FeesTreasuryProportion::get();
130
190
		let treasury_part = treasury_proportion.deconstruct();
131
190
		let burn_part = Perbill::one().deconstruct() - treasury_part;
132
190
		let (_, to_treasury) = amount.ration(burn_part, treasury_part);
133
190
		ResolveTo::<TreasuryAccountId<R>, pallet_balances::Pallet<R>>::on_unbalanced(to_treasury);
134
190
	}
135
}
136

            
137
pub struct BlockAuthorAccountId<R>(sp_std::marker::PhantomData<R>);
138
impl<R> TypedGet for BlockAuthorAccountId<R>
139
where
140
	R: frame_system::Config + pallet_author_inherent::Config,
141
	pallet_author_inherent::Pallet<R>: Get<R::AccountId>,
142
{
143
	type Type = R::AccountId;
144
99
	fn get() -> Self::Type {
145
99
		<pallet_author_inherent::Pallet<R> as Get<R::AccountId>>::get()
146
99
	}
147
}
148

            
149
/// Deal with ethereum based priority fees/tips. See DealWithEthereumBaseFees for base fees.
150
pub struct DealWithEthereumPriorityFees<R>(sp_std::marker::PhantomData<R>);
151
impl<R> OnUnbalanced<Credit<R::AccountId, pallet_balances::Pallet<R>>>
152
	for DealWithEthereumPriorityFees<R>
153
where
154
	R: pallet_balances::Config + pallet_author_inherent::Config,
155
	pallet_author_inherent::Pallet<R>: Get<R::AccountId>,
156
{
157
96
	fn on_nonzero_unbalanced(amount: Credit<R::AccountId, pallet_balances::Pallet<R>>) {
158
96
		ResolveTo::<BlockAuthorAccountId<R>, pallet_balances::Pallet<R>>::on_unbalanced(amount);
159
96
	}
160
}