use alloy::primitives::B256;
use serde;
use crate::l1_action;
use crate::types::requests::{
BuilderInfo, CancelRequest, CancelRequestCloid, ModifyRequest, OrderRequest,
};
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct UsdSend {
#[serde(serialize_with = "serialize_chain_id")]
pub signature_chain_id: u64,
pub hyperliquid_chain: String,
pub destination: String,
pub amount: String,
pub time: u64,
}
impl crate::types::eip712::HyperliquidAction for UsdSend {
const TYPE_STRING: &'static str =
"UsdSend(string hyperliquidChain,string destination,string amount,uint64 time)";
const USE_PREFIX: bool = true;
fn chain_id(&self) -> Option<u64> {
Some(self.signature_chain_id)
}
fn encode_data(&self) -> Vec<u8> {
use crate::types::eip712::encode_value;
let mut encoded = Vec::new();
encoded.extend_from_slice(&Self::type_hash()[..]);
encoded.extend_from_slice(&encode_value(&self.hyperliquid_chain)[..]);
encoded.extend_from_slice(&encode_value(&self.destination)[..]);
encoded.extend_from_slice(&encode_value(&self.amount)[..]);
encoded.extend_from_slice(&encode_value(&self.time)[..]);
encoded
}
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Withdraw {
#[serde(serialize_with = "serialize_chain_id")]
pub signature_chain_id: u64,
pub hyperliquid_chain: String,
pub destination: String,
pub amount: String,
pub time: u64,
}
impl crate::types::eip712::HyperliquidAction for Withdraw {
const TYPE_STRING: &'static str =
"Withdraw(string hyperliquidChain,string destination,string amount,uint64 time)";
const USE_PREFIX: bool = true;
fn chain_id(&self) -> Option<u64> {
Some(self.signature_chain_id)
}
fn encode_data(&self) -> Vec<u8> {
use crate::types::eip712::encode_value;
let mut encoded = Vec::new();
encoded.extend_from_slice(&Self::type_hash()[..]);
encoded.extend_from_slice(&encode_value(&self.hyperliquid_chain)[..]);
encoded.extend_from_slice(&encode_value(&self.destination)[..]);
encoded.extend_from_slice(&encode_value(&self.amount)[..]);
encoded.extend_from_slice(&encode_value(&self.time)[..]);
encoded
}
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SpotSend {
#[serde(serialize_with = "serialize_chain_id")]
pub signature_chain_id: u64,
pub hyperliquid_chain: String,
pub destination: String,
pub token: String,
pub amount: String,
pub time: u64,
}
impl crate::types::eip712::HyperliquidAction for SpotSend {
const TYPE_STRING: &'static str = "SpotSend(string hyperliquidChain,string destination,string token,string amount,uint64 time)";
const USE_PREFIX: bool = true;
fn chain_id(&self) -> Option<u64> {
Some(self.signature_chain_id)
}
fn encode_data(&self) -> Vec<u8> {
use crate::types::eip712::encode_value;
let mut encoded = Vec::new();
encoded.extend_from_slice(&Self::type_hash()[..]);
encoded.extend_from_slice(&encode_value(&self.hyperliquid_chain)[..]);
encoded.extend_from_slice(&encode_value(&self.destination)[..]);
encoded.extend_from_slice(&encode_value(&self.token)[..]);
encoded.extend_from_slice(&encode_value(&self.amount)[..]);
encoded.extend_from_slice(&encode_value(&self.time)[..]);
encoded
}
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ApproveAgent {
#[serde(serialize_with = "serialize_chain_id")]
pub signature_chain_id: u64,
pub hyperliquid_chain: String,
#[serde(serialize_with = "serialize_address")]
pub agent_address: alloy::primitives::Address,
pub agent_name: Option<String>,
pub nonce: u64,
}
pub(crate) fn serialize_address<S>(
address: &alloy::primitives::Address,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&format!("{:#x}", address))
}
pub(crate) fn serialize_chain_id<S>(
chain_id: &u64,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&format!("{:#x}", chain_id))
}
impl crate::types::eip712::HyperliquidAction for ApproveAgent {
const TYPE_STRING: &'static str = "ApproveAgent(string hyperliquidChain,address agentAddress,string agentName,uint64 nonce)";
const USE_PREFIX: bool = true;
fn chain_id(&self) -> Option<u64> {
Some(self.signature_chain_id)
}
fn encode_data(&self) -> Vec<u8> {
use crate::types::eip712::encode_value;
let mut encoded = Vec::new();
encoded.extend_from_slice(&Self::type_hash()[..]);
encoded.extend_from_slice(&encode_value(&self.hyperliquid_chain)[..]);
encoded.extend_from_slice(&encode_value(&self.agent_address)[..]);
let agent_name = self.agent_name.clone().unwrap_or_default();
encoded.extend_from_slice(&encode_value(&agent_name)[..]);
encoded.extend_from_slice(&encode_value(&self.nonce)[..]);
encoded
}
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ApproveBuilderFee {
#[serde(serialize_with = "serialize_chain_id")]
pub signature_chain_id: u64,
pub hyperliquid_chain: String,
pub max_fee_rate: String,
pub builder: String,
pub nonce: u64,
}
impl crate::types::eip712::HyperliquidAction for ApproveBuilderFee {
const TYPE_STRING: &'static str = "ApproveBuilderFee(string hyperliquidChain,string maxFeeRate,string builder,uint64 nonce)";
const USE_PREFIX: bool = true;
fn chain_id(&self) -> Option<u64> {
Some(self.signature_chain_id)
}
fn encode_data(&self) -> Vec<u8> {
use crate::types::eip712::encode_value;
let mut encoded = Vec::new();
encoded.extend_from_slice(&Self::type_hash()[..]);
encoded.extend_from_slice(&encode_value(&self.hyperliquid_chain)[..]);
encoded.extend_from_slice(&encode_value(&self.max_fee_rate)[..]);
encoded.extend_from_slice(&encode_value(&self.builder)[..]);
encoded.extend_from_slice(&encode_value(&self.nonce)[..]);
encoded
}
}
l1_action! {
struct Agent {
pub source: String,
pub connection_id: B256,
}
=> "Agent(string source,bytes32 connectionId)"
=> encode(source, connection_id)
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct UpdateLeverage {
pub asset: u32,
pub is_cross: bool,
pub leverage: u32,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct UpdateIsolatedMargin {
pub asset: u32,
pub is_buy: bool,
pub ntli: i64,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct VaultTransfer {
pub vault_address: String,
pub is_deposit: bool,
pub usd: u64,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SpotUser {
pub class_transfer: ClassTransfer,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ClassTransfer {
pub usd_size: u64,
pub to_perp: bool,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SetReferrer {
pub code: String,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BulkOrder {
pub orders: Vec<OrderRequest>,
pub grouping: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub builder: Option<BuilderInfo>,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BulkCancel {
pub cancels: Vec<CancelRequest>,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BulkModify {
pub modifies: Vec<ModifyRequest>,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BulkCancelCloid {
pub cancels: Vec<CancelRequestCloid>,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ScheduleCancel {
pub time: Option<u64>,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CreateSubAccount {
pub name: Option<String>,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SubAccountTransfer {
pub sub_account_user: String,
pub is_deposit: bool,
pub usd: u64,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SubAccountSpotTransfer {
pub sub_account_user: String,
pub is_deposit: bool,
pub token: String,
pub amount: String,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct UsdClassTransfer {
pub amount: String,
pub to_perp: bool,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TwapOrder {
#[serde(rename = "a")]
pub asset: u32,
#[serde(rename = "b")]
pub is_buy: bool,
#[serde(rename = "s")]
pub sz: String,
#[serde(rename = "r")]
pub reduce_only: bool,
#[serde(rename = "m")]
pub duration_minutes: u32,
#[serde(rename = "t")]
pub randomize: bool,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BulkTwapOrder {
pub twap: TwapOrder,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TwapCancel {
#[serde(rename = "a")]
pub asset: u32,
#[serde(rename = "t")]
pub twap_id: u64,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ConvertToMultiSigUser {
#[serde(serialize_with = "serialize_chain_id")]
pub signature_chain_id: u64,
pub hyperliquid_chain: String,
pub signers: Vec<MultiSigSigner>,
pub threshold: u32,
pub nonce: u64,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MultiSigSigner {
pub address: String,
pub weight: u32,
}
impl crate::types::eip712::HyperliquidAction for ConvertToMultiSigUser {
const TYPE_STRING: &'static str =
"ConvertToMultiSigUser(string hyperliquidChain,address[] authorizedUsers,uint32 threshold,uint64 nonce)";
const USE_PREFIX: bool = true;
fn chain_id(&self) -> Option<u64> {
Some(self.signature_chain_id)
}
fn encode_data(&self) -> Vec<u8> {
use crate::types::eip712::encode_value;
use alloy::primitives::keccak256;
let mut encoded = Vec::new();
encoded.extend_from_slice(&Self::type_hash()[..]);
encoded.extend_from_slice(&encode_value(&self.hyperliquid_chain)[..]);
let mut addresses_encoded = Vec::new();
for signer in &self.signers {
if let Ok(addr) = signer.address.parse::<alloy::primitives::Address>() {
addresses_encoded.extend_from_slice(&encode_value(&addr)[..]);
}
}
let addresses_hash = keccak256(&addresses_encoded);
encoded.extend_from_slice(&addresses_hash[..]);
encoded.extend_from_slice(&encode_value(&(self.threshold as u64))[..]);
encoded.extend_from_slice(&encode_value(&self.nonce)[..]);
encoded
}
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MultiSig {
#[serde(serialize_with = "serialize_chain_id")]
pub signature_chain_id: u64,
pub multi_sig_user: String,
pub outer_signer: String,
pub inner_action: serde_json::Value,
pub signatures: Vec<MultiSigSignature>,
pub nonce: u64,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MultiSigSignature {
pub r: String,
pub s: String,
pub v: u8,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AgentEnableDexAbstraction {
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SpotDeployRegisterToken {
pub token_name: String,
pub sz_decimals: u32,
pub wei_decimals: u32,
pub max_gas: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub full_name: Option<String>,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SpotDeployUserGenesis {
pub token: String,
pub user_and_wei: Vec<(String, String)>,
#[serde(skip_serializing_if = "Option::is_none")]
pub existing_token_and_wei: Option<(String, String)>,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SpotDeployFreezeUser {
pub token: String,
pub user: String,
pub freeze: bool,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SpotDeployEnableFreezePrivilege {
pub token: String,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SpotDeployRevokeFreezePrivilege {
pub token: String,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SpotDeployEnableQuoteToken {
pub token: String,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SpotDeployGenesis {
pub token: String,
pub max_supply: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub no_hyperliquidity: Option<bool>,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SpotDeployRegisterSpot {
pub base_token: String,
pub quote_token: String,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SpotDeployRegisterHyperliquidity {
pub spot: String,
pub start_px: String,
pub order_sz: String,
pub n_orders: u32,
pub n_seeded_levels: u32,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SpotDeploySetDeployerTradingFeeShare {
pub token: String,
pub share: String,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PerpDeployRegisterAsset {
pub dex: u32,
pub max_gas: String,
pub coin: String,
pub sz_decimals: u32,
pub oracle_px: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub margin_table_id: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub only_isolated: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub schema: Option<String>,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PerpDeploySetOracle {
pub dex: u32,
pub oracle_pxs: Vec<String>,
pub all_mark_pxs: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub external_perp_pxs: Option<Vec<String>>,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CSignerUnjailSelf {
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CSignerJailSelf {
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CValidatorRegister {
pub node_ip: String,
pub name: String,
pub description: String,
pub delegations_disabled: bool,
pub commission_bps: u32,
pub signer: String,
pub unjailed: bool,
pub initial_wei: String,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CValidatorChangeProfile {
#[serde(skip_serializing_if = "Option::is_none")]
pub node_ip: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub unjailed: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub disable_delegations: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub commission_bps: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub signer: Option<String>,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CValidatorUnregister {
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TokenDelegate {
pub validator: String,
pub wei: String,
pub is_undelegate: bool,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct UseBigBlocks {
pub enable: bool,
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Noop {
pub nonce: u64,
}
#[cfg(test)]
mod tests {
use alloy::primitives::keccak256;
use super::*;
use crate::types::eip712::HyperliquidAction;
#[test]
fn test_usd_send_type_hash() {
let expected = keccak256(
"HyperliquidTransaction:UsdSend(string hyperliquidChain,string destination,string amount,uint64 time)",
);
assert_eq!(UsdSend::type_hash(), expected);
}
#[test]
fn test_agent_type_hash() {
let expected = keccak256("Agent(string source,bytes32 connectionId)");
assert_eq!(Agent::type_hash(), expected);
}
#[test]
fn test_agent_domain() {
let agent = Agent {
source: "a".to_string(),
connection_id: B256::default(),
};
let domain = agent.domain();
let expected_domain = alloy::sol_types::eip712_domain! {
name: "Exchange",
version: "1",
chain_id: 1337u64,
verifying_contract: alloy::primitives::address!("0000000000000000000000000000000000000000"),
};
assert_eq!(domain.separator(), expected_domain.separator());
}
}