Skip to content

Commit b3e5e60

Browse files
authored
P 1729 add one more hyperliquidaction type sendasset (#3758)
* Add SendAsset action type to Hyperliquid signature data endpoint Extend omni_getHyperliquidSignatureData with support for the SendAsset action type, enabling generalized token transfers across perp DEXs, spot balances, users, and subaccounts (testnet-only feature). Changes: - Add SendAsset variant to HyperliquidActionType enum with fields for destination, source_dex, destination_dex, token, amount, and from_sub_account - Add SendAsset variant to HyperliquidAction response enum - Implement SendAsset action handler with address validation for destination and optional from_sub_account - Import SendAsset struct from hyperliquid-rust-sdk - Update SDK dependency to BillyWooo/hyperliquid-rust-sdk branch usdClassTransfer_sendAsset - Add comprehensive tests: signature generation, JSON deserialization, and sub-account handling The implementation follows the same patterns as existing actions (ApproveAgent, Withdraw3, ApproveBuilderFee), ensuring consistency with EIP-712 signature generation and error handling. * fmt
1 parent b822828 commit b3e5e60

File tree

3 files changed

+162
-6
lines changed

3 files changed

+162
-6
lines changed

tee-worker/omni-executor/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tee-worker/omni-executor/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ hex = "0.4"
5656
hex-literal = "0.4"
5757
hmac = "0.12.1"
5858
http = "1.3.1"
59-
hyperliquid_rust_sdk = { git = "https://github.com/silva-fj/hyperliquid-rust-sdk", branch = "eip712-public-trait" }
59+
hyperliquid_rust_sdk = { git = "https://github.com/BillyWooo/hyperliquid-rust-sdk", branch = "usdClassTransfer_sendAsset" }
6060
jsonrpsee = { version = "0.24.9", features = ["server"] }
6161
jsonwebtoken = "9.3.0"
6262
libsecp256k1 = "0.7.1"

tee-worker/omni-executor/rpc-server/src/methods/omni/get_hyperliquid_signature_data.rs

Lines changed: 160 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use executor_core::intent_executor::IntentExecutor;
1111
use executor_primitives::{
1212
to_omni_auth, utils::hex::hex_encode, ChainId, ClientAuth, Identity, UserAuth, UserId,
1313
};
14-
use hyperliquid_rust_sdk::{ApproveAgent, ApproveBuilderFee, Eip712, Withdraw3};
14+
use hyperliquid_rust_sdk::{ApproveAgent, ApproveBuilderFee, Eip712, SendAsset, Withdraw3};
1515
use jsonrpsee::{types::ErrorObject, RpcModule};
1616
use pumpx::pubkey_to_address;
1717
use serde::{Deserialize, Serialize};
@@ -32,9 +32,26 @@ pub struct GetHyperliquidSignatureDataParams {
3232
#[derive(Debug, Deserialize, Clone)]
3333
#[serde(tag = "type", rename_all = "snake_case")]
3434
pub enum HyperliquidActionType {
35-
ApproveAgent { agent_address: String, agent_name: Option<String> },
36-
Withdraw3 { amount: String, destination: String },
37-
ApproveBuilderFee { max_fee_rate: String, builder: String },
35+
ApproveAgent {
36+
agent_address: String,
37+
agent_name: Option<String>,
38+
},
39+
Withdraw3 {
40+
amount: String,
41+
destination: String,
42+
},
43+
ApproveBuilderFee {
44+
max_fee_rate: String,
45+
builder: String,
46+
},
47+
SendAsset {
48+
destination: String,
49+
source_dex: String,
50+
destination_dex: String,
51+
token: String,
52+
amount: String,
53+
from_sub_account: String,
54+
},
3855
}
3956

4057
#[derive(Serialize, Clone)]
@@ -56,6 +73,7 @@ pub enum HyperliquidAction {
5673
ApproveAgent(ApproveAgent),
5774
Withdraw3(Withdraw3),
5875
ApproveBuilderFee(ApproveBuilderFee),
76+
SendAsset(SendAsset),
5977
}
6078

6179
fn is_testnet_chain(chain_id: ChainId) -> bool {
@@ -299,6 +317,36 @@ pub fn register_get_hyperliquid_signature_data<
299317
generate_eip712_signature(&ctx, &action, omni_account.as_ref()).await?;
300318
(HyperliquidAction::ApproveBuilderFee(action), signature)
301319
},
320+
HyperliquidActionType::SendAsset {
321+
destination,
322+
source_dex,
323+
destination_dex,
324+
token,
325+
amount,
326+
from_sub_account,
327+
} => {
328+
let _ = validate_ethereum_address(&destination, "destination")
329+
.map_err(|e| e.to_error_object())?;
330+
// Validate from_sub_account if it's not empty
331+
if !from_sub_account.is_empty() {
332+
let _ = validate_ethereum_address(&from_sub_account, "from_sub_account")
333+
.map_err(|e| e.to_error_object())?;
334+
}
335+
let action = SendAsset {
336+
signature_chain_id: params.chain_id,
337+
hyperliquid_chain,
338+
destination,
339+
source_dex,
340+
destination_dex,
341+
token,
342+
amount,
343+
from_sub_account,
344+
nonce,
345+
};
346+
let signature =
347+
generate_eip712_signature(&ctx, &action, omni_account.as_ref()).await?;
348+
(HyperliquidAction::SendAsset(action), signature)
349+
},
302350
};
303351

304352
Ok(GetHyperliquidSignatureDataResponse {
@@ -494,6 +542,114 @@ mod tests {
494542
));
495543
}
496544

545+
#[test]
546+
fn test_send_asset_action_signature() {
547+
let action = SendAsset {
548+
signature_chain_id: 998,
549+
hyperliquid_chain: "Testnet".to_string(),
550+
destination: "0x1234567890123456789012345678901234567890".to_string(),
551+
source_dex: "".to_string(),
552+
destination_dex: "".to_string(),
553+
token: "PURR:0xc4bf3f870c0e9465323c0b6ed28096c2".to_string(),
554+
amount: "100.0".to_string(),
555+
from_sub_account: "".to_string(),
556+
nonce: 1234567890,
557+
};
558+
559+
// Test domain generation
560+
let domain = action.domain();
561+
assert_eq!(domain.name, Some("HyperliquidSignTransaction".into()));
562+
assert_eq!(domain.version, Some("1".into()));
563+
assert_eq!(domain.chain_id, Some(alloy::primitives::U256::from(998)));
564+
565+
// Test struct hash generation
566+
let struct_hash = action.struct_hash();
567+
assert_eq!(struct_hash.len(), 32);
568+
569+
// Test EIP-712 signing hash generation
570+
let signing_hash = action.eip712_signing_hash();
571+
assert_eq!(signing_hash.len(), 32);
572+
}
573+
574+
#[test]
575+
fn test_params_deserialization_send_asset() {
576+
let json = r#"{
577+
"user_id": {"type": "email", "value": "[email protected]"},
578+
"user_auth": {"type": "email", "value": "123456"},
579+
"client_id": "test_client",
580+
"action_type": {
581+
"type": "send_asset",
582+
"destination": "0x742d35Cc6634C0532925a3b844Bc9e7595f02A10",
583+
"source_dex": "",
584+
"destination_dex": "spot",
585+
"token": "PURR:0xc4bf3f870c0e9465323c0b6ed28096c2",
586+
"amount": "50.5",
587+
"from_sub_account": ""
588+
},
589+
"chain_id": 998
590+
}"#;
591+
592+
let params: GetHyperliquidSignatureDataParams = serde_json::from_str(json).unwrap();
593+
594+
assert!(matches!(
595+
params.action_type,
596+
HyperliquidActionType::SendAsset {
597+
destination,
598+
source_dex,
599+
destination_dex,
600+
token,
601+
amount,
602+
from_sub_account
603+
}
604+
if destination == "0x742d35Cc6634C0532925a3b844Bc9e7595f02A10"
605+
&& source_dex == ""
606+
&& destination_dex == "spot"
607+
&& token == "PURR:0xc4bf3f870c0e9465323c0b6ed28096c2"
608+
&& amount == "50.5"
609+
&& from_sub_account == ""
610+
));
611+
assert_eq!(params.chain_id, 998);
612+
}
613+
614+
#[test]
615+
fn test_params_deserialization_send_asset_with_sub_account() {
616+
let json = r#"{
617+
"user_id": {"type": "email", "value": "[email protected]"},
618+
"user_auth": {"type": "email", "value": "123456"},
619+
"client_id": "test_client",
620+
"action_type": {
621+
"type": "send_asset",
622+
"destination": "0x742d35Cc6634C0532925a3b844Bc9e7595f02A10",
623+
"source_dex": "hyperliquid",
624+
"destination_dex": "",
625+
"token": "USDC:0x0",
626+
"amount": "1000.0",
627+
"from_sub_account": "0x9876543210987654321098765432109876543210"
628+
},
629+
"chain_id": 998
630+
}"#;
631+
632+
let params: GetHyperliquidSignatureDataParams = serde_json::from_str(json).unwrap();
633+
634+
assert!(matches!(
635+
params.action_type,
636+
HyperliquidActionType::SendAsset {
637+
destination,
638+
source_dex,
639+
destination_dex,
640+
token,
641+
amount,
642+
from_sub_account
643+
}
644+
if destination == "0x742d35Cc6634C0532925a3b844Bc9e7595f02A10"
645+
&& source_dex == "hyperliquid"
646+
&& destination_dex == ""
647+
&& token == "USDC:0x0"
648+
&& amount == "1000.0"
649+
&& from_sub_account == "0x9876543210987654321098765432109876543210"
650+
));
651+
}
652+
497653
#[test]
498654
fn test_is_testnet_chain() {
499655
// Test mainnet chain IDs

0 commit comments

Comments
 (0)