mirror of
https://github.com/floor-licker/polyfill-rs.git
synced 2026-08-17 14:38:07 +00:00
Merge pull request #49 from floor-licker/fix/widen-order-token-amounts
fix: widen order token amounts
This commit is contained in:
+71
-48
@@ -175,12 +175,15 @@ fn generate_seed() -> u64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Convert decimal to token units (multiply by 1e6)
|
/// Convert decimal to token units (multiply by 1e6)
|
||||||
fn decimal_to_token_u32(amt: Decimal) -> u32 {
|
fn decimal_to_token_units(amt: Decimal) -> Result<U256> {
|
||||||
let mut amt = TOKEN_UNIT_SCALE * amt;
|
let mut amt = TOKEN_UNIT_SCALE * amt;
|
||||||
if amt.scale() > 0 {
|
if amt.scale() > 0 {
|
||||||
amt = amt.round_dp_with_strategy(0, MidpointTowardZero);
|
amt = amt.round_dp_with_strategy(0, MidpointTowardZero);
|
||||||
}
|
}
|
||||||
amt.try_into().expect("Couldn't round decimal to integer")
|
let units: u128 = amt
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| PolyfillError::validation(format!("Invalid token amount {amt}")))?;
|
||||||
|
Ok(U256::from(units))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_round_config(tick_size: Decimal) -> Result<&'static RoundConfig> {
|
fn parse_round_config(tick_size: Decimal) -> Result<&'static RoundConfig> {
|
||||||
@@ -332,17 +335,17 @@ impl OrderBuilder {
|
|||||||
size: Decimal,
|
size: Decimal,
|
||||||
price: Decimal,
|
price: Decimal,
|
||||||
round_config: &RoundConfig,
|
round_config: &RoundConfig,
|
||||||
) -> (u32, u32) {
|
) -> Result<(U256, U256)> {
|
||||||
let raw_price = price.round_dp_with_strategy(round_config.price, MidpointTowardZero);
|
let raw_price = price.round_dp_with_strategy(round_config.price, MidpointTowardZero);
|
||||||
|
|
||||||
match side {
|
let amounts = match side {
|
||||||
Side::BUY => {
|
Side::BUY => {
|
||||||
let raw_taker_amt = size.round_dp_with_strategy(round_config.size, ToZero);
|
let raw_taker_amt = size.round_dp_with_strategy(round_config.size, ToZero);
|
||||||
let raw_maker_amt = raw_taker_amt * raw_price;
|
let raw_maker_amt = raw_taker_amt * raw_price;
|
||||||
let raw_maker_amt = self.fix_amount_rounding(raw_maker_amt, round_config);
|
let raw_maker_amt = self.fix_amount_rounding(raw_maker_amt, round_config);
|
||||||
(
|
(
|
||||||
decimal_to_token_u32(raw_maker_amt),
|
decimal_to_token_units(raw_maker_amt)?,
|
||||||
decimal_to_token_u32(raw_taker_amt),
|
decimal_to_token_units(raw_taker_amt)?,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
Side::SELL => {
|
Side::SELL => {
|
||||||
@@ -351,11 +354,13 @@ impl OrderBuilder {
|
|||||||
let raw_taker_amt = self.fix_amount_rounding(raw_taker_amt, round_config);
|
let raw_taker_amt = self.fix_amount_rounding(raw_taker_amt, round_config);
|
||||||
|
|
||||||
(
|
(
|
||||||
decimal_to_token_u32(raw_maker_amt),
|
decimal_to_token_units(raw_maker_amt)?,
|
||||||
decimal_to_token_u32(raw_taker_amt),
|
decimal_to_token_units(raw_taker_amt)?,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
|
|
||||||
|
Ok(amounts)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get order amounts for a market order
|
/// Get order amounts for a market order
|
||||||
@@ -365,18 +370,18 @@ impl OrderBuilder {
|
|||||||
amount: Decimal,
|
amount: Decimal,
|
||||||
price: Decimal,
|
price: Decimal,
|
||||||
round_config: &RoundConfig,
|
round_config: &RoundConfig,
|
||||||
) -> (u32, u32) {
|
) -> Result<(U256, U256)> {
|
||||||
let raw_price = price.round_dp_with_strategy(round_config.price, MidpointTowardZero);
|
let raw_price = price.round_dp_with_strategy(round_config.price, MidpointTowardZero);
|
||||||
|
|
||||||
match side {
|
let amounts = match side {
|
||||||
Side::BUY => {
|
Side::BUY => {
|
||||||
let raw_maker_amt = amount.round_dp_with_strategy(round_config.size, ToZero);
|
let raw_maker_amt = amount.round_dp_with_strategy(round_config.size, ToZero);
|
||||||
let raw_taker_amt =
|
let raw_taker_amt =
|
||||||
self.fix_amount_rounding(raw_maker_amt / raw_price, round_config);
|
self.fix_amount_rounding(raw_maker_amt / raw_price, round_config);
|
||||||
|
|
||||||
(
|
(
|
||||||
decimal_to_token_u32(raw_maker_amt),
|
decimal_to_token_units(raw_maker_amt)?,
|
||||||
decimal_to_token_u32(raw_taker_amt),
|
decimal_to_token_units(raw_taker_amt)?,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
Side::SELL => {
|
Side::SELL => {
|
||||||
@@ -385,11 +390,13 @@ impl OrderBuilder {
|
|||||||
self.fix_amount_rounding(raw_maker_amt * raw_price, round_config);
|
self.fix_amount_rounding(raw_maker_amt * raw_price, round_config);
|
||||||
|
|
||||||
(
|
(
|
||||||
decimal_to_token_u32(raw_maker_amt),
|
decimal_to_token_units(raw_maker_amt)?,
|
||||||
decimal_to_token_u32(raw_taker_amt),
|
decimal_to_token_units(raw_taker_amt)?,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
|
|
||||||
|
Ok(amounts)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate market price from order book levels
|
/// Calculate market price from order book levels
|
||||||
@@ -446,7 +453,7 @@ impl OrderBuilder {
|
|||||||
let round_config = parse_round_config(tick_size)?;
|
let round_config = parse_round_config(tick_size)?;
|
||||||
|
|
||||||
let (maker_amount, taker_amount) =
|
let (maker_amount, taker_amount) =
|
||||||
self.get_market_order_amounts(order_args.side, order_args.amount, price, round_config);
|
self.get_market_order_amounts(order_args.side, order_args.amount, price, round_config)?;
|
||||||
|
|
||||||
let neg_risk = options
|
let neg_risk = options
|
||||||
.neg_risk
|
.neg_risk
|
||||||
@@ -489,7 +496,7 @@ impl OrderBuilder {
|
|||||||
order_args.size,
|
order_args.size,
|
||||||
order_args.price,
|
order_args.price,
|
||||||
round_config,
|
round_config,
|
||||||
);
|
)?;
|
||||||
|
|
||||||
let neg_risk = options
|
let neg_risk = options
|
||||||
.neg_risk
|
.neg_risk
|
||||||
@@ -523,8 +530,8 @@ impl OrderBuilder {
|
|||||||
side: Side,
|
side: Side,
|
||||||
chain_id: u64,
|
chain_id: u64,
|
||||||
exchange: Address,
|
exchange: Address,
|
||||||
maker_amount: u32,
|
maker_amount: U256,
|
||||||
taker_amount: u32,
|
taker_amount: U256,
|
||||||
expiration: u64,
|
expiration: u64,
|
||||||
builder_code: Option<&str>,
|
builder_code: Option<&str>,
|
||||||
metadata: Option<&str>,
|
metadata: Option<&str>,
|
||||||
@@ -545,8 +552,8 @@ impl OrderBuilder {
|
|||||||
maker: self.funder,
|
maker: self.funder,
|
||||||
signer: self.signer.address(),
|
signer: self.signer.address(),
|
||||||
token_id: u256_token_id,
|
token_id: u256_token_id,
|
||||||
maker_amount: U256::from(maker_amount),
|
maker_amount,
|
||||||
taker_amount: U256::from(taker_amount),
|
taker_amount,
|
||||||
side: side as u8,
|
side: side as u8,
|
||||||
signature_type: self.sig_type as u8,
|
signature_type: self.sig_type as u8,
|
||||||
timestamp: U256::from(timestamp),
|
timestamp: U256::from(timestamp),
|
||||||
@@ -593,9 +600,9 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_decimal_to_token_u32() {
|
fn test_decimal_to_token_units() {
|
||||||
let result = decimal_to_token_u32(Decimal::from_str("1.5").unwrap());
|
let result = decimal_to_token_units(Decimal::from_str("1.5").unwrap()).unwrap();
|
||||||
assert_eq!(result, 1_500_000);
|
assert_eq!(result, U256::from(1_500_000));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -606,18 +613,30 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_decimal_to_token_u32_edge_cases() {
|
fn test_decimal_to_token_units_edge_cases() {
|
||||||
// Test zero
|
// Test zero
|
||||||
let result = decimal_to_token_u32(Decimal::ZERO);
|
let result = decimal_to_token_units(Decimal::ZERO).unwrap();
|
||||||
assert_eq!(result, 0);
|
assert_eq!(result, U256::ZERO);
|
||||||
|
|
||||||
// Test small decimal
|
// Test small decimal
|
||||||
let result = decimal_to_token_u32(Decimal::from_str("0.000001").unwrap());
|
let result = decimal_to_token_units(Decimal::from_str("0.000001").unwrap()).unwrap();
|
||||||
assert_eq!(result, 1);
|
assert_eq!(result, U256::from(1));
|
||||||
|
|
||||||
// Test large number
|
// Test large number
|
||||||
let result = decimal_to_token_u32(Decimal::from_str("1000.0").unwrap());
|
let result = decimal_to_token_units(Decimal::from_str("1000.0").unwrap()).unwrap();
|
||||||
assert_eq!(result, 1_000_000_000);
|
assert_eq!(result, U256::from(1_000_000_000));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_decimal_to_token_units_supports_amounts_above_u32() {
|
||||||
|
let result = decimal_to_token_units(Decimal::from_str("5000").unwrap()).unwrap();
|
||||||
|
assert_eq!(result, U256::from(5_000_000_000_u64));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_decimal_to_token_units_rejects_negative_amounts() {
|
||||||
|
let result = decimal_to_token_units(Decimal::from_str("-1").unwrap());
|
||||||
|
assert!(matches!(result, Err(PolyfillError::Validation { .. })));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -810,23 +829,27 @@ mod tests {
|
|||||||
let builder = test_builder();
|
let builder = test_builder();
|
||||||
let round_config = parse_round_config(Decimal::from_str("0.01").unwrap()).unwrap();
|
let round_config = parse_round_config(Decimal::from_str("0.01").unwrap()).unwrap();
|
||||||
|
|
||||||
let (buy_maker, buy_taker) = builder.get_market_order_amounts(
|
let (buy_maker, buy_taker) = builder
|
||||||
Side::BUY,
|
.get_market_order_amounts(
|
||||||
Decimal::from_str("10").unwrap(),
|
Side::BUY,
|
||||||
Decimal::from_str("0.25").unwrap(),
|
Decimal::from_str("10").unwrap(),
|
||||||
round_config,
|
Decimal::from_str("0.25").unwrap(),
|
||||||
);
|
round_config,
|
||||||
let (sell_maker, sell_taker) = builder.get_market_order_amounts(
|
)
|
||||||
Side::SELL,
|
.unwrap();
|
||||||
Decimal::from_str("10").unwrap(),
|
let (sell_maker, sell_taker) = builder
|
||||||
Decimal::from_str("0.25").unwrap(),
|
.get_market_order_amounts(
|
||||||
round_config,
|
Side::SELL,
|
||||||
);
|
Decimal::from_str("10").unwrap(),
|
||||||
|
Decimal::from_str("0.25").unwrap(),
|
||||||
|
round_config,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(buy_maker, 10_000_000);
|
assert_eq!(buy_maker, U256::from(10_000_000));
|
||||||
assert_eq!(buy_taker, 40_000_000);
|
assert_eq!(buy_taker, U256::from(40_000_000));
|
||||||
assert_eq!(sell_maker, 10_000_000);
|
assert_eq!(sell_maker, U256::from(10_000_000));
|
||||||
assert_eq!(sell_taker, 2_500_000);
|
assert_eq!(sell_taker, U256::from(2_500_000));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user