Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
32 changes: 23 additions & 9 deletions pallets/vsbond-auction/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,27 @@ pub struct OrderInfo<T: Config> {
supply: BalanceOf<T>,
/// The quantity of vsbond has not be sold
remain: BalanceOf<T>,
unit_price: U64F64,
total_price: BalanceOf<T>,
order_id: OrderId,
order_state: OrderState,
}

impl<T: Config> OrderInfo<T> {
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<T: Config> core::fmt::Debug for OrderInfo<T> {
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()
Expand Down Expand Up @@ -175,7 +184,7 @@ pub mod pallet {
#[pallet::compact] first_slot: LeasePeriodOf<T>,
#[pallet::compact] last_slot: LeasePeriodOf<T>,
#[pallet::compact] supply: BalanceOf<T>,
unit_price: U64F64,
#[pallet::compact] total_price: BalanceOf<T>,
) -> DispatchResultWithPostInfo {
// Check origin
let owner = ensure_signed(origin)?;
Expand Down Expand Up @@ -212,7 +221,7 @@ pub mod pallet {
vsbond,
supply,
remain: supply,
unit_price,
total_price,
order_id,
order_state: OrderState::InTrade,
};
Expand Down Expand Up @@ -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::<T>::CantPayThePrice)?;
T::MultiCurrency::ensure_can_withdraw(
T::InvoicingCurrency::get(),
&buyer,
price_to_pay,
)
.map_err(|_| Error::<T>::CantPayThePrice)?;

// Get the new OrderInfo
let new_order_info = if quantity_clinchd == order_info.remain {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -434,7 +447,8 @@ pub mod pallet {
next_order_id
}

pub(crate) fn total_price(quantity: BalanceOf<T>, unit_price: U64F64) -> BalanceOf<T> {
/// Get the price(round up) needed to pay.
pub(crate) fn price_to_pay(quantity: BalanceOf<T>, unit_price: U64F64) -> BalanceOf<T> {
let quantity: u128 = quantity.saturated_into();
let total_price = u128::from_fixed((unit_price * quantity).ceil());

Expand Down
58 changes: 27 additions & 31 deletions pallets/vsbond-auction/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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);

Expand All @@ -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
);
});
Expand All @@ -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::<Test>::NotEnoughSupply
);
});
Expand All @@ -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::<Test>::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::<Test>::NotEnoughBalanceToReserve,
);
});
Expand All @@ -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::<Test>::ExceedMaximumOrderInTrade,
);
});
Expand All @@ -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);
Expand All @@ -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));

Expand All @@ -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::<Test>::NotFindOrderInfo
);
assert_noop!(Auction::revoke_order(Some(ALICE).into(), 0), Error::<Test>::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,
Expand All @@ -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::<Test>::ForbidRevokeOrderWithoutOwnership
Expand All @@ -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::<Test>::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),
Expand All @@ -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);
Expand Down Expand Up @@ -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::<Test>::NotFindOrderInfo);
assert_noop!(
Auction::partial_clinch_order(Some(BRUCE).into(), 1, 50),
Expand All @@ -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::<Test>::ForbidClinchOrderWithinOwnership
Expand All @@ -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::<Test>::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),
Expand All @@ -359,20 +355,20 @@ 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::<Test>::CantPayThePrice);
});
}

// Test Utilities
#[test]
fn check_total_price() {
fn check_price_to_pay() {
let unit_price: U64F64 = 0.333f64.to_fixed();
let quantities: [BalanceOf<Test>; 4] = [3, 33, 333, 3333];
let total_prices: [BalanceOf<Test>; 4] = [1, 11, 111, 1110];
let price_to_pays: [BalanceOf<Test>; 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);
}
}