Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
f0910a1
Add multi-sig example binaries
Sep 18, 2025
c5d76bd
Add complete multi-signature functionality with EIP-712 signing, acco…
Oct 21, 2025
61a10da
Change requests applied
Nov 8, 2025
638353b
Implement proper multi-sig signature collection pattern
Nov 9, 2025
7b7b4ec
remove SendAsset payload_multi_sig_user & outer_signer
luca992 Nov 10, 2025
189e36f
Merge pull request #1 from luca992/multi_sig
nicolad Nov 10, 2025
b5e353e
Revert "remove SendAsset payload_multi_sig_user & outer_signer"
luca992 Nov 10, 2025
fb71f22
add back outer_signer and payload_multi_sig_user to SendAsset but do …
luca992 Nov 10, 2025
4acc577
lowercase destinations
luca992 Nov 10, 2025
6abff7b
lowercase payload addresses
luca992 Nov 10, 2025
d1fe9e2
try signature_chain_id: "0x1"
luca992 Nov 11, 2025
9145271
set fixed nonce for debugging
luca992 Nov 11, 2025
4451ce2
log action_without_type
luca992 Nov 11, 2025
6960c72
serde_json enable preserve_order
luca992 Nov 11, 2025
8c46664
preserve map order when removing from action
luca992 Nov 11, 2025
1180276
insert type at the start of the action
luca992 Nov 11, 2025
11a489a
Revert "set fixed nonce for debugging"
luca992 Nov 11, 2025
3b03853
Revert "try signature_chain_id: "0x1""
luca992 Nov 11, 2025
5d81c38
Revert "log action_without_type"
luca992 Nov 11, 2025
d5ad466
Reapply "try signature_chain_id: "0x1""
luca992 Nov 11, 2025
b79d86a
debug with hardcoded nonce
luca992 Nov 11, 2025
c45b036
Reapply "log action_without_type"
luca992 Nov 11, 2025
97d4d1f
log hex
luca992 Nov 11, 2025
9351159
Prevent serializing actions to value before needed
luca992 Nov 11, 2025
c660e12
Revert "debug with hardcoded nonce"
luca992 Nov 11, 2025
3c72658
serialize Signature r & s to strings
luca992 Nov 11, 2025
700679d
Reapply "debug with hardcoded nonce"
luca992 Nov 11, 2025
97303d3
MultiSigEnvelope use the signature_chain_id from the MultiSigAction
luca992 Nov 11, 2025
48b33bd
Revert "Reapply "debug with hardcoded nonce""
luca992 Nov 11, 2025
2bc8732
revert debug print
luca992 Nov 11, 2025
5ef467f
revert adding preserve_order serde_json feature
luca992 Nov 11, 2025
81b6f51
Revert "revert adding preserve_order serde_json feature"
luca992 Nov 11, 2025
33ab804
Merge pull request #2 from luca992/multi_sig
nicolad Nov 11, 2025
285ffb2
make BaseUrl derive Debug, PartialEq, Eq
luca992 Dec 23, 2025
fdbbc1d
Add ""new() helpers for action structs with tests
luca992 Dec 23, 2025
0bfdd26
expose post
luca992 Dec 23, 2025
8e4343f
deserialize_with deserialize_hex for signature_chain_id
luca992 Dec 25, 2025
198f07e
create public Agent::new
luca992 Jan 2, 2026
c53c983
expose l1::Agent and signing functions for signature collection
luca992 Jan 2, 2026
9497292
migrate spot user to UsdClassTransfer
luca992 Jan 2, 2026
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
10 changes: 5 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ repository = "https://github.com/hyperliquid-dex/hyperliquid-rust-sdk"

