Skip to content

Add Alloy-rs and remove ethers #85

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
9 changes: 7 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@ repository = "https://github.com/hyperliquid-dex/hyperliquid-rust-sdk"
[dependencies]
chrono = "0.4.26"
env_logger = "0.10.0"
ethers = {version = "2.0.14", features = ["eip712", "abigen"]}
alloy-primitives = { version = "0.8.21", features = ["serde"] }
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you use alloy with features instead of everything as a single dependency?

It's easier for dependency management and you don't end up with a mess of dependencies.

Not 100% sure, but this should be it. Alloy core should have remaining deps

Suggested change
alloy-primitives = { version = "0.8.21", features = ["serde"] }
alloy = { version = "0.8", features = ["serde", "signer-local"] }

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes I also agree with this, much cleaner this way

alloy-sol-types = { version = "0.8.21", features = ["json"] }
alloy-signer = { version = "0.11.1", features = ["eip712"] }
alloy-signer-local = "0.11.1"
alloy-dyn-abi = "0.8"
k256 = { version = "0.13", features = ["ecdsa", "sha256"] }
futures-util = "0.3.28"
hex = "0.4.3"
http = "0.2.9"
Expand All @@ -28,4 +33,4 @@ rmp-serde = "1.0.0"
thiserror = "1.0.44"
tokio = {version = "1.29.1", features = ["full"]}
tokio-tungstenite = {version = "0.20.0", features = ["native-tls"]}
uuid = {version = "1.6.1", features = ["v4"]}
uuid = {version = "1.6.1", features = ["v4", "serde"]}
51 changes: 13 additions & 38 deletions src/bin/agent.rs
Original file line number Diff line number Diff line change
@@ -1,49 +1,24 @@
use alloy_primitives::{Address, U256};
use alloy_signer_local::PrivateKeySigner;
Copy link
Contributor

Choose a reason for hiding this comment

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

And then use alloy::primitives:...
and so on

use hyperliquid_rust_sdk::{BaseUrl, ExchangeClient};
use log::info;

use ethers::signers::{LocalWallet, Signer};
use hyperliquid_rust_sdk::{BaseUrl, ClientLimit, ClientOrder, ClientOrderRequest, ExchangeClient};

#[tokio::main]
async fn main() {
env_logger::init();
// Key was randomly generated for testing and shouldn't be used with any real funds
let wallet: LocalWallet = "e908f86dbb4d55ac876378565aafeabc187f6690f046459397b17d9b9a19688e"
.parse()
.unwrap();

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

/*
Create a new wallet with the agent.
This agent cannot transfer or withdraw funds, but can for example place orders.
*/

let (private_key, response) = exchange_client.approve_agent(None).await.unwrap();
info!("Agent creation response: {response:?}");

let wallet: LocalWallet = private_key.parse().unwrap();

info!("Agent address: {:?}", wallet.address());
let priv_key = "e908f86dbb4d55ac876378565aafeabc187f6690f046459397b17d9b9a19688e";
let wallet = priv_key.parse::<PrivateKeySigner>().unwrap();

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

let order = ClientOrderRequest {
asset: "ETH".to_string(),
is_buy: true,
reduce_only: false,
limit_px: 1795.0,
sz: 0.01,
cloid: None,
order_type: ClientOrder::Limit(ClientLimit {
tif: "Gtc".to_string(),
}),
};
let agent = "0x1234567890123456789012345678901234567890".parse::<Address>().unwrap();

let response = exchange_client.order(order, None).await.unwrap();
info!("Approving agent {}", agent);

info!("Order placed: {response:?}");
let res = exchange_client.approve_agent(agent, "Testnet".to_string()).await;
match res {
Ok(_) => info!("Successfully approved agent"),
Err(e) => eprintln!("Failed to approve agent: {}", e),
}
}
26 changes: 11 additions & 15 deletions src/bin/approve_builder_fee.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,21 @@
use ethers::signers::LocalWallet;
use hyperliquid_rust_sdk::{BaseUrl, ExchangeClient};
use alloy_primitives::{Address, U256};
use alloy_signer_local::LocalWallet;
use hyperliquid_rust_sdk::{BaseUrl, ExchangeClient, BuilderInfo};
use log::info;

