staging_xcm_builder/asset_exchange/single_asset_adapter/
adapter.rs1extern crate alloc;
20use alloc::vec;
21use core::marker::PhantomData;
22use frame_support::{ensure, traits::tokens::fungibles};
23use pallet_asset_conversion::{QuotePrice, SwapCredit};
24use xcm::prelude::*;
25use xcm_executor::{
26 traits::{AssetExchange, MatchesFungibles},
27 AssetsInHolding,
28};
29
30pub struct SingleAssetExchangeAdapter<AssetConversion, Fungibles, Matcher, AccountId>(
42 PhantomData<(AssetConversion, Fungibles, Matcher, AccountId)>,
43);
44impl<AssetConversion, Fungibles, Matcher, AccountId> AssetExchange
45 for SingleAssetExchangeAdapter<AssetConversion, Fungibles, Matcher, AccountId>
46where
47 AssetConversion: SwapCredit<
48 AccountId,
49 Balance = u128,
50 AssetKind = Fungibles::AssetId,
51 Credit = fungibles::Credit<AccountId, Fungibles>,
52 > + QuotePrice<Balance = u128, AssetKind = Fungibles::AssetId>,
53 Fungibles: fungibles::Balanced<AccountId, Balance = u128>,
54 Matcher: MatchesFungibles<Fungibles::AssetId, Fungibles::Balance>,
55{
56 fn exchange_asset(
57 _: Option<&Location>,
58 give: AssetsInHolding,
59 want: &Assets,
60 maximal: bool,
61 ) -> Result<AssetsInHolding, AssetsInHolding> {
62 let mut give_iter = give.fungible_assets_iter();
63 let give_asset = give_iter.next().ok_or_else(|| {
64 tracing::trace!(
65 target: "xcm::SingleAssetExchangeAdapter::exchange_asset",
66 ?give, "No fungible asset was in `give`.",
67 );
68 give.clone()
69 })?;
70 ensure!(give_iter.next().is_none(), give.clone()); ensure!(give.non_fungible_assets_iter().next().is_none(), give.clone()); ensure!(want.len() == 1, give.clone()); let want_asset = want.get(0).ok_or_else(|| give.clone())?;
74 let (give_asset_id, give_amount) =
75 Matcher::matches_fungibles(&give_asset).map_err(|error| {
76 tracing::trace!(
77 target: "xcm::SingleAssetExchangeAdapter::exchange_asset",
78 ?give_asset,
79 ?error,
80 "Could not map XCM asset give to FRAME asset.",
81 );
82 give.clone()
83 })?;
84 let (want_asset_id, want_amount) =
85 Matcher::matches_fungibles(&want_asset).map_err(|error| {
86 tracing::trace!(
87 target: "xcm::SingleAssetExchangeAdapter::exchange_asset",
88 ?want_asset,
89 ?error,
90 "Could not map XCM asset want to FRAME asset."
91 );
92 give.clone()
93 })?;
94
95 let swap_asset = give_asset_id.clone().into();
97 let credit_in = Fungibles::issue(give_asset_id, give_amount);
98
99 let (credit_out, maybe_credit_change) = if maximal {
101 let credit_out = <AssetConversion as SwapCredit<_>>::swap_exact_tokens_for_tokens(
104 vec![swap_asset, want_asset_id],
105 credit_in,
106 Some(want_amount),
107 )
108 .map_err(|(credit_in, error)| {
109 tracing::debug!(
110 target: "xcm::SingleAssetExchangeAdapter::exchange_asset",
111 ?error,
112 "Could not perform the swap"
113 );
114 drop(credit_in);
115 give.clone()
116 })?;
117
118 (credit_out, None)
120 } else {
121 let (credit_out, credit_change) =
124 <AssetConversion as SwapCredit<_>>::swap_tokens_for_exact_tokens(
125 vec![swap_asset, want_asset_id],
126 credit_in,
127 want_amount,
128 )
129 .map_err(|(credit_in, error)| {
130 tracing::debug!(
131 target: "xcm::SingleAssetExchangeAdapter::exchange_asset",
132 ?error,
133 "Could not perform the swap",
134 );
135 drop(credit_in);
136 give.clone()
137 })?;
138
139 (credit_out, if credit_change.peek() > 0 { Some(credit_change) } else { None })
140 };
141
142 let resulting_asset: Asset = (want_asset.id.clone(), credit_out.peek()).into();
145 let mut result: AssetsInHolding = resulting_asset.into();
146
147 if let Some(credit_change) = maybe_credit_change {
149 let leftover_asset: Asset = (give_asset.id.clone(), credit_change.peek()).into();
150 result.subsume(leftover_asset);
151 }
152
153 Ok(result.into())
154 }
155
156 fn quote_exchange_price(give: &Assets, want: &Assets, maximal: bool) -> Option<Assets> {
157 if give.len() != 1 || want.len() != 1 {
158 return None;
159 } let give_asset = give.get(0)?;
161 let want_asset = want.get(0)?;
162 let (give_asset_id, give_amount) = Matcher::matches_fungibles(give_asset)
164 .map_err(|error| {
165 tracing::trace!(
166 target: "xcm::SingleAssetExchangeAdapter::quote_exchange_price",
167 ?give_asset,
168 ?error,
169 "Could not map XCM asset to FRAME asset."
170 );
171 ()
172 })
173 .ok()?;
174 let (want_asset_id, want_amount) = Matcher::matches_fungibles(want_asset)
175 .map_err(|error| {
176 tracing::trace!(
177 target: "xcm::SingleAssetExchangeAdapter::quote_exchange_price",
178 ?want_asset,
179 ?error,
180 "Could not map XCM asset to FRAME asset"
181 );
182 ()
183 })
184 .ok()?;
185 if maximal {
187 let resulting_want =
189 <AssetConversion as QuotePrice>::quote_price_exact_tokens_for_tokens(
190 give_asset_id,
191 want_asset_id,
192 give_amount,
193 true, )?;
195
196 Some((want_asset.id.clone(), resulting_want).into())
197 } else {
198 let necessary_give =
200 <AssetConversion as QuotePrice>::quote_price_tokens_for_exact_tokens(
201 give_asset_id,
202 want_asset_id,
203 want_amount,
204 true, )?;
206
207 Some((give_asset.id.clone(), necessary_give).into())
208 }
209 }
210}