diff --git a/pallets/thea-executor/src/lib.rs b/pallets/thea-executor/src/lib.rs index 5def81c12..0fd90c017 100644 --- a/pallets/thea-executor/src/lib.rs +++ b/pallets/thea-executor/src/lib.rs @@ -22,6 +22,8 @@ #![cfg_attr(not(feature = "std"), no_std)] +extern crate core; + use frame_support::pallet_prelude::Weight; pub use pallet::*; @@ -52,7 +54,7 @@ pub mod pallet { }; use frame_system::pallet_prelude::*; use pallet_asset_conversion::Swap; - use polkadex_primitives::Resolver; + use polkadex_primitives::{AssetId, Resolver}; use sp_runtime::{traits::AccountIdConversion, Saturating}; use sp_std::vec::Vec; use thea_primitives::{ @@ -67,7 +69,7 @@ pub mod pallet { /// Configure the pallet by specifying the parameters and types on which it depends. #[pallet::config] - pub trait Config: frame_system::Config { + pub trait Config: frame_system::Config + pallet_asset_conversion::Config { /// Because this pallet emits events, it depends on the Runtime's definition of an /// event. type RuntimeEvent: From> + IsType<::RuntimeEvent>; @@ -86,6 +88,14 @@ pub mod pallet { + MaxEncodedLen + Into<<::Assets as Inspect>::AssetId> + From; + type MultiAssetIdAdapter: From + + Into<::MultiAssetId>; + + type AssetBalanceAdapter: Into<::AssetBalance> + + Copy + + From<::AssetBalance> + + From + + Into; /// Asset Create/ Update Origin type AssetCreateUpdateOrigin: EnsureOrigin<::RuntimeOrigin>; /// Something that executes the payload @@ -104,6 +114,9 @@ pub mod pallet { /// Total Withdrawals #[pallet::constant] type WithdrawalSize: Get; + /// Existential Deposit + #[pallet::constant] + type ExistentialDeposit: Get; /// Para Id type ParaId: Get; /// Type representing the weight of this pallet @@ -227,6 +240,7 @@ pub mod pallet { impl Pallet { #[pallet::call_index(0)] #[pallet::weight(::WeightInfo::withdraw(1))] + #[transactional] pub fn withdraw( origin: OriginFor, asset_id: u128, @@ -371,6 +385,7 @@ pub mod pallet { T::TheaPalletId::get().into_account_truncating() } + #[transactional] pub fn do_withdraw( user: T::AccountId, asset_id: u128, @@ -382,10 +397,8 @@ pub mod pallet { ) -> Result<(), DispatchError> { ensure!(beneficiary.len() <= 1000, Error::::BeneficiaryTooLong); ensure!(network != 0, Error::::WrongNetwork); - let mut pending_withdrawals = >::get(network); let metadata = >::get(asset_id).ok_or(Error::::AssetNotRegistered)?; - ensure!( pending_withdrawals.len() < T::WithdrawalSize::get() as usize, Error::::WithdrawalNotAllowed @@ -410,7 +423,6 @@ pub mod pallet { polkadex_primitives::AssetId::Asset(asset_id), polkadex_primitives::AssetId::Polkadex ]; - let token_taken = T::Swap::swap_tokens_for_exact_tokens( user.clone(), path, @@ -419,8 +431,8 @@ pub mod pallet { Self::thea_account(), false, )?; - amount = amount.saturating_sub(token_taken.saturated_into()); + ensure!(amount > 0, Error::::AmountCannotBeZero); } else { // Pay the fees ::Currency::transfer( @@ -490,6 +502,7 @@ pub mod pallet { Ok(()) } + #[transactional] pub fn execute_deposit( deposit: Deposit, recipient: &T::AccountId, @@ -504,24 +517,22 @@ pub mod pallet { polkadex_primitives::AssetId::Asset(deposit.asset_id), polkadex_primitives::AssetId::Polkadex ]; - - Self::resolver_deposit( - deposit.asset_id.into(), - deposit_amount, - recipient, + let amount_out: T::AssetBalanceAdapter = T::ExistentialDeposit::get().into(); + Self::resolve_mint(&Self::thea_account(), deposit.asset_id.into(), deposit_amount)?; + let fee_amount = T::Swap::swap_tokens_for_exact_tokens( Self::thea_account(), - 1u128, - Self::thea_account(), - )?; - - T::Swap::swap_tokens_for_exact_tokens( - recipient.clone(), path, - sp_runtime::traits::One::one(), - None, + amount_out.into(), + Some(deposit_amount), recipient.clone(), false, )?; + Self::resolve_transfer( + deposit.asset_id.into(), + &Self::thea_account(), + recipient, + deposit_amount.saturating_sub(fee_amount), + )?; } else { Self::resolver_deposit( deposit.asset_id.into(), diff --git a/pallets/thea-executor/src/mock.rs b/pallets/thea-executor/src/mock.rs index f030a2395..626c42dfd 100644 --- a/pallets/thea-executor/src/mock.rs +++ b/pallets/thea-executor/src/mock.rs @@ -186,8 +186,10 @@ parameter_types! { pub const PolkadexAssetId: u128 = 0; pub const ParaId: u32 = 2040; } +use polkadex_primitives::AssetId; use sp_core::ConstU32; use sp_runtime::Permill; + impl thea_executor::Config for Test { type RuntimeEvent = RuntimeEvent; type Currency = Balances; @@ -201,6 +203,9 @@ impl thea_executor::Config for Test { type ParaId = ParaId; type WeightInfo = crate::weights::WeightInfo; type Swap = AssetConversion; + type MultiAssetIdAdapter = AssetId; + type AssetBalanceAdapter = u128; + type ExistentialDeposit = ExistentialDeposit; } impl frame_system::offchain::SendTransactionTypes for Test diff --git a/pallets/thea-executor/src/tests.rs b/pallets/thea-executor/src/tests.rs index bbabb4335..31aa00bd9 100644 --- a/pallets/thea-executor/src/tests.rs +++ b/pallets/thea-executor/src/tests.rs @@ -341,3 +341,142 @@ fn test_update_asset_metadata_full() { assert_last_event::(Event::::AssetMetadataSet(md).into()); }) } + +#[test] +fn test_resolve_deposit() { + new_test_ext().execute_with(|| { + let asset_id = 2000u128; + let admin = 1u64; + let recipient = 2u64; + Balances::set_balance(&admin, 1_000_000_000_000_000_000); + assert_ok!(Assets::create( + RuntimeOrigin::signed(admin), + parity_scale_codec::Compact(asset_id), + admin, + 1u128 + )); + assert_ok!(TheaExecutor::update_asset_metadata(RuntimeOrigin::root(), asset_id, 12)); + Balances::set_balance(&recipient, 1_000_000_000_000_000_000); + let deposit = Deposit { + id: Vec::new(), + recipient, + asset_id, + amount: 1_000_000_000_000_000_000u128, + extra: vec![], + }; + assert_ok!(TheaExecutor::execute_deposit(deposit, &recipient)); + }) +} + +#[test] +fn test_deposit_without_account() { + new_test_ext().execute_with(|| { + setup_pool(); + let asset_id = 1u128; + let admin = 1u64; + let recipient = 2u64; + Balances::set_balance(&admin, 1_000_000_000_000_000_000); + assert_ok!(TheaExecutor::update_asset_metadata(RuntimeOrigin::root(), asset_id, 12)); + Balances::set_balance(&TheaExecutor::thea_account(), 1_000_000_000_000_000_000); + let deposit = Deposit { + id: Vec::new(), + recipient, + asset_id, + amount: 1_000_000_000_000_000u128, + extra: vec![], + }; + assert_ok!(TheaExecutor::execute_deposit(deposit, &recipient)); + assert_eq!(Balances::free_balance(&recipient), 50); + assert_eq!(Assets::balance(asset_id, &recipient), 999_999_994_984_954u128); + assert_eq!(Assets::balance(asset_id, &TheaExecutor::thea_account()), 0u128); + assert_eq!( + Balances::free_balance(&TheaExecutor::thea_account()), + 1_000_000_000_000_000_000 + ); + }) +} + +#[test] +fn test_do_withdrawal() { + new_test_ext().execute_with(|| { + setup_pool(); + let sender = 2u64; + let asset_id = 1u128; + // Set asset balance + Balances::set_balance(&sender, 1_000_000_000_000_000_000); + let _ = Assets::mint_into(asset_id, &sender, 1_000_000_000_000_000_000); + // Set withdrawal Fee + assert_ok!(TheaExecutor::set_withdrawal_fee(RuntimeOrigin::root(), 1, 100)); + assert_ok!(TheaExecutor::update_asset_metadata(RuntimeOrigin::root(), asset_id, 12)); + assert_ok!(TheaExecutor::withdraw( + RuntimeOrigin::signed(sender), + asset_id, + 1_000_000_000_000_000u128, + vec![1; 32], + true, + 1, + true + )); + assert_eq!(Balances::free_balance(&sender), 1_000_000_000_000_000_000); + assert_eq!(Assets::balance(asset_id, &sender), 999_000_000_000_000_000); + assert_eq!(Balances::free_balance(&TheaExecutor::thea_account()), 1_000u128); + }) +} + +#[test] +fn test_do_withdrawal_with_total_amount_consumed_returns_error() { + new_test_ext().execute_with(|| { + setup_pool(); + let sender = 2u64; + let asset_id = 1u128; + // Set asset balance + let _ = Balances::set_balance(&sender, 1_000_000_000_000_000_000); + assert_ok!(Assets::mint_into(asset_id, &sender, 100_300_903u128)); + // Set withdrawal Fee + assert_ok!(TheaExecutor::set_withdrawal_fee(RuntimeOrigin::root(), 1, 100)); + assert_ok!(TheaExecutor::update_asset_metadata(RuntimeOrigin::root(), asset_id, 12)); + assert_noop!( + TheaExecutor::withdraw( + RuntimeOrigin::signed(sender), + asset_id, + 1_000_000_000_000_000u128, + vec![1; 32], + true, + 1, + true + ), + sp_runtime::TokenError::FundsUnavailable + ); + }) +} + +fn setup_pool() { + let asset_id = 1u128; + let admin = 1u64; + Balances::set_balance(&admin, 2_000_000_000_000_000_000_000_000_000_000u128); + assert_ok!(Assets::force_create( + RuntimeOrigin::root(), + parity_scale_codec::Compact(asset_id), + admin, + false, + 1u128 + )); + // Mint tokens + Assets::mint_into(asset_id, &admin, 1_000_000_000_000_000_000_000_000_000u128).unwrap(); + // Create pool + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(admin), + polkadex_primitives::AssetId::Asset(asset_id), + polkadex_primitives::AssetId::Polkadex + )); + assert_ok!(AssetConversion::add_liquidity( + RuntimeOrigin::signed(admin), + polkadex_primitives::AssetId::Asset(asset_id), + polkadex_primitives::AssetId::Polkadex, + 1_000_000_000_000_000_000_000u128, + 10_000_000_000_000_000u128, + 1u128, + 1u128, + admin + )); +} diff --git a/pallets/thea-message-handler/src/mock.rs b/pallets/thea-message-handler/src/mock.rs index 33f6469a6..6c15b2113 100644 --- a/pallets/thea-message-handler/src/mock.rs +++ b/pallets/thea-message-handler/src/mock.rs @@ -22,6 +22,7 @@ use frame_support::{ }; use frame_system as system; use frame_system::{EnsureRoot, EnsureSigned}; +use polkadex_primitives::AssetId; use sp_core::H256; use sp_runtime::{ traits::{BlakeTwo256, IdentityLookup}, @@ -201,6 +202,9 @@ impl thea_executor::Config for Test { type ParaId = ParaId; type Swap = AssetConversion; type WeightInfo = thea_executor::weights::WeightInfo; + type MultiAssetIdAdapter = AssetId; + type AssetBalanceAdapter = u128; + type ExistentialDeposit = ExistentialDeposit; } impl crate::Config for Test { diff --git a/pallets/thea/src/mock.rs b/pallets/thea/src/mock.rs index 1730f64dc..8f5ee901f 100644 --- a/pallets/thea/src/mock.rs +++ b/pallets/thea/src/mock.rs @@ -20,11 +20,13 @@ use crate::*; use frame_support::{parameter_types, traits::AsEnsureOriginWithArg, PalletId}; use frame_system as system; use frame_system::{EnsureRoot, EnsureSigned}; +use polkadex_primitives::AssetId; use sp_core::H256; use sp_runtime::{ traits::{BlakeTwo256, IdentityLookup}, BuildStorage, Permill, }; + type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; type Balance = u128; @@ -197,6 +199,9 @@ impl thea_executor::Config for Test { type ParaId = ParaId; type WeightInfo = thea_executor::weights::WeightInfo; type Swap = AssetConversion; + type MultiAssetIdAdapter = AssetId; + type AssetBalanceAdapter = u128; + type ExistentialDeposit = ExistentialDeposit; } impl frame_system::offchain::SendTransactionTypes for Test diff --git a/primitives/polkadex/src/assets.rs b/primitives/polkadex/src/assets.rs index 522efe1fe..8b2f3efbb 100644 --- a/primitives/polkadex/src/assets.rs +++ b/primitives/polkadex/src/assets.rs @@ -131,11 +131,24 @@ pub trait Resolver< from, to, amount.saturated_into(), - Preservation::Preserve, + Preservation::Expendable, )?; } Ok(()) } + + fn resolve_mint( + recipeint: &AccountId, + asset: AssetId, + amount: Balance, + ) -> Result<(), DispatchError> { + if asset == NativeAssetId::get() { + return Err(DispatchError::Other("Cannot mint Native Asset")) + } else { + Others::mint_into(asset.into(), recipeint, amount.saturated_into())?; + } + Ok(()) + } } /// Enumerated asset on chain @@ -158,6 +171,7 @@ pub enum AssetId { Asset(u128), Polkadex, } + use sp_runtime::traits::Zero; impl From for AssetId { fn from(value: u128) -> Self { diff --git a/runtimes/mainnet/src/lib.rs b/runtimes/mainnet/src/lib.rs index 0f23f2e46..cdb480b87 100644 --- a/runtimes/mainnet/src/lib.rs +++ b/runtimes/mainnet/src/lib.rs @@ -1364,6 +1364,9 @@ impl thea_executor::Config for Runtime { type ParaId = ParaId; type WeightInfo = thea_executor::weights::WeightInfo; type Swap = AssetConversion; + type MultiAssetIdAdapter = AssetId; + type AssetBalanceAdapter = u128; + type ExistentialDeposit = ExistentialDeposit; } #[cfg(feature = "runtime-benchmarks")]