df_hyperliquid_core/account/settings/
mode.rs1use std::fmt;
6
7use serde::{Deserialize, Deserializer, de};
8
9use crate::client::{ApiError, Transport, info, user_request};
10use crate::utils::Address;
11use crate::utils::sign::{Field, L1Action, Map, NonceKey, UserAction, UserSigned};
12
13#[must_use]
15pub fn abstraction_request(user: Address) -> String {
16 user_request("userAbstraction", user)
17}
18
19pub async fn abstraction(transport: &impl Transport, user: Address) -> Result<Abstraction, ApiError> {
21 info(transport, abstraction_request(user)).await
22}
23
24#[derive(Debug, Clone, PartialEq, Eq, Hash)]
26pub enum Abstraction {
27 Default,
29 Disabled,
31 UnifiedAccount,
33 PortfolioMargin,
35 DexAbstraction,
38 Unknown(String),
40}
41
42impl Abstraction {
43 #[must_use]
44 pub fn parse(text: &str) -> Abstraction {
45 match text {
46 "default" => Abstraction::Default,
47 "disabled" => Abstraction::Disabled,
48 "unifiedAccount" => Abstraction::UnifiedAccount,
49 "portfolioMargin" => Abstraction::PortfolioMargin,
50 "dexAbstraction" => Abstraction::DexAbstraction,
51 other => Abstraction::Unknown(other.to_owned()),
52 }
53 }
54
55 #[must_use]
56 pub fn wire(&self) -> &str {
57 match self {
58 Abstraction::Default => "default",
59 Abstraction::Disabled => "disabled",
60 Abstraction::UnifiedAccount => "unifiedAccount",
61 Abstraction::PortfolioMargin => "portfolioMargin",
62 Abstraction::DexAbstraction => "dexAbstraction",
63 Abstraction::Unknown(other) => other,
64 }
65 }
66
67 #[must_use]
70 pub const fn is_pooled(&self) -> bool {
71 matches!(self, Abstraction::UnifiedAccount | Abstraction::PortfolioMargin)
72 }
73
74 #[must_use]
76 pub const fn is_standard(&self) -> bool {
77 !self.is_pooled()
78 }
79}
80
81impl fmt::Display for Abstraction {
82 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83 f.write_str(self.wire())
84 }
85}
86
87impl<'de> Deserialize<'de> for Abstraction {
89 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
90 struct Visitor;
91
92 impl de::Visitor<'_> for Visitor {
93 type Value = Abstraction;
94
95 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96 f.write_str("an account mode as a string")
97 }
98
99 fn visit_str<E: de::Error>(self, text: &str) -> Result<Abstraction, E> {
100 Ok(Abstraction::parse(text))
101 }
102 }
103
104 deserializer.deserialize_str(Visitor)
105 }
106}
107
108#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
110pub enum AbstractionMode {
111 Disabled,
113 UnifiedAccount,
115 PortfolioMargin,
116}
117
118impl AbstractionMode {
119 pub const ALL: [AbstractionMode; 3] =
120 [AbstractionMode::Disabled, AbstractionMode::UnifiedAccount, AbstractionMode::PortfolioMargin];
121
122 #[must_use]
124 pub const fn wire(self) -> &'static str {
125 match self {
126 AbstractionMode::Disabled => "disabled",
127 AbstractionMode::UnifiedAccount => "unifiedAccount",
128 AbstractionMode::PortfolioMargin => "portfolioMargin",
129 }
130 }
131
132 #[must_use]
134 pub const fn code(self) -> &'static str {
135 match self {
136 AbstractionMode::Disabled => "i",
137 AbstractionMode::UnifiedAccount => "u",
138 AbstractionMode::PortfolioMargin => "p",
139 }
140 }
141
142 #[must_use]
145 pub fn from_wire(text: &str) -> Option<AbstractionMode> {
146 AbstractionMode::ALL.into_iter().find(|mode| mode.wire() == text)
147 }
148
149 #[must_use]
151 pub fn from_code(text: &str) -> Option<AbstractionMode> {
152 AbstractionMode::ALL.into_iter().find(|mode| mode.code() == text)
153 }
154
155 #[must_use]
158 pub const fn is_refused_from(self, current: &Abstraction) -> bool {
159 matches!((current, self), (Abstraction::UnifiedAccount, AbstractionMode::Disabled))
160 }
161}
162
163#[derive(Debug, Clone, Copy, PartialEq, Eq)]
166pub struct UserSetAbstraction {
167 user: Address,
168 mode: AbstractionMode,
169 nonce: u64,
170}
171
172impl UserSetAbstraction {
173 #[must_use]
174 pub const fn new(user: Address, mode: AbstractionMode, nonce: u64) -> UserSetAbstraction {
175 UserSetAbstraction { user, mode, nonce }
176 }
177}
178
179impl UserAction for UserSetAbstraction {
180 fn user_signed(&self) -> UserSigned<'_> {
181 UserSigned::new("userSetAbstraction", "HyperliquidTransaction:UserSetAbstraction", NonceKey::Nonce, self.nonce)
182 .field("user", Field::Address(self.user))
183 .field("abstraction", Field::String(self.mode.wire()))
184 }
185}
186
187#[derive(Debug, Clone, Copy, PartialEq, Eq)]
191pub struct AgentSetAbstraction {
192 mode: AbstractionMode,
193}
194
195impl AgentSetAbstraction {
196 #[must_use]
197 pub const fn new(mode: AbstractionMode) -> AgentSetAbstraction {
198 AgentSetAbstraction { mode }
199 }
200}
201
202impl L1Action for AgentSetAbstraction {
203 fn action(&self) -> Map {
204 Map::new().put("type", "agentSetAbstraction").put("abstraction", self.mode.code())
205 }
206}
207
208#[cfg(test)]
209#[path = "tests/mode.rs"]
210mod tests;