use crate::client::{ApiError, Transport, info, user_request};
use crate::utils::sign::{L1Action, Map};
use crate::utils::{Address, Dec};
use serde::Deserialize;
pub const LONGEST_CODE: usize = 20;
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[error("a referral code is 1 to {LONGEST_CODE} characters, which `{0}` is not")]
pub struct BadCode(pub String);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SetReferrer {
code: String,
}
impl SetReferrer {
pub fn new(code: &str) -> Result<SetReferrer, BadCode> {
match (1..=LONGEST_CODE).contains(&code.chars().count()) {
true => Ok(SetReferrer { code: code.to_owned() }),
false => Err(BadCode(code.to_owned())),
}
}
}
impl L1Action for SetReferrer {
fn action(&self) -> Map {
Map::new().put("type", "setReferrer").put("code", self.code.clone())
}
}
#[must_use]
pub fn referral_request(user: Address) -> String {
user_request("referral", user)
}
pub async fn referral(transport: &impl Transport, user: Address) -> Result<Referral, ApiError> {
info(transport, referral_request(user)).await
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Referral {
#[serde(default)]
pub referred_by: Option<ReferredBy>,
pub cum_vlm: Dec,
pub unclaimed_rewards: Dec,
pub claimed_rewards: Dec,
#[serde(default)]
pub builder_rewards: Dec,
pub referrer_state: ReferrerState,
#[serde(default)]
pub token_to_state: Option<Vec<(u32, TokenRewards)>>,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TokenRewards {
pub cum_vlm: Dec,
pub unclaimed_rewards: Dec,
pub claimed_rewards: Dec,
pub builder_rewards: Dec,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
pub struct ReferredBy {
pub referrer: Address,
pub code: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
pub struct ReferrerState {
pub stage: String,
#[serde(default)]
pub data: Option<ReferrerData>,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ReferrerData {
#[serde(default)]
pub code: Option<String>,
#[serde(default)]
pub required: Option<Dec>,
#[serde(default)]
pub n_referrals: Option<u64>,
#[serde(default)]
pub referral_states: Vec<ReferralState>,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ReferralState {
pub user: Address,
#[serde(default)]
pub cum_vlm: Dec,
#[serde(default)]
pub cum_rewarded_fees_since_referred: Dec,
#[serde(default)]
pub cum_fees_rewarded_to_referrer: Dec,
#[serde(default)]
pub time_joined: u64,
}
impl Referral {
#[must_use]
pub fn token(&self, index: u32) -> Option<&TokenRewards> {
self.token_to_state.as_deref()?.iter().find(|(token, _)| *token == index).map(|(_, state)| state)
}
#[must_use]
pub fn own_code(&self) -> Option<&str> {
self.referrer_state.data.as_ref()?.code.as_deref()
}
#[must_use]
pub fn referred_code(&self) -> Option<&str> {
self.referred_by.as_ref().map(|by| by.code.as_str())
}
#[must_use]
pub fn referrer(&self) -> Option<Address> {
self.referred_by.as_ref().map(|by| by.referrer)
}
#[must_use]
pub fn owns(&self, code: &str) -> bool {
self.own_code().is_some_and(|own| own.eq_ignore_ascii_case(code))
}
}
#[cfg(test)]
#[path = "tests/referral.rs"]
mod tests;