use alloy::{
hex::encode_prefixed,
primitives::{Address, U256},
signers::{SignerSync, local::PrivateKeySigner},
};
use alloy_sol_types::{SolValue, sol};
use anyhow::Result;
use bigdecimal::BigDecimal;
use serde::Deserialize;
use uuid::Uuid;
use crate::{
actions::{
ActionData, ModuleData,
utils::{decimal_to_i256, decimal_to_u256},
},
constants::{CLIENT_NAME, REFFERAL_CODE},
models::{
AlgoType, CreateOrderRequest as OrderParams, Direction, Instrument, OrderType,
ReplaceOrderRequest as ReplaceParams, TimeInForce, TriggerPriceType, TriggerType,
},
types::Environment,
};
use bon::Builder;
fn default_max_fee() -> BigDecimal {
BigDecimal::from(1000u64)
}
#[derive(Clone, Debug, Deserialize, Builder)]
pub struct OrderArgs {
#[builder(into)]
pub amount: BigDecimal,
#[builder(into)]
pub limit_price: BigDecimal,
pub direction: Direction,
pub time_in_force: TimeInForce,
pub order_type: OrderType,
pub label: Option<String>,
pub mmp: Option<bool>,
pub instrument_name: String,
pub reduce_only: Option<bool>,
pub reject_post_only: Option<bool>,
pub reject_timestamp: Option<i64>,
pub trigger_price: Option<BigDecimal>,
pub trigger_price_type: Option<TriggerPriceType>,
pub trigger_type: Option<TriggerType>,
pub extra_fee: Option<BigDecimal>,
pub algo_duration_sec: Option<i32>,
pub algo_num_slices: Option<i32>,
pub algo_type: Option<AlgoType>,
}
#[derive(Clone, Debug, Deserialize, Builder)]
pub struct ReplaceArgs {
#[builder(into)]
pub amount: BigDecimal,
#[builder(into)]
pub limit_price: BigDecimal,
pub direction: Direction,
pub time_in_force: TimeInForce,
pub order_type: OrderType,
pub label: Option<String>,
pub mmp: Option<bool>,
pub instrument_name: String,
pub reduce_only: Option<bool>,
pub reject_post_only: Option<bool>,
pub nonce_to_cancel: Option<i64>,
pub order_id_to_cancel: Option<Uuid>,
pub expected_filled_amount: Option<BigDecimal>,
pub reject_timestamp: Option<i64>,
pub trigger_price: Option<BigDecimal>,
pub trigger_price_type: Option<TriggerPriceType>,
pub trigger_type: Option<TriggerType>,
pub extra_fee: Option<BigDecimal>,
pub algo_duration_sec: Option<i32>,
pub algo_num_slices: Option<i32>,
pub algo_type: Option<AlgoType>,
}
pub fn get_reject_millis(time_in_force: &TimeInForce) -> Result<i64> {
let reject_millis: i64 = 5000;
let taker_speedbump: i64 = 150;
let reject_millis = match time_in_force {
TimeInForce::PostOnly => reject_millis,
_ => reject_millis + taker_speedbump,
};
Ok(reject_millis)
}
sol! {
#![sol(all_derives)]
struct TradeData {
address asset_address;
uint256 sub_id;
int256 limit_price;
int256 amount;
uint256 max_fee;
uint256 subaccount_id;
bool is_bid;
}
}
impl TradeData {
pub fn new(
instrument: &Instrument,
subaccount_id: u64,
limit_price: &BigDecimal,
amount: &BigDecimal,
is_bid: bool,
) -> Result<Self> {
Ok(Self {
asset_address: instrument.base_asset_address.parse::<Address>()?,
sub_id: U256::from(instrument.base_asset_sub_id.parse::<u128>()?),
limit_price: decimal_to_i256(limit_price)?,
amount: decimal_to_i256(amount)?,
max_fee: decimal_to_u256(&default_max_fee())?,
subaccount_id: U256::try_from(subaccount_id)?,
is_bid,
})
}
}
impl ModuleData for TradeData {
fn get_action_data(&self) -> Vec<u8> {
self.abi_encode()
}
}
impl ActionData {
pub fn populate_order_params(
self,
signer: &PrivateKeySigner,
args: OrderArgs,
env: &Environment,
rounded_price: BigDecimal,
rounded_amount: BigDecimal,
) -> Result<OrderParams> {
Ok(OrderParams {
instrument_name: args.instrument_name,
amount: rounded_amount,
limit_price: rounded_price,
direction: args.direction,
time_in_force: args.time_in_force,
order_type: args.order_type,
mmp: args.mmp,
label: args.label,
reduce_only: args.reduce_only,
reject_post_only: args.reject_post_only,
is_atomic_signing: Some(false),
subaccount_id: i64::try_from(&self.subaccount_id)?,
max_fee: default_max_fee(),
nonce: self.nonce.to_string(),
signature_expiry_sec: i64::try_from(&self.expiry)?,
signer: encode_prefixed(self.signer),
referral_code: Some(REFFERAL_CODE.to_string()),
signature: format!("{}", signer.sign_hash_sync(&self.hash(env).clone())?),
client: Some(CLIENT_NAME.to_string()),
reject_timestamp: args.reject_timestamp,
trigger_price: args.trigger_price,
trigger_price_type: args.trigger_price_type,
trigger_type: args.trigger_type,
extra_fee: args.extra_fee,
algo_duration_sec: args.algo_duration_sec,
algo_num_slices: args.algo_num_slices,
algo_type: args.algo_type,
})
}
pub fn populate_replace_params(
self,
signer: &PrivateKeySigner,
args: ReplaceArgs,
env: &Environment,
rounded_price: BigDecimal,
rounded_amount: BigDecimal,
) -> Result<ReplaceParams> {
Ok(ReplaceParams {
instrument_name: args.instrument_name,
amount: rounded_amount,
limit_price: rounded_price,
direction: args.direction,
time_in_force: args.time_in_force,
order_type: args.order_type,
mmp: args.mmp,
label: args.label,
reduce_only: args.reduce_only,
reject_post_only: args.reject_post_only,
reject_timestamp: args.reject_timestamp,
is_atomic_signing: Some(false),
subaccount_id: i64::try_from(&self.subaccount_id)?,
max_fee: default_max_fee(),
nonce: self.nonce.to_string(),
signature_expiry_sec: i64::try_from(&self.expiry)?,
signer: encode_prefixed(self.signer),
referral_code: Some(REFFERAL_CODE.to_string()),
signature: format!("{}", signer.sign_hash_sync(&self.hash(env).clone())?),
client: Some(CLIENT_NAME.to_string()),
trigger_price: args.trigger_price,
trigger_price_type: args.trigger_price_type,
trigger_type: args.trigger_type,
extra_fee: args.extra_fee,
algo_duration_sec: args.algo_duration_sec,
algo_num_slices: args.algo_num_slices,
algo_type: args.algo_type,
nonce_to_cancel: args.nonce_to_cancel,
order_id_to_cancel: args.order_id_to_cancel,
expected_filled_amount: args.expected_filled_amount,
})
}
}