dotcodeschool

dotcodeschool

Submit Feedback

Implementing the call to mint

In a similar fashion, we could implement the call to mint_liquidity. We still have some parts that we have not discussed yet, but don't worry, we'll be implementing those in later modules. Here's how we can implement the mint_liqudity call:

#[pallet::call_index(1)]
#[pallet::weight(Weight::default())]
pub fn mint_liquidity(
origin: OriginFor<T>,
asset_a: AssetIdOf<T>,
asset_b: AssetIdOf<T>,
amount_a: AssetBalanceOf<T>,
amount_b: AssetBalanceOf<T>,
min_liquidity: AssetBalanceOf<T>,
) -> DispatchResult {
let sender = ensure_signed(origin)?;
let trading_pair = (asset_a, asset_b);
// Get the liquidity pool from storage
let mut liquidity_pool =
LiquidityPools::<T>::get(&trading_pair).ok_or(Error::<T>::LiquidityPoolNotFound)?;
// Calculate the liquidity minted based on the provided amounts and the current reserves
let liquidity_minted = Self::calculate_liquidity_minted(
(amount_a, amount_b),
(liquidity_pool.reserves.0, liquidity_pool.reserves.1),
liquidity_pool.total_liquidity,
)?;
// Ensure that the liquidity minted is greater than or equal to the minimum liquidity specified
ensure!(
liquidity_minted >= min_liquidity,
Error::<T>::InsufficientLiquidityMinted
);
// Transfer the assets from the sender to the liquidity pool
Self::transfer_asset_to_pool(&sender, trading_pair.0, amount_a)?;
Self::transfer_asset_to_pool(&sender, trading_pair.1, amount_b)?;
// Mint liquidity tokens to the sender
Self::mint_liquidity_tokens(&sender, liquidity_pool.liquidity_token, liquidity_minted)?;
// Update the liquidity pool reserves and total liquidity using the `mint` method
liquidity_pool.mint((amount_a, amount_b), liquidity_minted)?;
// Update the liquidity pool in storage
LiquidityPools::<T>::insert(&trading_pair, liquidity_pool);
// Emit the LiquidityMinted event
Self::deposit_event(Event::LiquidityMinted(
sender,
trading_pair,
liquidity_minted,
));
Ok(())
}

We've abstracted away some functionality to helper methods. For example, the calculate_liquidity_minted function, which calculates the amount of liquidity tokens to be minted based on the provided asset amounts and the current state of the liquidity pool. When total liquidity is non-zero, liquidity_minted is calculated as (amount * total_liquidity) / reserve for each asset. If there's no existing liquidity it calculates the geometric mean:

impl<T: Config> Pallet<T> {
fn calculate_liquidity_minted(
amounts: (AssetBalanceOf<T>, AssetBalanceOf<T>),
reserves: (AssetBalanceOf<T>, AssetBalanceOf<T>),
total_liquidity: AssetBalanceOf<T>,
) -> Result<AssetBalanceOf<T>, DispatchError> {
let (amount_a, amount_b) = amounts;
let (reserve_a, reserve_b) = reserves;
ensure!(
!amount_a.is_zero() && !amount_b.is_zero(),
Error::<T>::InsufficientLiquidityMinted
);
if total_liquidity.is_zero() {
// If the liquidity pool is empty, the minted liquidity is the geometric mean of the amounts
let liquidity_minted = Self::geometric_mean(amount_a, amount_b)?;
Ok(liquidity_minted)
} else {
// If the liquidity pool is not empty, calculate the minted liquidity proportionally
let liquidity_minted_a = amount_a
.checked_mul(&total_liquidity)
.ok_or(Error::<T>::ArithmeticOverflow)?
.checked_div(&reserve_a)
.ok_or(Error::<T>::DivisionByZero)?;
let liquidity_minted_b = amount_b
.checked_mul(&total_liquidity)
.ok_or(Error::<T>::ArithmeticOverflow)?
.checked_div(&reserve_b)
.ok_or(Error::<T>::DivisionByZero)?;
// Choose the smaller minted liquidity to maintain the desired asset ratio
let liquidity_minted = sp_std::cmp::min(liquidity_minted_a, liquidity_minted_b);
Ok(liquidity_minted)
}
}
fn geometric_mean(
amount_a: AssetBalanceOf<T>,
amount_b: AssetBalanceOf<T>,
) -> Result<AssetBalanceOf<T>, DispatchError> {
let sqrt_product = (amount_a
.checked_mul(&amount_b)
.ok_or(Error::<T>::ArithmeticOverflow)?)
.integer_sqrt();
Ok(sqrt_product)
}
}

Can you implement the call for burning the liquidity burn_liquidity next?

