use crate::api::response::{
AgentEnableDexAbstractionResponse, ApproveAgentResponse, ApproveBuilderFeeResponse,
BatchModifyResponse, CDepositResponse, CWithdrawResponse, CancelResponse, DefaultResponse,
ModifyResponse, NoopResponse, OrderResponse, ScheduleCancelResponse, SendAssetResponse,
TokenDelegateResponse, TwapCancelResponse, TwapOrderResponse, UpdateIsolatedMarginResponse,
UpdateLeverageResponse, UsdClassTransferResponse, UsdSendResponse, UserDexAbstractionResponse,
ValidatorL1StreamResponse, VaultTransferResponse, WithdrawResponse,
};
use crate::error::{HyperliquidError, Result};
use crate::signature::sign::{sig_v_from_bool, sign_l1_action, sign_typed_data};
use crate::types::exchange::{
Builder, CancelAction, CancelRequest, Grouping, ModifyRequest, NoopAction, OrderRequest,
TwapRequest,
};
use crate::types::signature::Eip712Signature;
use crate::utils::current_time_millis;
use crate::{
api::request_util::post_json,
client::HyperliquidClient,
types::exchange::{
AgentEnableDexAbstractionAction, ApproveAgentAction, ApproveBuilderFeeAction,
BatchModifyAction, CDepositAction, CWithdrawAction, ModifyAction, OrderAction,
ReserveRequestWeightAction, ScheduleCancelAction, SendAssetAction, TokenDelegateAction,
TwapCancelAction, TwapOrderAction, UpdateIsolatedMarginAction, UpdateLeverageAction,
UsdClassTransferAction, UsdSendAction, UserDexAbstractionAction, ValidatorL1StreamAction,
VaultTransferAction, WithdrawAction,
},
};
use alloy::primitives::Address;
use serde::de::DeserializeOwned;
use serde_json::json;
pub struct ExchangeApi<'client> {
client: &'client HyperliquidClient,
}
impl<'client> ExchangeApi<'client> {
pub fn new(client: &'client HyperliquidClient) -> Self {
Self { client }
}
async fn post<T>(&self, payload: serde_json::Value) -> Result<T>
where
T: DeserializeOwned,
{
post_json(
self.client.http_client(),
&format!("{}/exchange", self.client.base_url()),
payload,
)
.await
}
pub async fn place_order(
&self,
order: OrderRequest,
grouping: Grouping,
builder: Option<Builder>,
vault_address: Option<String>,
expires_after: Option<u64>,
) -> Result<OrderResponse> {
self.bulk_orders(vec![order], grouping, builder, vault_address, expires_after)
.await
}
pub async fn bulk_orders(
&self,
orders: Vec<OrderRequest>,
grouping: Grouping,
builder: Option<Builder>,
vault_address: Option<String>,
expires_after: Option<u64>,
) -> Result<OrderResponse> {
let signer = self
.client
.signer()
.ok_or_else(|| HyperliquidError::SignerRequired)?;
let nonce = current_time_millis();
let action = OrderAction {
type_: "order".to_owned(),
orders,
grouping,
builder,
};
let sig = sign_l1_action(
&signer,
&action,
vault_address.clone(),
nonce,
expires_after,
false,
)?;
let signature = Eip712Signature {
r: format!("0x{:x}", sig.r()),
s: format!("0x{:x}", sig.s()),
v: sig_v_from_bool(sig.v()),
};
let mut payload = json!({
"action": action,
"nonce": nonce,
"signature": signature
});
if let Some(vault_address) = vault_address {
payload
.as_object_mut()
.ok_or_else(|| HyperliquidError::Internal("payload wrongly formatted".to_owned()))?
.insert("vaultAddress".to_owned(), json!(vault_address));
}
if let Some(expires_after) = expires_after {
payload
.as_object_mut()
.ok_or_else(|| HyperliquidError::Internal("payload wrongly formatted".to_owned()))?
.insert("expiresAfter".to_owned(), json!(expires_after));
}
self.post(payload).await
}
pub async fn cancel_order(
&self,
cancel: CancelRequest,
vault_address: Option<String>,
expires_after: Option<u64>,
) -> Result<CancelResponse> {
self.bulk_cancel_orders(vec![cancel], vault_address, expires_after)
.await
}
pub async fn bulk_cancel_orders(
&self,
cancels: Vec<CancelRequest>,
vault_address: Option<String>,
expires_after: Option<u64>,
) -> Result<CancelResponse> {
let signer = self
.client
.signer()
.ok_or_else(|| HyperliquidError::SignerRequired)?;
let action = CancelAction {
type_: "cancel".to_string(),
cancels,
};
let nonce = current_time_millis();
let sig = sign_l1_action(
&signer,
&action,
vault_address.clone(),
nonce,
expires_after,
false,
)?;
let signature = Eip712Signature {
r: format!("0x{:x}", sig.r()),
s: format!("0x{:x}", sig.s()),
v: sig_v_from_bool(sig.v()),
};
let mut payload = json!({
"action": action,
"nonce": nonce,
"signature": signature
});
if let Some(vault_address) = vault_address {
payload
.as_object_mut()
.ok_or_else(|| HyperliquidError::Internal("payload wrongly formatted".to_owned()))?
.insert("vaultAddress".to_owned(), json!(vault_address));
}
if let Some(expires_after) = expires_after {
payload
.as_object_mut()
.ok_or_else(|| HyperliquidError::Internal("payload wrongly formatted".to_owned()))?
.insert("expiresAfter".to_owned(), json!(expires_after));
}
self.post(payload).await
}
pub async fn schedule_cancel(
&self,
time: Option<u64>,
vault_address: Option<String>,
expires_after: Option<u64>,
) -> Result<ScheduleCancelResponse> {
let signer = self
.client
.signer()
.ok_or_else(|| HyperliquidError::SignerRequired)?;
let action = ScheduleCancelAction {
type_: "scheduleCancel".to_string(),
time,
};
let nonce = current_time_millis();
let sig = sign_l1_action(
&signer,
&action,
vault_address.clone(),
nonce,
expires_after,
self.client.is_mainnet(),
)?;
let signature = Eip712Signature {
r: format!("0x{:x}", sig.r()),
s: format!("0x{:x}", sig.s()),
v: sig_v_from_bool(sig.v()),
};
let mut payload = json!({
"action": action,
"nonce": nonce,
"signature": signature
});
if let Some(vault_address) = vault_address {
payload
.as_object_mut()
.ok_or_else(|| HyperliquidError::Internal("payload wrongly formatted".to_owned()))?
.insert("vaultAddress".to_owned(), json!(vault_address));
}
if let Some(expires_after) = expires_after {
payload
.as_object_mut()
.ok_or_else(|| HyperliquidError::Internal("payload wrongly formatted".to_owned()))?
.insert("expiresAfter".to_owned(), json!(expires_after));
}
self.post(payload).await
}
pub async fn modify_an_order(
&self,
request: ModifyRequest,
vault_address: Option<String>,
expires_after: Option<u64>,
) -> Result<ModifyResponse> {
let signer = self
.client
.signer()
.ok_or_else(|| HyperliquidError::SignerRequired)?;
let nonce = current_time_millis();
let action = ModifyAction {
type_: "modify".to_string(),
oid: request.oid,
order: request.order,
};
let sig = sign_l1_action(
&signer,
&action,
vault_address.clone(),
nonce,
expires_after,
self.client.is_mainnet(),
)?;
let signature = Eip712Signature {
r: format!("0x{:x}", sig.r()),
s: format!("0x{:x}", sig.s()),
v: sig_v_from_bool(sig.v()),
};
let mut payload = json!({
"action": action,
"nonce": nonce,
"signature": signature
});
if let Some(vault_address) = vault_address {
payload
.as_object_mut()
.ok_or_else(|| HyperliquidError::Internal("payload wrongly formatted".to_owned()))?
.insert("vaultAddress".to_owned(), json!(vault_address));
}
if let Some(expires_after) = expires_after {
payload
.as_object_mut()
.ok_or_else(|| HyperliquidError::Internal("payload wrongly formatted".to_owned()))?
.insert("expiresAfter".to_owned(), json!(expires_after));
}
self.post(payload).await
}
pub async fn modify_multiple_orders(
&self,
requests: Vec<ModifyRequest>,
vault_address: Option<String>,
expires_after: Option<u64>,
) -> Result<BatchModifyResponse> {
let signer = self
.client
.signer()
.ok_or_else(|| HyperliquidError::SignerRequired)?;
let nonce = current_time_millis();
let action = BatchModifyAction {
type_: "batchModify".to_string(),
modifies: requests,
};
let sig = sign_l1_action(
&signer,
&action,
vault_address.clone(),
nonce,
expires_after,
self.client.is_mainnet(),
)?;
let signature = Eip712Signature {
r: format!("0x{:x}", sig.r()),
s: format!("0x{:x}", sig.s()),
v: sig_v_from_bool(sig.v()),
};
let mut payload = json!({
"action": action,
"nonce": nonce,
"signature": signature
});
if let Some(vault_address) = vault_address {
payload
.as_object_mut()
.ok_or_else(|| HyperliquidError::Internal("payload wrongly formatted".to_owned()))?
.insert("vaultAddress".to_owned(), json!(vault_address));
}
if let Some(expires_after) = expires_after {
payload
.as_object_mut()
.ok_or_else(|| HyperliquidError::Internal("payload wrongly formatted".to_owned()))?
.insert("expiresAfter".to_owned(), json!(expires_after));
}
self.post(payload).await
}
pub async fn update_isolated_margin(
&self,
asset: u32,
is_buy: bool,
ntli: u32,
vault_address: Option<String>,
expires_after: Option<u64>,
) -> Result<UpdateIsolatedMarginResponse> {
let signer = self
.client
.signer()
.ok_or_else(|| HyperliquidError::SignerRequired)?;
let nonce = current_time_millis();
let action = UpdateIsolatedMarginAction {
type_: "updateIsolatedMargin".to_string(),
asset,
is_buy,
ntli,
};
let sig = sign_l1_action(
&signer,
&action,
vault_address.clone(),
nonce,
expires_after,
self.client.is_mainnet(),
)?;
let signature = Eip712Signature {
r: format!("0x{:x}", sig.r()),
s: format!("0x{:x}", sig.s()),
v: sig_v_from_bool(sig.v()),
};
let mut payload = json!({
"action": action,
"nonce": nonce,
"signature": signature
});
if let Some(vault_address) = vault_address {
payload
.as_object_mut()
.ok_or_else(|| HyperliquidError::Internal("payload wrongly formatted".to_owned()))?
.insert("vaultAddress".to_owned(), json!(vault_address));
}
if let Some(expires_after) = expires_after {
payload
.as_object_mut()
.ok_or_else(|| HyperliquidError::Internal("payload wrongly formatted".to_owned()))?
.insert("expiresAfter".to_owned(), json!(expires_after));
}
self.post(payload).await
}
pub async fn update_leverage(
&self,
asset: u32,
is_cross: bool,
leverage: u32,
vault_address: Option<String>,
expires_after: Option<u64>,
) -> Result<UpdateLeverageResponse> {
let signer = self
.client
.signer()
.ok_or_else(|| HyperliquidError::SignerRequired)?;
let nonce = current_time_millis();
let action = UpdateLeverageAction {
type_: "updateLeverage".to_string(),
asset,
is_cross,
leverage,
};
let sig = sign_l1_action(
&signer,
&action,
vault_address.clone(),
nonce,
expires_after,
self.client.is_mainnet(),
)?;
let signature = Eip712Signature {
r: format!("0x{:x}", sig.r()),
s: format!("0x{:x}", sig.s()),
v: sig_v_from_bool(sig.v()),
};
let mut payload = json!({
"action": action,
"nonce": nonce,
"signature": signature
});
if let Some(vault_address) = vault_address {
payload
.as_object_mut()
.ok_or_else(|| HyperliquidError::Internal("payload wrongly formatted".to_owned()))?
.insert("vaultAddress".to_owned(), json!(vault_address));
}
if let Some(expires_after) = expires_after {
payload
.as_object_mut()
.ok_or_else(|| HyperliquidError::Internal("payload wrongly formatted".to_owned()))?
.insert("expiresAfter".to_owned(), json!(expires_after));
}
self.post(payload).await
}
pub async fn core_usdc_transfer(
&self,
destination: Address,
amount: String,
time: u64,
) -> Result<UsdSendResponse> {
let signer = self
.client
.signer()
.ok_or_else(|| HyperliquidError::SignerRequired)?;
let nonce = current_time_millis();
let action = UsdSendAction {
type_: "usdSend".to_string(),
hyperliquid_chain: self.client.network_type().hyperliquid_chain(),
signature_chain_id: self.client.network_type().signature_chain_id(),
destination: destination.to_string(),
amount,
time,
};
let sig = sign_typed_data(&action, &signer)?;
let signature = Eip712Signature {
r: format!("0x{:x}", sig.r()),
s: format!("0x{:x}", sig.s()),
v: sig_v_from_bool(sig.v()),
};
let payload = json!({
"action": action,
"nonce": nonce,
"signature": signature
});
self.post(payload).await
}
pub async fn initiate_withdrawal_request(
&self,
amount: String,
time: u64,
destination: Address,
) -> Result<WithdrawResponse> {
let signer = self
.client
.signer()
.ok_or_else(|| HyperliquidError::SignerRequired)?;
let nonce = current_time_millis();
let action = WithdrawAction {
type_: "withdraw3".to_string(),
hyperliquid_chain: self.client.network_type().hyperliquid_chain(),
signature_chain_id: self.client.network_type().signature_chain_id(),
amount,
time,
destination: destination.to_string(),
};
let sig = sign_l1_action(&signer, &action, None, nonce, None, false)?;
let signature = Eip712Signature {
r: format!("0x{:x}", sig.r()),
s: format!("0x{:x}", sig.s()),
v: sig_v_from_bool(sig.v()),
};
let payload = json!({
"action": action,
"nonce": nonce,
"signature": signature
});
self.post(payload).await
}
pub async fn transfer_to_or_from_spot_account_to_or_from_perp_account(
&self,
amount: String,
to_perp: bool,
) -> Result<UsdClassTransferResponse> {
let signer = self
.client
.signer()
.ok_or_else(|| HyperliquidError::SignerRequired)?;
let nonce = current_time_millis();
let action = UsdClassTransferAction {
type_: "usdClassTransfer".to_string(),
hyperliquid_chain: self.client.network_type().hyperliquid_chain(),
signature_chain_id: self.client.network_type().signature_chain_id(),
amount,
to_perp,
nonce,
};
let sig = sign_typed_data(&action, &signer)?;
let signature = Eip712Signature {
r: format!("0x{:x}", sig.r()),
s: format!("0x{:x}", sig.s()),
v: sig_v_from_bool(sig.v()),
};
let payload = json!({
"action": action,
"nonce": nonce,
"signature": signature
});
self.post(payload).await
}
pub async fn send_asset(
&self,
destination: Address,
source_dex: Address,
destination_dex: Address,
token: String,
amount: String,
from_sub_account: Option<String>,
) -> Result<SendAssetResponse> {
let signer = self
.client
.signer()
.ok_or_else(|| HyperliquidError::SignerRequired)?;
let nonce = current_time_millis();
let from_sub_account_param = from_sub_account.unwrap_or_default();
let action = SendAssetAction {
type_: "sendAsset".to_string(),
hyperliquid_chain: self.client.network_type().hyperliquid_chain(),
signature_chain_id: self.client.network_type().signature_chain_id(),
destination: destination.to_string(),
source_dex: source_dex.to_string(),
destination_dex: destination_dex.to_string(),
token,
amount,
from_sub_account: from_sub_account_param,
nonce,
};
let sig = sign_typed_data(&action, &signer)?;
let signature = Eip712Signature {
r: format!("0x{:x}", sig.r()),
s: format!("0x{:x}", sig.s()),
v: sig_v_from_bool(sig.v()),
};
let payload = json!({
"action": action,
"nonce": nonce,
"signature": signature
});
self.post(payload).await
}
pub async fn deposit_into_staking(&self, wei: u64) -> Result<CDepositResponse> {
let signer = self
.client
.signer()
.ok_or_else(|| HyperliquidError::SignerRequired)?;
let nonce = current_time_millis();
let action = CDepositAction {
type_: "cDeposit".to_string(),
hyperliquid_chain: self.client.network_type().hyperliquid_chain(),
signature_chain_id: self.client.network_type().signature_chain_id(),
wei,
nonce,
};
let sig = sign_l1_action(&signer, &action, None, nonce, None, false)?;
let signature = Eip712Signature {
r: format!("0x{:x}", sig.r()),
s: format!("0x{:x}", sig.s()),
v: sig_v_from_bool(sig.v()),
};
let payload = json!({
"action": action,
"nonce": nonce,
"signature": signature
});
self.post(payload).await
}
pub async fn withdraw_from_staking(&self, wei: u64) -> Result<CWithdrawResponse> {
let signer = self
.client
.signer()
.ok_or_else(|| HyperliquidError::SignerRequired)?;
let nonce = current_time_millis();
let action = CWithdrawAction {
type_: "cWithdraw".to_string(),
hyperliquid_chain: self.client.network_type().hyperliquid_chain(),
signature_chain_id: self.client.network_type().signature_chain_id(),
wei,
nonce,
};
let sig = sign_l1_action(&signer, &action, None, nonce, None, false)?;
let signature = Eip712Signature {
r: format!("0x{:x}", sig.r()),
s: format!("0x{:x}", sig.s()),
v: sig_v_from_bool(sig.v()),
};
let payload = json!({
"action": action,
"nonce": nonce,
"signature": signature
});
self.post(payload).await
}
pub async fn delegate_or_undelegate_from_validator(
&self,
validator: String,
is_undelegate: bool,
wei: u64,
) -> Result<TokenDelegateResponse> {
let signer = self
.client
.signer()
.ok_or_else(|| HyperliquidError::SignerRequired)?;
let nonce = current_time_millis();
let action = TokenDelegateAction {
type_: "tokenDelegate".to_string(),
hyperliquid_chain: self.client.network_type().hyperliquid_chain(),
signature_chain_id: self.client.network_type().signature_chain_id(),
is_undelegate,
wei,
nonce,
validator,
};
let sig = sign_l1_action(&signer, &action, None, nonce, None, false)?;
let signature = Eip712Signature {
r: format!("0x{:x}", sig.r()),
s: format!("0x{:x}", sig.s()),
v: sig_v_from_bool(sig.v()),
};
let payload = json!({
"action": action,
"nonce": nonce,
"signature": signature
});
self.post(payload).await
}
pub async fn deposit_or_withdraw_from_a_vault(
&self,
vault_address: String,
is_deposit: bool,
usd_amount: u64,
expires_after: Option<u64>,
) -> Result<VaultTransferResponse> {
let signer = self
.client
.signer()
.ok_or_else(|| HyperliquidError::SignerRequired)?;
let action = VaultTransferAction {
type_: "vaultTransfer".to_string(),
vault_address: vault_address.clone(),
is_deposit,
usd: usd_amount,
};
let nonce = current_time_millis();
let sig = sign_l1_action(
&signer,
&action,
Some(vault_address),
nonce,
expires_after,
false,
)?;
let signature = Eip712Signature {
r: format!("0x{:x}", sig.r()),
s: format!("0x{:x}", sig.s()),
v: sig_v_from_bool(sig.v()),
};
let mut payload = json!({
"action": action,
"nonce": nonce,
"signature": signature
});
if let Some(expires_after) = expires_after {
payload
.as_object_mut()
.ok_or_else(|| HyperliquidError::Internal("payload wrongly formatted".to_owned()))?
.insert("expiresAfter".to_owned(), json!(expires_after));
}
self.post(payload).await
}
pub async fn approve_an_api_wallet(
&self,
agent_address: String,
agent_name: Option<String>,
) -> Result<ApproveAgentResponse> {
let signer = self
.client
.signer()
.ok_or_else(|| HyperliquidError::SignerRequired)?;
let nonce = current_time_millis();
let action = ApproveAgentAction {
type_: "approveBuilderFee".to_string(),
hyperliquid_chain: self.client.network_type().hyperliquid_chain(),
signature_chain_id: self.client.network_type().signature_chain_id(),
nonce,
agent_address,
agent_name,
};
let sig = sign_l1_action(&signer, &action, None, nonce, None, false)?;
let signature = Eip712Signature {
r: format!("0x{:x}", sig.r()),
s: format!("0x{:x}", sig.s()),
v: sig_v_from_bool(sig.v()),
};
let payload = json!({
"action": action,
"nonce": nonce,
"signature": signature
});
self.post(payload).await
}
pub async fn approve_a_builder_fee(
&self,
max_fee_rate: String,
builder: String,
) -> Result<ApproveBuilderFeeResponse> {
let signer = self
.client
.signer()
.ok_or_else(|| HyperliquidError::SignerRequired)?;
let nonce = current_time_millis();
let action = ApproveBuilderFeeAction {
type_: "approveBuilderFee".to_string(),
hyperliquid_chain: self.client.network_type().hyperliquid_chain(),
signature_chain_id: self.client.network_type().signature_chain_id(),
max_fee_rate,
builder,
nonce,
};
let sig = sign_l1_action(&signer, &action, None, nonce, None, false)?;
let signature = Eip712Signature {
r: format!("0x{:x}", sig.r()),
s: format!("0x{:x}", sig.s()),
v: sig_v_from_bool(sig.v()),
};
let payload = json!({
"action": action,
"nonce": nonce,
"signature": signature
});
self.post(payload).await
}
pub async fn place_twap_order(
&self,
request: TwapRequest,
vault_address: Option<String>,
expires_after: Option<u64>,
) -> Result<TwapOrderResponse> {
let signer = self
.client
.signer()
.ok_or_else(|| HyperliquidError::SignerRequired)?;
let nonce = current_time_millis();
let action = TwapOrderAction {
type_: "twapOrder".to_string(),
twap: request,
};
let sig = sign_l1_action(
&signer,
&action,
vault_address.clone(),
nonce,
expires_after,
self.client.is_mainnet(),
)?;
let signature = Eip712Signature {
r: format!("0x{:x}", sig.r()),
s: format!("0x{:x}", sig.s()),
v: sig_v_from_bool(sig.v()),
};
let mut payload = json!({
"action": action,
"nonce": nonce,
"signature": signature
});
if let Some(vault_address) = vault_address {
payload
.as_object_mut()
.ok_or_else(|| HyperliquidError::Internal("payload wrongly formatted".to_owned()))?
.insert("vaultAddress".to_owned(), json!(vault_address));
}
if let Some(expires_after) = expires_after {
payload
.as_object_mut()
.ok_or_else(|| HyperliquidError::Internal("payload wrongly formatted".to_owned()))?
.insert("expiresAfter".to_owned(), json!(expires_after));
}
self.post(payload).await
}
pub async fn cancel_twap_order(
&self,
asset: usize,
twap_id: u32,
vault_address: Option<String>,
expires_after: Option<u64>,
) -> Result<TwapCancelResponse> {
let signer = self
.client
.signer()
.ok_or_else(|| HyperliquidError::SignerRequired)?;
let nonce = current_time_millis();
let action = TwapCancelAction {
type_: "twapCancel".to_string(),
a: asset,
t: twap_id,
};
let sig = sign_l1_action(
&signer,
&action,
vault_address.clone(),
nonce,
expires_after,
self.client.is_mainnet(),
)?;
let signature = Eip712Signature {
r: format!("0x{:x}", sig.r()),
s: format!("0x{:x}", sig.s()),
v: sig_v_from_bool(sig.v()),
};
let mut payload = json!({
"action": action,
"nonce": nonce,
"signature": signature
});
if let Some(vault_address) = vault_address {
payload
.as_object_mut()
.ok_or_else(|| HyperliquidError::Internal("payload wrongly formatted".to_owned()))?
.insert("vaultAddress".to_owned(), json!(vault_address));
}
if let Some(expires_after) = expires_after {
payload
.as_object_mut()
.ok_or_else(|| HyperliquidError::Internal("payload wrongly formatted".to_owned()))?
.insert("expiresAfter".to_owned(), json!(expires_after));
}
self.post(payload).await
}
pub async fn reserve_additional_actions(
&self,
weight: u32,
expires_after: Option<u64>,
) -> Result<DefaultResponse> {
let signer = self
.client
.signer()
.ok_or_else(|| HyperliquidError::SignerRequired)?;
let nonce = current_time_millis();
let action = ReserveRequestWeightAction {
type_: "reserveRequestWeight".to_string(),
weight,
};
let sig = sign_l1_action(
&signer,
&action,
None,
nonce,
expires_after,
self.client.is_mainnet(),
)?;
let signature = Eip712Signature {
r: format!("0x{:x}", sig.r()),
s: format!("0x{:x}", sig.s()),
v: sig_v_from_bool(sig.v()),
};
let mut payload = json!({
"action": action,
"nonce": nonce,
"signature": signature
});
if let Some(expires_after) = expires_after {
payload
.as_object_mut()
.ok_or_else(|| HyperliquidError::Internal("payload wrongly formatted".to_owned()))?
.insert("expiresAfter".to_owned(), json!(expires_after));
}
self.post(payload).await
}
pub async fn invalidate_pending_nonce_noop(
&self,
expires_after: Option<u64>,
) -> Result<NoopResponse> {
let signer = self
.client
.signer()
.ok_or_else(|| HyperliquidError::SignerRequired)?;
let nonce = current_time_millis();
let action = NoopAction {
type_: "noop".to_string(),
};
let sig = sign_l1_action(
&signer,
&action,
None,
nonce,
expires_after,
self.client.is_mainnet(),
)?;
let signature = Eip712Signature {
r: format!("0x{:x}", sig.r()),
s: format!("0x{:x}", sig.s()),
v: sig_v_from_bool(sig.v()),
};
let mut payload = json!({
"action": json!({ "type": "noop" }),
"nonce": nonce,
"signature": signature
});
if let Some(expires_after) = expires_after {
payload
.as_object_mut()
.ok_or_else(|| HyperliquidError::Internal("payload wrongly formatted".to_owned()))?
.insert("expiresAfter".to_owned(), json!(expires_after));
}
self.post(payload).await
}
pub async fn enable_hip3_dex_abstraction(&self) -> Result<UserDexAbstractionResponse> {
let signer = self
.client
.signer()
.ok_or_else(|| HyperliquidError::SignerRequired)?;
let nonce = current_time_millis();
let action = UserDexAbstractionAction {
type_: "userDexAbstraction".to_string(),
hyperliquid_chain: self.client.network_type().hyperliquid_chain(),
signature_chain_id: self.client.network_type().signature_chain_id(),
user: signer.address().to_string(),
enabled: true,
nonce,
};
let sig = sign_l1_action(&signer, &action, None, nonce, None, false)?;
let signature = Eip712Signature {
r: format!("0x{:x}", sig.r()),
s: format!("0x{:x}", sig.s()),
v: sig_v_from_bool(sig.v()),
};
let payload = json!({
"action": action,
"nonce": nonce,
"signature": signature
});
self.post(payload).await
}
pub async fn enable_hip3_dex_abstraction_agent(
&self,
) -> Result<AgentEnableDexAbstractionResponse> {
let signer = self
.client
.signer()
.ok_or_else(|| HyperliquidError::SignerRequired)?;
let nonce = current_time_millis();
let action = AgentEnableDexAbstractionAction {
type_: "agentEnableDexAbstraction".to_string(),
};
let sig = sign_l1_action(&signer, &action, None, nonce, None, false)?;
let signature = Eip712Signature {
r: format!("0x{:x}", sig.r()),
s: format!("0x{:x}", sig.s()),
v: sig_v_from_bool(sig.v()),
};
let payload = json!({
"action": action,
"nonce": nonce,
"signature": signature
});
self.post(payload).await
}
pub async fn validator_vote_on_risk_free_rate_for_aligned_quote_asset(
&self,
risk_free_rate: String,
) -> Result<ValidatorL1StreamResponse> {
let signer = self
.client
.signer()
.ok_or_else(|| HyperliquidError::SignerRequired)?;
let nonce = current_time_millis();
let action = ValidatorL1StreamAction {
type_: "validatorL1Stream".to_string(),
risk_free_rate,
};
let sig = sign_l1_action(&signer, &action, None, nonce, None, false)?;
let signature = Eip712Signature {
r: format!("0x{:x}", sig.r()),
s: format!("0x{:x}", sig.s()),
v: sig_v_from_bool(sig.v()),
};
let payload = json!({
"action": action,
"nonce": nonce,
"signature": signature
});
self.post(payload).await
}
}