Forráskód Böngészése

下单接口整理了80%了,还差个计算,tnnd

skyfffire 3 hete
szülő
commit
0b7a14f614
3 módosított fájl, 150 hozzáadás és 21 törlés
  1. 9 1
      src/exchange/extended_account.rs
  2. 139 18
      src/exchange/extended_rest_client.rs
  3. 2 2
      src/utils/mod.rs

+ 9 - 1
src/exchange/extended_account.rs

@@ -1,16 +1,20 @@
+use rust_decimal::prelude::Zero;
+
 #[derive(Clone, Debug)]
 pub struct ExtendedAccount {
     pub api_key: String,
     pub stark_public_key: String,
     pub stark_private_key: String,
+    pub vault_number: u32,
 }
 
 impl ExtendedAccount {
-    pub fn new(api_key: &str, stark_public_key: &str, stark_private_key: &str) -> Self {
+    pub fn new(api_key: &str, stark_public_key: &str, stark_private_key: &str, vault_number: u32) -> Self {
         ExtendedAccount {
             api_key: api_key.to_string(),
             stark_public_key: stark_public_key.to_string(),
             stark_private_key: stark_private_key.to_string(),
+            vault_number,
         }
     }
     
@@ -26,6 +30,10 @@ impl ExtendedAccount {
         if self.stark_private_key.is_empty() {
             return false;
         }
+
+        if self.vault_number.is_zero() {
+            return false;
+        }
         
         true
     }

+ 139 - 18
src/exchange/extended_rest_client.rs

@@ -1,13 +1,19 @@
+use std::str::FromStr;
 use chrono::Utc;
-use anyhow::{anyhow, Result};
+use anyhow::{anyhow, bail, Result};
 use reqwest::Client;
 use reqwest::header::HeaderMap;
+use rust_decimal::Decimal;
 use serde_json::{json, Value};
-use tracing::{error};
+use starknet::core::types::Felt;
+use tracing::{error, info, warn};
+use tracing_subscriber::fmt::format;
 use uuid::{Uuid};
 use crate::exchange::extended_account::ExtendedAccount;
+use crate::utils::lib::{get_order_hash, sign_message};
 use crate::utils::response::Response;
 use crate::utils::rest_utils::RestUtils;
+use crate::utils::starknet_messages::{AssetId, Order, PositionId, Timestamp};
 
 pub struct ExtendedRestClient {
     pub tag: String,
@@ -45,11 +51,46 @@ impl ExtendedRestClient {
 
         // 获取该client要操作的market的info
         let response = client.get_market_info(None).await;
-        client.market_info = response.data;
-        
-        Err(anyhow!("固定报错不要慌张"))
 
-        // Ok(client)
+        // 进一步解析
+        if response.code != 200 {
+            bail!(response.message);
+        } else {
+            let market_info = response.data;
+
+            match market_info.get("status") {
+                None => {
+                    bail!(serde_json::to_string_pretty(&market_info)?);
+                }
+                Some(status_value) => {
+                    match status_value.as_str() {
+                        None => {
+                            bail!(serde_json::to_string_pretty(&market_info)?);
+                        }
+                        Some(status) => {
+                            match status {
+                                "OK" => {
+                                    client.market_info = market_info.get("data").unwrap().as_array().unwrap().get(0).unwrap().clone();
+
+                                    let status = client.market_info.get("status").unwrap().as_str().unwrap();
+                                    if status != "ACTIVE" {
+                                        warn!("{}", serde_json::to_string_pretty(&client.market_info)?);
+                                        bail!("交易对状态异常,期待:ACTIVE,实际:{}", status);
+                                    }
+                                }
+                                _ => {
+                                    bail!(serde_json::to_string_pretty(&market_info)?);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        // bail!("固定报错不要慌张")
+
+        Ok(client)
     }
 
     // =================================== 公共方法区 ====================================
@@ -108,9 +149,11 @@ impl ExtendedRestClient {
         ).await
     }
 
-    pub async fn post_order(&mut self, order_type: &str, side: &str, qty: &str, price: &str) -> Response {
+    pub async fn post_order(&mut self, order_type: &str, side: &str, qty: &str, price: &str) -> Result<Response> {
+        let account = self.account.clone().ok_or_else(|| anyhow!("请将账户传入再进行下单操作"))?;
+
         // 需要传给extended的参数整理
-        let id = Uuid::new_v4().to_string().as_str();
+        let id = Uuid::new_v4().to_string();
         let market = self.market.as_str();
         // type
         // side
@@ -118,29 +161,90 @@ impl ExtendedRestClient {
         // price
         let time_in_force = "GTT";
         let expiry_epoch_millis = Utc::now().timestamp_millis() + (24 * 60 * 60 * 1000);
-        let fee = "";
         let nonce_u32: u32 = rand::random();
-        let nonce = nonce_u32.to_string().as_str();
+        let nonce = nonce_u32.to_string();
         let self_trade_protection_level = "ACCOUNT";
 
-
         // 准备OrderHash
+        let l2_config = self.market_info.get("l2Config").unwrap();
+
+        // 涉及到计算的
+        let is_buying_synthetic = side == "BUY";
+        let synthetic_amount = Decimal::from_str(qty)?;
+        let price = Decimal::from_str(price)?;
+        let collateral_amount = synthetic_amount.checked_mul(price)
+            .ok_or_else(|| anyhow!("Collateral amount multiplication overflowed"))?;
+
+        // 其余参数
+        let position_id = format!("{}", account.vault_number);
+        let base_asset_id_hex = l2_config.get("syntheticId").unwrap().as_str().unwrap().to_string();
+        let base_amount = "100".to_string();
+        let quote_asset_id_hex = l2_config.get("collateralId").unwrap().as_str().unwrap().to_string();
+        let quote_amount = "-156".to_string();
+        let fee_asset_id_hex = l2_config.get("collateralId").unwrap().as_str().unwrap().to_string();
+        let fee_amount = "74".to_string();
+        let expiration = format!("{}", (expiry_epoch_millis / 1000) as u64);
+        let salt = nonce.clone();
+        let user_public_key_hex = account.stark_public_key.clone();
+        let domain_name = "Perpetuals".to_string();
+        let domain_version = "v0".to_string();
+        let domain_chain_id = "SN_SEPOLIA".to_string();
+        let domain_revision = "1".to_string();
+
+        let hash = get_order_hash(
+            position_id.clone(),
+            base_asset_id_hex,
+            base_amount,
+            quote_asset_id_hex,
+            quote_amount,
+            fee_asset_id_hex,
+            fee_amount.clone(),
+            expiration,
+            salt,
+            user_public_key_hex,
+            domain_name,
+            domain_version,
+            domain_chain_id,
+            domain_revision,
+        ).unwrap();
+        let private_key = Felt::from_hex(account.stark_private_key.as_str())?;
+
+        // 签名
+        let signature = sign_message(&hash, &private_key).unwrap();
 
         // 生成settlement
-        let settlement = json!({});
+        let settlement = json!({
+            "signature": {
+                "r": format!("0x{:x}", signature.r),
+                "s": format!("0x{:x}", signature.s)
+            },
+            "starkKey": account.stark_public_key,
+            "collateralPosition": position_id,
+        });
 
         // 组装最后参数
         let params = json!({
-
+            "id": id,
+            "market": market,
+            "type": order_type,
+            "side": side,
+            "qty": qty,
+            "price": price,
+            "timeInForce": time_in_force,
+            "expiryEpochMillis": expiry_epoch_millis,
+            "fee": fee_amount,
+            "nonce": nonce,
+            "settlement": settlement,
+            "selfTradeProtectionLevel": self_trade_protection_level,
         });
 
         // 发送订单
-        self.request("POST",
+        Ok(self.request("POST",
                      "/api/v1",
                      "/user/order",
                      true,
                      params,
-        ).await
+        ).await)
     }
 
     // =================================== 网络层基础 ====================================
@@ -272,8 +376,8 @@ impl ExtendedRestClient {
                 error
             }
             Err(e) => {
-                error!("解析错误:{:?}", e);
-                let error = Response::error("".to_string(), "请检查apikey配置是否正确".to_string());
+                error!("解析错误,请检查apikey或参数配置是否正确:{:?}", e);
+                let error = Response::error("".to_string(), "请检查apikey或参数配置是否正确".to_string());
                 error
             }
         }
@@ -310,7 +414,7 @@ impl ExtendedRestClient {
 
 #[cfg(test)]
 mod tests {
-    use tracing::info;
+    use tracing::{info, warn};
     use crate::exchange::extended_account::ExtendedAccount;
     use crate::exchange::extended_rest_client::ExtendedRestClient;
     use crate::utils::log_setup::setup_logging;
@@ -320,6 +424,7 @@ mod tests {
             "a7b197d06d35de11387b8b71f34c87e4",
             "0x41efadf5ceebc77b0798b0af797fb97e610c87c669494bea54338c5ef8c0f19",
             "0x484b399394c4d76cdc62a1dc490f96cf5197f0e307832e59fdeec2e16c50078",
+            500089,
         );
         let tag = "Extended";
         let market = "BTC-USD";
@@ -354,6 +459,22 @@ mod tests {
         info!("{}", serde_json::to_string_pretty(&response.data).unwrap());
     }
 
+    #[tokio::test]
+    async fn test_create_order() {
+        let _guard = setup_logging().unwrap();
+        let mut client = get_client().await;
+        let response_result = client.post_order("limit", "BUY", "0.0001", "90000").await;
+
+        match response_result {
+            Ok(response) => {
+                info!("{}", serde_json::to_string_pretty(&response.data).unwrap());
+            }
+            Err(error) => {
+                warn!("{}", error.to_string());
+            }
+        }
+    }
+
     #[tokio::test]
     async fn test_cancel_order() {
         let _guard = setup_logging().unwrap();

+ 2 - 2
src/utils/mod.rs

@@ -3,5 +3,5 @@ pub mod rest_utils;
 pub mod stream_utils;
 pub(crate) mod response;
 pub(crate) mod proxy;
-mod starknet_messages;
-mod lib;
+pub mod starknet_messages;
+pub mod lib;