referrerpolicy=no-referrer-when-downgrade

pallet_asset_conversion/
swap.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Traits and implementations for swap between the various asset classes.
19
20use super::*;
21
22/// Trait for providing methods to swap between the various asset classes.
23pub trait Swap<AccountId> {
24	/// Measure units of the asset classes for swapping.
25	type Balance: Balance;
26	/// Kind of assets that are going to be swapped.
27	type AssetKind;
28
29	/// Returns the upper limit on the length of the swap path.
30	fn max_path_len() -> u32;
31
32	/// Swap exactly `amount_in` of asset `path[0]` for asset `path[last]`.
33	/// If an `amount_out_min` is specified, it will return an error if it is unable to acquire
34	/// the amount desired.
35	///
36	/// Withdraws the `path[0]` asset from `sender`, deposits the `path[last]` asset to `send_to`,
37	/// respecting `keep_alive`.
38	///
39	/// If successful, returns the amount of `path[last]` acquired for the `amount_in`.
40	///
41	/// This operation is expected to be atomic.
42	fn swap_exact_tokens_for_tokens(
43		sender: AccountId,
44		path: Vec<Self::AssetKind>,
45		amount_in: Self::Balance,
46		amount_out_min: Option<Self::Balance>,
47		send_to: AccountId,
48		keep_alive: bool,
49	) -> Result<Self::Balance, DispatchError>;
50
51	/// Take the `path[0]` asset and swap some amount for `amount_out` of the `path[last]`. If an
52	/// `amount_in_max` is specified, it will return an error if acquiring `amount_out` would be
53	/// too costly.
54	///
55	/// Withdraws `path[0]` asset from `sender`, deposits `path[last]` asset to `send_to`,
56	/// respecting `keep_alive`.
57	///
58	/// If successful returns the amount of the `path[0]` taken to provide `path[last]`.
59	///
60	/// This operation is expected to be atomic.
61	fn swap_tokens_for_exact_tokens(
62		sender: AccountId,
63		path: Vec<Self::AssetKind>,
64		amount_out: Self::Balance,
65		amount_in_max: Option<Self::Balance>,
66		send_to: AccountId,
67		keep_alive: bool,
68	) -> Result<Self::Balance, DispatchError>;
69}
70
71/// Trait providing methods to swap between the various asset classes.
72pub trait SwapCredit<AccountId> {
73	/// Measure units of the asset classes for swapping.
74	type Balance: Balance;
75	/// Kind of assets that are going to be swapped.
76	type AssetKind;
77	/// Credit implying a negative imbalance in the system that can be placed into an account or
78	/// alter the total supply.
79	type Credit;
80
81	/// Returns the upper limit on the length of the swap path.
82	fn max_path_len() -> u32;
83
84	/// Swap exactly `credit_in` of asset `path[0]` for asset `path[last]`.  If `amount_out_min` is
85	/// provided and the swap can't achieve at least this amount, an error is returned.
86	///
87	/// On a successful swap, the function returns the `credit_out` of `path[last]` obtained from
88	/// the `credit_in`. On failure, it returns an `Err` containing the original `credit_in` and the
89	/// associated error code.
90	///
91	/// This operation is expected to be atomic.
92	fn swap_exact_tokens_for_tokens(
93		path: Vec<Self::AssetKind>,
94		credit_in: Self::Credit,
95		amount_out_min: Option<Self::Balance>,
96	) -> Result<Self::Credit, (Self::Credit, DispatchError)>;
97
98	/// Swaps a portion of `credit_in` of `path[0]` asset to obtain the desired `amount_out` of
99	/// the `path[last]` asset. The provided `credit_in` must be adequate to achieve the target
100	/// `amount_out`, or an error will occur.
101	///
102	/// On success, the function returns a (`credit_out`, `credit_change`) tuple, where `credit_out`
103	/// represents the acquired amount of the `path[last]` asset, and `credit_change` is the
104	/// remaining portion from the `credit_in`. On failure, an `Err` with the initial `credit_in`
105	/// and error code is returned.
106	///
107	/// This operation is expected to be atomic.
108	fn swap_tokens_for_exact_tokens(
109		path: Vec<Self::AssetKind>,
110		credit_in: Self::Credit,
111		amount_out: Self::Balance,
112	) -> Result<(Self::Credit, Self::Credit), (Self::Credit, DispatchError)>;
113}
114
115/// Trait providing methods to quote swap prices between asset classes.
116///
117/// The quoted price is only guaranteed if no other swaps are made after the price is quoted and
118/// before the target swap (e.g., the swap is made immediately within the same transaction).
119pub trait QuotePrice {
120	/// Measurement units of the asset classes for pricing.
121	type Balance: Balance;
122	/// Type representing the kind of assets for which the price is being quoted.
123	type AssetKind;
124	/// Quotes the amount of `asset1` required to obtain the exact `amount` of `asset2`.
125	///
126	/// If `include_fee` is set to `true`, the price will include the pool's fee.
127	/// If the pool does not exist or the swap cannot be made, `None` is returned.
128	fn quote_price_tokens_for_exact_tokens(
129		asset1: Self::AssetKind,
130		asset2: Self::AssetKind,
131		amount: Self::Balance,
132		include_fee: bool,
133	) -> Option<Self::Balance>;
134	/// Quotes the amount of `asset2` resulting from swapping the exact `amount` of `asset1`.
135	///
136	/// If `include_fee` is set to `true`, the price will include the pool's fee.
137	/// If the pool does not exist or the swap cannot be made, `None` is returned.
138	fn quote_price_exact_tokens_for_tokens(
139		asset1: Self::AssetKind,
140		asset2: Self::AssetKind,
141		amount: Self::Balance,
142		include_fee: bool,
143	) -> Option<Self::Balance>;
144}
145
146impl<T: Config> Swap<T::AccountId> for Pallet<T> {
147	type Balance = T::Balance;
148	type AssetKind = T::AssetKind;
149
150	fn max_path_len() -> u32 {
151		T::MaxSwapPathLength::get()
152	}
153
154	fn swap_exact_tokens_for_tokens(
155		sender: T::AccountId,
156		path: Vec<Self::AssetKind>,
157		amount_in: Self::Balance,
158		amount_out_min: Option<Self::Balance>,
159		send_to: T::AccountId,
160		keep_alive: bool,
161	) -> Result<Self::Balance, DispatchError> {
162		let amount_out = with_storage_layer(|| {
163			Self::do_swap_exact_tokens_for_tokens(
164				sender,
165				path,
166				amount_in,
167				amount_out_min,
168				send_to,
169				keep_alive,
170			)
171		})?;
172		Ok(amount_out)
173	}
174
175	fn swap_tokens_for_exact_tokens(
176		sender: T::AccountId,
177		path: Vec<Self::AssetKind>,
178		amount_out: Self::Balance,
179		amount_in_max: Option<Self::Balance>,
180		send_to: T::AccountId,
181		keep_alive: bool,
182	) -> Result<Self::Balance, DispatchError> {
183		let amount_in = with_storage_layer(|| {
184			Self::do_swap_tokens_for_exact_tokens(
185				sender,
186				path,
187				amount_out,
188				amount_in_max,
189				send_to,
190				keep_alive,
191			)
192		})?;
193		Ok(amount_in)
194	}
195}
196
197impl<T: Config> SwapCredit<T::AccountId> for Pallet<T> {
198	type Balance = T::Balance;
199	type AssetKind = T::AssetKind;
200	type Credit = CreditOf<T>;
201
202	fn max_path_len() -> u32 {
203		T::MaxSwapPathLength::get()
204	}
205
206	fn swap_exact_tokens_for_tokens(
207		path: Vec<Self::AssetKind>,
208		credit_in: Self::Credit,
209		amount_out_min: Option<Self::Balance>,
210	) -> Result<Self::Credit, (Self::Credit, DispatchError)> {
211		let credit_asset = credit_in.asset();
212		with_transaction(|| -> TransactionOutcome<Result<_, DispatchError>> {
213			let res = Self::do_swap_exact_credit_tokens_for_tokens(path, credit_in, amount_out_min);
214			match &res {
215				Ok(_) => TransactionOutcome::Commit(Ok(res)),
216				// wrapping `res` with `Ok`, since our `Err` doesn't satisfy the
217				// `From<DispatchError>` bound of the `with_transaction` function.
218				Err(_) => TransactionOutcome::Rollback(Ok(res)),
219			}
220		})
221		// should never map an error since `with_transaction` above never returns it.
222		.map_err(|_| (Self::Credit::zero(credit_asset), DispatchError::Corruption))?
223	}
224
225	fn swap_tokens_for_exact_tokens(
226		path: Vec<Self::AssetKind>,
227		credit_in: Self::Credit,
228		amount_out: Self::Balance,
229	) -> Result<(Self::Credit, Self::Credit), (Self::Credit, DispatchError)> {
230		let credit_asset = credit_in.asset();
231		with_transaction(|| -> TransactionOutcome<Result<_, DispatchError>> {
232			let res = Self::do_swap_credit_tokens_for_exact_tokens(path, credit_in, amount_out);
233			match &res {
234				Ok(_) => TransactionOutcome::Commit(Ok(res)),
235				// wrapping `res` with `Ok`, since our `Err` doesn't satisfy the
236				// `From<DispatchError>` bound of the `with_transaction` function.
237				Err(_) => TransactionOutcome::Rollback(Ok(res)),
238			}
239		})
240		// should never map an error since `with_transaction` above never returns it.
241		.map_err(|_| (Self::Credit::zero(credit_asset), DispatchError::Corruption))?
242	}
243}
244
245impl<T: Config> QuotePrice for Pallet<T> {
246	type Balance = T::Balance;
247	type AssetKind = T::AssetKind;
248	fn quote_price_exact_tokens_for_tokens(
249		asset1: Self::AssetKind,
250		asset2: Self::AssetKind,
251		amount: Self::Balance,
252		include_fee: bool,
253	) -> Option<Self::Balance> {
254		Self::quote_price_exact_tokens_for_tokens(asset1, asset2, amount, include_fee)
255	}
256	fn quote_price_tokens_for_exact_tokens(
257		asset1: Self::AssetKind,
258		asset2: Self::AssetKind,
259		amount: Self::Balance,
260		include_fee: bool,
261	) -> Option<Self::Balance> {
262		Self::quote_price_tokens_for_exact_tokens(asset1, asset2, amount, include_fee)
263	}
264}