[dependencies]
alloy = { version = "1.0", default-features = false, features = [
"dyn-abi",
"sol-types",
"signer-local",
"dyn-abi",
"sol-types",
"signer-local",
] }
chrono = "0.4.26"
env_logger = "0.11.8"
Expand All @@ -24,9 +24,9 @@ lazy_static = "1.0"
log = "0.4.19"
reqwest = "0.12.19"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_json = { version = "1.0", features = ["preserve_order"] }
rmp-serde = "1.0"
thiserror = "2.0"
tokio = { version = "1.0", features = ["full"] }
tokio-tungstenite = { version = "0.20.0", features = ["native-tls"] }
tokio-tungstenite = { version = "0.28.0", features = ["native-tls"] }
uuid = { version = "1.0", features = ["v4"] }
18 changes: 11 additions & 7 deletions src/bin/agent.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use alloy::signers::local::PrivateKeySigner;
use hyperliquid_rust_sdk::{BaseUrl, ClientLimit, ClientOrder, ClientOrderRequest, ExchangeClient};
use hyperliquid_rust_sdk::{
ClientLimit, ClientOrder, ClientOrderRequest, ExchangeClient, HyperliquidChain,
};
use log::info;

#[tokio::main]
Expand All @@ -11,9 +13,10 @@ async fn main() {
.parse()
.unwrap();

let exchange_client = ExchangeClient::new(None, wallet, Some(BaseUrl::Testnet), None, None)
.await
.unwrap();
let exchange_client =
ExchangeClient::new(None, wallet, Some(HyperliquidChain::Testnet), None, None)
.await
.unwrap();

/*
Create a new wallet with the agent.
Expand All @@ -27,9 +30,10 @@ async fn main() {

info!("Agent address: {:?}", wallet.address());

let exchange_client = ExchangeClient::new(None, wallet, Some(BaseUrl::Testnet), None, None)
.await
.unwrap();
let exchange_client =
ExchangeClient::new(None, wallet, Some(HyperliquidChain::Testnet), None, None)
.await
.unwrap();

let order = ClientOrderRequest {
asset: "ETH".to_string(),
Expand Down
15 changes: 10 additions & 5 deletions src/bin/approve_builder_fee.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use alloy::{primitives::address, signers::local::PrivateKeySigner};
use hyperliquid_rust_sdk::{BaseUrl, ExchangeClient};
use hyperliquid_rust_sdk::{ExchangeClient, HyperliquidChain};
use log::info;

#[tokio::main]
Expand All @@ -11,10 +11,15 @@ async fn main() {
.parse()
.unwrap();

let exchange_client =
ExchangeClient::new(None, wallet.clone(), Some(BaseUrl::Testnet), None, None)
.await
.unwrap();
let exchange_client = ExchangeClient::new(
None,
wallet.clone(),
Some(HyperliquidChain::Testnet),
None,
None,
)
.await
.unwrap();

let max_fee_rate = "0.1%";
let builder = address!("0x1ab189B7801140900C711E458212F9c76F8dAC79");
Expand Down
9 changes: 5 additions & 4 deletions src/bin/bridge_withdraw.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use alloy::signers::local::PrivateKeySigner;
use hyperliquid_rust_sdk::{BaseUrl, ExchangeClient};
use hyperliquid_rust_sdk::{ExchangeClient, HyperliquidChain};
use log::info;

#[tokio::main]
Expand All @@ -11,9 +11,10 @@ async fn main() {
.parse()
.unwrap();

let exchange_client = ExchangeClient::new(None, wallet, Some(BaseUrl::Testnet), None, None)
.await
.unwrap();
let exchange_client =
ExchangeClient::new(None, wallet, Some(HyperliquidChain::Testnet), None, None)
.await
.unwrap();

let usd = "5"; // 5 USD
let destination = "0x0D1d9635D0640821d15e323ac8AdADfA9c111414";
Expand Down
9 changes: 5 additions & 4 deletions src/bin/claim_rewards.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use alloy::signers::local::PrivateKeySigner;
use hyperliquid_rust_sdk::{BaseUrl, ExchangeClient, ExchangeResponseStatus};
use hyperliquid_rust_sdk::{ExchangeClient, ExchangeResponseStatus, HyperliquidChain};
use log::info;

#[tokio::main]
Expand All @@ -11,9 +11,10 @@ async fn main() {
.parse()
.unwrap();

let exchange_client = ExchangeClient::new(None, wallet, Some(BaseUrl::Testnet), None, None)
.await
.unwrap();
let exchange_client =
ExchangeClient::new(None, wallet, Some(HyperliquidChain::Testnet), None, None)
.await
.unwrap();

let response = exchange_client.claim_rewards(None).await.unwrap();

Expand Down
11 changes: 6 additions & 5 deletions src/bin/class_transfer.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use alloy::signers::local::PrivateKeySigner;
use hyperliquid_rust_sdk::{BaseUrl, ExchangeClient};
use hyperliquid_rust_sdk::{ExchangeClient, HyperliquidChain};
use log::info;

#[tokio::main]
Expand All @@ -11,15 +11,16 @@ async fn main() {
.parse()
.unwrap();

let exchange_client = ExchangeClient::new(None, wallet, Some(BaseUrl::Testnet), None, None)
.await
.unwrap();
let exchange_client =
ExchangeClient::new(None, wallet, Some(HyperliquidChain::Testnet), None, None)
.await
.unwrap();

let usdc = 1.0; // 1 USD
let to_perp = false;

let res = exchange_client
.class_transfer(usdc, to_perp, None)
.usd_class_transfer(usdc, to_perp, None)
.await
.unwrap();
info!("Class transfer result: {res:?}");
Expand Down
102 changes: 102 additions & 0 deletions src/bin/convert_to_multi_sig_user.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use alloy::{primitives::Address, signers::local::PrivateKeySigner};
use hyperliquid_rust_sdk::{ExchangeClient, HyperliquidChain};
use log::info;

async fn setup_exchange_client() -> (Address, ExchangeClient) {
// Key was randomly generated for testing and shouldn't be used with any real funds
let wallet: PrivateKeySigner =
"e908f86dbb4d55ac876378565aafeabc187f6690f046459397b17d9b9a19688e"
.parse()
.unwrap();

let address = wallet.address();
let exchange_client =
ExchangeClient::new(None, wallet, Some(HyperliquidChain::Testnet), None, None)
.await
.unwrap();

(address, exchange_client)
}

#[tokio::main]
async fn main() {
env_logger::init();

let (address, exchange_client) = setup_exchange_client().await;

// Ensure we're using the actual user's wallet, not an agent
if address != exchange_client.wallet.address() {
panic!("Agents do not have permission to convert to multi-sig user");
}

// Addresses that will be authorized to sign for the multi-sig account
let authorized_user_1: Address = "0x0000000000000000000000000000000000000000"
.parse()
.unwrap();
let authorized_user_2: Address = "0x0000000000000000000000000000000000000001"
.parse()
.unwrap();

// Threshold: minimum number of signatures required to execute any transaction
// This matches the Python example where threshold is 1
let threshold = 1;

info!("=== Convert to Multi-Sig User Example ===");
info!("Current user address: {}", address);
info!("Connected to: {:?}", exchange_client.http_client.chain);
info!("");
info!("Configuration:");
info!(" Authorized user 1: {}", authorized_user_1);
info!(" Authorized user 2: {}", authorized_user_2);
info!(" Threshold: {}", threshold);
info!("");

// Step 1: Convert the user to a multi-sig account
info!("Step 1: Converting to multi-sig account...");
match exchange_client.convert_to_multi_sig(threshold, None).await {
Ok(response) => {
info!("Convert to multi-sig response: {:?}", response);
info!("Successfully converted to multi-sig!");
}
Err(e) => {
info!("Convert to multi-sig failed (this is expected if already converted or on testnet): {}", e);
}
}

// Step 2: Add authorized addresses
info!("Step 2: Adding authorized addresses...");
match exchange_client
.update_multi_sig_addresses(
vec![authorized_user_1, authorized_user_2],
vec![], // No addresses to remove
None,
)
.await
{
Ok(response) => {
info!("Update multi-sig addresses response: {:?}", response);
info!("Successfully added authorized addresses!");
}
Err(e) => {
info!("Update multi-sig addresses failed: {}", e);
}
}

info!("");
info!("Multi-sig setup complete!");
info!("Now you can use the multi-sig methods with the authorized wallets:");
info!("- multi_sig_order()");
info!("- multi_sig_usdc_transfer()");
info!("- multi_sig_spot_transfer()");
info!("");
info!("IMPORTANT: After converting to multi-sig:");
info!("1. The account can only be controlled by the authorized addresses");
info!(
"2. You need {} signatures to execute any transaction",
threshold
);
info!("3. Make sure you have access to the authorized private keys!");
info!("4. This is a one-way conversion - test on testnet first!");

info!("Example completed - multi-sig conversion functionality demonstrated");
}
6 changes: 4 additions & 2 deletions src/bin/info.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
use alloy::primitives::Address;
use hyperliquid_rust_sdk::{BaseUrl, InfoClient};
use hyperliquid_rust_sdk::{HyperliquidChain, InfoClient};
use log::info;

const ADDRESS: &str = "0xc64cc00b46101bd40aa1c3121195e85c0b0918d8";

#[tokio::main]
async fn main() {
env_logger::init();
let info_client = InfoClient::new(None, Some(BaseUrl::Testnet)).await.unwrap();
let info_client = InfoClient::new(None, Some(HyperliquidChain::Testnet))
.await
.unwrap();
open_orders_example(&info_client).await;
user_state_example(&info_client).await;
user_states_example(&info_client).await;
Expand Down
9 changes: 6 additions & 3 deletions src/bin/leverage.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use alloy::signers::local::PrivateKeySigner;
use hyperliquid_rust_sdk::{BaseUrl, ExchangeClient, InfoClient};
use hyperliquid_rust_sdk::{ExchangeClient, HyperliquidChain, InfoClient};
use log::info;

#[tokio::main]
Expand All @@ -13,10 +13,13 @@ async fn main() {
.unwrap();

let address = wallet.address();
let exchange_client = ExchangeClient::new(None, wallet, Some(BaseUrl::Testnet), None, None)
let exchange_client =
ExchangeClient::new(None, wallet, Some(HyperliquidChain::Testnet), None, None)
.await
.unwrap();
let info_client = InfoClient::new(None, Some(HyperliquidChain::Testnet))
.await
.unwrap();
let info_client = InfoClient::new(None, Some(BaseUrl::Testnet)).await.unwrap();

let response = exchange_client
.update_leverage(5, "ETH", false, None)
Expand Down
11 changes: 6 additions & 5 deletions src/bin/market_order_and_cancel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use std::{thread::sleep, time::Duration};

use alloy::signers::local::PrivateKeySigner;
use hyperliquid_rust_sdk::{
BaseUrl, ExchangeClient, ExchangeDataStatus, ExchangeResponseStatus, MarketCloseParams,
MarketOrderParams,
ExchangeClient, ExchangeDataStatus, ExchangeResponseStatus, HyperliquidChain,
MarketCloseParams, MarketOrderParams,
};
use log::info;

Expand All @@ -16,9 +16,10 @@ async fn main() {
.parse()
.unwrap();

let exchange_client = ExchangeClient::new(None, wallet, Some(BaseUrl::Testnet), None, None)
.await
.unwrap();
let exchange_client =
ExchangeClient::new(None, wallet, Some(HyperliquidChain::Testnet), None, None)
.await
.unwrap();

// Market open order
let market_open_params = MarketOrderParams {
Expand Down
9 changes: 5 additions & 4 deletions src/bin/market_order_with_builder_and_cancel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::{thread::sleep, time::Duration};

use alloy::signers::local::PrivateKeySigner;
use hyperliquid_rust_sdk::{
BaseUrl, BuilderInfo, ExchangeClient, ExchangeDataStatus, ExchangeResponseStatus,
BuilderInfo, ExchangeClient, ExchangeDataStatus, ExchangeResponseStatus, HyperliquidChain,
MarketCloseParams, MarketOrderParams,
};
use log::info;
Expand All @@ -16,9 +16,10 @@ async fn main() {
.parse()
.unwrap();

let exchange_client = ExchangeClient::new(None, wallet, Some(BaseUrl::Testnet), None, None)
.await
.unwrap();
let exchange_client =
ExchangeClient::new(None, wallet, Some(HyperliquidChain::Testnet), None, None)
.await
.unwrap();

// Market open order
let market_open_params = MarketOrderParams {
Expand Down
Loading