2025-11-30 21:27:04 -05:00
|
|
|
#![doc = include_str!("../README.md")]
|
2023-11-05 20:02:34 +03:00
|
|
|
#![deny(missing_docs)]
|
2025-11-30 21:27:04 -05:00
|
|
|
#![cfg_attr(not(any(feature = "std", test)), no_std)]
|
2023-11-05 20:02:34 +03:00
|
|
|
|
2025-11-30 21:27:04 -05:00
|
|
|
extern crate alloc;
|
2023-11-05 20:02:34 +03:00
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod mock;
|
|
|
|
|
|
2025-11-30 21:27:04 -05:00
|
|
|
#[expect(clippy::cast_possible_truncation)]
|
2023-11-05 20:02:34 +03:00
|
|
|
#[frame_support::pallet]
|
2025-11-30 21:27:04 -05:00
|
|
|
mod pallet {
|
|
|
|
|
use frame_system::pallet_prelude::*;
|
|
|
|
|
use frame_support::pallet_prelude::*;
|
|
|
|
|
|
|
|
|
|
use serai_abi::{
|
|
|
|
|
primitives::{
|
|
|
|
|
prelude::*,
|
|
|
|
|
dex::{Error as PrimitivesError, Reserves, Premise},
|
|
|
|
|
},
|
|
|
|
|
Event,
|
|
|
|
|
};
|
2023-11-12 14:37:31 +03:00
|
|
|
|
2025-11-30 21:27:04 -05:00
|
|
|
use serai_core_pallet::Pallet as Core;
|
|
|
|
|
type Coins<T> = serai_coins_pallet::Pallet<T, serai_coins_pallet::CoinsInstance>;
|
|
|
|
|
type LiquidityTokens<T> =
|
|
|
|
|
serai_coins_pallet::Pallet<T, serai_coins_pallet::LiquidityTokensInstance>;
|
2023-11-05 20:02:34 +03:00
|
|
|
|
2025-11-30 21:27:04 -05:00
|
|
|
use super::*;
|
2023-11-05 20:02:34 +03:00
|
|
|
|
2025-11-30 21:27:04 -05:00
|
|
|
/// The configuration of this pallet.
|
2023-11-05 20:02:34 +03:00
|
|
|
#[pallet::config]
|
2023-11-12 14:37:31 +03:00
|
|
|
pub trait Config:
|
2025-11-30 21:27:04 -05:00
|
|
|
frame_system::Config
|
|
|
|
|
+ serai_core_pallet::Config
|
|
|
|
|
+ serai_coins_pallet::Config<serai_coins_pallet::CoinsInstance>
|
|
|
|
|
+ serai_coins_pallet::Config<serai_coins_pallet::LiquidityTokensInstance>
|
2023-11-12 14:37:31 +03:00
|
|
|
{
|
2023-11-05 20:02:34 +03:00
|
|
|
}
|
|
|
|
|
|
2025-11-30 21:27:04 -05:00
|
|
|
/// An error incurred.
|
2023-11-05 20:02:34 +03:00
|
|
|
#[pallet::error]
|
|
|
|
|
pub enum Error<T> {
|
2025-11-30 21:27:04 -05:00
|
|
|
/// The effected/would-be-used liquidity is invalid.
|
|
|
|
|
InvalidLiquidity,
|
|
|
|
|
/// An arithmetic overflow occurred.
|
2023-11-05 20:02:34 +03:00
|
|
|
Overflow,
|
2025-11-30 21:27:04 -05:00
|
|
|
/// An arithmetic underflow occured.
|
|
|
|
|
Underflow,
|
|
|
|
|
/// The requested swap wasn't satisfied.
|
|
|
|
|
Unsatisfied,
|
|
|
|
|
/// The swap was from the coin it's to.
|
|
|
|
|
FromToSelf,
|
2023-11-05 20:02:34 +03:00
|
|
|
}
|
|
|
|
|
|
2025-11-30 21:27:04 -05:00
|
|
|
impl<T> From<PrimitivesError> for Error<T> {
|
|
|
|
|
fn from(error: PrimitivesError) -> Error<T> {
|
|
|
|
|
match error {
|
|
|
|
|
PrimitivesError::Overflow => Error::Overflow,
|
|
|
|
|
PrimitivesError::Underflow => Error::Underflow,
|
|
|
|
|
PrimitivesError::KInvariant => Error::Unsatisfied,
|
2023-12-05 16:52:50 +03:00
|
|
|
}
|
2023-11-05 20:02:34 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-30 21:27:04 -05:00
|
|
|
#[pallet::storage]
|
|
|
|
|
type LpFeeInThousandths<T: Config> = StorageMap<_, Identity, ExternalCoin, u8, OptionQuery>;
|
2023-11-05 20:02:34 +03:00
|
|
|
|
2025-11-30 21:27:04 -05:00
|
|
|
/// The Pallet struct.
|
|
|
|
|
#[pallet::pallet]
|
|
|
|
|
pub struct Pallet<T>(_);
|
2023-12-05 16:52:50 +03:00
|
|
|
|
2025-11-30 21:27:04 -05:00
|
|
|
impl<T: Config> Pallet<T> {
|
|
|
|
|
fn emit_event(event: Event) {
|
|
|
|
|
Core::<T>::emit_event(event)
|
2023-12-05 16:52:50 +03:00
|
|
|
}
|
2023-11-05 20:02:34 +03:00
|
|
|
}
|
|
|
|
|
|
2025-11-30 21:27:04 -05:00
|
|
|
const MINIMUM_LIQUIDITY: u64 = 1 << 16;
|
|
|
|
|
|
2023-11-05 20:02:34 +03:00
|
|
|
#[pallet::call]
|
|
|
|
|
impl<T: Config> Pallet<T> {
|
2025-11-30 21:27:04 -05:00
|
|
|
/// Add liquidity.
|
2023-11-05 20:02:34 +03:00
|
|
|
#[pallet::call_index(0)]
|
2025-11-30 21:27:04 -05:00
|
|
|
#[pallet::weight((0, DispatchClass::Normal))] // TODO
|
2023-11-05 20:02:34 +03:00
|
|
|
pub fn add_liquidity(
|
|
|
|
|
origin: OriginFor<T>,
|
2025-11-30 21:27:04 -05:00
|
|
|
external_coin: ExternalCoin,
|
|
|
|
|
sri_intended: Amount,
|
|
|
|
|
external_coin_intended: Amount,
|
|
|
|
|
sri_minimum: Amount,
|
|
|
|
|
external_coin_minimum: Amount,
|
2023-11-05 20:02:34 +03:00
|
|
|
) -> DispatchResult {
|
2025-11-30 21:27:04 -05:00
|
|
|
let from = ensure_signed(origin)?;
|
2023-11-05 20:02:34 +03:00
|
|
|
|
2025-11-30 21:27:04 -05:00
|
|
|
let pool = serai_abi::dex::address(external_coin);
|
|
|
|
|
let supply = LiquidityTokens::<T>::supply(Coin::from(external_coin)).0;
|
2023-11-05 20:02:34 +03:00
|
|
|
|
2025-11-30 21:27:04 -05:00
|
|
|
let (sri_actual, external_coin_actual, liquidity) = if supply == 0 {
|
|
|
|
|
let sri_actual = sri_intended;
|
|
|
|
|
let external_coin_actual = external_coin_intended;
|
|
|
|
|
let liquidity = Amount(
|
|
|
|
|
u64::try_from((u128::from(sri_actual.0) * u128::from(external_coin_actual.0)).isqrt())
|
|
|
|
|
.map_err(|_| Error::<T>::Overflow)?,
|
|
|
|
|
);
|
|
|
|
|
if liquidity.0 < MINIMUM_LIQUIDITY {
|
|
|
|
|
Err(Error::<T>::InvalidLiquidity)?;
|
2023-11-05 20:02:34 +03:00
|
|
|
}
|
2025-11-30 21:27:04 -05:00
|
|
|
(sri_intended, external_coin_intended, liquidity)
|
|
|
|
|
} else {
|
|
|
|
|
let reserves = Reserves {
|
|
|
|
|
sri: Coins::<T>::balance(pool, Coin::Serai),
|
|
|
|
|
external_coin: Coins::<T>::balance(pool, Coin::from(external_coin)),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let (sri_actual, external_coin_actual) = {
|
|
|
|
|
let (sri_optimal, external_coin_optimal) = (
|
|
|
|
|
Premise::establish(Coin::from(external_coin), Coin::Serai)
|
|
|
|
|
.expect("ext, sri satisfies sri ^ sri")
|
|
|
|
|
.quote_for_in(reserves, external_coin_intended)
|
|
|
|
|
.map_err(Error::<T>::from)?,
|
|
|
|
|
Premise::establish(Coin::Serai, Coin::from(external_coin))
|
|
|
|
|
.expect("sri, ext satisfies sri ^ sri")
|
|
|
|
|
.quote_for_in(reserves, sri_intended)
|
|
|
|
|
.map_err(Error::<T>::from)?,
|
|
|
|
|
);
|
|
|
|
|
if sri_optimal < sri_intended {
|
|
|
|
|
if sri_optimal < sri_minimum {
|
|
|
|
|
Err(Error::<T>::Unsatisfied)?;
|
|
|
|
|
}
|
|
|
|
|
(sri_optimal, external_coin_intended)
|
|
|
|
|
} else {
|
|
|
|
|
if external_coin_optimal < external_coin_minimum {
|
|
|
|
|
Err(Error::<T>::Unsatisfied)?;
|
|
|
|
|
}
|
|
|
|
|
(sri_intended, external_coin_optimal)
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let liquidity = {
|
|
|
|
|
let supply = u128::from(supply);
|
|
|
|
|
let sri_liquidity =
|
|
|
|
|
u64::try_from((u128::from(sri_actual.0) * supply) / u128::from(reserves.sri.0))
|
|
|
|
|
.map_err(|_| Error::<T>::Overflow)?;
|
|
|
|
|
let external_coin_liquidity = u64::try_from(
|
|
|
|
|
(u128::from(external_coin_actual.0) * supply) / u128::from(reserves.external_coin.0),
|
|
|
|
|
)
|
|
|
|
|
.map_err(|_| Error::<T>::Overflow)?;
|
|
|
|
|
Amount(sri_liquidity.min(external_coin_liquidity))
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
(sri_actual, external_coin_actual, liquidity)
|
|
|
|
|
};
|
2023-11-05 20:02:34 +03:00
|
|
|
|
2025-11-30 21:27:04 -05:00
|
|
|
Coins::<T>::transfer_fn(
|
|
|
|
|
from,
|
|
|
|
|
pool.into(),
|
|
|
|
|
Balance { coin: Coin::Serai, amount: sri_actual },
|
2023-11-12 14:37:31 +03:00
|
|
|
)?;
|
2025-11-30 21:27:04 -05:00
|
|
|
Coins::<T>::transfer_fn(
|
|
|
|
|
from,
|
|
|
|
|
pool.into(),
|
|
|
|
|
Balance { coin: Coin::from(external_coin), amount: external_coin_actual },
|
2024-10-07 05:16:11 +03:00
|
|
|
)?;
|
|
|
|
|
LiquidityTokens::<T>::mint(
|
2025-11-30 21:27:04 -05:00
|
|
|
from,
|
|
|
|
|
Balance { coin: Coin::from(external_coin), amount: liquidity },
|
2024-10-07 05:16:11 +03:00
|
|
|
)?;
|
2023-11-05 20:02:34 +03:00
|
|
|
|
2025-11-30 21:27:04 -05:00
|
|
|
// TODO: Event
|
2023-11-05 20:02:34 +03:00
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-30 21:27:04 -05:00
|
|
|
/// Transfer these liquidity tokens to the specified address.
|
2023-11-05 20:02:34 +03:00
|
|
|
#[pallet::call_index(1)]
|
2025-11-30 21:27:04 -05:00
|
|
|
#[pallet::weight((0, DispatchClass::Normal))] // TODO
|
|
|
|
|
pub fn transfer_liquidity(
|
2023-11-05 20:02:34 +03:00
|
|
|
origin: OriginFor<T>,
|
2025-11-30 21:27:04 -05:00
|
|
|
to: SeraiAddress,
|
|
|
|
|
liquidity_tokens: ExternalBalance,
|
2023-11-05 20:02:34 +03:00
|
|
|
) -> DispatchResult {
|
2025-11-30 21:27:04 -05:00
|
|
|
let from = ensure_signed(origin)?;
|
|
|
|
|
LiquidityTokens::<T>::transfer_fn(from, to.into(), liquidity_tokens.into())?;
|
2023-11-05 20:02:34 +03:00
|
|
|
|
2025-11-30 21:27:04 -05:00
|
|
|
// TODO: Event
|
2023-11-05 20:02:34 +03:00
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-30 21:27:04 -05:00
|
|
|
/// Remove liquidity.
|
2023-11-05 20:02:34 +03:00
|
|
|
#[pallet::call_index(2)]
|
2025-11-30 21:27:04 -05:00
|
|
|
#[pallet::weight((0, DispatchClass::Normal))] // TODO
|
|
|
|
|
pub fn remove_liquidity(
|
2023-11-05 20:02:34 +03:00
|
|
|
origin: OriginFor<T>,
|
2025-11-30 21:27:04 -05:00
|
|
|
liquidity_tokens: ExternalBalance,
|
|
|
|
|
sri_minimum: Amount,
|
|
|
|
|
external_coin_minimum: Amount,
|
2023-11-05 20:02:34 +03:00
|
|
|
) -> DispatchResult {
|
2025-11-30 21:27:04 -05:00
|
|
|
let from = ensure_signed(origin)?;
|
2023-11-05 20:02:34 +03:00
|
|
|
|
2025-11-30 21:27:04 -05:00
|
|
|
let external_coin = liquidity_tokens.coin;
|
|
|
|
|
let pool = serai_abi::dex::address(external_coin);
|
|
|
|
|
let supply = LiquidityTokens::<T>::supply(Coin::from(external_coin)).0;
|
|
|
|
|
if supply.saturating_sub(liquidity_tokens.amount.0) < MINIMUM_LIQUIDITY {
|
|
|
|
|
Err(Error::<T>::InvalidLiquidity)?;
|
2023-11-05 20:02:34 +03:00
|
|
|
}
|
2025-11-30 21:27:04 -05:00
|
|
|
let supply = u128::from(supply);
|
2023-11-05 20:02:34 +03:00
|
|
|
|
2025-11-30 21:27:04 -05:00
|
|
|
let reserves = Reserves {
|
|
|
|
|
sri: Coins::<T>::balance(pool, Coin::Serai),
|
|
|
|
|
external_coin: Coins::<T>::balance(pool, Coin::from(external_coin)),
|
|
|
|
|
};
|
|
|
|
|
let sri_amount =
|
|
|
|
|
(u128::from(liquidity_tokens.amount.0) * u128::from(reserves.sri.0)) / supply;
|
|
|
|
|
let sri_amount = Amount(u64::try_from(sri_amount).map_err(|_| Error::<T>::Overflow)?);
|
|
|
|
|
let external_coin_amount =
|
|
|
|
|
(u128::from(liquidity_tokens.amount.0) * u128::from(reserves.external_coin.0)) / supply;
|
|
|
|
|
let external_coin_amount =
|
|
|
|
|
Amount(u64::try_from(external_coin_amount).map_err(|_| Error::<T>::Overflow)?);
|
|
|
|
|
if (sri_amount < sri_minimum) || (external_coin_amount < external_coin_minimum) {
|
|
|
|
|
Err(Error::<T>::Unsatisfied)?;
|
2023-11-05 20:02:34 +03:00
|
|
|
}
|
|
|
|
|
|
2025-11-30 21:27:04 -05:00
|
|
|
LiquidityTokens::<T>::burn_fn(from, liquidity_tokens.into())?;
|
|
|
|
|
Coins::<T>::transfer_fn(
|
|
|
|
|
from,
|
|
|
|
|
pool.into(),
|
|
|
|
|
Balance { coin: Coin::Serai, amount: sri_amount },
|
|
|
|
|
)?;
|
|
|
|
|
Coins::<T>::transfer_fn(
|
|
|
|
|
from,
|
|
|
|
|
pool.into(),
|
|
|
|
|
Balance { coin: Coin::from(external_coin), amount: external_coin_amount },
|
|
|
|
|
)?;
|
2024-08-15 06:12:04 +03:00
|
|
|
|
2025-11-30 21:27:04 -05:00
|
|
|
// TODO: Event
|
2024-08-15 06:12:04 +03:00
|
|
|
|
2023-11-05 20:02:34 +03:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-30 21:27:04 -05:00
|
|
|
/// Swap an exact amount of coins.
|
|
|
|
|
#[pallet::call_index(3)]
|
|
|
|
|
#[pallet::weight((0, DispatchClass::Normal))] // TODO
|
|
|
|
|
pub fn swap(
|
|
|
|
|
origin: OriginFor<T>,
|
|
|
|
|
coins_to_swap: Balance,
|
|
|
|
|
minimum_to_receive: Balance,
|
|
|
|
|
) -> DispatchResult {
|
|
|
|
|
let origin = SeraiAddress::from(ensure_signed(origin)?);
|
|
|
|
|
|
|
|
|
|
let mut transfer_from = origin;
|
|
|
|
|
let mut next_amount = coins_to_swap.amount;
|
|
|
|
|
|
|
|
|
|
let swaps = Premise::route(coins_to_swap.coin, minimum_to_receive.coin)
|
|
|
|
|
.ok_or(Error::<T>::FromToSelf)?;
|
|
|
|
|
for swap in &swaps {
|
|
|
|
|
let external_coin = swap.external_coin();
|
|
|
|
|
let pool = serai_abi::dex::address(external_coin);
|
|
|
|
|
|
|
|
|
|
// Fetch the pool's reserves
|
|
|
|
|
let reserves = Reserves {
|
|
|
|
|
sri: Coins::<T>::balance(pool, Coin::Serai),
|
|
|
|
|
external_coin: Coins::<T>::balance(pool, Coin::from(external_coin)),
|
|
|
|
|
};
|
|
|
|
|
if (reserves.sri == Amount(0)) || (reserves.external_coin == Amount(0)) {
|
|
|
|
|
Err(Error::<T>::InvalidLiquidity)?;
|
2023-11-05 20:02:34 +03:00
|
|
|
}
|
|
|
|
|
|
2025-11-30 21:27:04 -05:00
|
|
|
// Transfer from the prior (the originating account or pool) to the current pool
|
|
|
|
|
/*
|
|
|
|
|
This is impossible to reach, yet would cause the amount _yet to be transferred out_ to
|
|
|
|
|
be credited both as part of the reserves _and_ the amount in if violated.
|
|
|
|
|
*/
|
|
|
|
|
assert!(transfer_from != pool, "swap routed from a coin to itself");
|
|
|
|
|
Coins::<T>::transfer_fn(
|
|
|
|
|
transfer_from.into(),
|
|
|
|
|
pool.into(),
|
|
|
|
|
Balance { coin: swap.r#in(), amount: next_amount },
|
|
|
|
|
)?;
|
2025-01-30 12:23:03 +03:00
|
|
|
|
2025-11-30 21:27:04 -05:00
|
|
|
// Update the current status
|
|
|
|
|
transfer_from = pool;
|
|
|
|
|
next_amount = swap.quote_for_in(reserves, next_amount).map_err(Error::<T>::from)?;
|
2023-11-05 20:02:34 +03:00
|
|
|
}
|
2025-01-30 12:23:03 +03:00
|
|
|
|
2025-11-30 21:27:04 -05:00
|
|
|
// Check the amount meets the expectation
|
|
|
|
|
if next_amount.0 < minimum_to_receive.amount.0 {
|
|
|
|
|
Err(Error::<T>::Unsatisfied)?;
|
2023-11-05 20:02:34 +03:00
|
|
|
}
|
2025-01-30 12:23:03 +03:00
|
|
|
|
2025-11-30 21:27:04 -05:00
|
|
|
// Transfer the resulting coins to the origin
|
|
|
|
|
Coins::<T>::transfer_fn(
|
|
|
|
|
transfer_from.into(),
|
|
|
|
|
origin.into(),
|
|
|
|
|
Balance { coin: minimum_to_receive.coin, amount: next_amount },
|
|
|
|
|
)?;
|
2023-11-12 14:37:31 +03:00
|
|
|
|
2025-11-30 21:27:04 -05:00
|
|
|
// TODO: Event
|
2023-11-05 20:02:34 +03:00
|
|
|
|
2025-11-30 21:27:04 -05:00
|
|
|
Ok(())
|
2023-11-05 20:02:34 +03:00
|
|
|
}
|
|
|
|
|
|
2025-11-30 21:27:04 -05:00
|
|
|
/// Swap for an exact amount of coins.
|
|
|
|
|
#[pallet::call_index(4)]
|
|
|
|
|
#[pallet::weight((0, DispatchClass::Normal))] // TODO
|
|
|
|
|
pub fn swap_for(
|
|
|
|
|
origin: OriginFor<T>,
|
|
|
|
|
coins_to_receive: Balance,
|
|
|
|
|
maximum_to_swap: Balance,
|
|
|
|
|
) -> DispatchResult {
|
|
|
|
|
let origin = SeraiAddress::from(ensure_signed(origin)?);
|
|
|
|
|
|
|
|
|
|
let mut transfer_to = origin;
|
|
|
|
|
let mut next_amount = coins_to_receive.amount;
|
|
|
|
|
|
|
|
|
|
let swaps = Premise::route(maximum_to_swap.coin, coins_to_receive.coin)
|
|
|
|
|
.ok_or(Error::<T>::FromToSelf)?;
|
|
|
|
|
let mut i = swaps.len();
|
|
|
|
|
while {
|
|
|
|
|
i -= 1;
|
|
|
|
|
let swap = swaps[i];
|
|
|
|
|
|
|
|
|
|
let external_coin = swap.external_coin();
|
|
|
|
|
let pool = serai_abi::dex::address(external_coin);
|
|
|
|
|
|
|
|
|
|
// Fetch the pool's reserves
|
|
|
|
|
let reserves = Reserves {
|
|
|
|
|
sri: Coins::<T>::balance(pool, Coin::Serai),
|
|
|
|
|
external_coin: Coins::<T>::balance(pool, Coin::from(external_coin)),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
Transfer the output requested.
|
|
|
|
|
|
|
|
|
|
While this violates the traditional Checks-Effects-Interactions pattern, in a way
|
|
|
|
|
favoring the caller, this function is within a `transactional` layer
|
|
|
|
|
(due to being a call) making all its DB operations only persisted upon success.
|
|
|
|
|
*/
|
|
|
|
|
/*
|
|
|
|
|
This is impossible to reach, yet ensures the amount not yet transferred in isn't
|
|
|
|
|
excluded when determining the reserves on the next iteration.
|
|
|
|
|
*/
|
|
|
|
|
assert!(transfer_to != pool, "swap routed to a coin from itself");
|
|
|
|
|
Coins::<T>::transfer_fn(
|
|
|
|
|
pool.into(),
|
|
|
|
|
transfer_to.into(),
|
|
|
|
|
Balance { coin: swap.out(), amount: next_amount },
|
|
|
|
|
)?;
|
2023-11-05 20:02:34 +03:00
|
|
|
|
2025-11-30 21:27:04 -05:00
|
|
|
transfer_to = pool;
|
|
|
|
|
next_amount = swap.quote_for_out(reserves, next_amount).map_err(Error::<T>::from)?;
|
2023-11-12 14:37:31 +03:00
|
|
|
|
2025-11-30 21:27:04 -05:00
|
|
|
i != 0
|
|
|
|
|
} {}
|
2023-11-05 20:02:34 +03:00
|
|
|
|
2025-11-30 21:27:04 -05:00
|
|
|
// Check the amount meets the expectation
|
|
|
|
|
if next_amount.0 > maximum_to_swap.amount.0 {
|
|
|
|
|
Err(Error::<T>::Unsatisfied)?;
|
2023-11-05 20:02:34 +03:00
|
|
|
}
|
|
|
|
|
|
2025-11-30 21:27:04 -05:00
|
|
|
// Transfer the necessary coins from the origin
|
|
|
|
|
Coins::<T>::transfer_fn(
|
|
|
|
|
origin.into(),
|
|
|
|
|
transfer_to.into(),
|
|
|
|
|
Balance { coin: maximum_to_swap.coin, amount: next_amount },
|
|
|
|
|
)?;
|
2023-11-05 20:02:34 +03:00
|
|
|
|
2025-11-30 21:27:04 -05:00
|
|
|
// TODO: Event
|
2023-11-05 20:02:34 +03:00
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-30 21:27:04 -05:00
|
|
|
pub use pallet::*;
|