1use crate::{Config, Pallet};
21
22use alloc::collections::vec_deque::VecDeque;
23use bp_messages::{
24 source_chain::{DeliveryConfirmationPayments, RelayersRewards},
25 MessageNonce,
26};
27pub use bp_relayers::PayRewardFromAccount;
28use bp_relayers::{RewardsAccountOwner, RewardsAccountParams};
29use bp_runtime::Chain;
30use core::{marker::PhantomData, ops::RangeInclusive};
31use frame_support::{sp_runtime::SaturatedConversion, traits::Get};
32use pallet_bridge_messages::LaneIdOf;
33use sp_arithmetic::traits::{Saturating, Zero};
34
35pub struct DeliveryConfirmationPaymentsAdapter<T, MI, RI, DeliveryReward>(
38 PhantomData<(T, MI, RI, DeliveryReward)>,
39);
40
41impl<T, MI, RI, DeliveryReward> DeliveryConfirmationPayments<T::AccountId, LaneIdOf<T, MI>>
42 for DeliveryConfirmationPaymentsAdapter<T, MI, RI, DeliveryReward>
43where
44 T: Config<RI> + pallet_bridge_messages::Config<MI>,
45 MI: 'static,
46 RI: 'static,
47 DeliveryReward: Get<T::RewardBalance>,
48 <T as Config<RI>>::Reward: From<RewardsAccountParams<LaneIdOf<T, MI>>>,
49{
50 type Error = &'static str;
51
52 fn pay_reward(
53 lane_id: LaneIdOf<T, MI>,
54 messages_relayers: VecDeque<bp_messages::UnrewardedRelayer<T::AccountId>>,
55 confirmation_relayer: &T::AccountId,
56 received_range: &RangeInclusive<bp_messages::MessageNonce>,
57 ) -> MessageNonce {
58 let relayers_rewards =
59 bp_messages::calc_relayers_rewards::<T::AccountId>(messages_relayers, received_range);
60 let rewarded_relayers = relayers_rewards.len();
61
62 register_relayers_rewards::<T, RI, MI>(
63 confirmation_relayer,
64 relayers_rewards,
65 RewardsAccountParams::new(
66 lane_id,
67 T::BridgedChain::ID,
68 RewardsAccountOwner::BridgedChain,
69 ),
70 DeliveryReward::get(),
71 );
72
73 rewarded_relayers as _
74 }
75}
76
77fn register_relayers_rewards<
79 T: Config<RI> + pallet_bridge_messages::Config<MI>,
80 RI: 'static,
81 MI: 'static,
82>(
83 confirmation_relayer: &T::AccountId,
84 relayers_rewards: RelayersRewards<T::AccountId>,
85 lane_id: RewardsAccountParams<LaneIdOf<T, MI>>,
86 delivery_fee: T::RewardBalance,
87) where
88 <T as Config<RI>>::Reward: From<RewardsAccountParams<LaneIdOf<T, MI>>>,
89{
90 let mut confirmation_relayer_reward = T::RewardBalance::zero();
92 for (relayer, messages) in relayers_rewards {
93 let relayer_reward =
96 T::RewardBalance::saturated_from(messages).saturating_mul(delivery_fee);
97
98 if relayer != *confirmation_relayer {
99 Pallet::<T, RI>::register_relayer_reward(lane_id.into(), &relayer, relayer_reward);
100 } else {
101 confirmation_relayer_reward =
102 confirmation_relayer_reward.saturating_add(relayer_reward);
103 }
104 }
105
106 Pallet::<T, RI>::register_relayer_reward(
108 lane_id.into(),
109 confirmation_relayer,
110 confirmation_relayer_reward,
111 );
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117 use crate::{mock::*, RelayerRewards};
118 use bp_messages::LaneIdType;
119 use bp_relayers::PaymentProcedure;
120 use frame_support::{
121 assert_ok,
122 traits::fungible::{Inspect, Mutate},
123 };
124
125 const RELAYER_1: ThisChainAccountId = 1;
126 const RELAYER_2: ThisChainAccountId = 2;
127 const RELAYER_3: ThisChainAccountId = 3;
128
129 fn relayers_rewards() -> RelayersRewards<ThisChainAccountId> {
130 vec![(RELAYER_1, 2), (RELAYER_2, 3)].into_iter().collect()
131 }
132
133 #[test]
134 fn confirmation_relayer_is_rewarded_if_it_has_also_delivered_messages() {
135 run_test(|| {
136 register_relayers_rewards::<TestRuntime, (), ()>(
137 &RELAYER_2,
138 relayers_rewards(),
139 test_reward_account_param(),
140 50,
141 );
142
143 assert_eq!(
144 RelayerRewards::<TestRuntime>::get(RELAYER_1, test_reward_account_param()),
145 Some(100)
146 );
147 assert_eq!(
148 RelayerRewards::<TestRuntime>::get(RELAYER_2, test_reward_account_param()),
149 Some(150)
150 );
151 });
152 }
153
154 #[test]
155 fn confirmation_relayer_is_not_rewarded_if_it_has_not_delivered_any_messages() {
156 run_test(|| {
157 register_relayers_rewards::<TestRuntime, (), ()>(
158 &RELAYER_3,
159 relayers_rewards(),
160 test_reward_account_param(),
161 50,
162 );
163
164 assert_eq!(
165 RelayerRewards::<TestRuntime>::get(RELAYER_1, test_reward_account_param()),
166 Some(100)
167 );
168 assert_eq!(
169 RelayerRewards::<TestRuntime>::get(RELAYER_2, test_reward_account_param()),
170 Some(150)
171 );
172 assert_eq!(
173 RelayerRewards::<TestRuntime>::get(RELAYER_3, test_reward_account_param()),
174 None
175 );
176 });
177 }
178
179 #[test]
180 fn pay_reward_from_account_actually_pays_reward() {
181 type Balances = pallet_balances::Pallet<TestRuntime>;
182 type PayLaneRewardFromAccount =
183 PayRewardFromAccount<Balances, ThisChainAccountId, TestLaneIdType, RewardBalance>;
184
185 run_test(|| {
186 let in_lane_0 = RewardsAccountParams::new(
187 TestLaneIdType::try_new(1, 2).unwrap(),
188 *b"test",
189 RewardsAccountOwner::ThisChain,
190 );
191 let out_lane_1 = RewardsAccountParams::new(
192 TestLaneIdType::try_new(1, 3).unwrap(),
193 *b"test",
194 RewardsAccountOwner::BridgedChain,
195 );
196
197 let in_lane0_rewards_account = PayLaneRewardFromAccount::rewards_account(in_lane_0);
198 let out_lane1_rewards_account = PayLaneRewardFromAccount::rewards_account(out_lane_1);
199
200 assert_ok!(Balances::mint_into(&in_lane0_rewards_account, 200));
201 assert_ok!(Balances::mint_into(&out_lane1_rewards_account, 100));
202 assert_eq!(Balances::balance(&in_lane0_rewards_account), 200);
203 assert_eq!(Balances::balance(&out_lane1_rewards_account), 100);
204 assert_eq!(Balances::balance(&1), 0);
205 assert_eq!(Balances::balance(&2), 0);
206
207 assert_ok!(PayLaneRewardFromAccount::pay_reward(&1, in_lane_0, 100, 1_u64));
208 assert_eq!(Balances::balance(&in_lane0_rewards_account), 100);
209 assert_eq!(Balances::balance(&out_lane1_rewards_account), 100);
210 assert_eq!(Balances::balance(&1), 100);
211 assert_eq!(Balances::balance(&2), 0);
212
213 assert_ok!(PayLaneRewardFromAccount::pay_reward(&1, out_lane_1, 100, 1_u64));
214 assert_eq!(Balances::balance(&in_lane0_rewards_account), 100);
215 assert_eq!(Balances::balance(&out_lane1_rewards_account), 0);
216 assert_eq!(Balances::balance(&1), 200);
217 assert_eq!(Balances::balance(&2), 0);
218
219 assert_ok!(PayLaneRewardFromAccount::pay_reward(&1, in_lane_0, 100, 2_u64));
220 assert_eq!(Balances::balance(&in_lane0_rewards_account), 0);
221 assert_eq!(Balances::balance(&out_lane1_rewards_account), 0);
222 assert_eq!(Balances::balance(&1), 200);
223 assert_eq!(Balances::balance(&2), 100);
224 });
225 }
226}