Skip to main content

canic_cdk/types/
account.rs

1use crate::icrc_ledger_types::icrc1::account::Account as Icrc1Account;
2use candid::{CandidType, Principal};
3use serde::{Deserialize, Serialize};
4use std::{
5    cmp::Ordering,
6    fmt::{self, Display},
7    hash::{Hash, Hasher},
8    str::FromStr,
9};
10
11///
12/// Subaccount
13///
14
15pub type Subaccount = [u8; 32];
16
17pub const DEFAULT_SUBACCOUNT: &Subaccount = &[0; 32];
18
19///
20/// Account
21///
22/// Code ported from icrc-ledger-types as we don't want to include that one, it's out of
23/// date and has a lot of extra dependencies
24///
25
26#[derive(CandidType, Clone, Copy, Debug, Deserialize, Serialize)]
27pub struct Account {
28    pub owner: Principal,
29    pub subaccount: Option<Subaccount>,
30}
31
32impl Account {
33    pub fn new<P: Into<Principal>, S: Into<Subaccount>>(owner: P, subaccount: Option<S>) -> Self {
34        Self {
35            owner: owner.into(),
36            subaccount: subaccount.map(Into::into),
37        }
38    }
39
40    /// The effective subaccount of an account - the subaccount if it is set, otherwise the default
41    /// subaccount of all zeroes.
42    #[must_use]
43    pub fn effective_subaccount(&self) -> &Subaccount {
44        self.subaccount.as_ref().unwrap_or(DEFAULT_SUBACCOUNT)
45    }
46}
47
48impl Display for Account {
49    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50        let icrc = Icrc1Account::from(self);
51        Display::fmt(&icrc, f)
52    }
53}
54
55impl Eq for Account {}
56
57impl FromStr for Account {
58    type Err = String;
59
60    fn from_str(s: &str) -> Result<Self, Self::Err> {
61        let acc = Icrc1Account::from_str(s).map_err(|e| e.to_string())?;
62
63        Ok(Self::new(acc.owner, acc.subaccount))
64    }
65}
66
67impl PartialEq for Account {
68    fn eq(&self, other: &Self) -> bool {
69        self.owner == other.owner && self.effective_subaccount() == other.effective_subaccount()
70    }
71}
72
73impl From<Account> for Icrc1Account {
74    fn from(a: Account) -> Self {
75        Self {
76            owner: a.owner,
77            subaccount: a.subaccount,
78        }
79    }
80}
81
82impl From<&Account> for Icrc1Account {
83    fn from(a: &Account) -> Self {
84        Self {
85            owner: a.owner,
86            subaccount: a.subaccount,
87        }
88    }
89}
90
91impl From<Principal> for Account {
92    fn from(owner: Principal) -> Self {
93        Self {
94            owner,
95            subaccount: None,
96        }
97    }
98}
99
100impl Hash for Account {
101    fn hash<H: Hasher>(&self, state: &mut H) {
102        self.owner.hash(state);
103        self.effective_subaccount().hash(state);
104    }
105}
106
107impl Ord for Account {
108    fn cmp(&self, other: &Self) -> Ordering {
109        self.owner.cmp(&other.owner).then_with(|| {
110            self.effective_subaccount()
111                .cmp(other.effective_subaccount())
112        })
113    }
114}
115
116impl PartialOrd for Account {
117    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
118        Some(self.cmp(other))
119    }
120}