Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Asset Conversion: Pool Touch Call #3251

Merged
merged 17 commits into from
Apr 17, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -154,4 +154,26 @@ impl<T: frame_system::Config> pallet_asset_conversion::WeightInfo for WeightInfo
.saturating_add(T::DbWeight::get().writes(4))
.saturating_add(Weight::from_parts(0, 393).saturating_mul(n.into()))
}
/// Storage: `AssetConversion::Pools` (r:1 w:0)
/// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`)
/// Storage: `Assets::Asset` (r:2 w:2)
/// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
/// Storage: `Assets::Account` (r:2 w:2)
/// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
/// Storage: `PoolAssets::Asset` (r:1 w:1)
/// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
/// Storage: `PoolAssets::Account` (r:1 w:1)
/// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
/// The range of component `n` is `[0, 3]`.
fn touch(n: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `1571`
// Estimated: `6360`
// Minimum execution time: 381_000_000 picoseconds.
Weight::from_parts(398_540_909, 6360)
// Standard Error: 1_330_283
.saturating_add(Weight::from_parts(209_463_636, 0).saturating_mul(n.into()))
.saturating_add(T::DbWeight::get().reads(7_u64))
.saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(n.into())))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -153,4 +153,26 @@ impl<T: frame_system::Config> pallet_asset_conversion::WeightInfo for WeightInfo
.saturating_add(T::DbWeight::get().writes(4))
.saturating_add(Weight::from_parts(0, 393).saturating_mul(n.into()))
}
/// Storage: `AssetConversion::Pools` (r:1 w:0)
/// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`)
/// Storage: `Assets::Asset` (r:2 w:2)
/// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
/// Storage: `Assets::Account` (r:2 w:2)
/// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
/// Storage: `PoolAssets::Asset` (r:1 w:1)
/// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
/// Storage: `PoolAssets::Account` (r:1 w:1)
/// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
/// The range of component `n` is `[0, 3]`.
fn touch(n: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `1571`
// Estimated: `6360`
// Minimum execution time: 381_000_000 picoseconds.
Weight::from_parts(398_540_909, 6360)
// Standard Error: 1_330_283
.saturating_add(Weight::from_parts(209_463_636, 0).saturating_mul(n.into()))
.saturating_add(T::DbWeight::get().reads(7_u64))
.saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(n.into())))
}
}
14 changes: 14 additions & 0 deletions prdoc/pr_3251.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json

title: "Asset Conversion: Pool Touch Call"

doc:
- audience: Runtime Dev
description: |
Introduce `touch` call designed to address operational prerequisites before providing liquidity to a pool.
This function ensures that essential requirements, such as the presence of the pool's accounts, are fulfilled.
It is particularly beneficial in scenarios where a pool creator removes the pool's accounts without providing liquidity.

crates:
- name: pallet-asset-conversion
67 changes: 59 additions & 8 deletions substrate/frame/asset-conversion/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use frame_support::{
assert_ok,
traits::{
fungible::NativeOrWithId,
fungibles::{Create, Inspect, Mutate},
fungibles::{Create, Inspect, Mutate, Refund},
},
};
use frame_system::RawOrigin as SystemOrigin;
Expand Down Expand Up @@ -75,12 +75,21 @@ where
}

