Add event emissions to the DEX pallet

This commit is contained in:
Luke Parker
2025-12-02 13:31:33 -05:00
parent d711d8915f
commit af74c318aa
2 changed files with 76 additions and 55 deletions

View File

@@ -79,41 +79,43 @@ impl Call {
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)] #[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
pub enum Event { pub enum Event {
/// Liquidity was added to a pool. /// Liquidity was added to a pool.
LiquidityAdded { LiquidityAddition {
/// The account which added the liquidity. /// The account which received the minted liquidity tokens.
origin: SeraiAddress,
/// The account which received the liquidity tokens.
recipient: SeraiAddress, recipient: SeraiAddress,
/// The pool liquidity was added to. /// The liquidity tokens which were minted.
pool: ExternalCoin, liquidity_tokens: ExternalBalance,
/// The amount of liquidity tokens which were minted.
liquidity_tokens_minted: Amount,
/// The amount of the coin which was added to the pool's liquidity.
external_coin_amount: Amount,
/// The amount of SRI which was added to the pool's liquidity. /// The amount of SRI which was added to the pool's liquidity.
sri_amount: Amount, sri_amount: Amount,
/// The amount of the coin which was added to the pool's liquidity.
external_coin_amount: Amount,
},
/// The specified liquidity tokens were transferred.
LiquidityTransfer {
/// The address transferred from.
from: SeraiAddress,
/// The address transferred to.
to: SeraiAddress,
/// The liquidity tokens transferred.
liquidity_tokens: ExternalBalance,
}, },
/// Liquidity was removed from a pool. /// Liquidity was removed from a pool.
LiquidityRemoved { LiquidityRemoval {
/// The account which removed the liquidity. /// The account which removed the liquidity.
origin: SeraiAddress, from: SeraiAddress,
/// The pool liquidity was removed from. /// The liquidity tokens which were burnt.
pool: ExternalCoin, liquidity_tokens: ExternalBalance,
/// The mount of liquidity tokens which were burnt.
liquidity_tokens_burnt: Amount,
/// The amount of the coin which was removed from the pool's liquidity.
external_coin_amount: Amount,
/// The amount of SRI which was removed from the pool's liquidity. /// The amount of SRI which was removed from the pool's liquidity.
sri_amount: Amount, sri_amount: Amount,
/// The amount of the coin which was removed from the pool's liquidity.
external_coin_amount: Amount,
}, },
/// A swap through the liquidity pools occurred. /// A swap through the liquidity pools occurred.
Swap { Swap {
/// The account which made the swap. /// The account which made the swap.
origin: SeraiAddress, from: SeraiAddress,
/// The recipient for the output of the swap.
recipient: SeraiAddress,
/// The deltas incurred by the pools. /// The deltas incurred by the pools.
/// ///
/// For a swap of sriABC to sriDEF, this would be /// For a swap of sriABC to sriDEF, this would be

View File

@@ -10,6 +10,8 @@ mod mock;
#[expect(clippy::cast_possible_truncation)] #[expect(clippy::cast_possible_truncation)]
#[frame_support::pallet] #[frame_support::pallet]
mod pallet { mod pallet {
use alloc::vec::Vec;
use frame_system::pallet_prelude::*; use frame_system::pallet_prelude::*;
use frame_support::pallet_prelude::*; use frame_support::pallet_prelude::*;
@@ -18,7 +20,7 @@ mod pallet {
prelude::*, prelude::*,
dex::{Error as PrimitivesError, Reserves, Premise}, dex::{Error as PrimitivesError, Reserves, Premise},
}, },
Event, dex::Event,
}; };
use serai_core_pallet::Pallet as Core; use serai_core_pallet::Pallet as Core;
@@ -99,10 +101,20 @@ mod pallet {
let (sri_actual, external_coin_actual, liquidity) = if supply == 0 { let (sri_actual, external_coin_actual, liquidity) = if supply == 0 {
let sri_actual = sri_intended; let sri_actual = sri_intended;
let external_coin_actual = external_coin_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()) The best way to explain this is to first consider how would one would write shares of a
.map_err(|_| Error::<T>::Overflow)?, liquidity pool with only a single coin (however purposeless that may be). The immediate
); suggestion would simply be to use the amount of the singular coin initially added as the
initial amount of shares, with further shares being distributed pro-rata as further
liquidity is added. This inherently has the amount of liquidity tokens approximate the
magnitude and scale of the underlying coin.
When we scale the two-coin case, this methodology no longer immediately applies. The
solution here is to take the product, and then the square root, of the two values. This
provides a magnitude/scale of the liquidity tokens approximately in-between both coins.
*/
let liquidity = (u128::from(sri_actual.0) * u128::from(external_coin_actual.0)).isqrt();
let liquidity = Amount(u64::try_from(liquidity).map_err(|_| Error::<T>::Overflow)?);
if liquidity.0 < MINIMUM_LIQUIDITY { if liquidity.0 < MINIMUM_LIQUIDITY {
Err(Error::<T>::InvalidLiquidity)?; Err(Error::<T>::InvalidLiquidity)?;
} }
@@ -149,6 +161,10 @@ mod pallet {
Amount(sri_liquidity.min(external_coin_liquidity)) Amount(sri_liquidity.min(external_coin_liquidity))
}; };
if liquidity == Amount(0) {
Err(Error::<T>::Unsatisfied)?;
}
(sri_actual, external_coin_actual, liquidity) (sri_actual, external_coin_actual, liquidity)
}; };
@@ -162,12 +178,15 @@ mod pallet {
pool.into(), pool.into(),
Balance { coin: Coin::from(external_coin), amount: external_coin_actual }, Balance { coin: Coin::from(external_coin), amount: external_coin_actual },
)?; )?;
LiquidityTokens::<T>::mint( let liquidity_tokens = ExternalBalance { coin: external_coin, amount: liquidity };
from, LiquidityTokens::<T>::mint(from, liquidity_tokens.into())?;
Balance { coin: Coin::from(external_coin), amount: liquidity },
)?;
// TODO: Event Self::emit_event(Event::LiquidityAddition {
recipient: from.into(),
liquidity_tokens,
sri_amount: sri_actual,
external_coin_amount: external_coin_actual,
});
Ok(()) Ok(())
} }
@@ -183,7 +202,7 @@ mod pallet {
let from = ensure_signed(origin)?; let from = ensure_signed(origin)?;
LiquidityTokens::<T>::transfer_fn(from, to.into(), liquidity_tokens.into())?; LiquidityTokens::<T>::transfer_fn(from, to.into(), liquidity_tokens.into())?;
// TODO: Event Self::emit_event(Event::LiquidityTransfer { from: from.into(), to, liquidity_tokens });
Ok(()) Ok(())
} }
@@ -234,7 +253,12 @@ mod pallet {
Balance { coin: Coin::from(external_coin), amount: external_coin_amount }, Balance { coin: Coin::from(external_coin), amount: external_coin_amount },
)?; )?;
// TODO: Event Self::emit_event(Event::LiquidityRemoval {
from: from.into(),
liquidity_tokens,
sri_amount,
external_coin_amount,
});
Ok(()) Ok(())
} }
@@ -254,6 +278,7 @@ mod pallet {
let swaps = Premise::route(coins_to_swap.coin, minimum_to_receive.coin) let swaps = Premise::route(coins_to_swap.coin, minimum_to_receive.coin)
.ok_or(Error::<T>::FromToSelf)?; .ok_or(Error::<T>::FromToSelf)?;
let mut deltas = Vec::with_capacity(swaps.len() + 1);
for swap in &swaps { for swap in &swaps {
let external_coin = swap.external_coin(); let external_coin = swap.external_coin();
let pool = serai_abi::dex::address(external_coin); let pool = serai_abi::dex::address(external_coin);
@@ -273,11 +298,9 @@ mod pallet {
be credited both as part of the reserves _and_ the amount in if violated. 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"); assert!(transfer_from != pool, "swap routed from a coin to itself");
Coins::<T>::transfer_fn( let delta = Balance { coin: swap.r#in(), amount: next_amount };
transfer_from.into(), Coins::<T>::transfer_fn(transfer_from.into(), pool.into(), delta)?;
pool.into(), deltas.push(delta);
Balance { coin: swap.r#in(), amount: next_amount },
)?;
// Update the current status // Update the current status
transfer_from = pool; transfer_from = pool;
@@ -290,13 +313,11 @@ mod pallet {
} }
// Transfer the resulting coins to the origin // Transfer the resulting coins to the origin
Coins::<T>::transfer_fn( let delta = Balance { coin: minimum_to_receive.coin, amount: next_amount };
transfer_from.into(), Coins::<T>::transfer_fn(transfer_from.into(), origin.into(), delta)?;
origin.into(), deltas.push(delta);
Balance { coin: minimum_to_receive.coin, amount: next_amount },
)?;
// TODO: Event Self::emit_event(Event::Swap { from: origin, deltas });
Ok(()) Ok(())
} }
@@ -316,6 +337,7 @@ mod pallet {
let swaps = Premise::route(maximum_to_swap.coin, coins_to_receive.coin) let swaps = Premise::route(maximum_to_swap.coin, coins_to_receive.coin)
.ok_or(Error::<T>::FromToSelf)?; .ok_or(Error::<T>::FromToSelf)?;
let mut deltas = Vec::with_capacity(swaps.len() + 1);
let mut i = swaps.len(); let mut i = swaps.len();
while { while {
i -= 1; i -= 1;
@@ -342,11 +364,9 @@ mod pallet {
excluded when determining the reserves on the next iteration. excluded when determining the reserves on the next iteration.
*/ */
assert!(transfer_to != pool, "swap routed to a coin from itself"); assert!(transfer_to != pool, "swap routed to a coin from itself");
Coins::<T>::transfer_fn( let delta = Balance { coin: swap.out(), amount: next_amount };
pool.into(), Coins::<T>::transfer_fn(pool.into(), transfer_to.into(), delta)?;
transfer_to.into(), deltas.push(delta);
Balance { coin: swap.out(), amount: next_amount },
)?;
transfer_to = pool; transfer_to = pool;
next_amount = swap.quote_for_out(reserves, next_amount).map_err(Error::<T>::from)?; next_amount = swap.quote_for_out(reserves, next_amount).map_err(Error::<T>::from)?;
@@ -360,13 +380,12 @@ mod pallet {
} }
// Transfer the necessary coins from the origin // Transfer the necessary coins from the origin
Coins::<T>::transfer_fn( let delta = Balance { coin: maximum_to_swap.coin, amount: next_amount };
origin.into(), Coins::<T>::transfer_fn(origin.into(), transfer_to.into(), delta)?;
transfer_to.into(), deltas.push(delta);
Balance { coin: maximum_to_swap.coin, amount: next_amount },
)?;
// TODO: Event deltas.reverse();
Self::emit_event(Event::Swap { from: origin, deltas });
Ok(()) Ok(())
} }