From e7390f0b8ba48035e4db82378113a662f23982e1 Mon Sep 17 00:00:00 2001 From: wangjj9219 <183318287@qq.com> Date: Mon, 9 Sep 2024 21:12:04 +0800 Subject: [PATCH] extend DEX precompile for bootstrap --- modules/dex/src/lib.rs | 208 ++++++--- modules/support/src/dex.rs | 74 +++- runtime/common/src/precompile/dex.rs | 628 ++++++++++++++++++++++++++- 3 files changed, 846 insertions(+), 64 deletions(-) diff --git a/modules/dex/src/lib.rs b/modules/dex/src/lib.rs index 92d1d25672..90888a1406 100644 --- a/modules/dex/src/lib.rs +++ b/modules/dex/src/lib.rs @@ -35,7 +35,7 @@ use frame_support::{pallet_prelude::*, transactional, PalletId}; use frame_system::pallet_prelude::*; -use module_support::{DEXIncentives, DEXManager, Erc20InfoMapping, ExchangeRate, Ratio, SwapLimit}; +use module_support::{DEXBootstrap, DEXIncentives, DEXManager, Erc20InfoMapping, ExchangeRate, Ratio, SwapLimit}; use orml_traits::{Happened, MultiCurrency, MultiCurrencyExtended}; use parity_scale_codec::MaxEncodedLen; use primitives::{Balance, CurrencyId, TradingPair}; @@ -757,40 +757,9 @@ pub mod module { currency_id_b: CurrencyId, ) -> DispatchResult { let _ = ensure_signed(origin)?; - let trading_pair = - TradingPair::from_currency_ids(currency_id_a, currency_id_b).ok_or(Error::::InvalidCurrencyId)?; - ensure!( - matches!( - Self::trading_pair_statuses(trading_pair), - TradingPairStatus::<_, _>::Disabled - ), - Error::::MustBeDisabled - ); - - // Make sure the trading pair has not been successfully ended provisioning. - ensure!( - InitialShareExchangeRates::::get(trading_pair) == Default::default(), - Error::::NotAllowedRefund - ); - ProvisioningPool::::try_mutate_exists(trading_pair, &owner, |maybe_contribution| -> DispatchResult { - if let Some((contribution_0, contribution_1)) = maybe_contribution.take() { - T::Currency::transfer(trading_pair.first(), &Self::account_id(), &owner, contribution_0)?; - T::Currency::transfer(trading_pair.second(), &Self::account_id(), &owner, contribution_1)?; - - // decrease ref count - frame_system::Pallet::::dec_consumers(&owner); - - Self::deposit_event(Event::RefundProvision { - who: owner.clone(), - currency_0: trading_pair.first(), - contribution_0, - currency_1: trading_pair.second(), - contribution_1, - }); - } - Ok(()) - }) + Self::do_refund_provision(&owner, currency_id_a, currency_id_b)?; + Ok(()) } /// Abort provision when it's don't meet the target and expired. @@ -859,7 +828,11 @@ impl Pallet { }) } - fn do_claim_dex_share(who: &T::AccountId, currency_id_a: CurrencyId, currency_id_b: CurrencyId) -> DispatchResult { + fn do_claim_dex_share( + who: &T::AccountId, + currency_id_a: CurrencyId, + currency_id_b: CurrencyId, + ) -> Result { let trading_pair = TradingPair::from_currency_ids(currency_id_a, currency_id_b).ok_or(Error::::InvalidCurrencyId)?; ensure!( @@ -870,38 +843,82 @@ impl Pallet { Error::::StillProvisioning ); - ProvisioningPool::::try_mutate_exists(trading_pair, who, |maybe_contribution| -> DispatchResult { - if let Some((contribution_0, contribution_1)) = maybe_contribution.take() { - let (exchange_rate_0, exchange_rate_1) = Self::initial_share_exchange_rates(trading_pair); - let shares_from_provision_0 = exchange_rate_0 - .checked_mul_int(contribution_0) - .ok_or(ArithmeticError::Overflow)?; - let shares_from_provision_1 = exchange_rate_1 - .checked_mul_int(contribution_1) - .ok_or(ArithmeticError::Overflow)?; - let shares_to_claim = shares_from_provision_0 - .checked_add(shares_from_provision_1) - .ok_or(ArithmeticError::Overflow)?; - - T::Currency::transfer( - trading_pair.dex_share_currency_id(), - &Self::account_id(), - who, - shares_to_claim, - )?; + let claimed_share = ProvisioningPool::::try_mutate_exists( + trading_pair, + who, + |maybe_contribution| -> Result { + if let Some((contribution_0, contribution_1)) = maybe_contribution.take() { + let (exchange_rate_0, exchange_rate_1) = Self::initial_share_exchange_rates(trading_pair); + let shares_from_provision_0 = exchange_rate_0 + .checked_mul_int(contribution_0) + .ok_or(ArithmeticError::Overflow)?; + let shares_from_provision_1 = exchange_rate_1 + .checked_mul_int(contribution_1) + .ok_or(ArithmeticError::Overflow)?; + let shares_to_claim = shares_from_provision_0 + .checked_add(shares_from_provision_1) + .ok_or(ArithmeticError::Overflow)?; - // decrease ref count - frame_system::Pallet::::dec_consumers(who); - } - Ok(()) - })?; + T::Currency::transfer( + trading_pair.dex_share_currency_id(), + &Self::account_id(), + who, + shares_to_claim, + )?; + + // decrease ref count + frame_system::Pallet::::dec_consumers(who); + + Ok(shares_to_claim) + } else { + Ok(Default::default()) + } + }, + )?; // clear InitialShareExchangeRates once it is all claimed if ProvisioningPool::::iter_prefix(trading_pair).next().is_none() { InitialShareExchangeRates::::remove(trading_pair); } - Ok(()) + Ok(claimed_share) + } + + fn do_refund_provision(who: &T::AccountId, currency_id_a: CurrencyId, currency_id_b: CurrencyId) -> DispatchResult { + let trading_pair = + TradingPair::from_currency_ids(currency_id_a, currency_id_b).ok_or(Error::::InvalidCurrencyId)?; + ensure!( + matches!( + Self::trading_pair_statuses(trading_pair), + TradingPairStatus::<_, _>::Disabled + ), + Error::::MustBeDisabled + ); + + // Make sure the trading pair has not been successfully ended provisioning. + ensure!( + InitialShareExchangeRates::::get(trading_pair) == Default::default(), + Error::::NotAllowedRefund + ); + + ProvisioningPool::::try_mutate_exists(trading_pair, who, |maybe_contribution| -> DispatchResult { + if let Some((contribution_0, contribution_1)) = maybe_contribution.take() { + T::Currency::transfer(trading_pair.first(), &Self::account_id(), who, contribution_0)?; + T::Currency::transfer(trading_pair.second(), &Self::account_id(), who, contribution_1)?; + + // decrease ref count + frame_system::Pallet::::dec_consumers(who); + + Self::deposit_event(Event::RefundProvision { + who: who.clone(), + currency_0: trading_pair.first(), + contribution_0, + currency_1: trading_pair.second(), + contribution_1, + }); + } + Ok(()) + }) } fn do_add_provision( @@ -1544,3 +1561,74 @@ impl DEXManager for Pallet { ) } } + +impl DEXBootstrap for Pallet { + fn get_provision_pool(currency_id_a: CurrencyId, currency_id_b: CurrencyId) -> (Balance, Balance) { + if let Some(trading_pair) = TradingPair::from_currency_ids(currency_id_a, currency_id_b) { + if let TradingPairStatus::<_, _>::Provisioning(provision_parameters) = + Self::trading_pair_statuses(trading_pair) + { + let (total_provision_0, total_provision_1) = provision_parameters.accumulated_provision; + if currency_id_a == trading_pair.first() { + return (total_provision_0, total_provision_1); + } else { + return (total_provision_1, total_provision_0); + } + } + } + + (Zero::zero(), Zero::zero()) + } + + fn get_provision_pool_of( + who: &T::AccountId, + currency_id_a: CurrencyId, + currency_id_b: CurrencyId, + ) -> (Balance, Balance) { + if let Some(trading_pair) = TradingPair::from_currency_ids(currency_id_a, currency_id_b) { + let (provision_0, provision_1) = Self::provisioning_pool(trading_pair, who); + if currency_id_a == trading_pair.first() { + (provision_0, provision_1) + } else { + (provision_1, provision_0) + } + } else { + (Zero::zero(), Zero::zero()) + } + } + + fn get_initial_share_exchange_rate(currency_id_a: CurrencyId, currency_id_b: CurrencyId) -> (Balance, Balance) { + if let Some(trading_pair) = TradingPair::from_currency_ids(currency_id_a, currency_id_b) { + let (exchange_rate_0, exchange_rate_1) = Self::initial_share_exchange_rates(trading_pair); + if currency_id_a == trading_pair.first() { + (exchange_rate_0.into_inner(), exchange_rate_1.into_inner()) + } else { + (exchange_rate_1.into_inner(), exchange_rate_0.into_inner()) + } + } else { + (Zero::zero(), Zero::zero()) + } + } + + fn add_provision( + who: &T::AccountId, + currency_id_a: CurrencyId, + currency_id_b: CurrencyId, + contribution_a: Balance, + contribution_b: Balance, + ) -> DispatchResult { + Self::do_add_provision(who, currency_id_a, currency_id_b, contribution_a, contribution_b) + } + + fn claim_dex_share( + who: &T::AccountId, + currency_id_a: CurrencyId, + currency_id_b: CurrencyId, + ) -> Result { + Self::do_claim_dex_share(who, currency_id_a, currency_id_b) + } + + fn refund_provision(who: &T::AccountId, currency_id_a: CurrencyId, currency_id_b: CurrencyId) -> DispatchResult { + Self::do_refund_provision(who, currency_id_a, currency_id_b) + } +} diff --git a/modules/support/src/dex.rs b/modules/support/src/dex.rs index c07c6bac62..83f997373d 100644 --- a/modules/support/src/dex.rs +++ b/modules/support/src/dex.rs @@ -23,7 +23,7 @@ use scale_info::TypeInfo; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; use sp_core::H160; -use sp_runtime::{DispatchError, RuntimeDebug}; +use sp_runtime::{DispatchError, DispatchResult, RuntimeDebug}; use sp_std::{cmp::PartialEq, prelude::*, result::Result}; #[derive(RuntimeDebug, Encode, Decode, Clone, Copy, PartialEq, Eq, TypeInfo)] @@ -82,6 +82,34 @@ pub trait DEXManager { ) -> Result<(Balance, Balance), DispatchError>; } +pub trait DEXBootstrap: DEXManager { + fn get_provision_pool(currency_id_a: CurrencyId, currency_id_b: CurrencyId) -> (Balance, Balance); + + fn get_provision_pool_of( + who: &AccountId, + currency_id_a: CurrencyId, + currency_id_b: CurrencyId, + ) -> (Balance, Balance); + + fn get_initial_share_exchange_rate(currency_id_a: CurrencyId, currency_id_b: CurrencyId) -> (Balance, Balance); + + fn add_provision( + who: &AccountId, + currency_id_a: CurrencyId, + currency_id_b: CurrencyId, + contribution_a: Balance, + contribution_b: Balance, + ) -> DispatchResult; + + fn claim_dex_share( + who: &AccountId, + currency_id_a: CurrencyId, + currency_id_b: CurrencyId, + ) -> Result; + + fn refund_provision(who: &AccountId, currency_id_a: CurrencyId, currency_id_b: CurrencyId) -> DispatchResult; +} + pub trait Swap where CurrencyId: Clone, @@ -250,3 +278,47 @@ where Ok(Default::default()) } } + +#[cfg(feature = "std")] +impl DEXBootstrap for () +where + Balance: Default, +{ + fn get_provision_pool(_currency_id_a: CurrencyId, _currency_id_b: CurrencyId) -> (Balance, Balance) { + Default::default() + } + + fn get_provision_pool_of( + _who: &AccountId, + _currency_id_a: CurrencyId, + _currency_id_b: CurrencyId, + ) -> (Balance, Balance) { + Default::default() + } + + fn get_initial_share_exchange_rate(_currency_id_a: CurrencyId, _currency_id_b: CurrencyId) -> (Balance, Balance) { + Default::default() + } + + fn add_provision( + _who: &AccountId, + _currency_id_a: CurrencyId, + _currency_id_b: CurrencyId, + _contribution_a: Balance, + _contribution_b: Balance, + ) -> DispatchResult { + Ok(()) + } + + fn claim_dex_share( + _who: &AccountId, + _currency_id_a: CurrencyId, + _currency_id_b: CurrencyId, + ) -> Result { + Ok(Default::default()) + } + + fn refund_provision(_who: &AccountId, _currency_id_a: CurrencyId, _currency_id_b: CurrencyId) -> DispatchResult { + Ok(()) + } +} diff --git a/runtime/common/src/precompile/dex.rs b/runtime/common/src/precompile/dex.rs index 2c29989c4e..29fe761e0e 100644 --- a/runtime/common/src/precompile/dex.rs +++ b/runtime/common/src/precompile/dex.rs @@ -24,7 +24,7 @@ use module_evm::{ precompiles::Precompile, ExitRevert, ExitSucceed, PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult, }; -use module_support::{DEXManager, SwapLimit}; +use module_support::{DEXBootstrap, DEXManager, SwapLimit}; use num_enum::{IntoPrimitive, TryFromPrimitive}; use primitives::{Balance, CurrencyId}; use sp_runtime::{traits::Convert, RuntimeDebug}; @@ -53,12 +53,19 @@ pub enum Action { SwapWithExactTarget = "swapWithExactTarget(address,address[],uint256,uint256)", AddLiquidity = "addLiquidity(address,address,address,uint256,uint256,uint256)", RemoveLiquidity = "removeLiquidity(address,address,address,uint256,uint256,uint256)", + GetProvisionPool = "getProvisionPool(address,address)", + GetProvisionPoolOf = "getProvisionPoolOf(address,address,address)", + GetInitialShareExchangeRate = "getInitialShareExchangeRate(address,address)", + AddProvision = "addProvision(address,address,address,uint256,uint256)", + ClaimDexShare = "claimDexShare(address,address,address)", + RefundProvision = "refundProvision(address,address,address)", } impl Precompile for DEXPrecompile where Runtime: module_evm::Config + module_dex::Config + module_prices::Config, - module_dex::Pallet: DEXManager, + module_dex::Pallet: + DEXManager + DEXBootstrap, { fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { let gas_cost = Pricer::::cost(handle)?; @@ -281,6 +288,151 @@ where output: Output::encode_error_msg("DEX RemoveLiquidity failed", e), })?; + Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output: vec![], + }) + } + Action::GetProvisionPool => { + let currency_id_a = input.currency_id_at(1)?; + let currency_id_b = input.currency_id_at(2)?; + log::debug!( + target: "evm", + "dex: get_provision_pool currency_id_a: {:?}, currency_id_b: {:?}", + currency_id_a, currency_id_b + ); + + let (balance_a, balance_b) = as DEXBootstrap< + Runtime::AccountId, + Balance, + CurrencyId, + >>::get_provision_pool(currency_id_a, currency_id_b); + + Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output: Output::encode_uint_tuple(vec![balance_a, balance_b]), + }) + } + Action::GetProvisionPoolOf => { + let who = input.account_id_at(1)?; + let currency_id_a = input.currency_id_at(2)?; + let currency_id_b = input.currency_id_at(3)?; + log::debug!( + target: "evm", + "dex: get_provision_pool_of who: {:?}, currency_id_a: {:?}, currency_id_b: {:?}", + who, currency_id_a, currency_id_b + ); + + let (balance_a, balance_b) = as DEXBootstrap< + Runtime::AccountId, + Balance, + CurrencyId, + >>::get_provision_pool_of(&who, currency_id_a, currency_id_b); + + Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output: Output::encode_uint_tuple(vec![balance_a, balance_b]), + }) + } + Action::GetInitialShareExchangeRate => { + let currency_id_a = input.currency_id_at(1)?; + let currency_id_b = input.currency_id_at(2)?; + log::debug!( + target: "evm", + "dex: get_provision_pool currency_id_a: {:?}, currency_id_b: {:?}", + currency_id_a, currency_id_b + ); + + let (exchange_rate_a, exchange_rate_b) = as DEXBootstrap< + Runtime::AccountId, + Balance, + CurrencyId, + >>::get_initial_share_exchange_rate( + currency_id_a, currency_id_b + ); + + Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output: Output::encode_uint_tuple(vec![exchange_rate_a, exchange_rate_b]), + }) + } + Action::AddProvision => { + let who = input.account_id_at(1)?; + let currency_id_a = input.currency_id_at(2)?; + let currency_id_b = input.currency_id_at(3)?; + let contribution_a = input.balance_at(4)?; + let contribution_b = input.balance_at(5)?; + + log::debug!( + target: "evm", + "dex: add_provision who: {:?}, currency_id_a: {:?}, currency_id_b: {:?}, contribution_a: {:?}, contribution_b: {:?}", + who, currency_id_a, currency_id_b, contribution_a, contribution_b, + ); + + as DEXBootstrap>::add_provision( + &who, + currency_id_a, + currency_id_b, + contribution_a, + contribution_b, + ) + .map_err(|e| PrecompileFailure::Revert { + exit_status: ExitRevert::Reverted, + output: Output::encode_error_msg("DEX AddProvision failed", e), + })?; + + Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output: vec![], + }) + } + Action::ClaimDexShare => { + let who = input.account_id_at(1)?; + let currency_id_a = input.currency_id_at(2)?; + let currency_id_b = input.currency_id_at(3)?; + + log::debug!( + target: "evm", + "dex: claim_dex_share who: {:?}, currency_id_a: {:?}, currency_id_b: {:?}", + who, currency_id_a, currency_id_b, + ); + + let claimed_share = as DEXBootstrap< + Runtime::AccountId, + Balance, + CurrencyId, + >>::claim_dex_share(&who, currency_id_a, currency_id_b) + .map_err(|e| PrecompileFailure::Revert { + exit_status: ExitRevert::Reverted, + output: Output::encode_error_msg("DEX ClaimDexShare failed", e), + })?; + + Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output: Output::encode_uint(claimed_share), + }) + } + Action::RefundProvision => { + let who = input.account_id_at(1)?; + let currency_id_a = input.currency_id_at(2)?; + let currency_id_b = input.currency_id_at(3)?; + + log::debug!( + target: "evm", + "dex: refund_provision who: {:?}, currency_id_a: {:?}, currency_id_b: {:?}", + who, currency_id_a, currency_id_b, + ); + + as DEXBootstrap>::refund_provision( + &who, + currency_id_a, + currency_id_b, + ) + .map_err(|e| PrecompileFailure::Revert { + exit_status: ExitRevert::Reverted, + output: Output::encode_error_msg("DEX RefundProvision failed", e), + })?; + Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, output: vec![], @@ -435,6 +587,98 @@ where let weight = ::WeightInfo::remove_liquidity(); + Self::BASE_COST + .saturating_add(read_account) + .saturating_add(read_currency_a) + .saturating_add(read_currency_b) + .saturating_add(WeightToGas::convert(weight)) + } + Action::GetProvisionPool => { + let currency_id_a = input.currency_id_at(1)?; + let currency_id_b = input.currency_id_at(2)?; + let read_currency_a = InputPricer::::read_currency(currency_id_a); + let read_currency_b = InputPricer::::read_currency(currency_id_b); + + // DEX::TradingPairStatuses (r: 1) + let weight = ::DbWeight::get().reads(1); + + Self::BASE_COST + .saturating_add(read_currency_a) + .saturating_add(read_currency_b) + .saturating_add(WeightToGas::convert(weight)) + } + Action::GetProvisionPoolOf => { + let read_account = InputPricer::::read_accounts(1); + let currency_id_a = input.currency_id_at(2)?; + let currency_id_b = input.currency_id_at(3)?; + let read_currency_a = InputPricer::::read_currency(currency_id_a); + let read_currency_b = InputPricer::::read_currency(currency_id_b); + + // DEX::ProvisioningPool (r: 1) + let weight = ::DbWeight::get().reads(1); + + Self::BASE_COST + .saturating_add(read_account) + .saturating_add(read_currency_a) + .saturating_add(read_currency_b) + .saturating_add(WeightToGas::convert(weight)) + } + Action::GetInitialShareExchangeRate => { + let currency_id_a = input.currency_id_at(1)?; + let currency_id_b = input.currency_id_at(2)?; + let read_currency_a = InputPricer::::read_currency(currency_id_a); + let read_currency_b = InputPricer::::read_currency(currency_id_b); + + // DEX::InitialShareExchangeRates (r: 1) + let weight = ::DbWeight::get().reads(1); + + Self::BASE_COST + .saturating_add(read_currency_a) + .saturating_add(read_currency_b) + .saturating_add(WeightToGas::convert(weight)) + } + Action::AddProvision => { + let read_account = InputPricer::::read_accounts(1); + let currency_id_a = input.currency_id_at(2)?; + let currency_id_b = input.currency_id_at(3)?; + + let read_currency_a = InputPricer::::read_currency(currency_id_a); + let read_currency_b = InputPricer::::read_currency(currency_id_b); + + let weight = ::WeightInfo::add_provision(); + + Self::BASE_COST + .saturating_add(read_account) + .saturating_add(read_currency_a) + .saturating_add(read_currency_b) + .saturating_add(WeightToGas::convert(weight)) + } + Action::ClaimDexShare => { + let read_account = InputPricer::::read_accounts(1); + let currency_id_a = input.currency_id_at(2)?; + let currency_id_b = input.currency_id_at(3)?; + + let read_currency_a = InputPricer::::read_currency(currency_id_a); + let read_currency_b = InputPricer::::read_currency(currency_id_b); + + let weight = ::WeightInfo::claim_dex_share(); + + Self::BASE_COST + .saturating_add(read_account) + .saturating_add(read_currency_a) + .saturating_add(read_currency_b) + .saturating_add(WeightToGas::convert(weight)) + } + Action::RefundProvision => { + let read_account = InputPricer::::read_accounts(1); + let currency_id_a = input.currency_id_at(2)?; + let currency_id_b = input.currency_id_at(3)?; + + let read_currency_a = InputPricer::::read_currency(currency_id_a); + let read_currency_b = InputPricer::::read_currency(currency_id_b); + + let weight = ::WeightInfo::refund_provision(); + Self::BASE_COST .saturating_add(read_account) .saturating_add(read_currency_a) @@ -450,7 +694,9 @@ where mod tests { use super::*; - use crate::precompile::mock::{alice_evm_addr, new_test_ext, DexModule, RuntimeOrigin, Test, ALICE, AUSD, DOT}; + use crate::precompile::mock::{ + alice, alice_evm_addr, new_test_ext, run_to_block, Currencies, DexModule, RuntimeOrigin, Test, ALICE, AUSD, DOT, + }; use frame_support::{assert_noop, assert_ok}; use hex_literal::hex; use module_evm::{precompiles::tests::MockPrecompileHandle, Context, ExitRevert}; @@ -759,4 +1005,380 @@ mod tests { assert_eq!(resp.output, expected_output.to_vec()); }); } + + #[test] + fn get_provision_pool_works() { + new_test_ext().execute_with(|| { + let context = Context { + address: Default::default(), + caller: alice_evm_addr(), + apparent_value: Default::default(), + }; + + // getProvisionPool(address,address) -> 0x5859df34 + // DOT + // AUSD + let input = hex! {" + 5859df34 + 000000000000000000000000 0000000000000000000100000000000000000002 + 000000000000000000000000 0000000000000000000100000000000000000001 + "}; + + // 0 + // 0 + let expected_output = hex! {" + 00000000000000000000000000000000 00000000000000000000000000000000 + 00000000000000000000000000000000 00000000000000000000000000000000 + "}; + + let resp = DEXPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); + assert_eq!(resp.exit_status, ExitSucceed::Returned); + assert_eq!(resp.output, expected_output.to_vec()); + + // list provision DOT/AUSD + assert_ok!(DexModule::list_provisioning( + RuntimeOrigin::signed(ALICE), + DOT, + AUSD, + 10, + 10, + 10_000, + 10_000, + 100_000 + )); + + assert_ok!(DexModule::add_provision( + RuntimeOrigin::signed(ALICE), + DOT, + AUSD, + 1_000, + 1_000_000, + )); + + // 1_000 + // 1_000_000 + let expected_output = hex! {" + 00000000000000000000000000000000 000000000000000000000000000003e8 + 00000000000000000000000000000000 000000000000000000000000000f4240 + "}; + + let resp = DEXPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); + assert_eq!(resp.exit_status, ExitSucceed::Returned); + assert_eq!(resp.output, expected_output.to_vec()); + }); + } + + #[test] + fn get_provision_pool_of_works() { + new_test_ext().execute_with(|| { + // list provision DOT/AUSD + assert_ok!(DexModule::list_provisioning( + RuntimeOrigin::signed(ALICE), + DOT, + AUSD, + 10, + 10, + 10_000, + 10_000, + 100_000 + )); + + assert_ok!(Currencies::update_balance( + RuntimeOrigin::root(), + alice(), + DOT, + 1_000_000_000 + )); + assert_ok!(Currencies::update_balance( + RuntimeOrigin::root(), + alice(), + AUSD, + 1_000_000_000 + )); + assert_ok!(DexModule::add_provision( + RuntimeOrigin::signed(alice()), + DOT, + AUSD, + 1_000, + 1_000_000, + )); + + assert_eq!( + DexModule::get_provision_pool_of(&crate::precompile::mock::alice(), DOT, AUSD), + (1_000, 1_000_000) + ); + + let context = Context { + address: Default::default(), + caller: alice_evm_addr(), + apparent_value: Default::default(), + }; + + // getProvisionPoolOf(address,address,address) -> 0x8ef239cf + // alice + // DOT + // AUSD + let input = hex! {" + 8ef239cf + 000000000000000000000000 1000000000000000000000000000000000000001 + 000000000000000000000000 0000000000000000000100000000000000000002 + 000000000000000000000000 0000000000000000000100000000000000000001 + "}; + + // 1_000 + // 1_000_000 + let expected_output = hex! {" + 00000000000000000000000000000000 000000000000000000000000000003e8 + 00000000000000000000000000000000 000000000000000000000000000f4240 + "}; + + let resp = DEXPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); + assert_eq!(resp.exit_status, ExitSucceed::Returned); + assert_eq!(resp.output, expected_output.to_vec()); + }); + } + + #[test] + fn get_initial_share_exchange_rate_works() { + new_test_ext().execute_with(|| { + // list provision DOT/AUSD + assert_ok!(DexModule::list_provisioning( + RuntimeOrigin::signed(ALICE), + DOT, + AUSD, + 10, + 10, + 10_000, + 10_000, + 100_000 + )); + + assert_ok!(DexModule::add_provision( + RuntimeOrigin::signed(ALICE), + DOT, + AUSD, + 1_000_000, + 4_000_000, + )); + + run_to_block(100_001); + assert_ok!(DexModule::end_provisioning(RuntimeOrigin::signed(ALICE), DOT, AUSD)); + + let context = Context { + address: Default::default(), + caller: alice_evm_addr(), + apparent_value: Default::default(), + }; + + // getInitialShareExchangeRate(address,address) -> 0x165c7c9a + // DOT + // AUSD + let input = hex! {" + 165c7c9a + 000000000000000000000000 0000000000000000000100000000000000000002 + 000000000000000000000000 0000000000000000000100000000000000000001 + "}; + + // 4_000_000_000_000_000_000 + // 1_000_000_000_000_000_000 + let expected_output = hex! {" + 00000000000000000000000000000000 00000000000000003782dace9d900000 + 00000000000000000000000000000000 00000000000000000de0b6b3a7640000 + "}; + + let resp = DEXPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); + assert_eq!(resp.exit_status, ExitSucceed::Returned); + assert_eq!(resp.output, expected_output.to_vec()); + + // let hex_string: String = resp.output.iter().map(|byte| format!("{:02x}", + // byte)).collect(); assert_eq!(hex_string, ""); + }); + } + + #[test] + fn add_provision_works() { + new_test_ext().execute_with(|| { + // list provision DOT/AUSD + assert_ok!(DexModule::list_provisioning( + RuntimeOrigin::signed(ALICE), + DOT, + AUSD, + 10, + 10, + 10_000, + 10_000, + 100_000 + )); + + assert_ok!(Currencies::update_balance( + RuntimeOrigin::root(), + alice(), + DOT, + 1_000_000_000 + )); + assert_ok!(Currencies::update_balance( + RuntimeOrigin::root(), + alice(), + AUSD, + 1_000_000_000 + )); + + let context = Context { + address: Default::default(), + caller: alice_evm_addr(), + apparent_value: Default::default(), + }; + + // addProvision(address,address,address,uint256,uint256) -> 0x97a20516 + // alice + // DOT + // AUSD + // 1_000 + // 1_000_000 + let input = hex! {" + 97a20516 + 000000000000000000000000 1000000000000000000000000000000000000001 + 000000000000000000000000 0000000000000000000100000000000000000002 + 000000000000000000000000 0000000000000000000100000000000000000001 + 00000000000000000000000000000000 000000000000000000000000000003e8 + 00000000000000000000000000000000 000000000000000000000000000f4240 + "}; + + let resp = DEXPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); + assert_eq!(resp.exit_status, ExitSucceed::Returned); + + assert_eq!( + DexModule::get_provision_pool_of(&crate::precompile::mock::alice(), DOT, AUSD), + (1_000, 1_000_000) + ); + }); + } + + #[test] + fn claim_dex_share_works() { + new_test_ext().execute_with(|| { + // list provision DOT/AUSD + assert_ok!(DexModule::list_provisioning( + RuntimeOrigin::signed(ALICE), + DOT, + AUSD, + 10, + 10, + 10_000, + 10_000, + 100_000 + )); + + assert_ok!(Currencies::update_balance( + RuntimeOrigin::root(), + alice(), + DOT, + 1_000_000_000 + )); + assert_ok!(Currencies::update_balance( + RuntimeOrigin::root(), + alice(), + AUSD, + 1_000_000_000 + )); + assert_ok!(DexModule::add_provision( + RuntimeOrigin::signed(alice()), + DOT, + AUSD, + 1_000_000, + 4_000_000, + )); + + run_to_block(100_001); + assert_ok!(DexModule::end_provisioning(RuntimeOrigin::signed(ALICE), DOT, AUSD)); + + let context = Context { + address: Default::default(), + caller: alice_evm_addr(), + apparent_value: Default::default(), + }; + + // claimDexShare(address,address,address) -> 0xf1e908f8 + // alice + // DOT + // AUSD + let input = hex! {" + f1e908f8 + 000000000000000000000000 1000000000000000000000000000000000000001 + 000000000000000000000000 0000000000000000000100000000000000000002 + 000000000000000000000000 0000000000000000000100000000000000000001 + "}; + + // 8_000_000 + let expected_output = hex! {" + 00000000000000000000000000000000 000000000000000000000000007a1200 + "}; + + let resp = DEXPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); + assert_eq!(resp.exit_status, ExitSucceed::Returned); + assert_eq!(resp.output, expected_output.to_vec()); + }); + } + + #[test] + fn refund_provision_works() { + new_test_ext().execute_with(|| { + // list provision DOT/AUSD + assert_ok!(DexModule::list_provisioning( + RuntimeOrigin::signed(ALICE), + DOT, + AUSD, + 10, + 10, + 10_000, + 10_000, + 100_000 + )); + + assert_ok!(Currencies::update_balance( + RuntimeOrigin::root(), + alice(), + DOT, + 1_000_000_000 + )); + assert_ok!(Currencies::update_balance( + RuntimeOrigin::root(), + alice(), + AUSD, + 1_000_000_000 + )); + assert_ok!(DexModule::add_provision( + RuntimeOrigin::signed(alice()), + DOT, + AUSD, + 1_000, + 1_000, + )); + + run_to_block(100_001); + assert_ok!(DexModule::abort_provisioning(RuntimeOrigin::signed(ALICE), DOT, AUSD)); + + let context = Context { + address: Default::default(), + caller: alice_evm_addr(), + apparent_value: Default::default(), + }; + + // refundProvision(address,address,address) -> 0xaa02e9d3 + // alice + // DOT + // AUSD + let input = hex! {" + aa02e9d3 + 000000000000000000000000 1000000000000000000000000000000000000001 + 000000000000000000000000 0000000000000000000100000000000000000002 + 000000000000000000000000 0000000000000000000100000000000000000001 + "}; + + let resp = DEXPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); + assert_eq!(resp.exit_status, ExitSucceed::Returned); + }); + } } + +// RefundProvision = "refundProvision(address,address,address)",