|
|
@@ -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 = ð_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("e_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("e_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 = ð_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("e_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("e_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);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|