Skip to content

Commit d8f980b

Browse files
Merge pull request #2276 from multiversx/order-book-fungible
Order book refactor (FungiblePayment) + big num proportion methods
2 parents dcf00bb + ebb1f7c commit d8f980b

File tree

14 files changed

+346
-119
lines changed

14 files changed

+346
-119
lines changed

contracts/examples/order-book/pair/src/common.rs

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,10 @@ pub enum OrderType {
1515
Sell,
1616
}
1717

18-
/// TODO: naming conflict with new FungiblePayment type in the framework, switch to that
19-
#[derive(ManagedVecItem, Clone)]
20-
pub struct OrderBookFungiblePayment<M: ManagedTypeApi> {
21-
pub token_id: TokenId<M>,
22-
pub amount: BigUint<M>,
23-
}
24-
2518
#[derive(ManagedVecItem, Clone)]
2619
pub struct Transfer<M: ManagedTypeApi> {
2720
pub to: ManagedAddress<M>,
28-
pub payment: OrderBookFungiblePayment<M>,
21+
pub payment: FungiblePayment<M>,
2922
}
3023

3124
#[type_abi]
@@ -52,7 +45,7 @@ pub struct DealConfig {
5245
#[type_abi]
5346
#[derive(TopEncode, TopDecode, Clone)]
5447
pub struct OrderInputParams<M: ManagedTypeApi> {
55-
pub amount: BigUint<M>,
48+
pub amount: NonZeroBigUint<M>,
5649
pub match_provider: ManagedAddress<M>,
5750
pub fee_config: FeeConfig<M>,
5851
pub deal_config: DealConfig,
@@ -64,8 +57,8 @@ pub struct Order<M: ManagedTypeApi> {
6457
pub id: u64,
6558
pub creator: ManagedAddress<M>,
6659
pub match_provider: ManagedAddress<M>,
67-
pub input_amount: BigUint<M>,
68-
pub output_amount: BigUint<M>,
60+
pub input_amount: NonZeroBigUint<M>,
61+
pub output_amount: NonZeroBigUint<M>,
6962
pub fee_config: FeeConfig<M>,
7063
pub deal_config: DealConfig,
7164
pub create_epoch: u64,
@@ -84,7 +77,7 @@ pub trait CommonModule {
8477
fn new_order(
8578
&self,
8679
id: u64,
87-
payment: OrderBookFungiblePayment<Self::Api>,
80+
payment: FungiblePayment<Self::Api>,
8881
params: OrderInputParams<Self::Api>,
8982
order_type: OrderType,
9083
) -> Order<Self::Api> {
@@ -101,23 +94,25 @@ pub trait CommonModule {
10194
}
10295
}
10396

104-
fn rule_of_three(&self, part: &BigUint, total: &BigUint, value: &BigUint) -> BigUint {
105-
&(part * value) / total
106-
}
107-
108-
fn calculate_fee_amount(&self, amount: &BigUint, fee_config: &FeeConfig<Self::Api>) -> BigUint {
97+
fn calculate_fee_amount(
98+
&self,
99+
amount: &NonZeroBigUint,
100+
fee_config: &FeeConfig<Self::Api>,
101+
) -> BigUint {
109102
match fee_config.fee_type {
110103
FeeConfigEnum::Fixed => fee_config.fixed_fee.clone(),
111-
FeeConfigEnum::Percent => amount * fee_config.percent_fee / PERCENT_BASE_POINTS,
104+
FeeConfigEnum::Percent => amount
105+
.as_big_uint()
106+
.proportion(fee_config.percent_fee, PERCENT_BASE_POINTS),
112107
}
113108
}
114109

115110
fn calculate_amount_after_fee(
116111
&self,
117-
amount: &BigUint,
112+
amount: &NonZeroBigUint,
118113
fee_config: &FeeConfig<Self::Api>,
119114
) -> BigUint {
120-
amount - &self.calculate_fee_amount(amount, fee_config)
115+
amount.as_big_uint() - &self.calculate_fee_amount(amount, fee_config)
121116
}
122117

123118
#[view(getFirstTokenId)]

contracts/examples/order-book/pair/src/orders.rs

Lines changed: 68 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ use crate::common::{FEE_PENALTY_INCREASE_EPOCHS, FEE_PENALTY_INCREASE_PERCENT};
55
use super::{common, events, validation};
66

77
use super::common::{
8-
FREE_ORDER_FROM_STORAGE_MIN_PENALTIES, Order, OrderBookFungiblePayment, OrderInputParams,
9-
OrderType, PERCENT_BASE_POINTS, Transfer,
8+
FREE_ORDER_FROM_STORAGE_MIN_PENALTIES, Order, OrderInputParams, OrderType, PERCENT_BASE_POINTS,
9+
Transfer,
1010
};
1111

1212
#[multiversx_sc::module]
@@ -15,7 +15,7 @@ pub trait OrdersModule:
1515
{
1616
fn create_order(
1717
&self,
18-
payment: OrderBookFungiblePayment<Self::Api>,
18+
payment: FungiblePayment<Self::Api>,
1919
params: OrderInputParams<Self::Api>,
2020
order_type: OrderType,
2121
) {
@@ -150,33 +150,35 @@ pub trait OrdersModule:
150150
"Too early to free order"
151151
);
152152

153+
// penalty_count is at least FREE_ORDER_FROM_STORAGE_MIN_PENALTIES (6),
154+
// so this cannot be zero
153155
let penalty_percent = penalty_count * FEE_PENALTY_INCREASE_PERCENT;
154-
let penalty_amount = self.rule_of_three(
155-
&BigUint::from(penalty_percent),
156-
&BigUint::from(PERCENT_BASE_POINTS),
157-
&order.input_amount,
158-
);
159-
let amount = &order.input_amount - &penalty_amount;
160-
161-
let creator_transfer = Transfer {
162-
to: order.creator.clone(),
163-
payment: OrderBookFungiblePayment {
164-
token_id: token_id.clone(),
165-
amount,
166-
},
167-
};
168-
let caller_transfer = Transfer {
169-
to: caller.clone(),
170-
payment: OrderBookFungiblePayment {
171-
token_id,
172-
amount: penalty_amount,
173-
},
174-
};
156+
157+
let penalty_amount = order
158+
.input_amount
159+
.as_big_uint()
160+
.proportion(penalty_percent, PERCENT_BASE_POINTS);
161+
162+
let creator_amount = order.input_amount.as_big_uint() - &penalty_amount;
175163

176164
self.orders(order_id).clear();
165+
177166
let mut transfers = ManagedVec::new();
178-
transfers.push(creator_transfer);
179-
transfers.push(caller_transfer);
167+
if let Some(creator_amount_nz) = NonZeroBigUint::new(creator_amount) {
168+
let creator_transfer = Transfer {
169+
to: order.creator.clone(),
170+
payment: FungiblePayment::new(token_id.clone(), creator_amount_nz),
171+
};
172+
transfers.push(creator_transfer);
173+
}
174+
175+
if let Some(penalty_amount_nz) = NonZeroBigUint::new(penalty_amount) {
176+
let caller_transfer = Transfer {
177+
to: caller.clone(),
178+
payment: FungiblePayment::new(token_id.clone(), penalty_amount_nz),
179+
};
180+
transfers.push(caller_transfer);
181+
}
180182
self.execute_transfers(transfers);
181183

182184
order
@@ -199,21 +201,23 @@ pub trait OrdersModule:
199201

200202
let penalty_count = (epoch - order.create_epoch) / FEE_PENALTY_INCREASE_EPOCHS;
201203
let penalty_percent = penalty_count * FEE_PENALTY_INCREASE_PERCENT;
202-
let penalty_amount = self.rule_of_three(
203-
&BigUint::from(penalty_percent),
204-
&BigUint::from(PERCENT_BASE_POINTS),
205-
&order.input_amount,
206-
);
207-
let amount = &order.input_amount - &penalty_amount;
204+
let penalty_amount = order
205+
.input_amount
206+
.as_big_uint()
207+
.proportion(penalty_percent, PERCENT_BASE_POINTS);
208208

209-
let transfer = Transfer {
210-
to: caller.clone(),
211-
payment: OrderBookFungiblePayment { token_id, amount },
212-
};
209+
let amount = order.input_amount.as_big_uint() - &penalty_amount;
213210

214211
self.orders(order_id).clear();
212+
215213
let mut transfers = ManagedVec::new();
216-
transfers.push(transfer);
214+
if let Some(nz_amount) = NonZeroBigUint::new(amount) {
215+
let transfer = Transfer {
216+
to: caller.clone(),
217+
payment: FungiblePayment::new(token_id, nz_amount),
218+
};
219+
transfers.push(transfer);
220+
}
217221
self.execute_transfers(transfers);
218222

219223
order
@@ -297,8 +301,8 @@ pub trait OrdersModule:
297301
let mut amount_requested = BigUint::zero();
298302

299303
orders.iter().for_each(|x| {
300-
amount_paid += &x.input_amount;
301-
amount_requested += &x.output_amount;
304+
amount_paid += x.input_amount.as_big_uint();
305+
amount_requested += x.output_amount.as_big_uint();
302306
});
303307

304308
(amount_paid, amount_requested)
@@ -312,54 +316,50 @@ pub trait OrdersModule:
312316
leftover: BigUint,
313317
) -> ManagedVec<Transfer<Self::Api>> {
314318
let mut transfers: ManagedVec<Self::Api, Transfer<Self::Api>> = ManagedVec::new();
315-
316-
let mut match_provider_transfer = Transfer {
317-
to: self.blockchain().get_caller(),
318-
payment: OrderBookFungiblePayment {
319-
token_id: token_requested.clone(),
320-
amount: BigUint::zero(),
321-
},
322-
};
319+
let mut match_provider_amount_accumulator = BigUint::zero();
323320

324321
for order in orders.iter() {
325322
let match_provider_amount =
326323
self.calculate_fee_amount(&order.output_amount, &order.fee_config);
327-
let creator_amount = &order.output_amount - &match_provider_amount;
324+
let creator_amount = order.output_amount.as_big_uint() - &match_provider_amount;
328325

329-
let order_deal = self.rule_of_three(&order.input_amount, &total_paid, &leftover);
330-
let match_provider_deal_amount = self.rule_of_three(
331-
&order.deal_config.match_provider_percent.into(),
332-
&PERCENT_BASE_POINTS.into(),
333-
&order_deal,
326+
let order_deal = order.input_amount.as_big_uint() * &leftover / &total_paid;
327+
let match_provider_deal_amount = order_deal.proportion(
328+
order.deal_config.match_provider_percent,
329+
PERCENT_BASE_POINTS,
334330
);
335331
let creator_deal_amount = &order_deal - &match_provider_deal_amount;
336332

337333
transfers.push(Transfer {
338334
to: order.creator.clone(),
339-
payment: OrderBookFungiblePayment {
340-
token_id: token_requested.clone(),
341-
amount: creator_amount + creator_deal_amount,
342-
},
335+
payment: FungiblePayment::new(
336+
token_requested.clone(),
337+
NonZeroBigUint::new_or_panic(creator_amount + creator_deal_amount),
338+
),
343339
});
344340

345-
match_provider_transfer.payment.amount +=
346-
match_provider_amount + match_provider_deal_amount;
341+
match_provider_amount_accumulator += match_provider_amount + match_provider_deal_amount;
342+
}
343+
344+
if match_provider_amount_accumulator > 0 {
345+
transfers.push(Transfer {
346+
to: self.blockchain().get_caller(),
347+
payment: FungiblePayment::new(
348+
token_requested.clone(),
349+
NonZeroBigUint::new_or_panic(match_provider_amount_accumulator),
350+
),
351+
});
347352
}
348-
transfers.push(match_provider_transfer);
349353

350354
transfers
351355
}
352356

353357
fn execute_transfers(&self, transfers: ManagedVec<Transfer<Self::Api>>) {
354358
for transfer in &transfers {
355-
if transfer.payment.amount > 0 {
356-
let token_id = transfer.payment.token_id.clone();
357-
let amount = NonZeroBigUint::new(transfer.payment.amount.clone()).unwrap();
358-
self.tx()
359-
.to(&transfer.to)
360-
.payment(Payment::new(token_id, 0, amount))
361-
.transfer();
362-
}
359+
self.tx()
360+
.to(&transfer.to)
361+
.payment(transfer.payment.as_payment_refs())
362+
.transfer();
363363
}
364364
}
365365

contracts/examples/order-book/pair/src/validation.rs

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
11
use multiversx_sc::imports::*;
22

3-
use crate::common::{FeeConfig, FeeConfigEnum, OrderBookFungiblePayment};
4-
53
use super::{
64
common,
75
common::{
8-
FEE_PENALTY_INCREASE_PERCENT, MAX_ORDERS_PER_USER, Order, OrderInputParams,
9-
PERCENT_BASE_POINTS,
6+
FEE_PENALTY_INCREASE_PERCENT, FeeConfig, FeeConfigEnum, MAX_ORDERS_PER_USER, Order,
7+
OrderInputParams, PERCENT_BASE_POINTS,
108
},
119
};
1210

1311
#[multiversx_sc::module]
1412
pub trait ValidationModule: common::CommonModule {
1513
fn require_valid_order_input_amount(&self, params: &OrderInputParams<Self::Api>) {
16-
require!(params.amount != BigUint::zero(), "Amount cannot be zero");
1714
require!(
1815
self.calculate_fee_amount(
1916
&params.amount,
@@ -38,7 +35,7 @@ pub trait ValidationModule: common::CommonModule {
3835
match params.fee_config.fee_type.clone() {
3936
FeeConfigEnum::Fixed => {
4037
require!(
41-
params.fee_config.fixed_fee < params.amount,
38+
params.fee_config.fixed_fee < *params.amount.as_big_uint(),
4239
"Invalid fee config fixed amount"
4340
);
4441
}
@@ -68,7 +65,7 @@ pub trait ValidationModule: common::CommonModule {
6865
self.require_valid_order_input_deal_config(params);
6966
}
7067

71-
fn require_valid_buy_payment(&self) -> OrderBookFungiblePayment<Self::Api> {
68+
fn require_valid_buy_payment(&self) -> FungiblePayment<Self::Api> {
7269
let payment = self.call_value().single();
7370
let second_token_id = self.second_token_id().get();
7471
require!(
@@ -80,13 +77,10 @@ pub trait ValidationModule: common::CommonModule {
8077
"Token id and second token id should be the same"
8178
);
8279

83-
OrderBookFungiblePayment {
84-
token_id: payment.token_identifier.clone(),
85-
amount: payment.amount.as_big_uint().clone(),
86-
}
80+
FungiblePayment::new(payment.token_identifier.clone(), payment.amount.clone())
8781
}
8882

89-
fn require_valid_sell_payment(&self) -> OrderBookFungiblePayment<Self::Api> {
83+
fn require_valid_sell_payment(&self) -> FungiblePayment<Self::Api> {
9084
let payment = self.call_value().single();
9185
let first_token_id = self.first_token_id().get();
9286
require!(payment.is_fungible(), "Payment is not a fungible token");
@@ -95,10 +89,7 @@ pub trait ValidationModule: common::CommonModule {
9589
"Token id and first token id should be the same"
9690
);
9791

98-
OrderBookFungiblePayment {
99-
token_id: payment.token_identifier.clone(),
100-
amount: payment.amount.as_big_uint().clone(),
101-
}
92+
FungiblePayment::new(payment.token_identifier.clone(), payment.amount.clone())
10293
}
10394

10495
fn require_valid_match_input_order_ids(&self, order_ids: &ManagedVec<u64>) {

contracts/feature-tests/basic-features/src/big_num_methods.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ pub trait BigIntMethods {
109109
}
110110

111111
#[endpoint]
112-
fn bigint_overwrite_i64(&self, bi: BigInt, small: i64) -> BigInt {
112+
fn bigint_overwrite_i64(&self, mut bi: BigInt, small: i64) -> BigInt {
113113
bi.overwrite_i64(small);
114114
bi
115115
}

framework/base/src/err_msg.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ pub const BIG_UINT_EXCEEDS_SLICE: &str = "big uint as_bytes exceed target slice"
4848
pub const BIG_UINT_SUB_NEGATIVE: &str = "cannot subtract because result would be negative";
4949
pub const UNSIGNED_NEGATIVE: &str = "cannot convert to unsigned, number is negative";
5050
pub const ZERO_VALUE_NOT_ALLOWED: &str = "zero value not allowed";
51+
pub const PROPORTION_OVERFLOW_ERR: &str = "proportion overflow";
5152

5253
pub const DESERIALIZATION_INVALID_BYTE: &str = "call data deserialization error: not a valid byte";
5354
pub const DESERIALIZATION_NOT_32_BYTES: &str =

0 commit comments

Comments
 (0)