1use bp_relayers::StakeAndSlash;
21use codec::Codec;
22use core::{fmt::Debug, marker::PhantomData};
23use frame_support::traits::{tokens::BalanceStatus, NamedReservableCurrency};
24use sp_runtime::{traits::Get, DispatchError, DispatchResult};
25
26pub struct StakeAndSlashNamed<AccountId, BlockNumber, Currency, ReserveId, Stake, Lease>(
32 PhantomData<(AccountId, BlockNumber, Currency, ReserveId, Stake, Lease)>,
33);
34
35impl<AccountId, BlockNumber, Currency, ReserveId, Stake, Lease>
36 StakeAndSlash<AccountId, BlockNumber, Currency::Balance>
37 for StakeAndSlashNamed<AccountId, BlockNumber, Currency, ReserveId, Stake, Lease>
38where
39 AccountId: Codec + Debug,
40 Currency: NamedReservableCurrency<AccountId>,
41 ReserveId: Get<Currency::ReserveIdentifier>,
42 Stake: Get<Currency::Balance>,
43 Lease: Get<BlockNumber>,
44{
45 type RequiredStake = Stake;
46 type RequiredRegistrationLease = Lease;
47
48 fn reserve(relayer: &AccountId, amount: Currency::Balance) -> DispatchResult {
49 Currency::reserve_named(&ReserveId::get(), relayer, amount)
50 }
51
52 fn unreserve(relayer: &AccountId, amount: Currency::Balance) -> Currency::Balance {
53 Currency::unreserve_named(&ReserveId::get(), relayer, amount)
54 }
55
56 fn repatriate_reserved(
57 relayer: &AccountId,
58 beneficiary: &AccountId,
59 amount: Currency::Balance,
60 ) -> Result<Currency::Balance, DispatchError> {
61 Currency::repatriate_reserved_named(
62 &ReserveId::get(),
63 relayer,
64 &beneficiary,
65 amount,
66 BalanceStatus::Free,
67 )
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74 use crate::mock::*;
75 use bp_relayers::ExplicitOrAccountParams;
76
77 use frame_support::traits::fungible::Mutate;
78 use sp_runtime::traits::IdentifyAccount;
79
80 fn test_stake() -> ThisChainBalance {
81 Stake::get()
82 }
83
84 #[test]
85 fn reserve_works() {
86 run_test(|| {
87 assert!(TestStakeAndSlash::reserve(&1, test_stake()).is_err());
88 assert_eq!(Balances::free_balance(1), 0);
89 assert_eq!(Balances::reserved_balance(1), 0);
90
91 Balances::mint_into(&2, test_stake() - 1).unwrap();
92 assert!(TestStakeAndSlash::reserve(&2, test_stake()).is_err());
93 assert_eq!(Balances::free_balance(2), test_stake() - 1);
94 assert_eq!(Balances::reserved_balance(2), 0);
95
96 Balances::mint_into(&3, test_stake() * 2).unwrap();
97 assert_eq!(TestStakeAndSlash::reserve(&3, test_stake()), Ok(()));
98 assert_eq!(Balances::free_balance(3), test_stake());
99 assert_eq!(Balances::reserved_balance(3), test_stake());
100 })
101 }
102
103 #[test]
104 fn unreserve_works() {
105 run_test(|| {
106 assert_eq!(TestStakeAndSlash::unreserve(&1, test_stake()), test_stake());
107 assert_eq!(Balances::free_balance(1), 0);
108 assert_eq!(Balances::reserved_balance(1), 0);
109
110 Balances::mint_into(&2, test_stake() * 2).unwrap();
111 TestStakeAndSlash::reserve(&2, test_stake() / 3).unwrap();
112 assert_eq!(
113 TestStakeAndSlash::unreserve(&2, test_stake()),
114 test_stake() - test_stake() / 3
115 );
116 assert_eq!(Balances::free_balance(2), test_stake() * 2);
117 assert_eq!(Balances::reserved_balance(2), 0);
118
119 Balances::mint_into(&3, test_stake() * 2).unwrap();
120 TestStakeAndSlash::reserve(&3, test_stake()).unwrap();
121 assert_eq!(TestStakeAndSlash::unreserve(&3, test_stake()), 0);
122 assert_eq!(Balances::free_balance(3), test_stake() * 2);
123 assert_eq!(Balances::reserved_balance(3), 0);
124 })
125 }
126
127 #[test]
128 fn repatriate_reserved_works() {
129 run_test(|| {
130 let beneficiary = test_reward_account_param();
131 let beneficiary_account = TestPaymentProcedure::rewards_account(beneficiary);
132
133 let mut expected_balance = ExistentialDeposit::get();
134 Balances::mint_into(&beneficiary_account, expected_balance).unwrap();
135
136 assert_eq!(
137 TestStakeAndSlash::repatriate_reserved(
138 &1,
139 &(ExplicitOrAccountParams::Params(beneficiary).into_account()),
140 test_stake()
141 ),
142 Ok(test_stake())
143 );
144 assert_eq!(Balances::free_balance(1), 0);
145 assert_eq!(Balances::reserved_balance(1), 0);
146 assert_eq!(Balances::free_balance(beneficiary_account), expected_balance);
147 assert_eq!(Balances::reserved_balance(beneficiary_account), 0);
148
149 expected_balance += test_stake() / 3;
150 Balances::mint_into(&2, test_stake() * 2).unwrap();
151 TestStakeAndSlash::reserve(&2, test_stake() / 3).unwrap();
152 assert_eq!(
153 TestStakeAndSlash::repatriate_reserved(
154 &2,
155 &(ExplicitOrAccountParams::Params(beneficiary).into_account()),
156 test_stake()
157 ),
158 Ok(test_stake() - test_stake() / 3)
159 );
160 assert_eq!(Balances::free_balance(2), test_stake() * 2 - test_stake() / 3);
161 assert_eq!(Balances::reserved_balance(2), 0);
162 assert_eq!(Balances::free_balance(beneficiary_account), expected_balance);
163 assert_eq!(Balances::reserved_balance(beneficiary_account), 0);
164
165 expected_balance += test_stake();
166 Balances::mint_into(&3, test_stake() * 2).unwrap();
167 TestStakeAndSlash::reserve(&3, test_stake()).unwrap();
168 assert_eq!(
169 TestStakeAndSlash::repatriate_reserved(
170 &3,
171 &(ExplicitOrAccountParams::Params(beneficiary).into_account()),
172 test_stake()
173 ),
174 Ok(0)
175 );
176 assert_eq!(Balances::free_balance(3), test_stake());
177 assert_eq!(Balances::reserved_balance(3), 0);
178 assert_eq!(Balances::free_balance(beneficiary_account), expected_balance);
179 assert_eq!(Balances::reserved_balance(beneficiary_account), 0);
180 })
181 }
182
183 #[test]
184 fn repatriate_reserved_doesnt_work_when_beneficiary_account_is_missing() {
185 run_test(|| {
186 let beneficiary = test_reward_account_param();
187 let beneficiary_account = TestPaymentProcedure::rewards_account(beneficiary);
188
189 Balances::mint_into(&3, test_stake() * 2).unwrap();
190 TestStakeAndSlash::reserve(&3, test_stake()).unwrap();
191 assert!(TestStakeAndSlash::repatriate_reserved(
192 &3,
193 &(ExplicitOrAccountParams::Params(beneficiary).into_account()),
194 test_stake()
195 )
196 .is_err());
197 assert_eq!(Balances::free_balance(3), test_stake());
198 assert_eq!(Balances::reserved_balance(3), test_stake());
199 assert_eq!(Balances::free_balance(beneficiary_account), 0);
200 assert_eq!(Balances::reserved_balance(beneficiary_account), 0);
201 });
202 }
203}