Skip to main content

pallet_asset_conversion/
lib.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//! # Substrate Asset Conversion pallet
19//!
20//! Substrate Asset Conversion pallet based on the [Uniswap V2](https://github.com/Uniswap/v2-core) logic.
21//!
22//! ## Overview
23//!
24//! This pallet allows you to:
25//!
26//!  - [create a liquidity pool](`Pallet::create_pool()`) for 2 assets
27//!  - [provide the liquidity](`Pallet::add_liquidity()`) and receive back an LP token
28//!  - [exchange the LP token back to assets](`Pallet::remove_liquidity()`)
29//!  - [swap a specific amount of assets for another](`Pallet::swap_exact_tokens_for_tokens()`) if
30//!    there is a pool created, or
31//!  - [swap some assets for a specific amount of
32//!    another](`Pallet::swap_tokens_for_exact_tokens()`).
33//!  - [query for an exchange price](`AssetConversionApi::quote_price_exact_tokens_for_tokens`) via
34//!    a runtime call endpoint
35//!  - [query the size of a liquidity pool](`AssetConversionApi::get_reserves`) via a runtime api
36//!    endpoint.
37//!
38//! The `quote_price_exact_tokens_for_tokens` and `quote_price_tokens_for_exact_tokens` functions
39//! both take a path parameter of the route to take. If you want to swap from native asset to
40//! non-native asset 1, you would pass in a path of `[DOT, 1]` or `[1, DOT]`. If you want to swap
41//! from non-native asset 1 to non-native asset 2, you would pass in a path of `[1, DOT, 2]`.
42//!
43//! (For an example of configuring this pallet to use `Location` as an asset id, see the
44//! cumulus repo).
45//!
46//! Here is an example `state_call` that asks for a quote of a pool of native versus asset 1:
47//!
48//! ```text
49//! curl -sS -H "Content-Type: application/json" -d \
50//! '{"id":1, "jsonrpc":"2.0", "method": "state_call", "params": ["AssetConversionApi_quote_price_tokens_for_exact_tokens", "0x0101000000000000000000000011000000000000000000"]}' \
51//! http://localhost:9933/
52//! ```
53//! (This can be run against the kitchen sync node in the `node` folder of this repo.)
54#![deny(missing_docs)]
55#![cfg_attr(not(feature = "std"), no_std)]
56
57#[cfg(feature = "runtime-benchmarks")]
58mod benchmarking;
59mod liquidity;
60#[cfg(test)]
61mod mock;
62mod swap;
63#[cfg(test)]
64mod tests;
65mod types;
66pub mod weights;
67#[cfg(feature = "runtime-benchmarks")]
68pub use benchmarking::{BenchmarkHelper, NativeOrWithIdFactory};
69pub use liquidity::*;
70pub use pallet::*;
71pub use swap::*;
72pub use types::*;
73pub use weights::WeightInfo;
74
75extern crate alloc;
76
77use alloc::{boxed::Box, collections::btree_set::BTreeSet, vec::Vec};
78use codec::Codec;
79use frame_support::{
80	traits::{
81		fungibles::{Balanced, Create, Credit, Inspect, Mutate},
82		tokens::{
83			AssetId, Balance,
84			Fortitude::Polite,
85			Precision::Exact,
86			Preservation::{Expendable, Preserve},
87		},
88		AccountTouch, Incrementable, OnUnbalanced,
89	},
90	PalletId,
91};
92use sp_core::Get;
93use sp_runtime::{
94	traits::{
95		CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Ensure, IntegerSquareRoot, MaybeDisplay,
96		MaybeSerializeDeserialize, One, TrailingZeroInput, Zero,
97	},
98	DispatchError, Saturating, TokenError, TransactionOutcome,
99};
100
101#[frame_support::pallet]
102pub mod pallet {
103	use super::*;
104	use frame_support::{pallet_prelude::*, traits::fungibles::Refund};
105	use frame_system::pallet_prelude::*;
106	use sp_arithmetic::{traits::Unsigned, PerThing, Permill};
107
108	#[pallet::pallet]
109	pub struct Pallet<T>(_);
110
111	#[pallet::config]
112	pub trait Config: frame_system::Config {
113		/// Overarching event type.
114		#[allow(deprecated)]
115		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
116
117		/// The type in which the assets for swapping are measured.
118		type Balance: Balance;
119
120		/// A type used for calculations concerning the `Balance` type to avoid possible overflows.
121		type HigherPrecisionBalance: IntegerSquareRoot
122			+ One
123			+ Ensure
124			+ Unsigned
125			+ From<u32>
126			+ From<Self::Balance>
127			+ TryInto<Self::Balance>;
128
129		/// Type of asset class, sourced from [`Config::Assets`], utilized to offer liquidity to a
130		/// pool.
131		type AssetKind: Parameter + MaxEncodedLen + MaybeSerializeDeserialize;
132
133		/// Registry of assets utilized for providing liquidity to pools.
134		type Assets: Inspect<Self::AccountId, AssetId = Self::AssetKind, Balance = Self::Balance>
135			+ Mutate<Self::AccountId>
136			+ AccountTouch<Self::AssetKind, Self::AccountId, Balance = Self::Balance>
137			+ Balanced<Self::AccountId>
138			+ Refund<Self::AccountId, AssetId = Self::AssetKind>;
139
140		/// Liquidity pool identifier.
141		type PoolId: Parameter + MaxEncodedLen + Ord;
142
143		/// Provides means to resolve the [`Config::PoolId`] and it's `AccountId` from a pair
144		/// of [`Config::AssetKind`]s.
145		///
146		/// Examples: [`crate::types::WithFirstAsset`], [`crate::types::Ascending`].
147		type PoolLocator: PoolLocator<Self::AccountId, Self::AssetKind, Self::PoolId>;
148
149		/// Asset class for the lp tokens from [`Self::PoolAssets`].
150		type PoolAssetId: AssetId + PartialOrd + Incrementable + From<u32>;
151
152		/// Registry for the lp tokens. Ideally only this pallet should have create permissions on
153		/// the assets.
154		type PoolAssets: Inspect<Self::AccountId, AssetId = Self::PoolAssetId, Balance = Self::Balance>
155			+ Create<Self::AccountId>
156			+ Mutate<Self::AccountId>
157			+ AccountTouch<Self::PoolAssetId, Self::AccountId, Balance = Self::Balance>
158			+ Refund<Self::AccountId, AssetId = Self::PoolAssetId>;
159
160		/// The fraction of every swap that the liquidity providers take as a fee.
161		#[pallet::constant]
162		type LPFee: Get<Permill>;
163
164		/// A one-time fee to setup the pool.
165		#[pallet::constant]
166		type PoolSetupFee: Get<Self::Balance>;
167
168		/// Asset class from [`Config::Assets`] used to pay the [`Config::PoolSetupFee`].
169		#[pallet::constant]
170		type PoolSetupFeeAsset: Get<Self::AssetKind>;
171
172		/// Handler for the [`Config::PoolSetupFee`].
173		type PoolSetupFeeTarget: OnUnbalanced<CreditOf<Self>>;
174
175		/// A fee to withdraw the liquidity.
176		#[pallet::constant]
177		type LiquidityWithdrawalFee: Get<Permill>;
178
179		/// The minimum LP token amount that could be minted. Ameliorates rounding errors.
180		#[pallet::constant]
181		type MintMinLiquidity: Get<Self::Balance>;
182
183		/// The max number of hops in a swap.
184		#[pallet::constant]
185		type MaxSwapPathLength: Get<u32>;
186
187		/// The pallet's id, used for deriving its sovereign account ID.
188		#[pallet::constant]
189		type PalletId: Get<PalletId>;
190
191		/// Weight information for extrinsics in this pallet.
192		type WeightInfo: WeightInfo;
193
194		/// The benchmarks need a way to create asset ids from u32s.
195		#[cfg(feature = "runtime-benchmarks")]
196		type BenchmarkHelper: BenchmarkHelper<Self::AssetKind>;
197	}
198
199	/// Map from `PoolAssetId` to `PoolInfo`. This establishes whether a pool has been officially
200	/// created rather than people sending tokens directly to a pool's public account.
201	#[pallet::storage]
202	pub type Pools<T: Config> =
203		StorageMap<_, Blake2_128Concat, T::PoolId, PoolInfo<T::PoolAssetId>, OptionQuery>;
204
205	/// Stores the `PoolAssetId` that is going to be used for the next lp token.
206	/// This gets incremented whenever a new lp pool is created.
207	#[pallet::storage]
208	pub type NextPoolAssetId<T: Config> = StorageValue<_, T::PoolAssetId, OptionQuery>;
209
210	/// Genesis config for the asset conversion pallet.
211	#[pallet::genesis_config]
212	#[derive(frame_support::DefaultNoBound)]
213	pub struct GenesisConfig<T: Config> {
214		/// Pools to create at genesis with initial liquidity.
215		///
216		/// Each entry is `(asset1, asset2, liquidity_provider, amount1, amount2)`.
217		/// The `liquidity_provider` must hold sufficient balances of both assets
218		/// (e.g. via `pallet_balances` / `pallet_assets` genesis configs).
219		/// Set both amounts to zero to create a pool without initial liquidity.
220		/// No pool setup fee is charged at genesis.
221		pub pools: Vec<(T::AssetKind, T::AssetKind, T::AccountId, T::Balance, T::Balance)>,
222	}
223
224	#[pallet::genesis_build]
225	impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
226		fn build(&self) {
227			for (asset1, asset2, lp_provider, amount1, amount2) in &self.pools {
228				Pallet::<T>::setup_pool_from_genesis(
229					asset1,
230					asset2,
231					lp_provider,
232					*amount1,
233					*amount2,
234				)
235				.unwrap_or_else(|e| {
236					panic!("Genesis pool ({asset1:?}, {asset2:?}) setup failed: {e:?}")
237				});
238			}
239		}
240	}
241
242	// Pallet's events.
243	#[pallet::event]
244	#[pallet::generate_deposit(pub(super) fn deposit_event)]
245	pub enum Event<T: Config> {
246		/// A successful call of the `CreatePool` extrinsic will create this event.
247		PoolCreated {
248			/// The account that created the pool.
249			creator: T::AccountId,
250			/// The pool id associated with the pool. Note that the order of the assets may not be
251			/// the same as the order specified in the create pool extrinsic.
252			pool_id: T::PoolId,
253			/// The account ID of the pool.
254			pool_account: T::AccountId,
255			/// The id of the liquidity tokens that will be minted when assets are added to this
256			/// pool.
257			lp_token: T::PoolAssetId,
258		},
259
260		/// A successful call of the `AddLiquidity` extrinsic will create this event.
261		LiquidityAdded {
262			/// The account that the liquidity was taken from.
263			who: T::AccountId,
264			/// The account that the liquidity tokens were minted to.
265			mint_to: T::AccountId,
266			/// The pool id of the pool that the liquidity was added to.
267			pool_id: T::PoolId,
268			/// The amount of the first asset that was added to the pool.
269			amount1_provided: T::Balance,
270			/// The amount of the second asset that was added to the pool.
271			amount2_provided: T::Balance,
272			/// The id of the lp token that was minted.
273			lp_token: T::PoolAssetId,
274			/// The amount of lp tokens that were minted of that id.
275			lp_token_minted: T::Balance,
276		},
277
278		/// A successful call of the `RemoveLiquidity` extrinsic will create this event.
279		LiquidityRemoved {
280			/// The account that the liquidity tokens were burned from.
281			who: T::AccountId,
282			/// The account that the assets were transferred to.
283			withdraw_to: T::AccountId,
284			/// The pool id that the liquidity was removed from.
285			pool_id: T::PoolId,
286			/// The amount of the first asset that was removed from the pool.
287			amount1: T::Balance,
288			/// The amount of the second asset that was removed from the pool.
289			amount2: T::Balance,
290			/// The id of the lp token that was burned.
291			lp_token: T::PoolAssetId,
292			/// The amount of lp tokens that were burned of that id.
293			lp_token_burned: T::Balance,
294			/// Liquidity withdrawal fee (%).
295			withdrawal_fee: Permill,
296		},
297		/// Assets have been converted from one to another. Both `SwapExactTokenForToken`
298		/// and `SwapTokenForExactToken` will generate this event.
299		SwapExecuted {
300			/// Which account was the instigator of the swap.
301			who: T::AccountId,
302			/// The account that the assets were transferred to.
303			send_to: T::AccountId,
304			/// The amount of the first asset that was swapped.
305			amount_in: T::Balance,
306			/// The amount of the second asset that was received.
307			amount_out: T::Balance,
308			/// The route of asset IDs with amounts that the swap went through.
309			/// E.g. (A, amount_in) -> (Dot, amount_out) -> (B, amount_out)
310			path: BalancePath<T>,
311		},
312		/// Assets have been converted from one to another.
313		SwapCreditExecuted {
314			/// The amount of the first asset that was swapped.
315			amount_in: T::Balance,
316			/// The amount of the second asset that was received.
317			amount_out: T::Balance,
318			/// The route of asset IDs with amounts that the swap went through.
319			/// E.g. (A, amount_in) -> (Dot, amount_out) -> (B, amount_out)
320			path: BalancePath<T>,
321		},
322		/// Pool has been touched in order to fulfill operational requirements.
323		Touched {
324			/// The ID of the pool.
325			pool_id: T::PoolId,
326			/// The account initiating the touch.
327			who: T::AccountId,
328		},
329	}
330
331	#[pallet::error]
332	pub enum Error<T> {
333		/// Provided asset pair is not supported for pool.
334		InvalidAssetPair,
335		/// Pool already exists.
336		PoolExists,
337		/// Desired amount can't be zero.
338		WrongDesiredAmount,
339		/// Provided amount should be greater than or equal to the existential deposit/asset's
340		/// minimal amount.
341		AmountOneLessThanMinimal,
342		/// Provided amount should be greater than or equal to the existential deposit/asset's
343		/// minimal amount.
344		AmountTwoLessThanMinimal,
345		/// Reserve needs to always be greater than or equal to the existential deposit/asset's
346		/// minimal amount.
347		ReserveLeftLessThanMinimal,
348		/// Desired amount can't be equal to the pool reserve.
349		AmountOutTooHigh,
350		/// The pool doesn't exist.
351		PoolNotFound,
352		/// An overflow happened.
353		Overflow,
354		/// The minimal amount requirement for the first token in the pair wasn't met.
355		AssetOneDepositDidNotMeetMinimum,
356		/// The minimal amount requirement for the second token in the pair wasn't met.
357		AssetTwoDepositDidNotMeetMinimum,
358		/// The minimal amount requirement for the first token in the pair wasn't met.
359		AssetOneWithdrawalDidNotMeetMinimum,
360		/// The minimal amount requirement for the second token in the pair wasn't met.
361		AssetTwoWithdrawalDidNotMeetMinimum,
362		/// Optimal calculated amount is less than desired.
363		OptimalAmountLessThanDesired,
364		/// Insufficient liquidity minted.
365		InsufficientLiquidityMinted,
366		/// Requested liquidity can't be zero.
367		ZeroLiquidity,
368		/// Amount can't be zero.
369		ZeroAmount,
370		/// Calculated amount out is less than provided minimum amount.
371		ProvidedMinimumNotSufficientForSwap,
372		/// Provided maximum amount is not sufficient for swap.
373		ProvidedMaximumNotSufficientForSwap,
374		/// The provided path must consists of 2 assets at least.
375		InvalidPath,
376		/// The provided path must consists of unique assets.
377		NonUniquePath,
378		/// It was not possible to get or increment the Id of the pool.
379		IncorrectPoolAssetId,
380		/// The destination account cannot exist with the swapped funds.
381		BelowMinimum,
382		/// The pool exists but has no liquidity (at least one of the reserves is zero).
383		PoolEmpty,
384	}
385
386	#[pallet::hooks]
387	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
388		fn integrity_test() {
389			assert!(
390				T::MaxSwapPathLength::get() > 1,
391				"the `MaxSwapPathLength` should be greater than 1",
392			);
393		}
394	}
395
396	/// Pallet's callable functions.
397	#[pallet::call]
398	impl<T: Config> Pallet<T> {
399		/// Creates an empty liquidity pool and an associated new `lp_token` asset
400		/// (the id of which is returned in the `Event::PoolCreated` event).
401		///
402		/// Once a pool is created, someone may [`Pallet::add_liquidity`] to it.
403		#[pallet::call_index(0)]
404		#[pallet::weight(T::WeightInfo::create_pool())]
405		pub fn create_pool(
406			origin: OriginFor<T>,
407			asset1: Box<T::AssetKind>,
408			asset2: Box<T::AssetKind>,
409		) -> DispatchResult {
410			let sender = ensure_signed(origin)?;
411			Self::do_create_pool(&sender, *asset1, *asset2)?;
412			Ok(())
413		}
414
415		/// Provide liquidity into the pool of `asset1` and `asset2`.
416		/// NOTE: an optimal amount of asset1 and asset2 will be calculated and
417		/// might be different than the provided `amount1_desired`/`amount2_desired`
418		/// thus you should provide the min amount you're happy to provide.
419		/// Params `amount1_min`/`amount2_min` represent that.
420		/// `mint_to` will be sent the liquidity tokens that represent this share of the pool.
421		///
422		/// NOTE: when encountering an incorrect exchange rate and non-withdrawable pool liquidity,
423		/// batch an atomic call with [`Pallet::add_liquidity`] and
424		/// [`Pallet::swap_exact_tokens_for_tokens`] or [`Pallet::swap_tokens_for_exact_tokens`]
425		/// calls to render the liquidity withdrawable and rectify the exchange rate.
426		///
427		/// Once liquidity is added, someone may successfully call
428		/// [`Pallet::swap_exact_tokens_for_tokens`].
429		#[pallet::call_index(1)]
430		#[pallet::weight(T::WeightInfo::add_liquidity())]
431		pub fn add_liquidity(
432			origin: OriginFor<T>,
433			asset1: Box<T::AssetKind>,
434			asset2: Box<T::AssetKind>,
435			amount1_desired: T::Balance,
436			amount2_desired: T::Balance,
437			amount1_min: T::Balance,
438			amount2_min: T::Balance,
439			mint_to: T::AccountId,
440		) -> DispatchResult {
441			let sender = ensure_signed(origin)?;
442			Self::do_add_liquidity(
443				&sender,
444				*asset1,
445				*asset2,
446				amount1_desired,
447				amount2_desired,
448				amount1_min,
449				amount2_min,
450				&mint_to,
451			)?;
452			Ok(())
453		}
454
455		/// Allows you to remove liquidity by providing the `lp_token_burn` tokens that will be
456		/// burned in the process. With the usage of `amount1_min_receive`/`amount2_min_receive`
457		/// it's possible to control the min amount of returned tokens you're happy with.
458		#[pallet::call_index(2)]
459		#[pallet::weight(T::WeightInfo::remove_liquidity())]
460		pub fn remove_liquidity(
461			origin: OriginFor<T>,
462			asset1: Box<T::AssetKind>,
463			asset2: Box<T::AssetKind>,
464			lp_token_burn: T::Balance,
465			amount1_min_receive: T::Balance,
466			amount2_min_receive: T::Balance,
467			withdraw_to: T::AccountId,
468		) -> DispatchResult {
469			let sender = ensure_signed(origin)?;
470			Self::do_remove_liquidity(
471				&sender,
472				*asset1,
473				*asset2,
474				lp_token_burn,
475				amount1_min_receive,
476				amount2_min_receive,
477				&withdraw_to,
478			)?;
479			Ok(())
480		}
481
482		/// Swap the exact amount of `asset1` into `asset2`.
483		/// `amount_out_min` param allows you to specify the min amount of the `asset2`
484		/// you're happy to receive.
485		///
486		/// [`AssetConversionApi::quote_price_exact_tokens_for_tokens`] runtime call can be called
487		/// for a quote.
488		#[pallet::call_index(3)]
489		#[pallet::weight(T::WeightInfo::swap_exact_tokens_for_tokens(path.len() as u32))]
490		pub fn swap_exact_tokens_for_tokens(
491			origin: OriginFor<T>,
492			path: Vec<Box<T::AssetKind>>,
493			amount_in: T::Balance,
494			amount_out_min: T::Balance,
495			send_to: T::AccountId,
496			keep_alive: bool,
497		) -> DispatchResult {
498			let sender = ensure_signed(origin)?;
499			Self::do_swap_exact_tokens_for_tokens(
500				sender,
501				path.into_iter().map(|a| *a).collect(),
502				amount_in,
503				Some(amount_out_min),
504				send_to,
505				keep_alive,
506			)?;
507			Ok(())
508		}
509
510		/// Swap any amount of `asset1` to get the exact amount of `asset2`.
511		/// `amount_in_max` param allows to specify the max amount of the `asset1`
512		/// you're happy to provide.
513		///
514		/// [`AssetConversionApi::quote_price_tokens_for_exact_tokens`] runtime call can be called
515		/// for a quote.
516		#[pallet::call_index(4)]
517		#[pallet::weight(T::WeightInfo::swap_tokens_for_exact_tokens(path.len() as u32))]
518		pub fn swap_tokens_for_exact_tokens(
519			origin: OriginFor<T>,
520			path: Vec<Box<T::AssetKind>>,
521			amount_out: T::Balance,
522			amount_in_max: T::Balance,
523			send_to: T::AccountId,
524			keep_alive: bool,
525		) -> DispatchResult {
526			let sender = ensure_signed(origin)?;
527			Self::do_swap_tokens_for_exact_tokens(
528				sender,
529				path.into_iter().map(|a| *a).collect(),
530				amount_out,
531				Some(amount_in_max),
532				send_to,
533				keep_alive,
534			)?;
535			Ok(())
536		}
537
538		/// Touch an existing pool to fulfill prerequisites before providing liquidity, such as
539		/// ensuring that the pool's accounts are in place. It is typically useful when a pool
540		/// creator removes the pool's accounts and does not provide a liquidity. This action may
541		/// involve holding assets from the caller as a deposit for creating the pool's accounts.
542		///
543		/// The origin must be Signed.
544		///
545		/// - `asset1`: The asset ID of an existing pool with a pair (asset1, asset2).
546		/// - `asset2`: The asset ID of an existing pool with a pair (asset1, asset2).
547		///
548		/// Emits `Touched` event when successful.
549		#[pallet::call_index(5)]
550		#[pallet::weight(T::WeightInfo::touch(3))]
551		pub fn touch(
552			origin: OriginFor<T>,
553			asset1: Box<T::AssetKind>,
554			asset2: Box<T::AssetKind>,
555		) -> DispatchResultWithPostInfo {
556			let who = ensure_signed(origin)?;
557
558			let pool_id = T::PoolLocator::pool_id(&asset1, &asset2)
559				.map_err(|_| Error::<T>::InvalidAssetPair)?;
560			let pool = Pools::<T>::get(&pool_id).ok_or(Error::<T>::PoolNotFound)?;
561			let pool_account =
562				T::PoolLocator::address(&pool_id).map_err(|_| Error::<T>::InvalidAssetPair)?;
563
564			let mut refunds_number: u32 = 0;
565			if T::Assets::should_touch(*asset1.clone(), &pool_account) {
566				T::Assets::touch(*asset1, &pool_account, &who)?;
567				refunds_number += 1;
568			}
569			if T::Assets::should_touch(*asset2.clone(), &pool_account) {
570				T::Assets::touch(*asset2, &pool_account, &who)?;
571				refunds_number += 1;
572			}
573			if T::PoolAssets::should_touch(pool.lp_token.clone(), &pool_account) {
574				T::PoolAssets::touch(pool.lp_token, &pool_account, &who)?;
575				refunds_number += 1;
576			}
577			Self::deposit_event(Event::Touched { pool_id, who });
578			Ok(Some(T::WeightInfo::touch(refunds_number)).into())
579		}
580	}
581
582	impl<T: Config> Pallet<T> {
583		/// Create a pool at genesis, bypassing the setup fee.
584		///
585		/// The `lp_provider` must already hold sufficient balances of both assets.
586		/// If both `amount1` and `amount2` are non-zero, initial liquidity is added.
587		/// Returns the LP token amount minted to `lp_provider` (zero if no liquidity).
588		pub(crate) fn setup_pool_from_genesis(
589			asset1: &T::AssetKind,
590			asset2: &T::AssetKind,
591			lp_provider: &T::AccountId,
592			amount1: T::Balance,
593			amount2: T::Balance,
594		) -> Result<T::Balance, DispatchError> {
595			ensure!(asset1 != asset2, Error::<T>::InvalidAssetPair);
596
597			let pool_id = T::PoolLocator::pool_id(asset1, asset2)
598				.map_err(|_| Error::<T>::InvalidAssetPair)?;
599			ensure!(!Pools::<T>::contains_key(&pool_id), Error::<T>::PoolExists);
600
601			let pool_account =
602				T::PoolLocator::address(&pool_id).map_err(|_| Error::<T>::InvalidAssetPair)?;
603
604			// Allocate LP token ID.
605			let lp_token = NextPoolAssetId::<T>::get()
606				.or(T::PoolAssetId::initial_value())
607				.ok_or(Error::<T>::IncorrectPoolAssetId)?;
608			let next_lp_token_id = lp_token.increment().ok_or(Error::<T>::IncorrectPoolAssetId)?;
609			NextPoolAssetId::<T>::set(Some(next_lp_token_id));
610
611			// Create LP token asset.
612			T::PoolAssets::create(lp_token.clone(), pool_account.clone(), false, 1u32.into())?;
613
614			// Touch asset accounts for the pool account.
615			if T::Assets::should_touch(asset1.clone(), &pool_account) {
616				T::Assets::touch(asset1.clone(), &pool_account, lp_provider)?;
617			}
618			if T::Assets::should_touch(asset2.clone(), &pool_account) {
619				T::Assets::touch(asset2.clone(), &pool_account, lp_provider)?;
620			}
621			if T::PoolAssets::should_touch(lp_token.clone(), &pool_account) {
622				T::PoolAssets::touch(lp_token.clone(), &pool_account, lp_provider)?;
623			}
624
625			// Register pool.
626			Pools::<T>::insert(pool_id, PoolInfo { lp_token: lp_token.clone() });
627
628			// Add initial liquidity if amounts are non-zero.
629			if !amount1.is_zero() && !amount2.is_zero() {
630				T::Assets::transfer(asset1.clone(), lp_provider, &pool_account, amount1, Preserve)?;
631				T::Assets::transfer(asset2.clone(), lp_provider, &pool_account, amount2, Preserve)?;
632
633				let lp_token_amount = Self::calc_lp_amount_for_zero_supply(&amount1, &amount2)?;
634				T::PoolAssets::mint_into(
635					lp_token.clone(),
636					&pool_account,
637					T::MintMinLiquidity::get(),
638				)?;
639				T::PoolAssets::mint_into(lp_token, lp_provider, lp_token_amount)?;
640
641				Ok(lp_token_amount)
642			} else {
643				Ok(Zero::zero())
644			}
645		}
646
647		/// Create a new liquidity pool.
648		///
649		/// **Warning**: The storage must be rolled back on error.
650		pub(crate) fn do_create_pool(
651			creator: &T::AccountId,
652			asset1: T::AssetKind,
653			asset2: T::AssetKind,
654		) -> Result<T::PoolId, DispatchError> {
655			ensure!(asset1 != asset2, Error::<T>::InvalidAssetPair);
656
657			// prepare pool_id
658			let pool_id = T::PoolLocator::pool_id(&asset1, &asset2)
659				.map_err(|_| Error::<T>::InvalidAssetPair)?;
660			ensure!(!Pools::<T>::contains_key(&pool_id), Error::<T>::PoolExists);
661
662			let pool_account =
663				T::PoolLocator::address(&pool_id).map_err(|_| Error::<T>::InvalidAssetPair)?;
664
665			// pay the setup fee
666			let fee =
667				Self::withdraw(T::PoolSetupFeeAsset::get(), creator, T::PoolSetupFee::get(), true)?;
668			T::PoolSetupFeeTarget::on_unbalanced(fee);
669
670			if T::Assets::should_touch(asset1.clone(), &pool_account) {
671				T::Assets::touch(asset1.clone(), &pool_account, creator)?
672			};
673
674			if T::Assets::should_touch(asset2.clone(), &pool_account) {
675				T::Assets::touch(asset2.clone(), &pool_account, creator)?
676			};
677
678			let lp_token = NextPoolAssetId::<T>::get()
679				.or(T::PoolAssetId::initial_value())
680				.ok_or(Error::<T>::IncorrectPoolAssetId)?;
681			let next_lp_token_id = lp_token.increment().ok_or(Error::<T>::IncorrectPoolAssetId)?;
682			NextPoolAssetId::<T>::set(Some(next_lp_token_id));
683
684			T::PoolAssets::create(lp_token.clone(), pool_account.clone(), false, 1u32.into())?;
685			if T::PoolAssets::should_touch(lp_token.clone(), &pool_account) {
686				T::PoolAssets::touch(lp_token.clone(), &pool_account, creator)?
687			};
688
689			let pool_info = PoolInfo { lp_token: lp_token.clone() };
690			Pools::<T>::insert(pool_id.clone(), pool_info);
691
692			Self::deposit_event(Event::PoolCreated {
693				creator: creator.clone(),
694				pool_id: pool_id.clone(),
695				pool_account,
696				lp_token,
697			});
698
699			Ok(pool_id)
700		}
701
702		/// Add liquidity to a pool.
703		pub(crate) fn do_add_liquidity(
704			who: &T::AccountId,
705			asset1: T::AssetKind,
706			asset2: T::AssetKind,
707			amount1_desired: T::Balance,
708			amount2_desired: T::Balance,
709			amount1_min: T::Balance,
710			amount2_min: T::Balance,
711			mint_to: &T::AccountId,
712		) -> Result<T::Balance, DispatchError> {
713			let pool_id = T::PoolLocator::pool_id(&asset1, &asset2)
714				.map_err(|_| Error::<T>::InvalidAssetPair)?;
715
716			ensure!(
717				amount1_desired > Zero::zero() && amount2_desired > Zero::zero(),
718				Error::<T>::WrongDesiredAmount
719			);
720
721			let pool = Pools::<T>::get(&pool_id).ok_or(Error::<T>::PoolNotFound)?;
722			let pool_account =
723				T::PoolLocator::address(&pool_id).map_err(|_| Error::<T>::InvalidAssetPair)?;
724
725			let reserve1 = Self::get_balance(&pool_account, asset1.clone());
726			let reserve2 = Self::get_balance(&pool_account, asset2.clone());
727
728			let amount1: T::Balance;
729			let amount2: T::Balance;
730			if reserve1.is_zero() || reserve2.is_zero() {
731				amount1 = amount1_desired;
732				amount2 = amount2_desired;
733			} else {
734				let amount2_optimal = Self::quote(&amount1_desired, &reserve1, &reserve2)?;
735
736				if amount2_optimal <= amount2_desired {
737					ensure!(
738						amount2_optimal >= amount2_min,
739						Error::<T>::AssetTwoDepositDidNotMeetMinimum
740					);
741					amount1 = amount1_desired;
742					amount2 = amount2_optimal;
743				} else {
744					let amount1_optimal = Self::quote(&amount2_desired, &reserve2, &reserve1)?;
745					ensure!(
746						amount1_optimal <= amount1_desired,
747						Error::<T>::OptimalAmountLessThanDesired
748					);
749					ensure!(
750						amount1_optimal >= amount1_min,
751						Error::<T>::AssetOneDepositDidNotMeetMinimum
752					);
753					amount1 = amount1_optimal;
754					amount2 = amount2_desired;
755				}
756			}
757
758			ensure!(
759				amount1.saturating_add(reserve1) >= T::Assets::minimum_balance(asset1.clone()),
760				Error::<T>::AmountOneLessThanMinimal
761			);
762			ensure!(
763				amount2.saturating_add(reserve2) >= T::Assets::minimum_balance(asset2.clone()),
764				Error::<T>::AmountTwoLessThanMinimal
765			);
766
767			T::Assets::transfer(asset1, who, &pool_account, amount1, Preserve)?;
768			T::Assets::transfer(asset2, who, &pool_account, amount2, Preserve)?;
769
770			let total_supply = T::PoolAssets::total_issuance(pool.lp_token.clone());
771
772			let lp_token_amount: T::Balance;
773			if total_supply.is_zero() {
774				lp_token_amount = Self::calc_lp_amount_for_zero_supply(&amount1, &amount2)?;
775				T::PoolAssets::mint_into(
776					pool.lp_token.clone(),
777					&pool_account,
778					T::MintMinLiquidity::get(),
779				)?;
780			} else {
781				let side1 = Self::mul_div(&amount1, &total_supply, &reserve1)?;
782				let side2 = Self::mul_div(&amount2, &total_supply, &reserve2)?;
783				lp_token_amount = side1.min(side2);
784			}
785
786			ensure!(
787				lp_token_amount > T::MintMinLiquidity::get(),
788				Error::<T>::InsufficientLiquidityMinted
789			);
790
791			T::PoolAssets::mint_into(pool.lp_token.clone(), mint_to, lp_token_amount)?;
792
793			Self::deposit_event(Event::LiquidityAdded {
794				who: who.clone(),
795				mint_to: mint_to.clone(),
796				pool_id,
797				amount1_provided: amount1,
798				amount2_provided: amount2,
799				lp_token: pool.lp_token,
800				lp_token_minted: lp_token_amount,
801			});
802
803			Ok(lp_token_amount)
804		}
805
806		/// Remove liquidity from a pool.
807		pub(crate) fn do_remove_liquidity(
808			who: &T::AccountId,
809			asset1: T::AssetKind,
810			asset2: T::AssetKind,
811			lp_token_burn: T::Balance,
812			amount1_min_receive: T::Balance,
813			amount2_min_receive: T::Balance,
814			withdraw_to: &T::AccountId,
815		) -> Result<(T::Balance, T::Balance), DispatchError> {
816			let pool_id = T::PoolLocator::pool_id(&asset1, &asset2)
817				.map_err(|_| Error::<T>::InvalidAssetPair)?;
818
819			ensure!(lp_token_burn > Zero::zero(), Error::<T>::ZeroLiquidity);
820
821			let pool = Pools::<T>::get(&pool_id).ok_or(Error::<T>::PoolNotFound)?;
822
823			let pool_account =
824				T::PoolLocator::address(&pool_id).map_err(|_| Error::<T>::InvalidAssetPair)?;
825			let reserve1 = Self::get_balance(&pool_account, asset1.clone());
826			let reserve2 = Self::get_balance(&pool_account, asset2.clone());
827
828			let total_supply = T::PoolAssets::total_issuance(pool.lp_token.clone());
829			let withdrawal_fee_amount = T::LiquidityWithdrawalFee::get() * lp_token_burn;
830			let lp_redeem_amount = lp_token_burn.saturating_sub(withdrawal_fee_amount);
831
832			let amount1 = Self::mul_div(&lp_redeem_amount, &reserve1, &total_supply)?;
833			let amount2 = Self::mul_div(&lp_redeem_amount, &reserve2, &total_supply)?;
834
835			ensure!(
836				!amount1.is_zero() && amount1 >= amount1_min_receive,
837				Error::<T>::AssetOneWithdrawalDidNotMeetMinimum
838			);
839			ensure!(
840				!amount2.is_zero() && amount2 >= amount2_min_receive,
841				Error::<T>::AssetTwoWithdrawalDidNotMeetMinimum
842			);
843			let reserve1_left = reserve1.saturating_sub(amount1);
844			let reserve2_left = reserve2.saturating_sub(amount2);
845			ensure!(
846				reserve1_left >= T::Assets::minimum_balance(asset1.clone()),
847				Error::<T>::ReserveLeftLessThanMinimal
848			);
849			ensure!(
850				reserve2_left >= T::Assets::minimum_balance(asset2.clone()),
851				Error::<T>::ReserveLeftLessThanMinimal
852			);
853
854			// burn the provided lp token amount that includes the fee
855			T::PoolAssets::burn_from(
856				pool.lp_token.clone(),
857				who,
858				lp_token_burn,
859				Expendable,
860				Exact,
861				Polite,
862			)?;
863
864			T::Assets::transfer(asset1, &pool_account, withdraw_to, amount1, Expendable)?;
865			T::Assets::transfer(asset2, &pool_account, withdraw_to, amount2, Expendable)?;
866
867			Self::deposit_event(Event::LiquidityRemoved {
868				who: who.clone(),
869				withdraw_to: withdraw_to.clone(),
870				pool_id,
871				amount1,
872				amount2,
873				lp_token: pool.lp_token,
874				lp_token_burned: lp_token_burn,
875				withdrawal_fee: T::LiquidityWithdrawalFee::get(),
876			});
877
878			Ok((amount1, amount2))
879		}
880
881		/// Swap exactly `amount_in` of asset `path[0]` for asset `path[1]`.
882		/// If an `amount_out_min` is specified, it will return an error if it is unable to acquire
883		/// the amount desired.
884		///
885		/// Withdraws the `path[0]` asset from `sender`, deposits the `path[1]` asset to `send_to`,
886		/// respecting `keep_alive`.
887		///
888		/// If successful, returns the amount of `path[1]` acquired for the `amount_in`.
889		///
890		/// WARNING: This may return an error after a partial storage mutation. It should be used
891		/// only inside a transactional storage context and an Err result must imply a storage
892		/// rollback.
893		pub(crate) fn do_swap_exact_tokens_for_tokens(
894			sender: T::AccountId,
895			path: Vec<T::AssetKind>,
896			amount_in: T::Balance,
897			amount_out_min: Option<T::Balance>,
898			send_to: T::AccountId,
899			keep_alive: bool,
900		) -> Result<T::Balance, DispatchError> {
901			ensure!(amount_in > Zero::zero(), Error::<T>::ZeroAmount);
902			if let Some(amount_out_min) = amount_out_min {
903				ensure!(amount_out_min > Zero::zero(), Error::<T>::ZeroAmount);
904			}
905
906			Self::validate_swap_path(&path)?;
907			let path = Self::balance_path_from_amount_in(amount_in, path)?;
908
909			let amount_out = path.last().map(|(_, a)| *a).ok_or(Error::<T>::InvalidPath)?;
910			if let Some(amount_out_min) = amount_out_min {
911				ensure!(
912					amount_out >= amount_out_min,
913					Error::<T>::ProvidedMinimumNotSufficientForSwap
914				);
915			}
916
917			Self::swap(&sender, &path, &send_to, keep_alive)?;
918
919			Self::deposit_event(Event::SwapExecuted {
920				who: sender,
921				send_to,
922				amount_in,
923				amount_out,
924				path,
925			});
926			Ok(amount_out)
927		}
928
929		/// Take the `path[0]` asset and swap some amount for `amount_out` of the `path[1]`. If an
930		/// `amount_in_max` is specified, it will return an error if acquiring `amount_out` would be
931		/// too costly.
932		///
933		/// Withdraws `path[0]` asset from `sender`, deposits the `path[1]` asset to `send_to`,
934		/// respecting `keep_alive`.
935		///
936		/// If successful returns the amount of the `path[0]` taken to provide `path[1]`.
937		///
938		/// WARNING: This may return an error after a partial storage mutation. It should be used
939		/// only inside a transactional storage context and an Err result must imply a storage
940		/// rollback.
941		pub(crate) fn do_swap_tokens_for_exact_tokens(
942			sender: T::AccountId,
943			path: Vec<T::AssetKind>,
944			amount_out: T::Balance,
945			amount_in_max: Option<T::Balance>,
946			send_to: T::AccountId,
947			keep_alive: bool,
948		) -> Result<T::Balance, DispatchError> {
949			ensure!(amount_out > Zero::zero(), Error::<T>::ZeroAmount);
950			if let Some(amount_in_max) = amount_in_max {
951				ensure!(amount_in_max > Zero::zero(), Error::<T>::ZeroAmount);
952			}
953
954			Self::validate_swap_path(&path)?;
955			let path = Self::balance_path_from_amount_out(amount_out, path)?;
956
957			let amount_in = path.first().map(|(_, a)| *a).ok_or(Error::<T>::InvalidPath)?;
958			if let Some(amount_in_max) = amount_in_max {
959				ensure!(
960					amount_in <= amount_in_max,
961					Error::<T>::ProvidedMaximumNotSufficientForSwap
962				);
963			}
964
965			Self::swap(&sender, &path, &send_to, keep_alive)?;
966
967			Self::deposit_event(Event::SwapExecuted {
968				who: sender,
969				send_to,
970				amount_in,
971				amount_out,
972				path,
973			});
974
975			Ok(amount_in)
976		}
977
978		/// Swap exactly `credit_in` of asset `path[0]` for asset `path[last]`.  If `amount_out_min`
979		/// is provided and the swap can't achieve at least this amount, an error is returned.
980		///
981		/// On a successful swap, the function returns the `credit_out` of `path[last]` obtained
982		/// from the `credit_in`. On failure, it returns an `Err` containing the original
983		/// `credit_in` and the associated error code.
984		///
985		/// WARNING: This may return an error after a partial storage mutation. It should be used
986		/// only inside a transactional storage context and an Err result must imply a storage
987		/// rollback.
988		pub(crate) fn do_swap_exact_credit_tokens_for_tokens(
989			path: Vec<T::AssetKind>,
990			credit_in: CreditOf<T>,
991			amount_out_min: Option<T::Balance>,
992		) -> Result<CreditOf<T>, (CreditOf<T>, DispatchError)> {
993			let amount_in = credit_in.peek();
994			let inspect_path = |credit_asset| {
995				ensure!(
996					path.first().map_or(false, |a| *a == credit_asset),
997					Error::<T>::InvalidPath
998				);
999				ensure!(!amount_in.is_zero(), Error::<T>::ZeroAmount);
1000				ensure!(amount_out_min.map_or(true, |a| !a.is_zero()), Error::<T>::ZeroAmount);
1001
1002				Self::validate_swap_path(&path)?;
1003				let path = Self::balance_path_from_amount_in(amount_in, path)?;
1004
1005				let amount_out = path.last().map(|(_, a)| *a).ok_or(Error::<T>::InvalidPath)?;
1006				ensure!(
1007					amount_out_min.map_or(true, |a| amount_out >= a),
1008					Error::<T>::ProvidedMinimumNotSufficientForSwap
1009				);
1010				Ok((path, amount_out))
1011			};
1012			let (path, amount_out) = match inspect_path(credit_in.asset()) {
1013				Ok((p, a)) => (p, a),
1014				Err(e) => return Err((credit_in, e)),
1015			};
1016
1017			let credit_out = Self::credit_swap(credit_in, &path)?;
1018
1019			Self::deposit_event(Event::SwapCreditExecuted { amount_in, amount_out, path });
1020
1021			Ok(credit_out)
1022		}
1023
1024		/// Swaps a portion of `credit_in` of `path[0]` asset to obtain the desired `amount_out` of
1025		/// the `path[last]` asset. The provided `credit_in` must be adequate to achieve the target
1026		/// `amount_out`, or an error will occur.
1027		///
1028		/// On success, the function returns a (`credit_out`, `credit_change`) tuple, where
1029		/// `credit_out` represents the acquired amount of the `path[last]` asset, and
1030		/// `credit_change` is the remaining portion from the `credit_in`. On failure, an `Err` with
1031		/// the initial `credit_in` and error code is returned.
1032		///
1033		/// WARNING: This may return an error after a partial storage mutation. It should be used
1034		/// only inside a transactional storage context and an Err result must imply a storage
1035		/// rollback.
1036		pub(crate) fn do_swap_credit_tokens_for_exact_tokens(
1037			path: Vec<T::AssetKind>,
1038			credit_in: CreditOf<T>,
1039			amount_out: T::Balance,
1040		) -> Result<(CreditOf<T>, CreditOf<T>), (CreditOf<T>, DispatchError)> {
1041			let amount_in_max = credit_in.peek();
1042			let inspect_path = |credit_asset| {
1043				ensure!(
1044					path.first().map_or(false, |a| a == &credit_asset),
1045					Error::<T>::InvalidPath
1046				);
1047				ensure!(amount_in_max > Zero::zero(), Error::<T>::ZeroAmount);
1048				ensure!(amount_out > Zero::zero(), Error::<T>::ZeroAmount);
1049
1050				Self::validate_swap_path(&path)?;
1051				let path = Self::balance_path_from_amount_out(amount_out, path)?;
1052
1053				let amount_in = path.first().map(|(_, a)| *a).ok_or(Error::<T>::InvalidPath)?;
1054				ensure!(
1055					amount_in <= amount_in_max,
1056					Error::<T>::ProvidedMaximumNotSufficientForSwap
1057				);
1058
1059				Ok((path, amount_in))
1060			};
1061			let (path, amount_in) = match inspect_path(credit_in.asset()) {
1062				Ok((p, a)) => (p, a),
1063				Err(e) => return Err((credit_in, e)),
1064			};
1065
1066			let (credit_in, credit_change) = credit_in.split(amount_in);
1067			let credit_out = Self::credit_swap(credit_in, &path)?;
1068
1069			Self::deposit_event(Event::SwapCreditExecuted { amount_in, amount_out, path });
1070
1071			Ok((credit_out, credit_change))
1072		}
1073
1074		/// Swap assets along the `path`, withdrawing from `sender` and depositing in `send_to`.
1075		///
1076		/// Note: It's assumed that the provided `path` is valid.
1077		///
1078		/// WARNING: This may return an error after a partial storage mutation. It should be used
1079		/// only inside a transactional storage context and an Err result must imply a storage
1080		/// rollback.
1081		fn swap(
1082			sender: &T::AccountId,
1083			path: &BalancePath<T>,
1084			send_to: &T::AccountId,
1085			keep_alive: bool,
1086		) -> Result<(), DispatchError> {
1087			let (asset_in, amount_in) = path.first().ok_or(Error::<T>::InvalidPath)?;
1088			let credit_in = Self::withdraw(asset_in.clone(), sender, *amount_in, keep_alive)?;
1089
1090			let credit_out = Self::credit_swap(credit_in, path).map_err(|(_, e)| e)?;
1091			T::Assets::resolve(send_to, credit_out).map_err(|_| Error::<T>::BelowMinimum)?;
1092
1093			Ok(())
1094		}
1095
1096		/// Swap assets along the specified `path`, consuming `credit_in` and producing
1097		/// `credit_out`.
1098		///
1099		/// If an error occurs, `credit_in` is returned back.
1100		///
1101		/// Note: It's assumed that the provided `path` is valid and `credit_in` corresponds to the
1102		/// first asset in the `path`.
1103		///
1104		/// WARNING: This may return an error after a partial storage mutation. It should be used
1105		/// only inside a transactional storage context and an Err result must imply a storage
1106		/// rollback.
1107		fn credit_swap(
1108			credit_in: CreditOf<T>,
1109			path: &BalancePath<T>,
1110		) -> Result<CreditOf<T>, (CreditOf<T>, DispatchError)> {
1111			let resolve_path = || -> Result<CreditOf<T>, DispatchError> {
1112				for pos in 0..=path.len() {
1113					if let Some([(asset1, _), (asset2, amount_out)]) = path.get(pos..=pos + 1) {
1114						let pool_from = T::PoolLocator::pool_address(asset1, asset2)
1115							.map_err(|_| Error::<T>::InvalidAssetPair)?;
1116
1117						if let Some((asset3, _)) = path.get(pos + 2) {
1118							let pool_to = T::PoolLocator::pool_address(asset2, asset3)
1119								.map_err(|_| Error::<T>::InvalidAssetPair)?;
1120
1121							T::Assets::transfer(
1122								asset2.clone(),
1123								&pool_from,
1124								&pool_to,
1125								*amount_out,
1126								Preserve,
1127							)?;
1128						} else {
1129							let credit_out =
1130								Self::withdraw(asset2.clone(), &pool_from, *amount_out, true)?;
1131							return Ok(credit_out);
1132						}
1133					}
1134				}
1135				Err(Error::<T>::InvalidPath.into())
1136			};
1137
1138			let credit_out = match resolve_path() {
1139				Ok(c) => c,
1140				Err(e) => return Err((credit_in, e)),
1141			};
1142
1143			let pool_to = if let Some([(asset1, _), (asset2, _)]) = path.get(0..2) {
1144				match T::PoolLocator::pool_address(asset1, asset2) {
1145					Ok(address) => address,
1146					Err(_) => return Err((credit_in, Error::<T>::InvalidAssetPair.into())),
1147				}
1148			} else {
1149				return Err((credit_in, Error::<T>::InvalidPath.into()));
1150			};
1151
1152			T::Assets::resolve(&pool_to, credit_in)
1153				.map_err(|c| (c, Error::<T>::BelowMinimum.into()))?;
1154
1155			Ok(credit_out)
1156		}
1157
1158		/// Removes `value` balance of `asset` from `who` account if possible.
1159		fn withdraw(
1160			asset: T::AssetKind,
1161			who: &T::AccountId,
1162			value: T::Balance,
1163			keep_alive: bool,
1164		) -> Result<CreditOf<T>, DispatchError> {
1165			let preservation = match keep_alive {
1166				true => Preserve,
1167				false => Expendable,
1168			};
1169			if preservation == Preserve {
1170				// TODO drop the ensure! when this issue addressed
1171				// https://github.com/paritytech/polkadot-sdk/issues/1698
1172				let free = T::Assets::reducible_balance(asset.clone(), who, preservation, Polite);
1173				ensure!(free >= value, TokenError::NotExpendable);
1174			}
1175			T::Assets::withdraw(asset, who, value, Exact, preservation, Polite)
1176		}
1177
1178		/// Get the `owner`'s balance of `asset`, which could be the chain's native asset or another
1179		/// fungible. Returns a value in the form of an `Balance`.
1180		pub(crate) fn get_balance(owner: &T::AccountId, asset: T::AssetKind) -> T::Balance {
1181			T::Assets::reducible_balance(asset, owner, Expendable, Polite)
1182		}
1183
1184		/// Leading to an amount at the end of a `path`, get the required amounts in.
1185		pub(crate) fn balance_path_from_amount_out(
1186			amount_out: T::Balance,
1187			path: Vec<T::AssetKind>,
1188		) -> Result<BalancePath<T>, DispatchError> {
1189			let mut balance_path: BalancePath<T> = Vec::with_capacity(path.len());
1190			let mut amount_in: T::Balance = amount_out;
1191
1192			let mut iter = path.into_iter().rev().peekable();
1193			while let Some(asset2) = iter.next() {
1194				let asset1 = match iter.peek() {
1195					Some(a) => a,
1196					None => {
1197						balance_path.push((asset2, amount_in));
1198						break;
1199					},
1200				};
1201				let (reserve_in, reserve_out) = Self::get_reserves(asset1.clone(), asset2.clone())?;
1202				balance_path.push((asset2, amount_in));
1203				amount_in = Self::get_amount_in(&amount_in, &reserve_in, &reserve_out)?;
1204			}
1205			balance_path.reverse();
1206
1207			Ok(balance_path)
1208		}
1209
1210		/// Following an amount into a `path`, get the corresponding amounts out.
1211		pub(crate) fn balance_path_from_amount_in(
1212			amount_in: T::Balance,
1213			path: Vec<T::AssetKind>,
1214		) -> Result<BalancePath<T>, DispatchError> {
1215			let mut balance_path: BalancePath<T> = Vec::with_capacity(path.len());
1216			let mut amount_out: T::Balance = amount_in;
1217
1218			let mut iter = path.into_iter().peekable();
1219			while let Some(asset1) = iter.next() {
1220				let asset2 = match iter.peek() {
1221					Some(a) => a,
1222					None => {
1223						balance_path.push((asset1, amount_out));
1224						break;
1225					},
1226				};
1227				let (reserve_in, reserve_out) = Self::get_reserves(asset1.clone(), asset2.clone())?;
1228				balance_path.push((asset1, amount_out));
1229				amount_out = Self::get_amount_out(&amount_out, &reserve_in, &reserve_out)?;
1230			}
1231			Ok(balance_path)
1232		}
1233
1234		/// Calculates the optimal amount from the reserves.
1235		pub fn quote(
1236			amount: &T::Balance,
1237			reserve1: &T::Balance,
1238			reserve2: &T::Balance,
1239		) -> Result<T::Balance, Error<T>> {
1240			// (amount * reserve2) / reserve1
1241			Self::mul_div(amount, reserve2, reserve1)
1242		}
1243
1244		pub(super) fn calc_lp_amount_for_zero_supply(
1245			amount1: &T::Balance,
1246			amount2: &T::Balance,
1247		) -> Result<T::Balance, Error<T>> {
1248			let amount1 = T::HigherPrecisionBalance::from(*amount1);
1249			let amount2 = T::HigherPrecisionBalance::from(*amount2);
1250
1251			let result = amount1
1252				.checked_mul(&amount2)
1253				.ok_or(Error::<T>::Overflow)?
1254				.integer_sqrt()
1255				.checked_sub(&T::MintMinLiquidity::get().into())
1256				.ok_or(Error::<T>::InsufficientLiquidityMinted)?;
1257
1258			result.try_into().map_err(|_| Error::<T>::Overflow)
1259		}
1260
1261		fn mul_div(a: &T::Balance, b: &T::Balance, c: &T::Balance) -> Result<T::Balance, Error<T>> {
1262			let a = T::HigherPrecisionBalance::from(*a);
1263			let b = T::HigherPrecisionBalance::from(*b);
1264			let c = T::HigherPrecisionBalance::from(*c);
1265
1266			let result = a
1267				.checked_mul(&b)
1268				.ok_or(Error::<T>::Overflow)?
1269				.checked_div(&c)
1270				.ok_or(Error::<T>::Overflow)?;
1271
1272			result.try_into().map_err(|_| Error::<T>::Overflow)
1273		}
1274
1275		/// Calculates amount out.
1276		///
1277		/// Given an input amount of an asset and pair reserves, returns the maximum output amount
1278		/// of the other asset.
1279		pub fn get_amount_out(
1280			amount_in: &T::Balance,
1281			reserve_in: &T::Balance,
1282			reserve_out: &T::Balance,
1283		) -> Result<T::Balance, Error<T>> {
1284			let amount_in = T::HigherPrecisionBalance::from(*amount_in);
1285			let reserve_in = T::HigherPrecisionBalance::from(*reserve_in);
1286			let reserve_out = T::HigherPrecisionBalance::from(*reserve_out);
1287
1288			if reserve_in.is_zero() || reserve_out.is_zero() {
1289				return Err(Error::<T>::ZeroLiquidity);
1290			}
1291
1292			let fee_complement = T::LPFee::get().left_from_one().deconstruct();
1293			let amount_in_with_fee = amount_in
1294				.checked_mul(&T::HigherPrecisionBalance::from(fee_complement))
1295				.ok_or(Error::<T>::Overflow)?;
1296
1297			let numerator =
1298				amount_in_with_fee.checked_mul(&reserve_out).ok_or(Error::<T>::Overflow)?;
1299
1300			let denominator = reserve_in
1301				.checked_mul(&T::HigherPrecisionBalance::from(Permill::ACCURACY))
1302				.ok_or(Error::<T>::Overflow)?
1303				.checked_add(&amount_in_with_fee)
1304				.ok_or(Error::<T>::Overflow)?;
1305
1306			let result = numerator.checked_div(&denominator).ok_or(Error::<T>::Overflow)?;
1307
1308			result.try_into().map_err(|_| Error::<T>::Overflow)
1309		}
1310
1311		/// Calculates amount in.
1312		///
1313		/// Given an output amount of an asset and pair reserves, returns a required input amount
1314		/// of the other asset.
1315		pub fn get_amount_in(
1316			amount_out: &T::Balance,
1317			reserve_in: &T::Balance,
1318			reserve_out: &T::Balance,
1319		) -> Result<T::Balance, Error<T>> {
1320			let amount_out = T::HigherPrecisionBalance::from(*amount_out);
1321			let reserve_in = T::HigherPrecisionBalance::from(*reserve_in);
1322			let reserve_out = T::HigherPrecisionBalance::from(*reserve_out);
1323
1324			if reserve_in.is_zero() || reserve_out.is_zero() {
1325				Err(Error::<T>::ZeroLiquidity)?
1326			}
1327
1328			if amount_out >= reserve_out {
1329				Err(Error::<T>::AmountOutTooHigh)?
1330			}
1331
1332			let fee_complement = T::LPFee::get().left_from_one().deconstruct();
1333			let numerator = reserve_in
1334				.checked_mul(&amount_out)
1335				.ok_or(Error::<T>::Overflow)?
1336				.checked_mul(&T::HigherPrecisionBalance::from(Permill::ACCURACY))
1337				.ok_or(Error::<T>::Overflow)?;
1338
1339			let denominator = reserve_out
1340				.checked_sub(&amount_out)
1341				.ok_or(Error::<T>::Overflow)?
1342				.checked_mul(&T::HigherPrecisionBalance::from(fee_complement))
1343				.ok_or(Error::<T>::Overflow)?;
1344
1345			let result = numerator
1346				.checked_div(&denominator)
1347				.ok_or(Error::<T>::Overflow)?
1348				.checked_add(&One::one())
1349				.ok_or(Error::<T>::Overflow)?;
1350
1351			result.try_into().map_err(|_| Error::<T>::Overflow)
1352		}
1353
1354		/// Ensure that a path is valid.
1355		fn validate_swap_path(path: &Vec<T::AssetKind>) -> Result<(), DispatchError> {
1356			ensure!(path.len() >= 2, Error::<T>::InvalidPath);
1357			ensure!(path.len() as u32 <= T::MaxSwapPathLength::get(), Error::<T>::InvalidPath);
1358
1359			// validate all the pools in the path are unique
1360			let mut pools = BTreeSet::<T::PoolId>::new();
1361			for assets_pair in path.windows(2) {
1362				if let [asset1, asset2] = assets_pair {
1363					let pool_id = T::PoolLocator::pool_id(asset1, asset2)
1364						.map_err(|_| Error::<T>::InvalidAssetPair)?;
1365
1366					let new_element = pools.insert(pool_id);
1367					if !new_element {
1368						return Err(Error::<T>::NonUniquePath.into());
1369					}
1370				}
1371			}
1372			Ok(())
1373		}
1374
1375		/// Returns the next pool asset id for benchmark purposes only.
1376		#[cfg(any(test, feature = "runtime-benchmarks"))]
1377		pub fn get_next_pool_asset_id() -> T::PoolAssetId {
1378			NextPoolAssetId::<T>::get()
1379				.or(T::PoolAssetId::initial_value())
1380				.expect("Next pool asset ID can not be None")
1381		}
1382	}
1383
1384	#[pallet::view_functions]
1385	impl<T: Config> Pallet<T> {
1386		/// Returns the balance of each asset in the pool.
1387		/// The tuple result is in the order requested (not necessarily the same as pool order).
1388		pub fn get_reserves(
1389			asset1: T::AssetKind,
1390			asset2: T::AssetKind,
1391		) -> Result<(T::Balance, T::Balance), Error<T>> {
1392			let pool_account = T::PoolLocator::pool_address(&asset1, &asset2)
1393				.map_err(|_| Error::<T>::InvalidAssetPair)?;
1394
1395			let balance1 = Self::get_balance(&pool_account, asset1);
1396			let balance2 = Self::get_balance(&pool_account, asset2);
1397
1398			if balance1.is_zero() || balance2.is_zero() {
1399				Err(Error::<T>::PoolEmpty)?;
1400			}
1401
1402			Ok((balance1, balance2))
1403		}
1404
1405		/// Gets a quote for swapping an exact amount of `asset1` for `asset2`.
1406		///
1407		/// If `include_fee` is true, the quote will include the liquidity provider fee.
1408		/// If the pool does not exist or has no liquidity, `None` is returned.
1409		/// Note that the price may have changed by the time the transaction is executed.
1410		/// (Use `amount_out_min` to control slippage.)
1411		/// Returns `Some(quoted_amount)` on success.
1412		pub fn quote_price_exact_tokens_for_tokens(
1413			asset1: T::AssetKind,
1414			asset2: T::AssetKind,
1415			amount: T::Balance,
1416			include_fee: bool,
1417		) -> Option<T::Balance> {
1418			// Swaps reject zero amounts, match that behavior.
1419			if amount.is_zero() {
1420				return None;
1421			}
1422
1423			let pool_account = T::PoolLocator::pool_address(&asset1, &asset2).ok()?;
1424
1425			let balance1 = Self::get_balance(&pool_account, asset1);
1426			let balance2 = Self::get_balance(&pool_account, asset2.clone());
1427
1428			if balance1.is_zero() {
1429				return None;
1430			}
1431
1432			let amount_out = if include_fee {
1433				Self::get_amount_out(&amount, &balance1, &balance2).ok()?
1434			} else {
1435				Self::quote(&amount, &balance1, &balance2).ok()?
1436			};
1437
1438			// Small inputs can round output to zero due to integer division.
1439			if amount_out.is_zero() {
1440				return None;
1441			}
1442
1443			// Swap withdrawals from pools use `keep_alive=true` (Preserve). Use the same
1444			// preservation level to determine the actual withdrawable amount.
1445			let max_output = T::Assets::reducible_balance(asset2, &pool_account, Preserve, Polite);
1446			if amount_out > max_output {
1447				return None;
1448			}
1449
1450			Some(amount_out)
1451		}
1452
1453		/// Gets a quote for swapping `amount` of `asset1` for an exact amount of `asset2`.
1454		///
1455		/// If `include_fee` is true, the quote will include the liquidity provider fee.
1456		/// If the pool does not exist or has no liquidity, `None` is returned.
1457		/// Note that the price may have changed by the time the transaction is executed.
1458		/// (Use `amount_in_max` to control slippage.)
1459		/// Returns `Some(quoted_amount)` on success.
1460		pub fn quote_price_tokens_for_exact_tokens(
1461			asset1: T::AssetKind,
1462			asset2: T::AssetKind,
1463			amount: T::Balance,
1464			include_fee: bool,
1465		) -> Option<T::Balance> {
1466			// Swaps reject zero amounts, match that behavior.
1467			if amount.is_zero() {
1468				return None;
1469			}
1470			let pool_account = T::PoolLocator::pool_address(&asset1, &asset2).ok()?;
1471
1472			let balance1 = Self::get_balance(&pool_account, asset1);
1473			let balance2 = Self::get_balance(&pool_account, asset2.clone());
1474
1475			if balance1.is_zero() {
1476				return None;
1477			}
1478
1479			// Swap withdrawals from pools use `keep_alive=true` (Preserve). Use the same
1480			// preservation level to determine the actual withdrawable amount.
1481			let max_output = T::Assets::reducible_balance(asset2, &pool_account, Preserve, Polite);
1482			if amount > max_output {
1483				return None;
1484			}
1485
1486			if include_fee {
1487				Self::get_amount_in(&amount, &balance1, &balance2).ok()
1488			} else {
1489				Self::quote(&amount, &balance2, &balance1).ok()
1490			}
1491		}
1492	}
1493}
1494
1495sp_api::decl_runtime_apis! {
1496	/// This runtime api allows people to query the size of the liquidity pools
1497	/// and quote prices for swaps.
1498	pub trait AssetConversionApi<Balance, AssetId>
1499	where
1500		Balance: frame_support::traits::tokens::Balance + MaybeDisplay,
1501		AssetId: Codec,
1502	{
1503		/// Provides a quote for [`Pallet::swap_tokens_for_exact_tokens`].
1504		///
1505		/// Note that the price may have changed by the time the transaction is executed.
1506		/// (Use `amount_in_max` to control slippage.)
1507		fn quote_price_tokens_for_exact_tokens(
1508			asset1: AssetId,
1509			asset2: AssetId,
1510			amount: Balance,
1511			include_fee: bool,
1512		) -> Option<Balance>;
1513
1514		/// Provides a quote for [`Pallet::swap_exact_tokens_for_tokens`].
1515		///
1516		/// Note that the price may have changed by the time the transaction is executed.
1517		/// (Use `amount_out_min` to control slippage.)
1518		fn quote_price_exact_tokens_for_tokens(
1519			asset1: AssetId,
1520			asset2: AssetId,
1521			amount: Balance,
1522			include_fee: bool,
1523		) -> Option<Balance>;
1524
1525		/// Returns the size of the liquidity pool for the given asset pair.
1526		fn get_reserves(asset1: AssetId, asset2: AssetId) -> Option<(Balance, Balance)>;
1527	}
1528}
1529
1530sp_core::generate_feature_enabled_macro!(runtime_benchmarks_enabled, feature = "runtime-benchmarks", $);