/// Create the `asset` and mint the `amount` for the `caller`.
fn create_asset<T: Config>(caller: &T::AccountId, asset: &T::AssetKind, amount: T::Balance)
where
fn create_asset<T: Config>(
caller: &T::AccountId,
asset: &T::AssetKind,
amount: T::Balance,
is_sufficient: bool,
) where
T::Assets: Create<T::AccountId> + Mutate<T::AccountId>,
{
if !T::Assets::asset_exists(asset.clone()) {
assert_ok!(T::Assets::create(asset.clone(), caller.clone(), true, T::Balance::one()));
assert_ok!(T::Assets::create(
asset.clone(),
caller.clone(),
is_sufficient,
T::Balance::one()
));
}
assert_ok!(T::Assets::mint_into(
asset.clone(),
Expand Down Expand Up @@ -141,8 +150,8 @@ where
T::Assets::minimum_balance(asset1.clone()),
T::Assets::minimum_balance(asset2.clone()),
);
create_asset::<T>(caller, asset1, liquidity1);
create_asset::<T>(caller, asset2, liquidity2);
create_asset::<T>(caller, asset1, liquidity1, true);
create_asset::<T>(caller, asset2, liquidity2, true);
let lp_token = AssetConversion::<T>::get_next_pool_asset_id();

mint_setup_fee_asset::<T>(caller, asset1, asset2, &lp_token);
Expand Down Expand Up @@ -172,8 +181,8 @@ mod benchmarks {
fn create_pool() {
let caller: T::AccountId = whitelisted_caller();
let (asset1, asset2) = T::BenchmarkHelper::create_pair(0, 1);
create_asset::<T>(&caller, &asset1, T::Assets::minimum_balance(asset1.clone()));
create_asset::<T>(&caller, &asset2, T::Assets::minimum_balance(asset2.clone()));
create_asset::<T>(&caller, &asset1, T::Assets::minimum_balance(asset1.clone()), true);
create_asset::<T>(&caller, &asset2, T::Assets::minimum_balance(asset2.clone()), true);

let lp_token = AssetConversion::<T>::get_next_pool_asset_id();
create_fee_asset::<T>(&caller);
Expand Down Expand Up @@ -358,5 +367,47 @@ mod benchmarks {
assert_eq!(actual_balance, init_caller_balance + T::Balance::one());
}

#[benchmark]
fn touch(n: Linear<0, 3>) {
let caller: T::AccountId = whitelisted_caller();
let (asset1, asset2) = T::BenchmarkHelper::create_pair(0, 1);
let pool_id = T::PoolLocator::pool_id(&asset1, &asset2).unwrap();
let pool_account = T::PoolLocator::address(&pool_id).unwrap();

create_fee_asset::<T>(&caller);
create_asset::<T>(&caller, &asset1, <T as Config>::Balance::one(), false);
create_asset::<T>(&caller, &asset2, <T as Config>::Balance::one(), false);
let lp_token = AssetConversion::<T>::get_next_pool_asset_id();
mint_setup_fee_asset::<T>(&caller, &asset1, &asset2, &lp_token);

assert_ok!(AssetConversion::<T>::create_pool(
SystemOrigin::Signed(caller.clone()).into(),
Box::new(asset1.clone()),
Box::new(asset2.clone())
));

if n > 0 &&
<T as Config>::Assets::deposit_held(asset1.clone(), pool_account.clone()).is_some()
{
let _ = <T as Config>::Assets::refund(asset1.clone(), pool_account.clone());
}
if n > 1 &&
<T as Config>::Assets::deposit_held(asset2.clone(), pool_account.clone()).is_some()
{
let _ = <T as Config>::Assets::refund(asset2.clone(), pool_account.clone());
}
if n > 2 &&
<T as Config>::PoolAssets::deposit_held(lp_token.clone(), pool_account.clone())
.is_some()
{
let _ = <T as Config>::PoolAssets::refund(lp_token, pool_account);
}

#[extrinsic_call]
_(SystemOrigin::Signed(caller.clone()), Box::new(asset1.clone()), Box::new(asset2.clone()));

assert_last_event::<T>(Event::Touched { pool_id, who: caller }.into());
}

impl_benchmark_test_suite!(AssetConversion, crate::mock::new_test_ext(), crate::mock::Test);
}
65 changes: 61 additions & 4 deletions substrate/frame/asset-conversion/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,10 @@ use sp_std::{boxed::Box, collections::btree_set::BTreeSet, vec::Vec};
#[frame_support::pallet]
pub mod pallet {
use super::*;
use frame_support::pallet_prelude::*;
use frame_support::{
pallet_prelude::{DispatchResult, *},
traits::fungibles::Refund,
};
use frame_system::pallet_prelude::*;
use sp_arithmetic::{traits::Unsigned, Permill};

Expand Down Expand Up @@ -130,7 +133,8 @@ pub mod pallet {
type Assets: Inspect<Self::AccountId, AssetId = Self::AssetKind, Balance = Self::Balance>
+ Mutate<Self::AccountId>
+ AccountTouch<Self::AssetKind, Self::AccountId, Balance = Self::Balance>
+ Balanced<Self::AccountId>;
+ Balanced<Self::AccountId>
+ Refund<Self::AccountId, AssetId = Self::AssetKind>;

/// Liquidity pool identifier.
type PoolId: Parameter + MaxEncodedLen + Ord;
Expand All @@ -149,7 +153,8 @@ pub mod pallet {
type PoolAssets: Inspect<Self::AccountId, AssetId = Self::PoolAssetId, Balance = Self::Balance>
+ Create<Self::AccountId>
+ Mutate<Self::AccountId>
+ AccountTouch<Self::PoolAssetId, Self::AccountId, Balance = Self::Balance>;
+ AccountTouch<Self::PoolAssetId, Self::AccountId, Balance = Self::Balance>
+ Refund<Self::AccountId, AssetId = Self::PoolAssetId>;

/// A % the liquidity providers will take of every swap. Represents 10ths of a percent.
#[pallet::constant]
Expand Down Expand Up @@ -281,6 +286,13 @@ pub mod pallet {
/// E.g. (A, amount_in) -> (Dot, amount_out) -> (B, amount_out)
path: BalancePath<T>,
},
/// Pool has been touched in order to fulfill operational requirements.
Touched {
/// The ID of the pool.
pool_id: T::PoolId,
/// The account initiating the touch.
who: T::AccountId,
},
}

#[pallet::error]
Expand Down Expand Up @@ -391,7 +403,9 @@ pub mod pallet {
NextPoolAssetId::<T>::set(Some(next_lp_token_id));

T::PoolAssets::create(lp_token.clone(), pool_account.clone(), false, 1u32.into())?;
T::PoolAssets::touch(lp_token.clone(), &pool_account, &sender)?;
if T::PoolAssets::should_touch(lp_token.clone(), &pool_account) {
T::PoolAssets::touch(lp_token.clone(), &pool_account, &sender)?
};

let pool_info = PoolInfo { lp_token: lp_token.clone() };
Pools::<T>::insert(pool_id.clone(), pool_info);
Expand Down Expand Up @@ -656,6 +670,49 @@ pub mod pallet {
)?;
Ok(())
}

/// Touch an existing pool to fulfill prerequisites before providing liquidity, such as
/// ensuring that the pool's accounts are in place. It is typically useful when a pool
/// creator removes the pool's accounts and does not provide a liquidity. This action may
/// involve holding assets from the caller as a deposit for creating the pool's accounts.
///
/// The origin must be Signed.
///
/// - `asset1`: The asset ID of an existing pool with a pair (asset1, asset2).
/// - `asset2`: The asset ID of an existing pool with a pair (asset1, asset2).
///
/// Emits `Touched` event when successful.
#[pallet::call_index(5)]
#[pallet::weight(T::WeightInfo::touch(3))]
pub fn touch(
origin: OriginFor<T>,
asset1: Box<T::AssetKind>,
asset2: Box<T::AssetKind>,
) -> DispatchResultWithPostInfo {
let who = ensure_signed(origin)?;

let pool_id = T::PoolLocator::pool_id(&asset1, &asset2)
.map_err(|_| Error::<T>::InvalidAssetPair)?;
let pool = Pools::<T>::get(&pool_id).ok_or(Error::<T>::PoolNotFound)?;
let pool_account =
T::PoolLocator::address(&pool_id).map_err(|_| Error::<T>::InvalidAssetPair)?;

let mut refunds_number: u32 = 0;
if T::Assets::should_touch(*asset1.clone(), &pool_account) {
T::Assets::touch(*asset1, &pool_account, &who)?;
refunds_number += 1;
}
if T::Assets::should_touch(*asset2.clone(), &pool_account) {
T::Assets::touch(*asset2, &pool_account, &who)?;
refunds_number += 1;
}
if T::PoolAssets::should_touch(pool.lp_token.clone(), &pool_account) {
T::PoolAssets::touch(pool.lp_token, &pool_account, &who)?;
refunds_number += 1;
}
Self::deposit_event(Event::Touched { pool_id, who });
Ok(Some(T::WeightInfo::touch(refunds_number)).into())
}
}

impl<T: Config> Pallet<T> {
Expand Down
Loading
Loading