staging_xcm_builder/asset_exchange/single_asset_adapter/
adapter.rs1extern crate alloc;
20use alloc::{boxed::Box, vec, vec::Vec};
21use core::marker::PhantomData;
22use frame_support::{
23 ensure,
24 traits::tokens::{fungibles, imbalance::UnsafeManualAccounting},
25};
26use pallet_asset_conversion::{QuotePrice, SwapCredit};
27use xcm::prelude::*;
28use xcm_executor::{
29 traits::{AssetExchange, MatchesFungibles},
30 AssetsInHolding,
31};
32
33pub struct SingleAssetExchangeAdapter<AssetConversion, Fungibles, Matcher, AccountId>(
45 PhantomData<(AssetConversion, Fungibles, Matcher, AccountId)>,
46);
47impl<AssetConversion, Fungibles, Matcher, AccountId> AssetExchange
48 for SingleAssetExchangeAdapter<AssetConversion, Fungibles, Matcher, AccountId>
49where
50 AssetConversion: SwapCredit<
51 AccountId,
52 Balance = u128,
53 AssetKind = Fungibles::AssetId,
54 Credit = fungibles::Credit<AccountId, Fungibles>,
55 > + QuotePrice<Balance = u128, AssetKind = Fungibles::AssetId>,
56 Fungibles: fungibles::Inspect<AccountId, AssetId: 'static>
57 + fungibles::Balanced<AccountId, Balance = u128, OnDropCredit: 'static, OnDropDebt: 'static>,
58 Matcher: MatchesFungibles<Fungibles::AssetId, Fungibles::Balance>,
59{
60 fn exchange_asset(
61 _: Option<&Location>,
62 mut give: AssetsInHolding,
63 want: &Assets,
64 maximal: bool,
65 ) -> Result<AssetsInHolding, AssetsInHolding> {
66 ensure!(want.len() == 1, give);
68 let Some(want_asset) = want.get(0) else { return Err(give) };
69 ensure!(give.non_fungible_assets_iter().next().is_none(), give);
71 let mut give_assets: Vec<Asset> = give.fungible_assets_iter().collect();
72 ensure!(give_assets.len() == 1, give);
74 let Some(give_asset) = give_assets.pop() else {
75 tracing::trace!(
76 target: "xcm::SingleAssetExchangeAdapter::exchange_asset",
77 ?give, "No fungible asset was in `give`.",
78 );
79 return Err(give);
80 };
81
82 let Ok((give_asset_id, _)) = Matcher::matches_fungibles(&give_asset) else {
83 tracing::trace!(
84 target: "xcm::SingleAssetExchangeAdapter::exchange_asset",
85 ?give_asset,
86 "Could not map XCM asset give to FRAME asset.",
87 );
88 return Err(give);
89 };
90 let Ok((want_asset_id, want_amount)) = Matcher::matches_fungibles(&want_asset) else {
91 tracing::trace!(
92 target: "xcm::SingleAssetExchangeAdapter::exchange_asset",
93 ?want_asset,
94 "Could not map XCM asset want to FRAME asset."
95 );
96 return Err(give);
97 };
98
99 let swap_asset = give_asset_id.clone().into();
101 let Some(imbalance) = give.fungible.remove(&give_asset.id) else { return Err(give) };
102 let mut credit_in = fungibles::Credit::<AccountId, Fungibles>::zero(give_asset_id);
104 credit_in.saturating_subsume(imbalance);
105
106 let (credit_out, maybe_credit_change) = if maximal {
108 let credit_out = match <AssetConversion as SwapCredit<_>>::swap_exact_tokens_for_tokens(
111 vec![swap_asset, want_asset_id],
112 credit_in,
113 Some(want_amount),
114 ) {
115 Ok(inner) => inner,
116 Err((credit_in, error)) => {
117 tracing::debug!(
118 target: "xcm::SingleAssetExchangeAdapter::exchange_asset",
119 ?error,
120 "Could not perform the swap"
121 );
122 let taken = AssetsInHolding::new_from_fungible_credit(
124 give_asset.id.clone(),
125 Box::new(credit_in),
126 );
127 give.subsume_assets(taken);
128 return Err(give);
129 },
130 };
131 (credit_out, None)
133 } else {
134 let (credit_out, credit_change) =
137 match <AssetConversion as SwapCredit<_>>::swap_tokens_for_exact_tokens(
138 vec![swap_asset, want_asset_id],
139 credit_in,
140 want_amount,
141 ) {
142 Ok(inner) => inner,
143 Err((credit_in, error)) => {
144 tracing::debug!(
145 target: "xcm::SingleAssetExchangeAdapter::exchange_asset",
146 ?error,
147 "Could not perform the swap",
148 );
149 let taken = AssetsInHolding::new_from_fungible_credit(
151 give_asset.id.clone(),
152 Box::new(credit_in),
153 );
154 give.subsume_assets(taken);
155 return Err(give);
156 },
157 };
158 (credit_out, if credit_change.peek() > 0 { Some(credit_change) } else { None })
159 };
160
161 let mut result =
164 AssetsInHolding::new_from_fungible_credit(want_asset.id.clone(), Box::new(credit_out));
165
166 if let Some(credit_change) = maybe_credit_change.filter(|credit| credit.peek() > 0) {
168 let leftover =
169 AssetsInHolding::new_from_fungible_credit(give_asset.id, Box::new(credit_change));
170 result.subsume_assets(leftover);
171 }
172
173 Ok(result)
174 }
175
176 fn quote_exchange_price(give: &Assets, want: &Assets, maximal: bool) -> Option<Assets> {
177 if give.len() != 1 || want.len() != 1 {
178 return None;
179 } let give_asset = give.get(0)?;
181 let want_asset = want.get(0)?;
182 let (give_asset_id, give_amount) = Matcher::matches_fungibles(give_asset)
184 .map_err(|error| {
185 tracing::trace!(
186 target: "xcm::SingleAssetExchangeAdapter::quote_exchange_price",
187 ?give_asset,
188 ?error,
189 "Could not map XCM asset to FRAME asset."
190 );
191 ()
192 })
193 .ok()?;
194 let (want_asset_id, want_amount) = Matcher::matches_fungibles(want_asset)
195 .map_err(|error| {
196 tracing::trace!(
197 target: "xcm::SingleAssetExchangeAdapter::quote_exchange_price",
198 ?want_asset,
199 ?error,
200 "Could not map XCM asset to FRAME asset"
201 );
202 ()
203 })
204 .ok()?;
205 if maximal {
207 let resulting_want =
209 <AssetConversion as QuotePrice>::quote_price_exact_tokens_for_tokens(
210 give_asset_id,
211 want_asset_id,
212 give_amount,
213 true, )?;
215
216 Some((want_asset.id.clone(), resulting_want).into())
217 } else {
218 let necessary_give =
220 <AssetConversion as QuotePrice>::quote_price_tokens_for_exact_tokens(
221 give_asset_id,
222 want_asset_id,
223 want_amount,
224 true, )?;
226
227 Some((give_asset.id.clone(), necessary_give).into())
228 }
229 }
230}