canic_cdk/types/
account.rs1use 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
11pub type Subaccount = [u8; 32];
16
17pub const DEFAULT_SUBACCOUNT: &Subaccount = &[0; 32];
18
19#[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 #[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}