diff --git a/pallets/vsbond-auction/src/lib.rs b/pallets/vsbond-auction/src/lib.rs index 5e7378f233..36125fc105 100644 --- a/pallets/vsbond-auction/src/lib.rs +++ b/pallets/vsbond-auction/src/lib.rs @@ -45,18 +45,27 @@ pub struct OrderInfo { supply: BalanceOf, /// The quantity of vsbond has not be sold remain: BalanceOf, - unit_price: U64F64, + total_price: BalanceOf, order_id: OrderId, order_state: OrderState, } +impl OrderInfo { + pub fn unit_price(&self) -> U64F64 { + let supply: u128 = self.supply.saturated_into(); + let total_price: u128 = self.total_price.saturated_into(); + + U64F64::from_num(total_price) / supply + } +} + impl core::fmt::Debug for OrderInfo { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_tuple("") .field(&self.owner) .field(&self.vsbond) .field(&self.supply) - .field(&self.unit_price) + .field(&self.unit_price()) .field(&self.order_id) .field(&self.order_state) .finish() @@ -175,7 +184,7 @@ pub mod pallet { #[pallet::compact] first_slot: LeasePeriodOf, #[pallet::compact] last_slot: LeasePeriodOf, #[pallet::compact] supply: BalanceOf, - unit_price: U64F64, + #[pallet::compact] total_price: BalanceOf, ) -> DispatchResultWithPostInfo { // Check origin let owner = ensure_signed(origin)?; @@ -212,7 +221,7 @@ pub mod pallet { vsbond, supply, remain: supply, - unit_price, + total_price, order_id, order_state: OrderState::InTrade, }; @@ -343,11 +352,15 @@ pub mod pallet { // Calculate the real quantity to clinch let quantity_clinchd = min(order_info.remain, quantity); // Calculate the total price that buyer need to pay - let total_price = Self::total_price(quantity_clinchd, order_info.unit_price); + let price_to_pay = Self::price_to_pay(quantity_clinchd, order_info.unit_price()); // Check the balance of buyer - T::MultiCurrency::ensure_can_withdraw(T::InvoicingCurrency::get(), &buyer, total_price) - .map_err(|_| Error::::CantPayThePrice)?; + T::MultiCurrency::ensure_can_withdraw( + T::InvoicingCurrency::get(), + &buyer, + price_to_pay, + ) + .map_err(|_| Error::::CantPayThePrice)?; // Get the new OrderInfo let new_order_info = if quantity_clinchd == order_info.remain { @@ -381,7 +394,7 @@ pub mod pallet { T::InvoicingCurrency::get(), &buyer, &new_order_info.owner, - total_price, + price_to_pay, )?; // Move order_id from InTrade to Clinchd if meets condition @@ -434,7 +447,8 @@ pub mod pallet { next_order_id } - pub(crate) fn total_price(quantity: BalanceOf, unit_price: U64F64) -> BalanceOf { + /// Get the price(round up) needed to pay. + pub(crate) fn price_to_pay(quantity: BalanceOf, unit_price: U64F64) -> BalanceOf { let quantity: u128 = quantity.saturated_into(); let total_price = u128::from_fixed((unit_price * quantity).ceil()); diff --git a/pallets/vsbond-auction/src/tests.rs b/pallets/vsbond-auction/src/tests.rs index 1e257928cd..1fe8e7f15a 100644 --- a/pallets/vsbond-auction/src/tests.rs +++ b/pallets/vsbond-auction/src/tests.rs @@ -25,7 +25,7 @@ use crate::{mock::*, *}; #[test] fn create_order_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 100, 1.to_fixed())); + assert_ok!(Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 100, 100)); assert_eq!(Auction::order_id(), 1); @@ -46,8 +46,8 @@ fn create_order_should_work() { #[test] fn double_create_order_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 50, 1.to_fixed())); - assert_ok!(Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 50, 1.to_fixed())); + assert_ok!(Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 50, 50)); + assert_ok!(Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 50, 50)); assert_eq!(Auction::order_id(), 2); @@ -71,11 +71,11 @@ fn double_create_order_should_work() { fn create_order_by_origin_illegal_should_fail() { new_test_ext().execute_with(|| { assert_noop!( - Auction::create_order(Origin::root(), 3000, 13, 20, 100, 1.to_fixed()), + Auction::create_order(Origin::root(), 3000, 13, 20, 100, 100), DispatchError::BadOrigin ); assert_noop!( - Auction::create_order(Origin::none(), 3000, 13, 20, 100, 1.to_fixed()), + Auction::create_order(Origin::none(), 3000, 13, 20, 100, 100), DispatchError::BadOrigin ); }); @@ -85,7 +85,7 @@ fn create_order_by_origin_illegal_should_fail() { fn create_order_under_minimum_supply_should_fail() { new_test_ext().execute_with(|| { assert_noop!( - Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 0, 1.to_fixed()), + Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 0, 0), Error::::NotEnoughSupply ); }); @@ -95,14 +95,14 @@ fn create_order_under_minimum_supply_should_fail() { fn create_order_without_enough_vsbond_should_fail() { new_test_ext().execute_with(|| { assert_noop!( - Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 1000, 1.to_fixed()), + Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 1000, 1000), Error::::NotEnoughBalanceToReserve, ); const LOCK_ID: LockIdentifier = 0u64.to_be_bytes(); assert_ok!(Tokens::set_lock(LOCK_ID, VSBOND, &ALICE, 50)); assert_noop!( - Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 51, 1.to_fixed()), + Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 51, 51), Error::::NotEnoughBalanceToReserve, ); }); @@ -112,11 +112,11 @@ fn create_order_without_enough_vsbond_should_fail() { fn create_order_exceed_maximum_order_in_trade_should_fail() { new_test_ext().execute_with(|| { for _ in 0..MaximumOrderInTrade::get() { - assert_ok!(Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 1, 1.to_fixed())); + assert_ok!(Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 1, 1)); } assert_noop!( - Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 1, 1.to_fixed()), + Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 1, 1), Error::::ExceedMaximumOrderInTrade, ); }); @@ -125,7 +125,7 @@ fn create_order_exceed_maximum_order_in_trade_should_fail() { #[test] fn revoke_order_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 100, 1.to_fixed())); + assert_ok!(Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 100, 100)); assert_ok!(Auction::revoke_order(Some(ALICE).into(), 0)); assert_eq!(Auction::order_id(), 1); @@ -150,7 +150,7 @@ fn revoke_order_should_work() { #[test] fn revoke_order_which_be_partial_clinchd_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 50, 1.to_fixed())); + assert_ok!(Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 50, 50)); assert_ok!(Auction::partial_clinch_order(Some(BRUCE).into(), 0, 25)); assert_ok!(Auction::revoke_order(Some(ALICE).into(), 0)); @@ -177,18 +177,14 @@ fn revoke_order_which_be_partial_clinchd_should_work() { #[test] fn revoke_order_not_exist_should_fail() { new_test_ext().execute_with(|| { - assert_ok!(Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 50, 1.to_fixed())); - assert_noop!( - Auction::partial_clinch_order(Some(BRUCE).into(), 1, 25), - Error::::NotFindOrderInfo - ); + assert_noop!(Auction::revoke_order(Some(ALICE).into(), 0), Error::::NotFindOrderInfo); }); } #[test] fn revoke_order_without_enough_reserved_vsbond_should_fail() { new_test_ext().execute_with(|| { - assert_ok!(Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 50, 1.to_fixed())); + assert_ok!(Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 50, 50)); assert_ok!(Tokens::repatriate_reserved( VSBOND, &ALICE, @@ -206,7 +202,7 @@ fn revoke_order_without_enough_reserved_vsbond_should_fail() { #[test] fn revoke_order_by_origin_illegal_should_fail() { new_test_ext().execute_with(|| { - assert_ok!(Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 50, 1.to_fixed())); + assert_ok!(Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 50, 50)); assert_noop!( Auction::revoke_order(Some(BRUCE).into(), 0), Error::::ForbidRevokeOrderWithoutOwnership @@ -219,14 +215,14 @@ fn revoke_order_by_origin_illegal_should_fail() { #[test] fn revoke_order_not_in_trade_should_fail() { new_test_ext().execute_with(|| { - assert_ok!(Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 50, 1.to_fixed())); + assert_ok!(Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 50, 50)); assert_ok!(Auction::revoke_order(Some(ALICE).into(), 0)); assert_noop!( Auction::revoke_order(Some(ALICE).into(), 0), Error::::ForbidRevokeOrderNotInTrade ); - assert_ok!(Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 50, 1.to_fixed())); + assert_ok!(Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 50, 50)); assert_ok!(Auction::clinch_order(Some(BRUCE).into(), 1)); assert_noop!( Auction::revoke_order(Some(ALICE).into(), 1), @@ -238,7 +234,7 @@ fn revoke_order_not_in_trade_should_fail() { #[test] fn partial_clinch_order_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 100, 0.33.to_fixed())); + assert_ok!(Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 100, 33)); assert_ok!(Auction::partial_clinch_order(Some(BRUCE).into(), 0, 33)); assert_eq!(Auction::order_id(), 1); @@ -304,7 +300,7 @@ fn partial_clinch_order_should_work() { #[test] fn partial_clinch_order_not_exist_should_fail() { new_test_ext().execute_with(|| { - assert_ok!(Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 100, 1.to_fixed())); + assert_ok!(Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 100, 100)); assert_noop!(Auction::clinch_order(Some(BRUCE).into(), 1), Error::::NotFindOrderInfo); assert_noop!( Auction::partial_clinch_order(Some(BRUCE).into(), 1, 50), @@ -316,7 +312,7 @@ fn partial_clinch_order_not_exist_should_fail() { #[test] fn clinch_order_by_origin_illegal_should_fail() { new_test_ext().execute_with(|| { - assert_ok!(Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 100, 1.to_fixed())); + assert_ok!(Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 100, 100)); assert_noop!( Auction::clinch_order(Some(ALICE).into(), 0), Error::::ForbidClinchOrderWithinOwnership @@ -340,14 +336,14 @@ fn clinch_order_by_origin_illegal_should_fail() { #[test] fn clinch_order_not_in_trade_should_fail() { new_test_ext().execute_with(|| { - assert_ok!(Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 100, 1.to_fixed())); + assert_ok!(Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 100, 100)); assert_ok!(Auction::revoke_order(Some(ALICE).into(), 0)); assert_noop!( Auction::partial_clinch_order(Some(BRUCE).into(), 0, 50), Error::::ForbidClinchOrderNotInTrade ); - assert_ok!(Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 100, 1.to_fixed())); + assert_ok!(Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 100, 100)); assert_ok!(Auction::clinch_order(Some(BRUCE).into(), 1)); assert_noop!( Auction::partial_clinch_order(Some(BRUCE).into(), 1, 50), @@ -359,7 +355,7 @@ fn clinch_order_not_in_trade_should_fail() { #[test] fn clinch_order_without_enough_token_should_fail() { new_test_ext().execute_with(|| { - assert_ok!(Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 100, 2.to_fixed())); + assert_ok!(Auction::create_order(Some(ALICE).into(), 3000, 13, 20, 100, 200)); assert_ok!(Auction::partial_clinch_order(Some(BRUCE).into(), 0, 50)); assert_noop!(Auction::clinch_order(Some(BRUCE).into(), 0), Error::::CantPayThePrice); }); @@ -367,12 +363,12 @@ fn clinch_order_without_enough_token_should_fail() { // Test Utilities #[test] -fn check_total_price() { +fn check_price_to_pay() { let unit_price: U64F64 = 0.333f64.to_fixed(); let quantities: [BalanceOf; 4] = [3, 33, 333, 3333]; - let total_prices: [BalanceOf; 4] = [1, 11, 111, 1110]; + let price_to_pays: [BalanceOf; 4] = [1, 11, 111, 1110]; - for (quantity, total_price) in quantities.iter().zip(total_prices.iter()) { - assert_eq!(Auction::total_price(*quantity, unit_price), *total_price); + for (quantity, price_to_pay) in quantities.iter().zip(price_to_pays.iter()) { + assert_eq!(Auction::price_to_pay(*quantity, unit_price), *price_to_pay); } }