#[tokio::main]
async fn main() {
env_logger::init();
// Key was randomly generated for testing and shouldn't be used with any real funds
let wallet: LocalWallet = "e908f86dbb4d55ac876378565aafeabc187f6690f046459397b17d9b9a19688e"
.parse()
.unwrap();
let priv_key = "e908f86dbb4d55ac876378565aafeabc187f6690f046459397b17d9b9a19688e";
let wallet = priv_key.parse::<LocalWallet>().unwrap();

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

let max_fee_rate = "0.1%";
let builder = "0x1ab189B7801140900C711E458212F9c76F8dAC79".to_lowercase();
let builder = BuilderInfo {
builder: "0x1ab189B7801140900C711E458212F9c76F8dAC79".to_string(),
fee: 1,
};

let resp = exchange_client
.approve_builder_fee(builder.to_string(), max_fee_rate.to_string(), Some(&wallet))
.await;
info!("resp: {resp:#?}");
info!("Builder info: {:?}", builder);
}
19 changes: 10 additions & 9 deletions src/bin/bridge_withdraw.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use ethers::signers::LocalWallet;
use hyperliquid_rust_sdk::{BaseUrl, ExchangeClient};
use alloy_primitives::{Address, U256};
use hyperliquid_rust_sdk::{BaseUrl, ExchangeClient, LocalWallet};
use log::info;

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

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

let usd = "5"; // 5 USD
let amount = "5"; // 5 USD
let destination = "0x0D1d9635D0640821d15e323ac8AdADfA9c111414";

let res = exchange_client
.withdraw_from_bridge(usd, destination, None)
let amount = amount.parse::<U256>().unwrap();
let destination = destination.parse::<Address>().unwrap();

exchange_client
.withdraw(destination, amount, "Testnet".to_string())
.await
.unwrap();
info!("Withdraw from bridge result: {res:?}");
info!("Withdraw completed");
}
27 changes: 15 additions & 12 deletions src/bin/class_transfer.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
use ethers::signers::LocalWallet;
use alloy_primitives::U256;
use alloy_signer_local::PrivateKeySigner;
use hyperliquid_rust_sdk::{BaseUrl, ExchangeClient};
use log::info;

#[tokio::main]
async fn main() {
env_logger::init();
// Key was randomly generated for testing and shouldn't be used with any real funds
let wallet: LocalWallet = "e908f86dbb4d55ac876378565aafeabc187f6690f046459397b17d9b9a19688e"
.parse()
.unwrap();
let priv_key = "e908f86dbb4d55ac876378565aafeabc187f6690f046459397b17d9b9a19688e";
let wallet = priv_key.parse::<PrivateKeySigner>().unwrap();

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

let usdc = 1.0; // 1 USD
let to_perp = false;
let usdc = 1000; // 1000 USDC
let to_perp = true; // Transfer to perp account

let res = exchange_client
.class_transfer(usdc, to_perp, None)
info!("Transferring {} USDC to {} account", usdc, if to_perp { "perp" } else { "spot" });

let amount = U256::from(usdc);

exchange_client
.class_transfer(amount, to_perp, "Testnet".to_string())
.await
.unwrap();
info!("Class transfer result: {res:?}");

info!("Class transfer completed");
}
70 changes: 47 additions & 23 deletions src/bin/info.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use ethers::types::H160;
use alloy_primitives::Address;
use hyperliquid_rust_sdk::{BaseUrl, InfoClient};
use log::info;

Expand All @@ -8,28 +8,52 @@ const ADDRESS: &str = "0xc64cc00b46101bd40aa1c3121195e85c0b0918d8";
async fn main() {
env_logger::init();
let info_client = InfoClient::new(None, Some(BaseUrl::Testnet)).await.unwrap();
open_orders_example(&info_client).await;
user_state_example(&info_client).await;
user_states_example(&info_client).await;
recent_trades(&info_client).await;
meta_example(&info_client).await;
all_mids_example(&info_client).await;
user_fills_example(&info_client).await;
funding_history_example(&info_client).await;
l2_snapshot_example(&info_client).await;
candles_snapshot_example(&info_client).await;
user_token_balances_example(&info_client).await;
user_fees_example(&info_client).await;
user_funding_example(&info_client).await;
spot_meta_example(&info_client).await;
spot_meta_and_asset_contexts_example(&info_client).await;
query_order_by_oid_example(&info_client).await;
query_referral_state_example(&info_client).await;
historical_orders_example(&info_client).await;
}

fn address() -> H160 {
ADDRESS.to_string().parse().unwrap()
let user = "0xc64cc00b46101bd40aa1c3121195e85c0b0918d8".parse::<Address>().unwrap();

let user_state = info_client.user_state(user).await.unwrap();
info!("User state: {user_state:?}");

let user_fills = info_client.user_fills(user).await.unwrap();
info!("User fills: {user_fills:?}");

let open_orders = info_client.open_orders(user).await.unwrap();
info!("Open orders: {open_orders:?}");

let meta = info_client.meta().await.unwrap();
info!("Meta: {meta:?}");

let all_mids = info_client.all_mids().await.unwrap();
info!("All mids: {all_mids:?}");

let l2_snapshot = info_client.l2_snapshot("ETH".to_string()).await.unwrap();
info!("L2 snapshot: {l2_snapshot:?}");

let recent_trades = info_client.recent_trades("ETH".to_string()).await.unwrap();
info!("Recent trades: {recent_trades:?}");

let candles_snapshot = info_client
.candles_snapshot(
"ETH".to_string(),
"1m".to_string(),
1704067200,
1704153600,
)
.await
.unwrap();
info!("Candles snapshot: {candles_snapshot:?}");

let order_status = info_client.query_order_by_oid(user, 1).await.unwrap();
info!("Order status: {order_status:?}");

let referral_state = info_client.query_referral_state(user).await.unwrap();
info!("Referral state: {referral_state:?}");

let historical_orders = info_client.historical_orders(user).await.unwrap();
info!("Historical orders: {historical_orders:?}");
}

