ソースを参照

继续完善rest client

skyfffire 3 週間 前
コミット
dc77a96af1

+ 10 - 14
src/data_manager.rs

@@ -1,16 +1,12 @@
-use std::cmp::Reverse;
-use std::collections::{BTreeMap, HashMap};
+use std::collections::{HashMap};
 use std::sync::atomic::{AtomicI64, AtomicU64, Ordering};
-use rust_decimal::Decimal;
-use serde_json::{from_value, Value};
-use anyhow::{bail, Context, Result};
-use serde::{Deserialize, Serialize};
-use tracing::{info, warn};
+use anyhow::{Result};
+use tracing::{warn};
 use crate::utils::response::Response;
 
 pub struct DataManager {
-    pub asks_map: HashMap<String, BTreeMap<Decimal, Decimal>>,
-    pub bids_map: HashMap<String, BTreeMap<Reverse<Decimal>, Decimal>>,
+    // pub asks_map: HashMap<String, BTreeMap<Decimal, Decimal>>,
+    // pub bids_map: HashMap<String, BTreeMap<Reverse<Decimal>, Decimal>>,
 
     pub delay_total: AtomicI64,
     pub delay_count: AtomicU64,
@@ -18,12 +14,12 @@ pub struct DataManager {
 
 impl DataManager {
     pub fn new() -> Self {
-        let asks_map: HashMap<String, BTreeMap<Decimal, Decimal>> = HashMap::new();
-        let bids_map: HashMap<String, BTreeMap<Reverse<Decimal>, Decimal>> = HashMap::new();
+        // let asks_map: HashMap<String, BTreeMap<Decimal, Decimal>> = HashMap::new();
+        // let bids_map: HashMap<String, BTreeMap<Reverse<Decimal>, Decimal>> = HashMap::new();
 
         DataManager {
-            asks_map,
-            bids_map,
+            // asks_map,
+            // bids_map,
             delay_total: AtomicI64::new(0),
             delay_count: AtomicU64::new(0),
         }
@@ -51,7 +47,7 @@ impl DataManager {
         self.delay_count.store(0, Ordering::Relaxed); // 原子写
     }
     
-    pub async fn dispatch_message(&mut self, response: &Response) -> Result<()> {
+    pub async fn dispatch_message(&mut self, _response: &Response) -> Result<()> {
         // // 1. 预解析为通用的 Value
         // let v = response.data.clone();
         // 

+ 36 - 20
src/exchange/extended_rest_client.rs

@@ -1,7 +1,7 @@
 use reqwest::Client;
 use reqwest::header::HeaderMap;
 use serde_json::{json, Value};
-use tracing::{error, info};
+use tracing::{error};
 use crate::exchange::extended_account::ExtendedAccount;
 use crate::utils::response::Response;
 use crate::utils::rest_utils::RestUtils;
@@ -24,8 +24,8 @@ impl ExtendedRestClient {
     pub fn new(tag: String, account: Option<ExtendedAccount>) -> Self {
         ExtendedRestClient {
             tag,
-            base_url: "https://api.starknet.extended.exchange".to_string(),
-            // base_url: "https://api.starknet.sepolia.extended.exchange".to_string(),
+            // base_url: "https://api.starknet.extended.exchange".to_string(),
+            base_url: "https://api.starknet.sepolia.extended.exchange".to_string(),
             client: Client::new(),
             account,
 
@@ -37,31 +37,50 @@ impl ExtendedRestClient {
 
     // =================================== 公共方法区 ====================================
 
-    // =================================== 私有方法区,这边仅需要携带header ====================================
-    pub async fn cancel_order(&mut self, external_id: String) -> Response {
+    // =================================== 私有方法区 ====================================
+    pub async fn get_open_orders(&mut self) -> Response {
+        let params = json!({});
+
+        self.request("GET",
+                     "/api/v1/user",
+                     "/orders",
+                     true,
+                     params,
+        ).await
+    }
+
+    pub async fn cancel_order(&mut self, id: String) -> Response {
+        let params = json!({});
+
+        self.request("DELETE",
+                     "/api/v1/user",
+                     format!("/order/{}", id).as_str(),
+                     true,
+                     params,
+        ).await
+    }
+
+    pub async fn cancel_order_by_external_id(&mut self, external_id: String) -> Response {
         let params = json!({
             "external_id": external_id,
         });
 
-        self.request("DELETE".to_string(),
-                     "/api/v1/user".to_string(),
-                     "/order".to_string(),
+        self.request("DELETE",
+                     "/api/v1/user",
+                     "/order",
                      true,
-                     false,
                      params,
         ).await
     }
-    // =================================== 签名方法区,这边需要签名=========================
 
     // =================================== 网络层基础 ====================================
     // 发送请求
     pub async fn request(&mut self,
-                         method: String,
-                         prefix_url: String,
-                         request_url: String,
+                         method: &str,
+                         prefix_url: &str,
+                         request_url: &str,
                          is_private: bool,
-                         _is_sign: bool,
-                         mut params: Value) -> Response
+                         params: Value) -> Response
     {
         // ----------------- 每个接口都有的公共参数 ------------------
         // let timestamp = Utc::now().timestamp_millis();
@@ -108,7 +127,7 @@ impl ExtendedRestClient {
         // --------------------- 最终发送 ---------------------------
         let start_time = chrono::Utc::now().timestamp_millis();
         let response = self.do_request(
-            format!("{}{}", prefix_url.clone(), request_url.clone()),
+            format!("{}{}", prefix_url, request_url),
             method.to_string(),
             params.to_string(),
             body,
@@ -152,8 +171,6 @@ impl ExtendedRestClient {
         // 先检查状态码
         let is_success = response.status().is_success();
         let text = response.text().await.unwrap();
-        
-        info!(text);
 
         if is_success {
             self.on_success_data(&text)
@@ -186,7 +203,7 @@ impl ExtendedRestClient {
             }
             Err(e) => {
                 error!("解析错误:{:?}", e);
-                let error = Response::error("".to_string(), text.clone());
+                let error = Response::error("".to_string(), "请检查apikey配置是否正确".to_string());
                 error
             }
         }
@@ -235,7 +252,6 @@ mod tests {
         let mut client = ExtendedRestClient::new("Extended".to_string(), Some(account));
         let response = client.cancel_order("123456".to_string()).await;
 
-        info!("{:?}", response);
         info!("{}", serde_json::to_string_pretty(&response.data).unwrap());
     }
 }

+ 1 - 2
src/exchange/extended_stream_client.rs

@@ -7,11 +7,10 @@ use serde_json::json;
 use serde_json::Value;
 use tokio::sync::Mutex;
 use tokio_tungstenite::tungstenite::{http, Message};
-use tracing::{error, info, trace, warn};
+use tracing::{error, trace, warn};
 use anyhow::Result;
 use chrono::Utc;
 use tokio_tungstenite::tungstenite::handshake::client::{generate_key, Request};
-use tracing_subscriber::fmt::format::json;
 use crate::exchange::extended_account::ExtendedAccount;
 use crate::utils::response::Response;
 use crate::utils::stream_utils::{StreamUtils, HeartbeatType};

+ 0 - 1
src/main.rs

@@ -6,7 +6,6 @@ mod data_manager;
 use anyhow::Result;
 use std::sync::Arc;
 use std::sync::atomic::{AtomicBool, Ordering};
-use std::time::Duration;
 use backtrace::Backtrace;
 use tokio::spawn;
 use tokio::sync::Mutex;

+ 423 - 423
src/utils/lib.rs

@@ -1,423 +1,423 @@
-use hex;
-use num_bigint::BigUint;
-use sha2::{Digest, Sha256};
-use starknet::core::crypto::ecdsa_sign;
-use starknet::core::types::Felt;
-use std::str::FromStr;
-
-use crate::utils::starknet_messages::{
-    AssetId, OffChainMessage, Order, PositionId, StarknetDomain, Timestamp, TransferArgs,
-};
-use utils::starknet_messages;
-use crate::utils;
-
-pub struct StarkSignature {
-    pub r: Felt,
-    pub s: Felt,
-    pub v: Felt,
-}
-
-fn grind_key(key_seed: BigUint) -> BigUint {
-    let two_256 = BigUint::from_str(
-        "115792089237316195423570985008687907853269984665640564039457584007913129639936",
-    )
-    .unwrap();
-    let key_value_limit = BigUint::from_str(
-        "3618502788666131213697322783095070105526743751716087489154079457884512865583",
-    )
-    .unwrap();
-
-    let max_allowed_value = two_256.clone() - (two_256.clone() % (&key_value_limit));
-    let mut index = BigUint::ZERO;
-    loop {
-        let hash_input = {
-            let mut input = Vec::new();
-            input.extend_from_slice(&key_seed.to_bytes_be());
-            input.extend_from_slice(&index.to_bytes_be());
-            input
-        };
-        let hash_result = Sha256::digest(&hash_input);
-        let hash = hash_result.as_slice();
-        let key = BigUint::from_bytes_be(&hash);
-
-        if key < max_allowed_value {
-            return key % (&key_value_limit);
-        }
-
-        index += BigUint::from_str("1").unwrap();
-    }
-}
-
-pub fn get_private_key_from_eth_signature(signature: &str) -> Result<Felt, String> {
-    let eth_sig_truncated = signature.trim_start_matches("0x");
-    if eth_sig_truncated.len() < 64 {
-        return Err("Invalid signature length".to_string());
-    }
-    let r = &eth_sig_truncated[..64];
-    let r_bytes = hex::decode(r).map_err(|e| format!("Failed to decode r as hex: {:?}", e))?;
-    let r_int = BigUint::from_bytes_be(&r_bytes);
-
-    let ground_key = grind_key(r_int);
-    return Ok(Felt::from_hex(&ground_key.to_str_radix(16)).unwrap());
-}
-
-pub fn sign_message(message: &Felt, private_key: &Felt) -> Result<StarkSignature, String> {
-    return ecdsa_sign(private_key, &message)
-        .map(|extended_signature| StarkSignature {
-            r: extended_signature.r,
-            s: extended_signature.s,
-            v: extended_signature.v,
-        })
-        .map_err(|e| format!("Failed to sign message: {:?}", e));
-}
-
-// these functions are designed to be called from other languages, such as Python or JavaScript,
-// so they take string arguments.
-pub fn get_order_hash(
-    position_id: String,
-    base_asset_id_hex: String,
-    base_amount: String,
-    quote_asset_id_hex: String,
-    quote_amount: String,
-    fee_asset_id_hex: String,
-    fee_amount: String,
-    expiration: String,
-    salt: String,
-    user_public_key_hex: String,
-    domain_name: String,
-    domain_version: String,
-    domain_chain_id: String,
-    domain_revision: String,
-) -> Result<Felt, String> {
-    let base_asset_id = Felt::from_hex(&base_asset_id_hex)
-        .map_err(|e| format!("Invalid base_asset_id_hex: {:?}", e))?;
-    let quote_asset_id = Felt::from_hex(&quote_asset_id_hex)
-        .map_err(|e| format!("Invalid quote_asset_id_hex: {:?}", e))?;
-    let fee_asset_id = Felt::from_hex(&fee_asset_id_hex)
-        .map_err(|e| format!("Invalid fee_asset_id_hex: {:?}", e))?;
-    let user_key = Felt::from_hex(&user_public_key_hex)
-        .map_err(|e| format!("Invalid user_public_key_hex: {:?}", e))?;
-
-    let position_id = u32::from_str_radix(&position_id, 10)
-        .map_err(|e| format!("Invalid position_id: {:?}", e))?;
-    let base_amount = i64::from_str_radix(&base_amount, 10)
-        .map_err(|e| format!("Invalid base_amount: {:?}", e))?;
-    let quote_amount = i64::from_str_radix(&quote_amount, 10)
-        .map_err(|e| format!("Invalid quote_amount: {:?}", e))?;
-    let fee_amount =
-        u64::from_str_radix(&fee_amount, 10).map_err(|e| format!("Invalid fee_amount: {:?}", e))?;
-    let expiration =
-        u64::from_str_radix(&expiration, 10).map_err(|e| format!("Invalid expiration: {:?}", e))?;
-    let salt = u64::from_str_radix(&salt, 10).map_err(|e| format!("Invalid salt: {:?}", e))?;
-    let revision = u32::from_str_radix(&domain_revision, 10)
-        .map_err(|e| format!("Invalid domain_revision: {:?}", e))?;
-
-    let order = Order {
-        position_id: PositionId { value: position_id },
-        base_asset_id: AssetId {
-            value: base_asset_id,
-        },
-        base_amount,
-        quote_asset_id: AssetId {
-            value: quote_asset_id,
-        },
-        quote_amount,
-        fee_asset_id: AssetId {
-            value: fee_asset_id,
-        },
-        fee_amount,
-        expiration: Timestamp {
-            seconds: expiration,
-        },
-        salt: salt
-            .try_into()
-            .map_err(|e| format!("Invalid salt vault: {:?}", e))?,
-    };
-    let domain = StarknetDomain {
-        name: domain_name,
-        version: domain_version,
-        chain_id: domain_chain_id,
-        revision,
-    };
-    order
-        .message_hash(&domain, user_key)
-        .map_err(|e| format!("Failed to compute message hash: {:?}", e))
-}
-
-pub fn get_transfer_hash(
-    recipient_position_id: String,
-    sender_position_id: String,
-    collateral_id_hex: String,
-    amount: String,
-    expiration: String,
-    salt: String,
-    user_public_key_hex: String,
-    domain_name: String,
-    domain_version: String,
-    domain_chain_id: String,
-    domain_revision: String,
-) -> Result<Felt, String> {
-    let collateral_id = Felt::from_hex(&collateral_id_hex)
-        .map_err(|e| format!("Invalid collateral_id_hex: {:?}", e))?;
-    let user_key = Felt::from_hex(&user_public_key_hex)
-        .map_err(|e| format!("Invalid user_public_key_hex: {:?}", e))?;
-
-    let recipient = u32::from_str_radix(&recipient_position_id, 10)
-        .map_err(|e| format!("Invalid recipient_position_id: {:?}", e))?;
-    let position_id = u32::from_str_radix(&sender_position_id, 10)
-        .map_err(|e| format!("Invalid sender_position_id: {:?}", e))?;
-    let amount =
-        u64::from_str_radix(&amount, 10).map_err(|e| format!("Invalid amount: {:?}", e))?;
-    let expiration =
-        u64::from_str_radix(&expiration, 10).map_err(|e| format!("Invalid expiration: {:?}", e))?;
-    let salt = Felt::from_dec_str(&salt).map_err(|e| format!("Invalid salt: {:?}", e))?;
-    let revision = u32::from_str_radix(&domain_revision, 10)
-        .map_err(|e| format!("Invalid domain_revision: {:?}", e))?;
-
-    let transfer_args = TransferArgs {
-        recipient: PositionId { value: recipient },
-        position_id: PositionId { value: position_id },
-        collateral_id: AssetId {
-            value: collateral_id,
-        },
-        amount,
-        expiration: Timestamp {
-            seconds: expiration,
-        },
-        salt,
-    };
-    let domain = StarknetDomain {
-        name: domain_name,
-        version: domain_version,
-        chain_id: domain_chain_id,
-        revision,
-    };
-    transfer_args
-        .message_hash(&domain, user_key)
-        .map_err(|e| format!("Failed to compute message hash: {:?}", e))
-}
-
-pub fn get_withdrawal_hash(
-    recipient_hex: String,
-    position_id: String,
-    collateral_id_hex: String,
-    amount: String,
-    expiration: String,
-    salt: String,
-    user_public_key_hex: String,
-    domain_name: String,
-    domain_version: String,
-    domain_chain_id: String,
-    domain_revision: String,
-) -> Result<Felt, String> {
-    let collateral_id = Felt::from_hex(&collateral_id_hex)
-        .map_err(|e| format!("Invalid collateral_id_hex: {:?}", e))?;
-    let user_key = Felt::from_hex(&user_public_key_hex)
-        .map_err(|e| format!("Invalid user_public_key_hex: {:?}", e))?;
-
-    let recipient =
-        Felt::from_hex(&recipient_hex).map_err(|e| format!("Invalid recipient_hex: {:?}", e))?;
-    let position_id = u32::from_str_radix(&position_id, 10)
-        .map_err(|e| format!("Invalid position_id: {:?}", e))?;
-    let amount =
-        u64::from_str_radix(&amount, 10).map_err(|e| format!("Invalid amount: {:?}", e))?;
-    let expiration =
-        u64::from_str_radix(&expiration, 10).map_err(|e| format!("Invalid expiration: {:?}", e))?;
-    let salt = Felt::from_dec_str(&salt).map_err(|e| format!("Invalid salt: {:?}", e))?;
-    let revision = u32::from_str_radix(&domain_revision, 10)
-        .map_err(|e| format!("Invalid domain_revision: {:?}", e))?;
-
-    let withdrawal_args = starknet_messages::WithdrawalArgs {
-        recipient,
-        position_id: PositionId { value: position_id },
-        collateral_id: AssetId {
-            value: collateral_id,
-        },
-        amount,
-        expiration: Timestamp {
-            seconds: expiration,
-        },
-        salt,
-    };
-    let domain = StarknetDomain {
-        name: domain_name,
-        version: domain_version,
-        chain_id: domain_chain_id,
-        revision,
-    };
-    withdrawal_args
-        .message_hash(&domain, user_key)
-        .map_err(|e| {
-            format!(
-                "Failed to compute message hash for withdrawal args: {:?}",
-                e
-            )
-        })
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn test_get_private_key_from_eth_signature() {
-        let signature = "0x9ef64d5936681edf44b4a7ad713f3bc24065d4039562af03fccf6a08d6996eab367df11439169b417b6a6d8ce81d409edb022597ce193916757c7d5d9cbf97301c";
-        let result = get_private_key_from_eth_signature(signature);
-
-        match result {
-            Ok(private_key) => {
-                assert_eq!(private_key, Felt::from_dec_str("3554363360756768076148116215296798451844584215587910826843139626172125285444").unwrap());
-            }
-            Err(err) => {
-                panic!("Expected Ok, got Err: {}", err);
-            }
-        }
-    }
-
-    #[test]
-    fn test_get_transfer_msg() {
-        let recipient_position_id = "1".to_string();
-        let sender_position_id = "2".to_string();
-        let collateral_id_hex = "0x3".to_string();
-        let amount = "4".to_string();
-        let expiration = "5".to_string();
-        let salt = "6".to_string();
-        let user_public_key_hex =
-            "0x5d05989e9302dcebc74e241001e3e3ac3f4402ccf2f8e6f74b034b07ad6a904".to_string();
-        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 result = get_transfer_hash(
-            recipient_position_id,
-            sender_position_id,
-            collateral_id_hex,
-            amount,
-            expiration,
-            salt,
-            user_public_key_hex,
-            domain_name,
-            domain_version,
-            domain_chain_id,
-            domain_revision,
-        );
-
-        match result {
-            Ok(hash) => {
-                assert_eq!(
-                    hash,
-                    Felt::from_hex(
-                        "0x56c7b21d13b79a33d7700dda20e22246c25e89818249504148174f527fc3f8f"
-                    )
-                    .unwrap()
-                );
-            }
-            Err(err) => {
-                panic!("Expected Ok, got Err: {}", err);
-            }
-        }
-    }
-
-    #[test]
-    fn test_get_order_hash() {
-        let position_id = "100".to_string();
-        let base_asset_id_hex = "0x2".to_string();
-        let base_amount = "100".to_string();
-        let quote_asset_id_hex = "0x1".to_string();
-        let quote_amount = "-156".to_string();
-        let fee_asset_id_hex = "0x1".to_string();
-        let fee_amount = "74".to_string();
-        let expiration = "100".to_string();
-        let salt = "123".to_string();
-        let user_public_key_hex =
-            "0x5d05989e9302dcebc74e241001e3e3ac3f4402ccf2f8e6f74b034b07ad6a904".to_string();
-        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 result = get_order_hash(
-            position_id,
-            base_asset_id_hex,
-            base_amount,
-            quote_asset_id_hex,
-            quote_amount,
-            fee_asset_id_hex,
-            fee_amount,
-            expiration,
-            salt,
-            user_public_key_hex,
-            domain_name,
-            domain_version,
-            domain_chain_id,
-            domain_revision,
-        );
-
-        match result {
-            Ok(hash) => {
-                assert_eq!(
-                    hash,
-                    Felt::from_hex(
-                        "0x4de4c009e0d0c5a70a7da0e2039fb2b99f376d53496f89d9f437e736add6b48"
-                    )
-                    .unwrap()
-                );
-            }
-            Err(err) => {
-                panic!("Expected Ok, got Err: {}", err);
-            }
-        }
-    }
-
-    #[test]
-    fn test_get_withdrawal_hash() {
-        let recipient_hex = Felt::from_dec_str(
-            "206642948138484946401984817000601902748248360221625950604253680558965863254",
-        )
-        .unwrap()
-        .to_hex_string();
-        let position_id = "2".to_string();
-        let collateral_id_hex = Felt::from_dec_str(
-            "1386727789535574059419576650469753513512158569780862144831829362722992755422",
-        )
-        .unwrap()
-        .to_hex_string();
-        let amount = "1000".to_string();
-        let expiration = "0".to_string();
-        let salt = "0".to_string();
-        let user_public_key_hex =
-            "0x5D05989E9302DCEBC74E241001E3E3AC3F4402CCF2F8E6F74B034B07AD6A904".to_string();
-        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 result = get_withdrawal_hash(
-            recipient_hex,
-            position_id,
-            collateral_id_hex,
-            amount,
-            expiration,
-            salt,
-            user_public_key_hex,
-            domain_name,
-            domain_version,
-            domain_chain_id,
-            domain_revision,
-        );
-        match result {
-            Ok(hash) => {
-                assert_eq!(
-                    hash,
-                    Felt::from_dec_str(
-                        "2182119571682827544073774098906745929330860211691330979324731407862023927178"
-                    )
-                    .unwrap()
-                );
-            }
-            Err(err) => {
-                panic!("Expected Ok, got Err: {}", err);
-            }
-        }
-    }
-}
+// use hex;
+// use num_bigint::BigUint;
+// use sha2::{Digest, Sha256};
+// use starknet::core::crypto::ecdsa_sign;
+// use starknet::core::types::Felt;
+// use std::str::FromStr;
+// 
+// use crate::utils::starknet_messages::{
+//     AssetId, OffChainMessage, Order, PositionId, StarknetDomain, Timestamp, TransferArgs,
+// };
+// use utils::starknet_messages;
+// use crate::utils;
+// 
+// pub struct StarkSignature {
+//     pub r: Felt,
+//     pub s: Felt,
+//     pub v: Felt,
+// }
+// 
+// fn grind_key(key_seed: BigUint) -> BigUint {
+//     let two_256 = BigUint::from_str(
+//         "115792089237316195423570985008687907853269984665640564039457584007913129639936",
+//     )
+//     .unwrap();
+//     let key_value_limit = BigUint::from_str(
+//         "3618502788666131213697322783095070105526743751716087489154079457884512865583",
+//     )
+//     .unwrap();
+// 
+//     let max_allowed_value = two_256.clone() - (two_256.clone() % (&key_value_limit));
+//     let mut index = BigUint::ZERO;
+//     loop {
+//         let hash_input = {
+//             let mut input = Vec::new();
+//             input.extend_from_slice(&key_seed.to_bytes_be());
+//             input.extend_from_slice(&index.to_bytes_be());
+//             input
+//         };
+//         let hash_result = Sha256::digest(&hash_input);
+//         let hash = hash_result.as_slice();
+//         let key = BigUint::from_bytes_be(&hash);
+// 
+//         if key < max_allowed_value {
+//             return key % (&key_value_limit);
+//         }
+// 
+//         index += BigUint::from_str("1").unwrap();
+//     }
+// }
+// 
+// pub fn get_private_key_from_eth_signature(signature: &str) -> Result<Felt, String> {
+//     let eth_sig_truncated = signature.trim_start_matches("0x");
+//     if eth_sig_truncated.len() < 64 {
+//         return Err("Invalid signature length".to_string());
+//     }
+//     let r = &eth_sig_truncated[..64];
+//     let r_bytes = hex::decode(r).map_err(|e| format!("Failed to decode r as hex: {:?}", e))?;
+//     let r_int = BigUint::from_bytes_be(&r_bytes);
+// 
+//     let ground_key = grind_key(r_int);
+//     return Ok(Felt::from_hex(&ground_key.to_str_radix(16)).unwrap());
+// }
+// 
+// pub fn sign_message(message: &Felt, private_key: &Felt) -> Result<StarkSignature, String> {
+//     return ecdsa_sign(private_key, &message)
+//         .map(|extended_signature| StarkSignature {
+//             r: extended_signature.r,
+//             s: extended_signature.s,
+//             v: extended_signature.v,
+//         })
+//         .map_err(|e| format!("Failed to sign message: {:?}", e));
+// }
+// 
+// // these functions are designed to be called from other languages, such as Python or JavaScript,
+// // so they take string arguments.
+// pub fn get_order_hash(
+//     position_id: String,
+//     base_asset_id_hex: String,
+//     base_amount: String,
+//     quote_asset_id_hex: String,
+//     quote_amount: String,
+//     fee_asset_id_hex: String,
+//     fee_amount: String,
+//     expiration: String,
+//     salt: String,
+//     user_public_key_hex: String,
+//     domain_name: String,
+//     domain_version: String,
+//     domain_chain_id: String,
+//     domain_revision: String,
+// ) -> Result<Felt, String> {
+//     let base_asset_id = Felt::from_hex(&base_asset_id_hex)
+//         .map_err(|e| format!("Invalid base_asset_id_hex: {:?}", e))?;
+//     let quote_asset_id = Felt::from_hex(&quote_asset_id_hex)
+//         .map_err(|e| format!("Invalid quote_asset_id_hex: {:?}", e))?;
+//     let fee_asset_id = Felt::from_hex(&fee_asset_id_hex)
+//         .map_err(|e| format!("Invalid fee_asset_id_hex: {:?}", e))?;
+//     let user_key = Felt::from_hex(&user_public_key_hex)
+//         .map_err(|e| format!("Invalid user_public_key_hex: {:?}", e))?;
+// 
+//     let position_id = u32::from_str_radix(&position_id, 10)
+//         .map_err(|e| format!("Invalid position_id: {:?}", e))?;
+//     let base_amount = i64::from_str_radix(&base_amount, 10)
+//         .map_err(|e| format!("Invalid base_amount: {:?}", e))?;
+//     let quote_amount = i64::from_str_radix(&quote_amount, 10)
+//         .map_err(|e| format!("Invalid quote_amount: {:?}", e))?;
+//     let fee_amount =
+//         u64::from_str_radix(&fee_amount, 10).map_err(|e| format!("Invalid fee_amount: {:?}", e))?;
+//     let expiration =
+//         u64::from_str_radix(&expiration, 10).map_err(|e| format!("Invalid expiration: {:?}", e))?;
+//     let salt = u64::from_str_radix(&salt, 10).map_err(|e| format!("Invalid salt: {:?}", e))?;
+//     let revision = u32::from_str_radix(&domain_revision, 10)
+//         .map_err(|e| format!("Invalid domain_revision: {:?}", e))?;
+// 
+//     let order = Order {
+//         position_id: PositionId { value: position_id },
+//         base_asset_id: AssetId {
+//             value: base_asset_id,
+//         },
+//         base_amount,
+//         quote_asset_id: AssetId {
+//             value: quote_asset_id,
+//         },
+//         quote_amount,
+//         fee_asset_id: AssetId {
+//             value: fee_asset_id,
+//         },
+//         fee_amount,
+//         expiration: Timestamp {
+//             seconds: expiration,
+//         },
+//         salt: salt
+//             .try_into()
+//             .map_err(|e| format!("Invalid salt vault: {:?}", e))?,
+//     };
+//     let domain = StarknetDomain {
+//         name: domain_name,
+//         version: domain_version,
+//         chain_id: domain_chain_id,
+//         revision,
+//     };
+//     order
+//         .message_hash(&domain, user_key)
+//         .map_err(|e| format!("Failed to compute message hash: {:?}", e))
+// }
+// 
+// pub fn get_transfer_hash(
+//     recipient_position_id: String,
+//     sender_position_id: String,
+//     collateral_id_hex: String,
+//     amount: String,
+//     expiration: String,
+//     salt: String,
+//     user_public_key_hex: String,
+//     domain_name: String,
+//     domain_version: String,
+//     domain_chain_id: String,
+//     domain_revision: String,
+// ) -> Result<Felt, String> {
+//     let collateral_id = Felt::from_hex(&collateral_id_hex)
+//         .map_err(|e| format!("Invalid collateral_id_hex: {:?}", e))?;
+//     let user_key = Felt::from_hex(&user_public_key_hex)
+//         .map_err(|e| format!("Invalid user_public_key_hex: {:?}", e))?;
+// 
+//     let recipient = u32::from_str_radix(&recipient_position_id, 10)
+//         .map_err(|e| format!("Invalid recipient_position_id: {:?}", e))?;
+//     let position_id = u32::from_str_radix(&sender_position_id, 10)
+//         .map_err(|e| format!("Invalid sender_position_id: {:?}", e))?;
+//     let amount =
+//         u64::from_str_radix(&amount, 10).map_err(|e| format!("Invalid amount: {:?}", e))?;
+//     let expiration =
+//         u64::from_str_radix(&expiration, 10).map_err(|e| format!("Invalid expiration: {:?}", e))?;
+//     let salt = Felt::from_dec_str(&salt).map_err(|e| format!("Invalid salt: {:?}", e))?;
+//     let revision = u32::from_str_radix(&domain_revision, 10)
+//         .map_err(|e| format!("Invalid domain_revision: {:?}", e))?;
+// 
+//     let transfer_args = TransferArgs {
+//         recipient: PositionId { value: recipient },
+//         position_id: PositionId { value: position_id },
+//         collateral_id: AssetId {
+//             value: collateral_id,
+//         },
+//         amount,
+//         expiration: Timestamp {
+//             seconds: expiration,
+//         },
+//         salt,
+//     };
+//     let domain = StarknetDomain {
+//         name: domain_name,
+//         version: domain_version,
+//         chain_id: domain_chain_id,
+//         revision,
+//     };
+//     transfer_args
+//         .message_hash(&domain, user_key)
+//         .map_err(|e| format!("Failed to compute message hash: {:?}", e))
+// }
+// 
+// pub fn get_withdrawal_hash(
+//     recipient_hex: String,
+//     position_id: String,
+//     collateral_id_hex: String,
+//     amount: String,
+//     expiration: String,
+//     salt: String,
+//     user_public_key_hex: String,
+//     domain_name: String,
+//     domain_version: String,
+//     domain_chain_id: String,
+//     domain_revision: String,
+// ) -> Result<Felt, String> {
+//     let collateral_id = Felt::from_hex(&collateral_id_hex)
+//         .map_err(|e| format!("Invalid collateral_id_hex: {:?}", e))?;
+//     let user_key = Felt::from_hex(&user_public_key_hex)
+//         .map_err(|e| format!("Invalid user_public_key_hex: {:?}", e))?;
+// 
+//     let recipient =
+//         Felt::from_hex(&recipient_hex).map_err(|e| format!("Invalid recipient_hex: {:?}", e))?;
+//     let position_id = u32::from_str_radix(&position_id, 10)
+//         .map_err(|e| format!("Invalid position_id: {:?}", e))?;
+//     let amount =
+//         u64::from_str_radix(&amount, 10).map_err(|e| format!("Invalid amount: {:?}", e))?;
+//     let expiration =
+//         u64::from_str_radix(&expiration, 10).map_err(|e| format!("Invalid expiration: {:?}", e))?;
+//     let salt = Felt::from_dec_str(&salt).map_err(|e| format!("Invalid salt: {:?}", e))?;
+//     let revision = u32::from_str_radix(&domain_revision, 10)
+//         .map_err(|e| format!("Invalid domain_revision: {:?}", e))?;
+// 
+//     let withdrawal_args = starknet_messages::WithdrawalArgs {
+//         recipient,
+//         position_id: PositionId { value: position_id },
+//         collateral_id: AssetId {
+//             value: collateral_id,
+//         },
+//         amount,
+//         expiration: Timestamp {
+//             seconds: expiration,
+//         },
+//         salt,
+//     };
+//     let domain = StarknetDomain {
+//         name: domain_name,
+//         version: domain_version,
+//         chain_id: domain_chain_id,
+//         revision,
+//     };
+//     withdrawal_args
+//         .message_hash(&domain, user_key)
+//         .map_err(|e| {
+//             format!(
+//                 "Failed to compute message hash for withdrawal args: {:?}",
+//                 e
+//             )
+//         })
+// }
+// 
+// #[cfg(test)]
+// mod tests {
+//     use super::*;
+// 
+//     #[test]
+//     fn test_get_private_key_from_eth_signature() {
+//         let signature = "0x9ef64d5936681edf44b4a7ad713f3bc24065d4039562af03fccf6a08d6996eab367df11439169b417b6a6d8ce81d409edb022597ce193916757c7d5d9cbf97301c";
+//         let result = get_private_key_from_eth_signature(signature);
+// 
+//         match result {
+//             Ok(private_key) => {
+//                 assert_eq!(private_key, Felt::from_dec_str("3554363360756768076148116215296798451844584215587910826843139626172125285444").unwrap());
+//             }
+//             Err(err) => {
+//                 panic!("Expected Ok, got Err: {}", err);
+//             }
+//         }
+//     }
+// 
+//     #[test]
+//     fn test_get_transfer_msg() {
+//         let recipient_position_id = "1".to_string();
+//         let sender_position_id = "2".to_string();
+//         let collateral_id_hex = "0x3".to_string();
+//         let amount = "4".to_string();
+//         let expiration = "5".to_string();
+//         let salt = "6".to_string();
+//         let user_public_key_hex =
+//             "0x5d05989e9302dcebc74e241001e3e3ac3f4402ccf2f8e6f74b034b07ad6a904".to_string();
+//         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 result = get_transfer_hash(
+//             recipient_position_id,
+//             sender_position_id,
+//             collateral_id_hex,
+//             amount,
+//             expiration,
+//             salt,
+//             user_public_key_hex,
+//             domain_name,
+//             domain_version,
+//             domain_chain_id,
+//             domain_revision,
+//         );
+// 
+//         match result {
+//             Ok(hash) => {
+//                 assert_eq!(
+//                     hash,
+//                     Felt::from_hex(
+//                         "0x56c7b21d13b79a33d7700dda20e22246c25e89818249504148174f527fc3f8f"
+//                     )
+//                     .unwrap()
+//                 );
+//             }
+//             Err(err) => {
+//                 panic!("Expected Ok, got Err: {}", err);
+//             }
+//         }
+//     }
+// 
+//     #[test]
+//     fn test_get_order_hash() {
+//         let position_id = "100".to_string();
+//         let base_asset_id_hex = "0x2".to_string();
+//         let base_amount = "100".to_string();
+//         let quote_asset_id_hex = "0x1".to_string();
+//         let quote_amount = "-156".to_string();
+//         let fee_asset_id_hex = "0x1".to_string();
+//         let fee_amount = "74".to_string();
+//         let expiration = "100".to_string();
+//         let salt = "123".to_string();
+//         let user_public_key_hex =
+//             "0x5d05989e9302dcebc74e241001e3e3ac3f4402ccf2f8e6f74b034b07ad6a904".to_string();
+//         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 result = get_order_hash(
+//             position_id,
+//             base_asset_id_hex,
+//             base_amount,
+//             quote_asset_id_hex,
+//             quote_amount,
+//             fee_asset_id_hex,
+//             fee_amount,
+//             expiration,
+//             salt,
+//             user_public_key_hex,
+//             domain_name,
+//             domain_version,
+//             domain_chain_id,
+//             domain_revision,
+//         );
+// 
+//         match result {
+//             Ok(hash) => {
+//                 assert_eq!(
+//                     hash,
+//                     Felt::from_hex(
+//                         "0x4de4c009e0d0c5a70a7da0e2039fb2b99f376d53496f89d9f437e736add6b48"
+//                     )
+//                     .unwrap()
+//                 );
+//             }
+//             Err(err) => {
+//                 panic!("Expected Ok, got Err: {}", err);
+//             }
+//         }
+//     }
+// 
+//     #[test]
+//     fn test_get_withdrawal_hash() {
+//         let recipient_hex = Felt::from_dec_str(
+//             "206642948138484946401984817000601902748248360221625950604253680558965863254",
+//         )
+//         .unwrap()
+//         .to_hex_string();
+//         let position_id = "2".to_string();
+//         let collateral_id_hex = Felt::from_dec_str(
+//             "1386727789535574059419576650469753513512158569780862144831829362722992755422",
+//         )
+//         .unwrap()
+//         .to_hex_string();
+//         let amount = "1000".to_string();
+//         let expiration = "0".to_string();
+//         let salt = "0".to_string();
+//         let user_public_key_hex =
+//             "0x5D05989E9302DCEBC74E241001E3E3AC3F4402CCF2F8E6F74B034B07AD6A904".to_string();
+//         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 result = get_withdrawal_hash(
+//             recipient_hex,
+//             position_id,
+//             collateral_id_hex,
+//             amount,
+//             expiration,
+//             salt,
+//             user_public_key_hex,
+//             domain_name,
+//             domain_version,
+//             domain_chain_id,
+//             domain_revision,
+//         );
+//         match result {
+//             Ok(hash) => {
+//                 assert_eq!(
+//                     hash,
+//                     Felt::from_dec_str(
+//                         "2182119571682827544073774098906745929330860211691330979324731407862023927178"
+//                     )
+//                     .unwrap()
+//                 );
+//             }
+//             Err(err) => {
+//                 panic!("Expected Ok, got Err: {}", err);
+//             }
+//         }
+//     }
+// }

+ 348 - 348
src/utils/starknet_messages.rs

@@ -1,348 +1,348 @@
-use starknet::core::utils::cairo_short_string_to_felt;
-
-use starknet::core::types::Felt;
-use starknet::macros::selector;
-use starknet_crypto::PoseidonHasher;
-
-use std::sync::LazyLock;
-
-static MESSAGE_FELT: LazyLock<Felt> =
-    LazyLock::new(|| cairo_short_string_to_felt("StarkNet Message").unwrap());
-
-pub trait Hashable {
-    const SELECTOR: Felt;
-    fn hash(&self) -> Felt;
-}
-
-pub trait OffChainMessage: Hashable {
-    fn message_hash(
-        &self,
-        stark_domain: &StarknetDomain,
-        public_key: Felt,
-    ) -> Result<Felt, String> {
-        let mut hasher = PoseidonHasher::new();
-        hasher.update(*MESSAGE_FELT);
-        hasher.update(stark_domain.hash());
-        hasher.update(public_key);
-        hasher.update(self.hash());
-        Ok(hasher.finalize())
-    }
-}
-
-pub struct StarknetDomain {
-    pub name: String,
-    pub version: String,
-    pub chain_id: String,
-    pub revision: u32,
-}
-
-impl Hashable for StarknetDomain {
-    const SELECTOR: Felt = selector!("\"StarknetDomain\"(\"name\":\"shortstring\",\"version\":\"shortstring\",\"chainId\":\"shortstring\",\"revision\":\"shortstring\")");
-    fn hash(&self) -> Felt {
-        let mut hasher = PoseidonHasher::new();
-        hasher.update(Self::SELECTOR);
-        hasher.update(cairo_short_string_to_felt(&self.name).unwrap());
-        hasher.update(cairo_short_string_to_felt(&self.version).unwrap());
-        hasher.update(cairo_short_string_to_felt(&self.chain_id).unwrap());
-        hasher.update(self.revision.into());
-        let hash = hasher.finalize();
-        return hash;
-    }
-}
-
-pub struct AssetId {
-    pub value: Felt,
-}
-pub struct PositionId {
-    pub value: u32,
-}
-
-pub struct AssetAmount {
-    pub asset_id: AssetId,
-    pub amount: i64,
-}
-
-pub struct Timestamp {
-    pub seconds: u64,
-}
-
-pub struct Order {
-    pub position_id: PositionId,
-    pub base_asset_id: AssetId,
-    pub base_amount: i64,
-    pub quote_asset_id: AssetId,
-    pub quote_amount: i64,
-    pub fee_asset_id: AssetId,
-    pub fee_amount: u64,
-    pub expiration: Timestamp,
-    pub salt: Felt,
-}
-
-impl Hashable for Order {
-    const SELECTOR: Felt = selector!("\"Order\"(\"position_id\":\"felt\",\"base_asset_id\":\"AssetId\",\"base_amount\":\"i64\",\"quote_asset_id\":\"AssetId\",\"quote_amount\":\"i64\",\"fee_asset_id\":\"AssetId\",\"fee_amount\":\"u64\",\"expiration\":\"Timestamp\",\"salt\":\"felt\")\"PositionId\"(\"value\":\"u32\")\"AssetId\"(\"value\":\"felt\")\"Timestamp\"(\"seconds\":\"u64\")");
-    fn hash(&self) -> Felt {
-        let mut hasher = PoseidonHasher::new();
-        hasher.update(Self::SELECTOR);
-        hasher.update(self.position_id.value.into());
-        hasher.update(self.base_asset_id.value.into());
-        hasher.update(self.base_amount.into());
-        hasher.update(self.quote_asset_id.value.into());
-        hasher.update(self.quote_amount.into());
-        hasher.update(self.fee_asset_id.value.into());
-        hasher.update(self.fee_amount.into());
-        hasher.update(self.expiration.seconds.into());
-        hasher.update(self.salt);
-        hasher.finalize()
-    }
-}
-impl OffChainMessage for Order {}
-
-pub struct TransferArgs {
-    pub recipient: PositionId,
-    pub position_id: PositionId,
-    pub collateral_id: AssetId,
-    pub amount: u64,
-    pub expiration: Timestamp,
-    pub salt: Felt,
-}
-
-impl Hashable for TransferArgs {
-    const SELECTOR: Felt = selector!("\"TransferArgs\"(\"recipient\":\"PositionId\",\"position_id\":\"PositionId\",\"collateral_id\":\"AssetId\",\"amount\":\"u64\",\"expiration\":\"Timestamp\",\"salt\":\"felt\")\"PositionId\"(\"value\":\"u32\")\"AssetId\"(\"value\":\"felt\")\"Timestamp\"(\"seconds\":\"u64\")");
-    fn hash(&self) -> Felt {
-        let mut hasher = PoseidonHasher::new();
-        hasher.update(Self::SELECTOR);
-        hasher.update(self.recipient.value.into());
-        hasher.update(self.position_id.value.into());
-        hasher.update(self.collateral_id.value.into());
-        hasher.update(self.amount.into());
-        hasher.update(self.expiration.seconds.into());
-        hasher.update(self.salt);
-        hasher.finalize()
-    }
-}
-
-impl OffChainMessage for TransferArgs {}
-
-pub static SEPOLIA_DOMAIN: LazyLock<StarknetDomain> = LazyLock::new(|| StarknetDomain {
-    name: "Perpetuals".to_string(),
-    version: "v0".to_string(),
-    chain_id: "SN_SEPOLIA".to_string(),
-    revision: 1,
-});
-
-pub struct WithdrawalArgs {
-    pub recipient: Felt,
-    pub position_id: PositionId,
-    pub collateral_id: AssetId,
-    pub amount: u64,
-    pub expiration: Timestamp,
-    pub salt: Felt,
-}
-
-impl Hashable for WithdrawalArgs {
-    const SELECTOR: Felt = selector!( "\"WithdrawArgs\"(\"recipient\":\"ContractAddress\",\"position_id\":\"PositionId\",\"collateral_id\":\"AssetId\",\"amount\":\"u64\",\"expiration\":\"Timestamp\",\"salt\":\"felt\")\"PositionId\"(\"value\":\"u32\")\"AssetId\"(\"value\":\"felt\")\"Timestamp\"(\"seconds\":\"u64\")");
-    fn hash(&self) -> Felt {
-        let mut hasher = PoseidonHasher::new();
-        hasher.update(Self::SELECTOR);
-        hasher.update(self.recipient);
-        hasher.update(self.position_id.value.into());
-        hasher.update(self.collateral_id.value.into());
-        hasher.update(self.amount.into());
-        hasher.update(self.expiration.seconds.into());
-        hasher.update(self.salt);
-        hasher.finalize()
-    }
-}
-
-impl OffChainMessage for WithdrawalArgs {}
-
-#[cfg(test)]
-mod tests {
-    use starknet::macros::{felt, felt_hex};
-
-    use super::*;
-
-    #[test]
-    fn test_starknet_domain_selector() {
-        let expected = Felt::from_hex_unchecked(
-            "0x1ff2f602e42168014d405a94f75e8a93d640751d71d16311266e140d8b0a210",
-        );
-        let actual = StarknetDomain::SELECTOR;
-        assert_eq!(expected, actual);
-    }
-
-    #[test]
-    fn test_starknet_domain_hashing() {
-        let domain = StarknetDomain {
-            name: "Perpetuals".to_string(),
-            version: "v0".to_string(),
-            chain_id: "SN_SEPOLIA".to_string(),
-            revision: 1,
-        };
-
-        let actual = domain.hash();
-        let expected =
-            felt!("2788850828067604540663615870177667078542240404906059806659101905868929188327");
-        assert_eq!(actual, expected, "Hashes do not match for StarknetDomain");
-    }
-
-    #[test]
-    fn test_order_selector() {
-        let expected = Felt::from_hex_unchecked(
-            "0x36da8d51815527cabfaa9c982f564c80fa7429616739306036f1f9b608dd112",
-        );
-        let actual = Order::SELECTOR;
-        assert_eq!(expected, actual);
-    }
-
-    #[test]
-    fn test_transfer_args_selector() {
-        let expected = Felt::from_hex_unchecked(
-            "0x1db88e2709fdf2c59e651d141c3296a42b209ce770871b40413ea109846a3b4",
-        );
-        let actual = TransferArgs::SELECTOR;
-        assert_eq!(expected, actual);
-    }
-
-    #[test]
-    fn test_transfer_args_hashing() {
-        let transfer_args = TransferArgs {
-            recipient: PositionId { value: 1 },
-            position_id: PositionId { value: 2 },
-            collateral_id: AssetId {
-                value: Felt::from_dec_str("3").unwrap(),
-            },
-            amount: 4,
-            expiration: Timestamp { seconds: 5 },
-            salt: Felt::from_dec_str("6").unwrap(),
-        };
-
-        let actual = transfer_args.hash();
-        let expected = Felt::from_dec_str(
-            "2223969487713427665389808888239017784545324676732964616876966103908214316949",
-        )
-        .unwrap();
-        assert_eq!(actual, expected, "Hashes do not match for TransferArgs");
-    }
-
-    #[test]
-    fn test_message_hash_transfer() {
-        let transfer_args = TransferArgs {
-            recipient: PositionId { value: 1 },
-            position_id: PositionId { value: 2 },
-            collateral_id: AssetId {
-                value: Felt::from_dec_str("3").unwrap(),
-            },
-            amount: 4,
-            expiration: Timestamp { seconds: 5 },
-            salt: Felt::from_dec_str("6").unwrap(),
-        };
-
-        let user_key = Felt::from_dec_str(
-            "2629686405885377265612250192330550814166101744721025672593857097107510831364",
-        )
-        .unwrap();
-
-        let actual = transfer_args
-            .message_hash(&SEPOLIA_DOMAIN, user_key)
-            .unwrap();
-
-        let expected =
-            felt_hex!("0x56c7b21d13b79a33d7700dda20e22246c25e89818249504148174f527fc3f8f");
-        assert_eq!(actual, expected, "Hashes do not match for TransferArgs");
-    }
-
-    #[test]
-    fn test_order_hashing() {
-        let order = Order {
-            position_id: PositionId { value: 1 },
-            base_asset_id: AssetId {
-                value: Felt::from_dec_str("2").unwrap(),
-            },
-            base_amount: 3,
-            quote_asset_id: AssetId {
-                value: Felt::from_dec_str("4").unwrap(),
-            },
-            quote_amount: 5,
-            fee_asset_id: AssetId {
-                value: Felt::from_dec_str("6").unwrap(),
-            },
-            fee_amount: 7,
-            expiration: Timestamp { seconds: 8 },
-            salt: Felt::from_dec_str("9").unwrap(),
-        };
-
-        let actual = order.hash();
-        let expected = Felt::from_dec_str(
-            "1329353150252109345267997901008558234696410103652961347079636617692652241760",
-        )
-        .unwrap();
-        assert_eq!(actual, expected, "Hashes do not match for Order");
-    }
-
-    #[test]
-    fn test_message_hash_order() {
-        let order = Order {
-            position_id: PositionId { value: 1 },
-            base_asset_id: AssetId {
-                value: Felt::from_dec_str("2").unwrap(),
-            },
-            base_amount: 3,
-            quote_asset_id: AssetId {
-                value: Felt::from_dec_str("4").unwrap(),
-            },
-            quote_amount: 5,
-            fee_asset_id: AssetId {
-                value: Felt::from_dec_str("6").unwrap(),
-            },
-            fee_amount: 7,
-            expiration: Timestamp { seconds: 8 },
-            salt: Felt::from_dec_str("9").unwrap(),
-        };
-
-        let user_key = Felt::from_dec_str(
-            "1528491859474308181214583355362479091084733880193869257167008343298409336538",
-        )
-        .unwrap();
-
-        let hash = order.message_hash(&SEPOLIA_DOMAIN, user_key).unwrap();
-        let expected_hash = Felt::from_dec_str(
-            "2788960362996410178586013462192086205585543858281504820767681025777602529597",
-        )
-        .unwrap();
-        println!("{}", expected_hash.to_hex_string());
-        assert_eq!(hash, expected_hash);
-    }
-
-    #[test]
-    fn test_withdrawal_args_selector() {
-        let expected = Felt::from_hex_unchecked(
-            "0x250a5fa378e8b771654bd43dcb34844534f9d1e29e16b14760d7936ea7f4b1d",
-        );
-        let actual = WithdrawalArgs::SELECTOR;
-        assert_eq!(expected, actual);
-    }
-
-    #[test]
-    fn test_withdrawal_args_hashing() {
-        let withdrawal_args = WithdrawalArgs {
-            recipient: Felt::from_hex(
-                "0x019ec96d4aea6fdc6f0b5f393fec3f186aefa8f0b8356f43d07b921ff48aa5da",
-            )
-            .unwrap(),
-            position_id: PositionId { value: 1 },
-            collateral_id: AssetId {
-                value: Felt::from_dec_str("4").unwrap(),
-            },
-            amount: 1000,
-            expiration: Timestamp { seconds: 5 },
-            salt: Felt::from_dec_str("123").unwrap(),
-        };
-
-        let actual = withdrawal_args.hash();
-        let expected =
-            Felt::from_hex("0x04c22f625c59651e1219c60d03055f11f5dc23959929de35861548d86c0bc4ec")
-                .unwrap();
-        assert_eq!(actual, expected, "Hashes do not match for WithdrawalArgs");
-    }
-}
+// use starknet::core::utils::cairo_short_string_to_felt;
+// 
+// use starknet::core::types::Felt;
+// use starknet::macros::selector;
+// use starknet_crypto::PoseidonHasher;
+// 
+// use std::sync::LazyLock;
+// 
+// static MESSAGE_FELT: LazyLock<Felt> =
+//     LazyLock::new(|| cairo_short_string_to_felt("StarkNet Message").unwrap());
+// 
+// pub trait Hashable {
+//     const SELECTOR: Felt;
+//     fn hash(&self) -> Felt;
+// }
+// 
+// pub trait OffChainMessage: Hashable {
+//     fn message_hash(
+//         &self,
+//         stark_domain: &StarknetDomain,
+//         public_key: Felt,
+//     ) -> Result<Felt, String> {
+//         let mut hasher = PoseidonHasher::new();
+//         hasher.update(*MESSAGE_FELT);
+//         hasher.update(stark_domain.hash());
+//         hasher.update(public_key);
+//         hasher.update(self.hash());
+//         Ok(hasher.finalize())
+//     }
+// }
+// 
+// pub struct StarknetDomain {
+//     pub name: String,
+//     pub version: String,
+//     pub chain_id: String,
+//     pub revision: u32,
+// }
+// 
+// impl Hashable for StarknetDomain {
+//     const SELECTOR: Felt = selector!("\"StarknetDomain\"(\"name\":\"shortstring\",\"version\":\"shortstring\",\"chainId\":\"shortstring\",\"revision\":\"shortstring\")");
+//     fn hash(&self) -> Felt {
+//         let mut hasher = PoseidonHasher::new();
+//         hasher.update(Self::SELECTOR);
+//         hasher.update(cairo_short_string_to_felt(&self.name).unwrap());
+//         hasher.update(cairo_short_string_to_felt(&self.version).unwrap());
+//         hasher.update(cairo_short_string_to_felt(&self.chain_id).unwrap());
+//         hasher.update(self.revision.into());
+//         let hash = hasher.finalize();
+//         return hash;
+//     }
+// }
+// 
+// pub struct AssetId {
+//     pub value: Felt,
+// }
+// pub struct PositionId {
+//     pub value: u32,
+// }
+// 
+// pub struct AssetAmount {
+//     pub asset_id: AssetId,
+//     pub amount: i64,
+// }
+// 
+// pub struct Timestamp {
+//     pub seconds: u64,
+// }
+// 
+// pub struct Order {
+//     pub position_id: PositionId,
+//     pub base_asset_id: AssetId,
+//     pub base_amount: i64,
+//     pub quote_asset_id: AssetId,
+//     pub quote_amount: i64,
+//     pub fee_asset_id: AssetId,
+//     pub fee_amount: u64,
+//     pub expiration: Timestamp,
+//     pub salt: Felt,
+// }
+// 
+// impl Hashable for Order {
+//     const SELECTOR: Felt = selector!("\"Order\"(\"position_id\":\"felt\",\"base_asset_id\":\"AssetId\",\"base_amount\":\"i64\",\"quote_asset_id\":\"AssetId\",\"quote_amount\":\"i64\",\"fee_asset_id\":\"AssetId\",\"fee_amount\":\"u64\",\"expiration\":\"Timestamp\",\"salt\":\"felt\")\"PositionId\"(\"value\":\"u32\")\"AssetId\"(\"value\":\"felt\")\"Timestamp\"(\"seconds\":\"u64\")");
+//     fn hash(&self) -> Felt {
+//         let mut hasher = PoseidonHasher::new();
+//         hasher.update(Self::SELECTOR);
+//         hasher.update(self.position_id.value.into());
+//         hasher.update(self.base_asset_id.value.into());
+//         hasher.update(self.base_amount.into());
+//         hasher.update(self.quote_asset_id.value.into());
+//         hasher.update(self.quote_amount.into());
+//         hasher.update(self.fee_asset_id.value.into());
+//         hasher.update(self.fee_amount.into());
+//         hasher.update(self.expiration.seconds.into());
+//         hasher.update(self.salt);
+//         hasher.finalize()
+//     }
+// }
+// impl OffChainMessage for Order {}
+// 
+// pub struct TransferArgs {
+//     pub recipient: PositionId,
+//     pub position_id: PositionId,
+//     pub collateral_id: AssetId,
+//     pub amount: u64,
+//     pub expiration: Timestamp,
+//     pub salt: Felt,
+// }
+// 
+// impl Hashable for TransferArgs {
+//     const SELECTOR: Felt = selector!("\"TransferArgs\"(\"recipient\":\"PositionId\",\"position_id\":\"PositionId\",\"collateral_id\":\"AssetId\",\"amount\":\"u64\",\"expiration\":\"Timestamp\",\"salt\":\"felt\")\"PositionId\"(\"value\":\"u32\")\"AssetId\"(\"value\":\"felt\")\"Timestamp\"(\"seconds\":\"u64\")");
+//     fn hash(&self) -> Felt {
+//         let mut hasher = PoseidonHasher::new();
+//         hasher.update(Self::SELECTOR);
+//         hasher.update(self.recipient.value.into());
+//         hasher.update(self.position_id.value.into());
+//         hasher.update(self.collateral_id.value.into());
+//         hasher.update(self.amount.into());
+//         hasher.update(self.expiration.seconds.into());
+//         hasher.update(self.salt);
+//         hasher.finalize()
+//     }
+// }
+// 
+// impl OffChainMessage for TransferArgs {}
+// 
+// pub static SEPOLIA_DOMAIN: LazyLock<StarknetDomain> = LazyLock::new(|| StarknetDomain {
+//     name: "Perpetuals".to_string(),
+//     version: "v0".to_string(),
+//     chain_id: "SN_SEPOLIA".to_string(),
+//     revision: 1,
+// });
+// 
+// pub struct WithdrawalArgs {
+//     pub recipient: Felt,
+//     pub position_id: PositionId,
+//     pub collateral_id: AssetId,
+//     pub amount: u64,
+//     pub expiration: Timestamp,
+//     pub salt: Felt,
+// }
+// 
+// impl Hashable for WithdrawalArgs {
+//     const SELECTOR: Felt = selector!( "\"WithdrawArgs\"(\"recipient\":\"ContractAddress\",\"position_id\":\"PositionId\",\"collateral_id\":\"AssetId\",\"amount\":\"u64\",\"expiration\":\"Timestamp\",\"salt\":\"felt\")\"PositionId\"(\"value\":\"u32\")\"AssetId\"(\"value\":\"felt\")\"Timestamp\"(\"seconds\":\"u64\")");
+//     fn hash(&self) -> Felt {
+//         let mut hasher = PoseidonHasher::new();
+//         hasher.update(Self::SELECTOR);
+//         hasher.update(self.recipient);
+//         hasher.update(self.position_id.value.into());
+//         hasher.update(self.collateral_id.value.into());
+//         hasher.update(self.amount.into());
+//         hasher.update(self.expiration.seconds.into());
+//         hasher.update(self.salt);
+//         hasher.finalize()
+//     }
+// }
+// 
+// impl OffChainMessage for WithdrawalArgs {}
+// 
+// #[cfg(test)]
+// mod tests {
+//     use starknet::macros::{felt, felt_hex};
+// 
+//     use super::*;
+// 
+//     #[test]
+//     fn test_starknet_domain_selector() {
+//         let expected = Felt::from_hex_unchecked(
+//             "0x1ff2f602e42168014d405a94f75e8a93d640751d71d16311266e140d8b0a210",
+//         );
+//         let actual = StarknetDomain::SELECTOR;
+//         assert_eq!(expected, actual);
+//     }
+// 
+//     #[test]
+//     fn test_starknet_domain_hashing() {
+//         let domain = StarknetDomain {
+//             name: "Perpetuals".to_string(),
+//             version: "v0".to_string(),
+//             chain_id: "SN_SEPOLIA".to_string(),
+//             revision: 1,
+//         };
+// 
+//         let actual = domain.hash();
+//         let expected =
+//             felt!("2788850828067604540663615870177667078542240404906059806659101905868929188327");
+//         assert_eq!(actual, expected, "Hashes do not match for StarknetDomain");
+//     }
+// 
+//     #[test]
+//     fn test_order_selector() {
+//         let expected = Felt::from_hex_unchecked(
+//             "0x36da8d51815527cabfaa9c982f564c80fa7429616739306036f1f9b608dd112",
+//         );
+//         let actual = Order::SELECTOR;
+//         assert_eq!(expected, actual);
+//     }
+// 
+//     #[test]
+//     fn test_transfer_args_selector() {
+//         let expected = Felt::from_hex_unchecked(
+//             "0x1db88e2709fdf2c59e651d141c3296a42b209ce770871b40413ea109846a3b4",
+//         );
+//         let actual = TransferArgs::SELECTOR;
+//         assert_eq!(expected, actual);
+//     }
+// 
+//     #[test]
+//     fn test_transfer_args_hashing() {
+//         let transfer_args = TransferArgs {
+//             recipient: PositionId { value: 1 },
+//             position_id: PositionId { value: 2 },
+//             collateral_id: AssetId {
+//                 value: Felt::from_dec_str("3").unwrap(),
+//             },
+//             amount: 4,
+//             expiration: Timestamp { seconds: 5 },
+//             salt: Felt::from_dec_str("6").unwrap(),
+//         };
+// 
+//         let actual = transfer_args.hash();
+//         let expected = Felt::from_dec_str(
+//             "2223969487713427665389808888239017784545324676732964616876966103908214316949",
+//         )
+//         .unwrap();
+//         assert_eq!(actual, expected, "Hashes do not match for TransferArgs");
+//     }
+// 
+//     #[test]
+//     fn test_message_hash_transfer() {
+//         let transfer_args = TransferArgs {
+//             recipient: PositionId { value: 1 },
+//             position_id: PositionId { value: 2 },
+//             collateral_id: AssetId {
+//                 value: Felt::from_dec_str("3").unwrap(),
+//             },
+//             amount: 4,
+//             expiration: Timestamp { seconds: 5 },
+//             salt: Felt::from_dec_str("6").unwrap(),
+//         };
+// 
+//         let user_key = Felt::from_dec_str(
+//             "2629686405885377265612250192330550814166101744721025672593857097107510831364",
+//         )
+//         .unwrap();
+// 
+//         let actual = transfer_args
+//             .message_hash(&SEPOLIA_DOMAIN, user_key)
+//             .unwrap();
+// 
+//         let expected =
+//             felt_hex!("0x56c7b21d13b79a33d7700dda20e22246c25e89818249504148174f527fc3f8f");
+//         assert_eq!(actual, expected, "Hashes do not match for TransferArgs");
+//     }
+// 
+//     #[test]
+//     fn test_order_hashing() {
+//         let order = Order {
+//             position_id: PositionId { value: 1 },
+//             base_asset_id: AssetId {
+//                 value: Felt::from_dec_str("2").unwrap(),
+//             },
+//             base_amount: 3,
+//             quote_asset_id: AssetId {
+//                 value: Felt::from_dec_str("4").unwrap(),
+//             },
+//             quote_amount: 5,
+//             fee_asset_id: AssetId {
+//                 value: Felt::from_dec_str("6").unwrap(),
+//             },
+//             fee_amount: 7,
+//             expiration: Timestamp { seconds: 8 },
+//             salt: Felt::from_dec_str("9").unwrap(),
+//         };
+// 
+//         let actual = order.hash();
+//         let expected = Felt::from_dec_str(
+//             "1329353150252109345267997901008558234696410103652961347079636617692652241760",
+//         )
+//         .unwrap();
+//         assert_eq!(actual, expected, "Hashes do not match for Order");
+//     }
+// 
+//     #[test]
+//     fn test_message_hash_order() {
+//         let order = Order {
+//             position_id: PositionId { value: 1 },
+//             base_asset_id: AssetId {
+//                 value: Felt::from_dec_str("2").unwrap(),
+//             },
+//             base_amount: 3,
+//             quote_asset_id: AssetId {
+//                 value: Felt::from_dec_str("4").unwrap(),
+//             },
+//             quote_amount: 5,
+//             fee_asset_id: AssetId {
+//                 value: Felt::from_dec_str("6").unwrap(),
+//             },
+//             fee_amount: 7,
+//             expiration: Timestamp { seconds: 8 },
+//             salt: Felt::from_dec_str("9").unwrap(),
+//         };
+// 
+//         let user_key = Felt::from_dec_str(
+//             "1528491859474308181214583355362479091084733880193869257167008343298409336538",
+//         )
+//         .unwrap();
+// 
+//         let hash = order.message_hash(&SEPOLIA_DOMAIN, user_key).unwrap();
+//         let expected_hash = Felt::from_dec_str(
+//             "2788960362996410178586013462192086205585543858281504820767681025777602529597",
+//         )
+//         .unwrap();
+//         println!("{}", expected_hash.to_hex_string());
+//         assert_eq!(hash, expected_hash);
+//     }
+// 
+//     #[test]
+//     fn test_withdrawal_args_selector() {
+//         let expected = Felt::from_hex_unchecked(
+//             "0x250a5fa378e8b771654bd43dcb34844534f9d1e29e16b14760d7936ea7f4b1d",
+//         );
+//         let actual = WithdrawalArgs::SELECTOR;
+//         assert_eq!(expected, actual);
+//     }
+// 
+//     #[test]
+//     fn test_withdrawal_args_hashing() {
+//         let withdrawal_args = WithdrawalArgs {
+//             recipient: Felt::from_hex(
+//                 "0x019ec96d4aea6fdc6f0b5f393fec3f186aefa8f0b8356f43d07b921ff48aa5da",
+//             )
+//             .unwrap(),
+//             position_id: PositionId { value: 1 },
+//             collateral_id: AssetId {
+//                 value: Felt::from_dec_str("4").unwrap(),
+//             },
+//             amount: 1000,
+//             expiration: Timestamp { seconds: 5 },
+//             salt: Felt::from_dec_str("123").unwrap(),
+//         };
+// 
+//         let actual = withdrawal_args.hash();
+//         let expected =
+//             Felt::from_hex("0x04c22f625c59651e1219c60d03055f11f5dc23959929de35861548d86c0bc4ec")
+//                 .unwrap();
+//         assert_eq!(actual, expected, "Hashes do not match for WithdrawalArgs");
+//     }
+// }