1use super::MintLocation;
20use core::{fmt::Debug, marker::PhantomData, result};
21use frame_support::traits::{
22 tokens::{
23 fungible, Fortitude::Polite, Precision::Exact, Preservation::Expendable, Provenance::Minted,
24 },
25 Get,
26};
27use xcm::latest::prelude::*;
28use xcm_executor::{
29 traits::{ConvertLocation, Error as MatchError, MatchesFungible, TransactAsset},
30 AssetsInHolding,
31};
32
33pub struct FungibleTransferAdapter<Fungible, Matcher, AccountIdConverter, AccountId>(
37 PhantomData<(Fungible, Matcher, AccountIdConverter, AccountId)>,
38);
39impl<
40 Fungible: fungible::Mutate<AccountId>,
41 Matcher: MatchesFungible<Fungible::Balance>,
42 AccountIdConverter: ConvertLocation<AccountId>,
43 AccountId: Eq + Clone + Debug,
44 > TransactAsset for FungibleTransferAdapter<Fungible, Matcher, AccountIdConverter, AccountId>
45{
46 fn internal_transfer_asset(
47 what: &Asset,
48 from: &Location,
49 to: &Location,
50 _context: &XcmContext,
51 ) -> result::Result<AssetsInHolding, XcmError> {
52 tracing::trace!(
53 target: "xcm::fungible_adapter",
54 ?what, ?from, ?to,
55 "internal_transfer_asset",
56 );
57 let amount = Matcher::matches_fungible(what).ok_or(MatchError::AssetNotHandled)?;
59 let source = AccountIdConverter::convert_location(from)
60 .ok_or(MatchError::AccountIdConversionFailed)?;
61 let dest = AccountIdConverter::convert_location(to)
62 .ok_or(MatchError::AccountIdConversionFailed)?;
63 Fungible::transfer(&source, &dest, amount, Expendable).map_err(|error| {
64 tracing::debug!(
65 target: "xcm::fungible_adapter", ?error, ?source, ?dest, ?amount,
66 "Failed to transfer asset",
67 );
68 XcmError::FailedToTransactAsset(error.into())
69 })?;
70 Ok(what.clone().into())
71 }
72}
73
74pub struct FungibleMutateAdapter<Fungible, Matcher, AccountIdConverter, AccountId, CheckingAccount>(
78 PhantomData<(Fungible, Matcher, AccountIdConverter, AccountId, CheckingAccount)>,
79);
80
81impl<
82 Fungible: fungible::Mutate<AccountId>,
83 Matcher: MatchesFungible<Fungible::Balance>,
84 AccountIdConverter: ConvertLocation<AccountId>,
85 AccountId: Eq + Clone + Debug,
86 CheckingAccount: Get<Option<(AccountId, MintLocation)>>,
87 > FungibleMutateAdapter<Fungible, Matcher, AccountIdConverter, AccountId, CheckingAccount>
88{
89 fn can_accrue_checked(checking_account: AccountId, amount: Fungible::Balance) -> XcmResult {
90 Fungible::can_deposit(&checking_account, amount, Minted)
91 .into_result()
92 .map_err(|error| {
93 tracing::debug!(
94 target: "xcm::fungible_adapter", ?error, ?checking_account, ?amount,
95 "Failed to deposit funds into account",
96 );
97 XcmError::NotDepositable
98 })
99 }
100
101 fn can_reduce_checked(checking_account: AccountId, amount: Fungible::Balance) -> XcmResult {
102 Fungible::can_withdraw(&checking_account, amount)
103 .into_result(false)
104 .map_err(|error| {
105 tracing::debug!(
106 target: "xcm::fungible_adapter", ?error, ?checking_account, ?amount,
107 "Failed to withdraw funds from account",
108 );
109 XcmError::NotWithdrawable
110 })
111 .map(|_| ())
112 }
113
114 fn accrue_checked(checking_account: AccountId, amount: Fungible::Balance) {
115 let ok = Fungible::mint_into(&checking_account, amount).is_ok();
116 debug_assert!(ok, "`can_accrue_checked` must have returned `true` immediately prior; qed");
117 }
118
119 fn reduce_checked(checking_account: AccountId, amount: Fungible::Balance) {
120 let ok = Fungible::burn_from(&checking_account, amount, Expendable, Exact, Polite).is_ok();
121 debug_assert!(ok, "`can_reduce_checked` must have returned `true` immediately prior; qed");
122 }
123}
124
125impl<
126 Fungible: fungible::Mutate<AccountId>,
127 Matcher: MatchesFungible<Fungible::Balance>,
128 AccountIdConverter: ConvertLocation<AccountId>,
129 AccountId: Eq + Clone + Debug,
130 CheckingAccount: Get<Option<(AccountId, MintLocation)>>,
131 > TransactAsset
132 for FungibleMutateAdapter<Fungible, Matcher, AccountIdConverter, AccountId, CheckingAccount>
133{
134 fn can_check_in(origin: &Location, what: &Asset, _context: &XcmContext) -> XcmResult {
135 tracing::trace!(
136 target: "xcm::fungible_adapter",
137 ?origin, ?what,
138 "can_check_in origin",
139 );
140 let amount = Matcher::matches_fungible(what).ok_or(MatchError::AssetNotHandled)?;
142 match CheckingAccount::get() {
143 Some((checking_account, MintLocation::Local)) =>
144 Self::can_reduce_checked(checking_account, amount),
145 Some((checking_account, MintLocation::NonLocal)) =>
146 Self::can_accrue_checked(checking_account, amount),
147 None => Ok(()),
148 }
149 }
150
151 fn check_in(origin: &Location, what: &Asset, _context: &XcmContext) {
152 tracing::trace!(
153 target: "xcm::fungible_adapter",
154 ?origin, ?what,
155 "check_in origin",
156 );
157 if let Some(amount) = Matcher::matches_fungible(what) {
158 match CheckingAccount::get() {
159 Some((checking_account, MintLocation::Local)) =>
160 Self::reduce_checked(checking_account, amount),
161 Some((checking_account, MintLocation::NonLocal)) =>
162 Self::accrue_checked(checking_account, amount),
163 None => (),
164 }
165 }
166 }
167
168 fn can_check_out(dest: &Location, what: &Asset, _context: &XcmContext) -> XcmResult {
169 tracing::trace!(
170 target: "xcm::fungible_adapter",
171 ?dest,
172 ?what,
173 "can_check_out",
174 );
175 let amount = Matcher::matches_fungible(what).ok_or(MatchError::AssetNotHandled)?;
176 match CheckingAccount::get() {
177 Some((checking_account, MintLocation::Local)) =>
178 Self::can_accrue_checked(checking_account, amount),
179 Some((checking_account, MintLocation::NonLocal)) =>
180 Self::can_reduce_checked(checking_account, amount),
181 None => Ok(()),
182 }
183 }
184
185 fn check_out(dest: &Location, what: &Asset, _context: &XcmContext) {
186 tracing::trace!(
187 target: "xcm::fungible_adapter",
188 ?dest,
189 ?what,
190 "check_out",
191 );
192 if let Some(amount) = Matcher::matches_fungible(what) {
193 match CheckingAccount::get() {
194 Some((checking_account, MintLocation::Local)) =>
195 Self::accrue_checked(checking_account, amount),
196 Some((checking_account, MintLocation::NonLocal)) =>
197 Self::reduce_checked(checking_account, amount),
198 None => (),
199 }
200 }
201 }
202
203 fn deposit_asset(what: &Asset, who: &Location, _context: Option<&XcmContext>) -> XcmResult {
204 tracing::trace!(
205 target: "xcm::fungible_adapter",
206 ?what, ?who,
207 "deposit_asset",
208 );
209 let amount = Matcher::matches_fungible(what).ok_or(MatchError::AssetNotHandled)?;
210 let who = AccountIdConverter::convert_location(who)
211 .ok_or(MatchError::AccountIdConversionFailed)?;
212 Fungible::mint_into(&who, amount).map_err(|error| {
213 tracing::debug!(
214 target: "xcm::fungible_adapter", ?error, ?who, ?amount,
215 "Failed to deposit assets",
216 );
217 XcmError::FailedToTransactAsset(error.into())
218 })?;
219 Ok(())
220 }
221
222 fn withdraw_asset(
223 what: &Asset,
224 who: &Location,
225 _context: Option<&XcmContext>,
226 ) -> result::Result<AssetsInHolding, XcmError> {
227 tracing::trace!(
228 target: "xcm::fungible_adapter",
229 ?what, ?who,
230 "withdraw_asset",
231 );
232 let amount = Matcher::matches_fungible(what).ok_or(MatchError::AssetNotHandled)?;
233 let who = AccountIdConverter::convert_location(who)
234 .ok_or(MatchError::AccountIdConversionFailed)?;
235 Fungible::burn_from(&who, amount, Expendable, Exact, Polite).map_err(|error| {
236 tracing::debug!(
237 target: "xcm::fungible_adapter", ?error, ?who, ?amount,
238 "Failed to withdraw assets",
239 );
240 XcmError::FailedToTransactAsset(error.into())
241 })?;
242 Ok(what.clone().into())
243 }
244}
245
246pub struct FungibleAdapter<Fungible, Matcher, AccountIdConverter, AccountId, CheckingAccount>(
250 PhantomData<(Fungible, Matcher, AccountIdConverter, AccountId, CheckingAccount)>,
251);
252impl<
253 Fungible: fungible::Mutate<AccountId>,
254 Matcher: MatchesFungible<Fungible::Balance>,
255 AccountIdConverter: ConvertLocation<AccountId>,
256 AccountId: Eq + Clone + Debug,
257 CheckingAccount: Get<Option<(AccountId, MintLocation)>>,
258 > TransactAsset
259 for FungibleAdapter<Fungible, Matcher, AccountIdConverter, AccountId, CheckingAccount>
260{
261 fn can_check_in(origin: &Location, what: &Asset, context: &XcmContext) -> XcmResult {
262 FungibleMutateAdapter::<
263 Fungible,
264 Matcher,
265 AccountIdConverter,
266 AccountId,
267 CheckingAccount,
268 >::can_check_in(origin, what, context)
269 }
270
271 fn check_in(origin: &Location, what: &Asset, context: &XcmContext) {
272 FungibleMutateAdapter::<
273 Fungible,
274 Matcher,
275 AccountIdConverter,
276 AccountId,
277 CheckingAccount,
278 >::check_in(origin, what, context)
279 }
280
281 fn can_check_out(dest: &Location, what: &Asset, context: &XcmContext) -> XcmResult {
282 FungibleMutateAdapter::<
283 Fungible,
284 Matcher,
285 AccountIdConverter,
286 AccountId,
287 CheckingAccount,
288 >::can_check_out(dest, what, context)
289 }
290
291 fn check_out(dest: &Location, what: &Asset, context: &XcmContext) {
292 FungibleMutateAdapter::<
293 Fungible,
294 Matcher,
295 AccountIdConverter,
296 AccountId,
297 CheckingAccount,
298 >::check_out(dest, what, context)
299 }
300
301 fn deposit_asset(what: &Asset, who: &Location, context: Option<&XcmContext>) -> XcmResult {
302 FungibleMutateAdapter::<
303 Fungible,
304 Matcher,
305 AccountIdConverter,
306 AccountId,
307 CheckingAccount,
308 >::deposit_asset(what, who, context)
309 }
310
311 fn withdraw_asset(
312 what: &Asset,
313 who: &Location,
314 maybe_context: Option<&XcmContext>,
315 ) -> result::Result<AssetsInHolding, XcmError> {
316 FungibleMutateAdapter::<
317 Fungible,
318 Matcher,
319 AccountIdConverter,
320 AccountId,
321 CheckingAccount,
322 >::withdraw_asset(what, who, maybe_context)
323 }
324
325 fn internal_transfer_asset(
326 what: &Asset,
327 from: &Location,
328 to: &Location,
329 context: &XcmContext,
330 ) -> result::Result<AssetsInHolding, XcmError> {
331 FungibleTransferAdapter::<Fungible, Matcher, AccountIdConverter, AccountId>::internal_transfer_asset(
332 what, from, to, context
333 )
334 }
335}