fn address() -> Address {
ADDRESS.parse().unwrap()
}

async fn open_orders_example(info_client: &InfoClient) {
Expand Down
34 changes: 8 additions & 26 deletions src/bin/leverage.rs
Original file line number Diff line number Diff line change
@@ -1,35 +1,17 @@
use ethers::signers::{LocalWallet, Signer};
use hyperliquid_rust_sdk::{BaseUrl, ExchangeClient, InfoClient};
use alloy_primitives::U256;
use alloy_signer_local::PrivateKeySigner;
use hyperliquid_rust_sdk::{BaseUrl, ExchangeClient};
use log::info;

#[tokio::main]
async fn main() {
// Example assumes you already have a position on ETH so you can update margin
env_logger::init();
// Key was randomly generated for testing and shouldn't be used with any real funds
let wallet: LocalWallet = "e908f86dbb4d55ac876378565aafeabc187f6690f046459397b17d9b9a19688e"
.parse()
.unwrap();
let priv_key = "e908f86dbb4d55ac876378565aafeabc187f6690f046459397b17d9b9a19688e";
let wallet = priv_key.parse::<PrivateKeySigner>().unwrap();

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

let response = exchange_client
.update_leverage(5, "ETH", false, None)
.await
.unwrap();
info!("Update leverage response: {response:?}");

let response = exchange_client
.update_isolated_margin(1.0, "ETH", None)
.await
.unwrap();

info!("Update isolated margin response: {response:?}");

let user_state = info_client.user_state(address).await.unwrap();
info!("User state: {user_state:?}");
// TODO: Implement leverage functionality using the new API
info!("Leverage functionality not yet implemented");
}
67 changes: 48 additions & 19 deletions src/bin/market_maker.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,56 @@
/*
This is an example of a basic market making strategy.

We subscribe to the current mid price and build a market around this price. Whenever our market becomes outdated, we place and cancel orders to renew it.
*/
use ethers::signers::LocalWallet;

use hyperliquid_rust_sdk::{MarketMaker, MarketMakerInput};
use alloy_primitives::Address;
use alloy_signer_local::PrivateKeySigner;
use hyperliquid_rust_sdk::{
BaseUrl, ExchangeClient, ClientLimit, ClientOrder, ClientOrderRequest, BuilderInfo,
};
use log::info;
use std::time::Duration;

#[tokio::main]
async fn main() {
env_logger::init();
// Key was randomly generated for testing and shouldn't be used with any real funds
let wallet: LocalWallet = "e908f86dbb4d55ac876378565aafeabc187f6690f046459397b17d9b9a19688e"
.parse()
.unwrap();
let market_maker_input = MarketMakerInput {
let priv_key = "e908f86dbb4d55ac876378565aafeabc187f6690f046459397b17d9b9a19688e";
let wallet = priv_key.parse::<PrivateKeySigner>().unwrap();

let exchange_client = ExchangeClient::new(BaseUrl::Testnet.get_url());

let builder_info = BuilderInfo {
builder: "test".to_string(),
fee: 1,
};

// Place a limit buy order
let buy_order = ClientOrderRequest {
asset: "ETH".to_string(),
target_liquidity: 0.25,
max_bps_diff: 2,
half_spread: 1,
max_absolute_position_size: 0.5,
decimals: 1,
wallet,
is_buy: true,
reduce_only: false,
limit_px: 2000.0,
sz: 0.1,
cloid: None,
order_type: ClientOrder::Limit(ClientLimit {
tif: "Gtc".to_string(),
}),
};
MarketMaker::new(market_maker_input).await.start().await

let response = exchange_client.order(buy_order, Some(builder_info.clone())).await.unwrap();
info!("Buy order response: {response:?}");

tokio::time::sleep(Duration::from_secs(5)).await;

// Place a limit sell order
let sell_order = ClientOrderRequest {
asset: "ETH".to_string(),
is_buy: false,
reduce_only: false,
limit_px: 2100.0,
sz: 0.1,
cloid: None,
order_type: ClientOrder::Limit(ClientLimit {
tif: "Gtc".to_string(),
}),
};

let response = exchange_client.order(sell_order, Some(builder_info)).await.unwrap();
info!("Sell order response: {response:?}");
}
Loading