| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446 |
- 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;
- #[allow(dead_code)]
- pub struct StarkSignature {
- pub r: Felt,
- pub s: Felt,
- pub v: Felt,
- }
- #[allow(dead_code)]
- #[allow(deprecated)]
- 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();
- }
- }
- #[allow(dead_code)]
- 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());
- }
- #[allow(dead_code)]
- 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.
- #[allow(dead_code)]
- 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> {
- // println!("position_id: {}", position_id);
- // println!("base_asset_id_hex: {}", base_asset_id_hex);
- // println!("base_amount: {}", base_amount);
- // println!("quote_asset_id_hex: {}", quote_asset_id_hex);
- // println!("quote_amount: {}", quote_amount);
- // println!("fee_asset_id_hex: {}", fee_asset_id_hex);
- // println!("fee_amount: {}", fee_amount);
- // println!("expiration: {}", expiration);
- // println!("salt: {}", salt);
- // println!("user_public_key_hex: {}", user_public_key_hex);
- // println!("domain_name: {}", domain_name);
- // println!("domain_version: {}", domain_version);
- // println!("domain_chain_id: {}", domain_chain_id);
- // println!("domain_revision: {}", domain_revision);
- 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))
- }
- #[allow(dead_code)]
- 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))
- }
- #[allow(dead_code)]
- 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);
- }
- }
- }
- }
|