Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion tee-worker/omni-executor/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion tee-worker/omni-executor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ hex = "0.4"
hex-literal = "0.4"
hmac = "0.12.1"
http = "1.3.1"
hyperliquid_rust_sdk = { git = "https://github.com/silva-fj/hyperliquid-rust-sdk", branch = "eip712-public-trait" }
hyperliquid_rust_sdk = { git = "https://github.com/BillyWooo/hyperliquid-rust-sdk", branch = "usdClassTransfer_sendAsset" }
jsonrpsee = { version = "0.24.9", features = ["server"] }
jsonwebtoken = "9.3.0"
libsecp256k1 = "0.7.1"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use executor_core::intent_executor::IntentExecutor;
use executor_primitives::{
to_omni_auth, utils::hex::hex_encode, ChainId, ClientAuth, Identity, UserAuth, UserId,
};
use hyperliquid_rust_sdk::{ApproveAgent, ApproveBuilderFee, Eip712, Withdraw3};
use hyperliquid_rust_sdk::{ApproveAgent, ApproveBuilderFee, Eip712, SendAsset, Withdraw3};
use jsonrpsee::{types::ErrorObject, RpcModule};
use pumpx::pubkey_to_address;
use serde::{Deserialize, Serialize};
Expand All @@ -32,9 +32,26 @@ pub struct GetHyperliquidSignatureDataParams {
#[derive(Debug, Deserialize, Clone)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum HyperliquidActionType {
ApproveAgent { agent_address: String, agent_name: Option<String> },
Withdraw3 { amount: String, destination: String },
ApproveBuilderFee { max_fee_rate: String, builder: String },
ApproveAgent {
agent_address: String,
agent_name: Option<String>,
},
Withdraw3 {
amount: String,
destination: String,
},
ApproveBuilderFee {
max_fee_rate: String,
builder: String,
},
SendAsset {
destination: String,
source_dex: String,
destination_dex: String,
token: String,
amount: String,
from_sub_account: String,
},
}

#[derive(Serialize, Clone)]
Expand All @@ -56,6 +73,7 @@ pub enum HyperliquidAction {
ApproveAgent(ApproveAgent),
Withdraw3(Withdraw3),
ApproveBuilderFee(ApproveBuilderFee),
SendAsset(SendAsset),
}

fn is_testnet_chain(chain_id: ChainId) -> bool {
Expand Down Expand Up @@ -299,6 +317,36 @@ pub fn register_get_hyperliquid_signature_data<
generate_eip712_signature(&ctx, &action, omni_account.as_ref()).await?;
(HyperliquidAction::ApproveBuilderFee(action), signature)
},
HyperliquidActionType::SendAsset {
destination,
source_dex,
destination_dex,
token,
amount,
from_sub_account,
} => {
let _ = validate_ethereum_address(&destination, "destination")
.map_err(|e| e.to_error_object())?;
// Validate from_sub_account if it's not empty
if !from_sub_account.is_empty() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This if seems redundant, validate_ethereum_address would fail anyways

let _ = validate_ethereum_address(&from_sub_account, "from_sub_account")
.map_err(|e| e.to_error_object())?;
}
let action = SendAsset {
signature_chain_id: params.chain_id,
hyperliquid_chain,
destination,
source_dex,
destination_dex,
token,
amount,
from_sub_account,
nonce,
};
let signature =
generate_eip712_signature(&ctx, &action, omni_account.as_ref()).await?;
(HyperliquidAction::SendAsset(action), signature)
},
};

Ok(GetHyperliquidSignatureDataResponse {
Expand Down Expand Up @@ -494,6 +542,114 @@ mod tests {
));
}

#[test]
fn test_send_asset_action_signature() {
let action = SendAsset {
signature_chain_id: 998,
hyperliquid_chain: "Testnet".to_string(),
destination: "0x1234567890123456789012345678901234567890".to_string(),
source_dex: "".to_string(),
destination_dex: "".to_string(),
token: "PURR:0xc4bf3f870c0e9465323c0b6ed28096c2".to_string(),
amount: "100.0".to_string(),
from_sub_account: "".to_string(),
nonce: 1234567890,
};

// Test domain generation
let domain = action.domain();
assert_eq!(domain.name, Some("HyperliquidSignTransaction".into()));
assert_eq!(domain.version, Some("1".into()));
assert_eq!(domain.chain_id, Some(alloy::primitives::U256::from(998)));

// Test struct hash generation
let struct_hash = action.struct_hash();
assert_eq!(struct_hash.len(), 32);

// Test EIP-712 signing hash generation
let signing_hash = action.eip712_signing_hash();
assert_eq!(signing_hash.len(), 32);
}

#[test]
fn test_params_deserialization_send_asset() {
let json = r#"{
"user_id": {"type": "email", "value": "test@example.com"},
"user_auth": {"type": "email", "value": "123456"},
"client_id": "test_client",
"action_type": {
"type": "send_asset",
"destination": "0x742d35Cc6634C0532925a3b844Bc9e7595f02A10",
"source_dex": "",
"destination_dex": "spot",
"token": "PURR:0xc4bf3f870c0e9465323c0b6ed28096c2",
"amount": "50.5",
"from_sub_account": ""
},
"chain_id": 998
}"#;

let params: GetHyperliquidSignatureDataParams = serde_json::from_str(json).unwrap();

assert!(matches!(
params.action_type,
HyperliquidActionType::SendAsset {
destination,
source_dex,
destination_dex,
token,
amount,
from_sub_account
}
if destination == "0x742d35Cc6634C0532925a3b844Bc9e7595f02A10"
&& source_dex == ""
&& destination_dex == "spot"
&& token == "PURR:0xc4bf3f870c0e9465323c0b6ed28096c2"
&& amount == "50.5"
&& from_sub_account == ""
));
assert_eq!(params.chain_id, 998);
}

#[test]
fn test_params_deserialization_send_asset_with_sub_account() {
let json = r#"{
"user_id": {"type": "email", "value": "test@example.com"},
"user_auth": {"type": "email", "value": "123456"},
"client_id": "test_client",
"action_type": {
"type": "send_asset",
"destination": "0x742d35Cc6634C0532925a3b844Bc9e7595f02A10",
"source_dex": "hyperliquid",
"destination_dex": "",
"token": "USDC:0x0",
"amount": "1000.0",
"from_sub_account": "0x9876543210987654321098765432109876543210"
},
"chain_id": 998
}"#;

let params: GetHyperliquidSignatureDataParams = serde_json::from_str(json).unwrap();

assert!(matches!(
params.action_type,
HyperliquidActionType::SendAsset {
destination,
source_dex,
destination_dex,
token,
amount,
from_sub_account
}
if destination == "0x742d35Cc6634C0532925a3b844Bc9e7595f02A10"
&& source_dex == "hyperliquid"
&& destination_dex == ""
&& token == "USDC:0x0"
&& amount == "1000.0"
&& from_sub_account == "0x9876543210987654321098765432109876543210"
));
}

#[test]
fn test_is_testnet_chain() {
// Test mainnet chain IDs
Expand Down