df_hyperliquid_core/account/settings/
referral.rs1use crate::client::{ApiError, Transport, info, user_request};
5use crate::utils::sign::{L1Action, Map};
6use crate::utils::{Address, Dec};
7use serde::Deserialize;
8
9pub const LONGEST_CODE: usize = 20;
11
12#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
13#[error("a referral code is 1 to {LONGEST_CODE} characters, which `{0}` is not")]
14pub struct BadCode(pub String);
15
16#[derive(Debug, Clone, PartialEq, Eq)]
18pub struct SetReferrer {
19 code: String,
20}
21
22impl SetReferrer {
23 pub fn new(code: &str) -> Result<SetReferrer, BadCode> {
24 match (1..=LONGEST_CODE).contains(&code.chars().count()) {
25 true => Ok(SetReferrer { code: code.to_owned() }),
26 false => Err(BadCode(code.to_owned())),
27 }
28 }
29}
30
31impl L1Action for SetReferrer {
32 fn action(&self) -> Map {
33 Map::new().put("type", "setReferrer").put("code", self.code.clone())
34 }
35}
36
37#[must_use]
39pub fn referral_request(user: Address) -> String {
40 user_request("referral", user)
41}
42
43pub async fn referral(transport: &impl Transport, user: Address) -> Result<Referral, ApiError> {
44 info(transport, referral_request(user)).await
45}
46
47#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
51#[serde(rename_all = "camelCase")]
52pub struct Referral {
53 #[serde(default)]
54 pub referred_by: Option<ReferredBy>,
55 pub cum_vlm: Dec,
56 pub unclaimed_rewards: Dec,
57 pub claimed_rewards: Dec,
58 #[serde(default)]
59 pub builder_rewards: Dec,
60 pub referrer_state: ReferrerState,
61 #[serde(default)]
63 pub token_to_state: Option<Vec<(u32, TokenRewards)>>,
64}
65
66#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
68#[serde(rename_all = "camelCase")]
69pub struct TokenRewards {
70 pub cum_vlm: Dec,
71 pub unclaimed_rewards: Dec,
72 pub claimed_rewards: Dec,
73 pub builder_rewards: Dec,
74}
75
76#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
77pub struct ReferredBy {
78 pub referrer: Address,
79 pub code: String,
80}
81
82#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
85pub struct ReferrerState {
86 pub stage: String,
87 #[serde(default)]
88 pub data: Option<ReferrerData>,
89}
90
91#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
92#[serde(rename_all = "camelCase")]
93pub struct ReferrerData {
94 #[serde(default)]
95 pub code: Option<String>,
96 #[serde(default)]
97 pub required: Option<Dec>,
98 #[serde(default)]
99 pub n_referrals: Option<u64>,
100 #[serde(default)]
102 pub referral_states: Vec<ReferralState>,
103}
104
105#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
107#[serde(rename_all = "camelCase")]
108pub struct ReferralState {
109 pub user: Address,
110 #[serde(default)]
111 pub cum_vlm: Dec,
112 #[serde(default)]
113 pub cum_rewarded_fees_since_referred: Dec,
114 #[serde(default)]
115 pub cum_fees_rewarded_to_referrer: Dec,
116 #[serde(default)]
118 pub time_joined: u64,
119}
120
121impl Referral {
122 #[must_use]
124 pub fn token(&self, index: u32) -> Option<&TokenRewards> {
125 self.token_to_state.as_deref()?.iter().find(|(token, _)| *token == index).map(|(_, state)| state)
126 }
127
128 #[must_use]
130 pub fn own_code(&self) -> Option<&str> {
131 self.referrer_state.data.as_ref()?.code.as_deref()
132 }
133
134 #[must_use]
136 pub fn referred_code(&self) -> Option<&str> {
137 self.referred_by.as_ref().map(|by| by.code.as_str())
138 }
139
140 #[must_use]
142 pub fn referrer(&self) -> Option<Address> {
143 self.referred_by.as_ref().map(|by| by.referrer)
144 }
145
146 #[must_use]
149 pub fn owns(&self, code: &str) -> bool {
150 self.own_code().is_some_and(|own| own.eq_ignore_ascii_case(code))
151 }
152}
153
154#[cfg(test)]
155#[path = "tests/referral.rs"]
156mod tests;