mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-28 16:47:57 +00:00
4f832ea865
closes https://github.com/paritytech/polkadot-sdk/issues/1842 Decoupling Pallet from the Concept of Native Currency Currently, the pallet is intrinsically linked with the concept of native currency, requiring users to provide implementations of the `fungible::*` and `fungibles::*` traits to interact with native and non native assets. This incapsulates some non-related to the pallet complexity and makes it less adaptable in contexts where the native currency concept is absent. With this PR, the dependence on `fungible::*` for liquidity-supplying assets has been removed. Instead, the native and non-native currencies' handling is now overseen by a single type that implements the `fungibles::*` traits. To simplify this integration, types have been introduced to facilitate the creation of a union between `fungible::*` and `fungibles::*` implementations, producing a unified `fungibles::*` type. One of the reasons driving these changes is the ambition to create a more user-friendly API for the `SwapCredit` implementation. Given that it interacts with two distinct credit types from `fungible` and `fungibles`, a unified type was introduced. Clients now manage potential conversion failures for those credit types. In certain contexts, it's vital to guarantee that operations are fail-safe, like in this impl - [PR](https://github.com/paritytech/polkadot-sdk/pull/1845), place in [code](https://github.com/paritytech/polkadot-sdk/blob/20b85a5fada8f55c98ba831964f5866ffeadf4da/cumulus/primitives/utility/src/lib.rs#L429). Additional Updates: - abstracted the pool ID and its account derivation logic via trait bounds, along with common implementation offerings; - removed `inc_providers` on a pool creation for the pool account; - benchmarks: -- swap complexity is N, not const; -- removed `From<u128> + Into<u128>` bound from `T::Balance`; -- removed swap/liquidity/.. amount constants, resolve them dynamically based on pallet configuration; -- migrated to v2 API; - `OnUnbalanced` handler for the pool creation fee, replacing direct transfers to a specified account ID; - renamed `MultiAssetId` to `AssetKind` aligning with naming across frame crates; related PRs: - (depends) https://github.com/paritytech/polkadot-sdk/pull/1677 - (caused) https://github.com/paritytech/polkadot-sdk/pull/2033 - (caused) https://github.com/paritytech/polkadot-sdk/pull/1876 --------- Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> Co-authored-by: Liam Aharon <liam.aharon@hotmail.com>
213 lines
7.2 KiB
Rust
213 lines
7.2 KiB
Rust
// This file is part of Substrate.
|
|
|
|
// Copyright (C) Parity Technologies (UK) Ltd.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
//! Traits and implementations for swap between the various asset classes.
|
|
|
|
use super::*;
|
|
|
|
/// Trait for providing methods to swap between the various asset classes.
|
|
pub trait Swap<AccountId> {
|
|
/// Measure units of the asset classes for swapping.
|
|
type Balance: Balance;
|
|
/// Kind of assets that are going to be swapped.
|
|
type AssetKind;
|
|
|
|
/// Returns the upper limit on the length of the swap path.
|
|
fn max_path_len() -> u32;
|
|
|
|
/// Swap exactly `amount_in` of asset `path[0]` for asset `path[last]`.
|
|
/// If an `amount_out_min` is specified, it will return an error if it is unable to acquire
|
|
/// the amount desired.
|
|
///
|
|
/// Withdraws the `path[0]` asset from `sender`, deposits the `path[last]` asset to `send_to`,
|
|
/// respecting `keep_alive`.
|
|
///
|
|
/// If successful, returns the amount of `path[last]` acquired for the `amount_in`.
|
|
///
|
|
/// This operation is expected to be atomic.
|
|
fn swap_exact_tokens_for_tokens(
|
|
sender: AccountId,
|
|
path: Vec<Self::AssetKind>,
|
|
amount_in: Self::Balance,
|
|
amount_out_min: Option<Self::Balance>,
|
|
send_to: AccountId,
|
|
keep_alive: bool,
|
|
) -> Result<Self::Balance, DispatchError>;
|
|
|
|
/// Take the `path[0]` asset and swap some amount for `amount_out` of the `path[last]`. If an
|
|
/// `amount_in_max` is specified, it will return an error if acquiring `amount_out` would be
|
|
/// too costly.
|
|
///
|
|
/// Withdraws `path[0]` asset from `sender`, deposits `path[last]` asset to `send_to`,
|
|
/// respecting `keep_alive`.
|
|
///
|
|
/// If successful returns the amount of the `path[0]` taken to provide `path[last]`.
|
|
///
|
|
/// This operation is expected to be atomic.
|
|
fn swap_tokens_for_exact_tokens(
|
|
sender: AccountId,
|
|
path: Vec<Self::AssetKind>,
|
|
amount_out: Self::Balance,
|
|
amount_in_max: Option<Self::Balance>,
|
|
send_to: AccountId,
|
|
keep_alive: bool,
|
|
) -> Result<Self::Balance, DispatchError>;
|
|
}
|
|
|
|
/// Trait providing methods to swap between the various asset classes.
|
|
pub trait SwapCredit<AccountId> {
|
|
/// Measure units of the asset classes for swapping.
|
|
type Balance: Balance;
|
|
/// Kind of assets that are going to be swapped.
|
|
type AssetKind;
|
|
/// Credit implying a negative imbalance in the system that can be placed into an account or
|
|
/// alter the total supply.
|
|
type Credit;
|
|
|
|
/// Returns the upper limit on the length of the swap path.
|
|
fn max_path_len() -> u32;
|
|
|
|
/// Swap exactly `credit_in` of asset `path[0]` for asset `path[last]`. If `amount_out_min` is
|
|
/// provided and the swap can't achieve at least this amount, an error is returned.
|
|
///
|
|
/// On a successful swap, the function returns the `credit_out` of `path[last]` obtained from
|
|
/// the `credit_in`. On failure, it returns an `Err` containing the original `credit_in` and the
|
|
/// associated error code.
|
|
///
|
|
/// This operation is expected to be atomic.
|
|
fn swap_exact_tokens_for_tokens(
|
|
path: Vec<Self::AssetKind>,
|
|
credit_in: Self::Credit,
|
|
amount_out_min: Option<Self::Balance>,
|
|
) -> Result<Self::Credit, (Self::Credit, DispatchError)>;
|
|
|
|
/// Swaps a portion of `credit_in` of `path[0]` asset to obtain the desired `amount_out` of
|
|
/// the `path[last]` asset. The provided `credit_in` must be adequate to achieve the target
|
|
/// `amount_out`, or an error will occur.
|
|
///
|
|
/// On success, the function returns a (`credit_out`, `credit_change`) tuple, where `credit_out`
|
|
/// represents the acquired amount of the `path[last]` asset, and `credit_change` is the
|
|
/// remaining portion from the `credit_in`. On failure, an `Err` with the initial `credit_in`
|
|
/// and error code is returned.
|
|
///
|
|
/// This operation is expected to be atomic.
|
|
fn swap_tokens_for_exact_tokens(
|
|
path: Vec<Self::AssetKind>,
|
|
credit_in: Self::Credit,
|
|
amount_out: Self::Balance,
|
|
) -> Result<(Self::Credit, Self::Credit), (Self::Credit, DispatchError)>;
|
|
}
|
|
|
|
impl<T: Config> Swap<T::AccountId> for Pallet<T> {
|
|
type Balance = T::Balance;
|
|
type AssetKind = T::AssetKind;
|
|
|
|
fn max_path_len() -> u32 {
|
|
T::MaxSwapPathLength::get()
|
|
}
|
|
|
|
fn swap_exact_tokens_for_tokens(
|
|
sender: T::AccountId,
|
|
path: Vec<Self::AssetKind>,
|
|
amount_in: Self::Balance,
|
|
amount_out_min: Option<Self::Balance>,
|
|
send_to: T::AccountId,
|
|
keep_alive: bool,
|
|
) -> Result<Self::Balance, DispatchError> {
|
|
let amount_out = with_storage_layer(|| {
|
|
Self::do_swap_exact_tokens_for_tokens(
|
|
sender,
|
|
path,
|
|
amount_in,
|
|
amount_out_min,
|
|
send_to,
|
|
keep_alive,
|
|
)
|
|
})?;
|
|
Ok(amount_out)
|
|
}
|
|
|
|
fn swap_tokens_for_exact_tokens(
|
|
sender: T::AccountId,
|
|
path: Vec<Self::AssetKind>,
|
|
amount_out: Self::Balance,
|
|
amount_in_max: Option<Self::Balance>,
|
|
send_to: T::AccountId,
|
|
keep_alive: bool,
|
|
) -> Result<Self::Balance, DispatchError> {
|
|
let amount_in = with_storage_layer(|| {
|
|
Self::do_swap_tokens_for_exact_tokens(
|
|
sender,
|
|
path,
|
|
amount_out,
|
|
amount_in_max,
|
|
send_to,
|
|
keep_alive,
|
|
)
|
|
})?;
|
|
Ok(amount_in)
|
|
}
|
|
}
|
|
|
|
impl<T: Config> SwapCredit<T::AccountId> for Pallet<T> {
|
|
type Balance = T::Balance;
|
|
type AssetKind = T::AssetKind;
|
|
type Credit = CreditOf<T>;
|
|
|
|
fn max_path_len() -> u32 {
|
|
T::MaxSwapPathLength::get()
|
|
}
|
|
|
|
fn swap_exact_tokens_for_tokens(
|
|
path: Vec<Self::AssetKind>,
|
|
credit_in: Self::Credit,
|
|
amount_out_min: Option<Self::Balance>,
|
|
) -> Result<Self::Credit, (Self::Credit, DispatchError)> {
|
|
let credit_asset = credit_in.asset();
|
|
with_transaction(|| -> TransactionOutcome<Result<_, DispatchError>> {
|
|
let res = Self::do_swap_exact_credit_tokens_for_tokens(path, credit_in, amount_out_min);
|
|
match &res {
|
|
Ok(_) => TransactionOutcome::Commit(Ok(res)),
|
|
// wrapping `res` with `Ok`, since our `Err` doesn't satisfy the
|
|
// `From<DispatchError>` bound of the `with_transaction` function.
|
|
Err(_) => TransactionOutcome::Rollback(Ok(res)),
|
|
}
|
|
})
|
|
// should never map an error since `with_transaction` above never returns it.
|
|
.map_err(|_| (Self::Credit::zero(credit_asset), DispatchError::Corruption))?
|
|
}
|
|
|
|
fn swap_tokens_for_exact_tokens(
|
|
path: Vec<Self::AssetKind>,
|
|
credit_in: Self::Credit,
|
|
amount_out: Self::Balance,
|
|
) -> Result<(Self::Credit, Self::Credit), (Self::Credit, DispatchError)> {
|
|
let credit_asset = credit_in.asset();
|
|
with_transaction(|| -> TransactionOutcome<Result<_, DispatchError>> {
|
|
let res = Self::do_swap_credit_tokens_for_exact_tokens(path, credit_in, amount_out);
|
|
match &res {
|
|
Ok(_) => TransactionOutcome::Commit(Ok(res)),
|
|
// wrapping `res` with `Ok`, since our `Err` doesn't satisfy the
|
|
// `From<DispatchError>` bound of the `with_transaction` function.
|
|
Err(_) => TransactionOutcome::Rollback(Ok(res)),
|
|
}
|
|
})
|
|
// should never map an error since `with_transaction` above never returns it.
|
|
.map_err(|_| (Self::Credit::zero(credit_asset), DispatchError::Corruption))?
|
|
}
|
|
}
|