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 storagelet 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 reserveslet 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 specifiedensure!(liquidity_minted >= min_liquidity,Error::<T>::InsufficientLiquidityMinted);// Transfer the assets from the sender to the liquidity poolSelf::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 senderSelf::mint_liquidity_tokens(&sender, liquidity_pool.liquidity_token, liquidity_minted)?;// Update the liquidity pool reserves and total liquidity using the `mint` methodliquidity_pool.mint((amount_a, amount_b), liquidity_minted)?;// Update the liquidity pool in storageLiquidityPools::<T>::insert(&trading_pair, liquidity_pool);// Emit the LiquidityMinted eventSelf::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 amountslet liquidity_minted = Self::geometric_mean(amount_a, amount_b)?;Ok(liquidity_minted)} else {// If the liquidity pool is not empty, calculate the minted liquidity proportionallylet 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 ratiolet 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.