No files edited in this step.

Implementing the call to mint

In a similar fashion, we could implement the call to mint_liquidity. We still have some parts that we have not discussed yet, but don't worry, we'll be implementing those in later modules. Here's how we can implement the mint_liqudity call:

#[pallet::call_index(1)]
#[pallet::weight(Weight::default())]
pub fn mint_liquidity(
origin: OriginFor<T>,
asset_a: AssetIdOf<T>,
asset_b: AssetIdOf<T>,
amount_a: AssetBalanceOf<T>,
amount_b: AssetBalanceOf<T>,
min_liquidity: AssetBalanceOf<T>,
) -> DispatchResult {
let sender = ensure_signed(origin)?;
let trading_pair = (asset_a, asset_b);
// Get the liquidity pool from storage
let mut liquidity_pool =
LiquidityPools::<T>::get(&trading_pair).ok_or(Error::<T>::LiquidityPoolNotFound)?;
// Calculate the liquidity minted based on the provided amounts and the current reserves
let liquidity_minted = Self::calculate_liquidity_minted(
(amount_a, amount_b),
(liquidity_pool.reserves.0, liquidity_pool.reserves.1),
liquidity_pool.total_liquidity,
)?;
// Ensure that the liquidity minted is greater than or equal to the minimum liquidity specified
ensure!(
liquidity_minted >= min_liquidity,
Error::<T>::InsufficientLiquidityMinted
);
// Transfer the assets from the sender to the liquidity pool
Self::transfer_asset_to_pool(&sender, trading_pair.0, amount_a)?;
Self::transfer_asset_to_pool(&sender, trading_pair.1, amount_b)?;
// Mint liquidity tokens to the sender
Self::mint_liquidity_tokens(&sender, liquidity_pool.liquidity_token, liquidity_minted)?;
// Update the liquidity pool reserves and total liquidity using the `mint` method
liquidity_pool.mint((amount_a, amount_b), liquidity_minted)?;
// Update the liquidity pool in storage
LiquidityPools::<T>::insert(&trading_pair, liquidity_pool);
// Emit the LiquidityMinted event
Self::deposit_event(Event::LiquidityMinted(
sender,
trading_pair,
liquidity_minted,
));
Ok(())
}

We've abstracted away some functionality to helper methods. For example, the calculate_liquidity_minted function, which calculates the amount of liquidity tokens to be minted based on the provided asset amounts and the current state of the liquidity pool. When total liquidity is non-zero, liquidity_minted is calculated as (amount * total_liquidity) / reserve for each asset. If there's no existing liquidity it calculates the geometric mean:

impl<T: Config> Pallet<T> {
fn calculate_liquidity_minted(
amounts: (AssetBalanceOf<T>, AssetBalanceOf<T>),
reserves: (AssetBalanceOf<T>, AssetBalanceOf<T>),
total_liquidity: AssetBalanceOf<T>,
) -> Result<AssetBalanceOf<T>, DispatchError> {
let (amount_a, amount_b) = amounts;
let (reserve_a, reserve_b) = reserves;
ensure!(
!amount_a.is_zero() && !amount_b.is_zero(),
Error::<T>::InsufficientLiquidityMinted
);
if total_liquidity.is_zero() {
// If the liquidity pool is empty, the minted liquidity is the geometric mean of the amounts
let liquidity_minted = Self::geometric_mean(amount_a, amount_b)?;
Ok(liquidity_minted)
} else {
// If the liquidity pool is not empty, calculate the minted liquidity proportionally
let liquidity_minted_a = amount_a
.checked_mul(&total_liquidity)
.ok_or(Error::<T>::ArithmeticOverflow)?
.checked_div(&reserve_a)
.ok_or(Error::<T>::DivisionByZero)?;
let liquidity_minted_b = amount_b
.checked_mul(&total_liquidity)
.ok_or(Error::<T>::ArithmeticOverflow)?
.checked_div(&reserve_b)
.ok_or(Error::<T>::DivisionByZero)?;
// Choose the smaller minted liquidity to maintain the desired asset ratio
let liquidity_minted = sp_std::cmp::min(liquidity_minted_a, liquidity_minted_b);
Ok(liquidity_minted)
}
}
fn geometric_mean(
amount_a: AssetBalanceOf<T>,
amount_b: AssetBalanceOf<T>,
) -> Result<AssetBalanceOf<T>, DispatchError> {
let sqrt_product = (amount_a
.checked_mul(&amount_b)
.ok_or(Error::<T>::ArithmeticOverflow)?)
.integer_sqrt();
Ok(sqrt_product)
}
}

Can you implement the call for burning the liquidity burn_liquidity next?

No files edited in this step.

Back

Next