canic_core/env/
sns.rs

1//! Preconfigured SNS deployments and helpers for looking up their canisters.
2
3use crate::{Error, env::EnvError};
4use candid::Principal;
5use thiserror::Error as ThisError;
6
7///
8/// SnsError
9///
10
11#[derive(Debug, ThisError)]
12pub enum SnsError {
13    #[error("invalid principal: {0} ({1})")]
14    InvalidPrincipal(String, String),
15}
16
17impl From<SnsError> for Error {
18    fn from(err: SnsError) -> Self {
19        EnvError::SnsError(err).into()
20    }
21}
22
23///
24/// SnsCanisters
25///
26
27#[derive(Clone, Debug)]
28pub struct SnsCanisters {
29    pub root: Principal,
30    pub governance: Principal,
31    pub index: Principal,
32    pub ledger: Principal,
33}
34
35///
36/// SnsRole
37///
38
39#[derive(Clone, Copy, Debug)]
40pub enum SnsRole {
41    Root,
42    Governance,
43    Index,
44    Ledger,
45}
46
47///
48/// SnsType
49///
50
51#[derive(Clone, Copy, Debug)]
52#[remain::sorted]
53pub enum SnsType {
54    Alice,
55    Catalyze,
56    DecideAi,
57    Dragginz,
58    GoldDao,
59    Kinic,
60    KongSwap,
61    Mimic,
62    Motoko,
63    Neutrinite,
64    Nuance,
65    OpenChat,
66    PokedBots,
67    Sneed,
68    Swampies,
69    TacoDao,
70    Tendies,
71    Trax,
72}
73
74// ---- Helpers ----
75
76fn parse_required(name: &str, text: &str) -> Result<Principal, SnsError> {
77    Principal::from_text(text)
78        .map_err(|_| SnsError::InvalidPrincipal(name.to_string(), text.to_string()))
79}
80
81fn bundle(root: &str, gov: &str, idx: &str, led: &str) -> Result<SnsCanisters, SnsError> {
82    Ok(SnsCanisters {
83        root: parse_required("root", root)?,
84        governance: parse_required("governance", gov)?,
85        index: parse_required("index", idx)?,
86        ledger: parse_required("ledger", led)?,
87    })
88}
89
90// ---- Table + impl (DRY via macro) ----
91
92macro_rules! define_sns_table {
93    (
94        $(
95            $name:ident {
96                root: $root:expr,
97                governance: $gov:expr,
98                index: $idx:expr,
99                ledger: $led:expr $(,)?
100            }
101        ),+ $(,)?
102    ) => {
103        impl SnsType {
104            pub fn principal(self, role: SnsRole) -> Result<Principal, Error> {
105                let set = self.principals()?;
106                Ok(match role {
107                    SnsRole::Root       => set.root,
108                    SnsRole::Governance => set.governance,
109                    SnsRole::Index      => set.index,
110                    SnsRole::Ledger     => set.ledger,
111                })
112            }
113
114            pub fn principals(self) -> Result<SnsCanisters, Error> {
115                match self {
116                    $(
117                        Self::$name => bundle($root, $gov, $idx, $led),
118                    )+
119                }
120                .map_err(EnvError::from)
121                .map_err(Error::from)
122            }
123        }
124
125        // Optional: test all non-empty entries parse (runs in `cargo test`)
126        #[cfg(test)]
127        mod __sns_parse_tests {
128            use super::*;
129            #[test]
130            fn all_configured_ids_parse() {
131                $(
132                    // If any of these are non-empty, principals() must succeed
133                    if !($root.is_empty() || $gov.is_empty() || $idx.is_empty() || $led.is_empty()) {
134                        let _ = SnsType::$name.principals().expect(concat!("failed for ", stringify!($name)));
135                    }
136                )+
137            }
138        }
139    }
140}
141
142// ---- Fill the table once (short & readable) ----
143
144define_sns_table! {
145
146    Alice {
147        root:       "oh4fn-kyaaa-aaaaq-aaega-cai",
148        governance: "oa5dz-haaaa-aaaaq-aaegq-cai",
149        index:      "mtcaz-pyaaa-aaaaq-aaeia-cai",
150        ledger:     "oj6if-riaaa-aaaaq-aaeha-cai",
151    },
152
153    Catalyze {
154        root:       "uly3p-iqaaa-aaaaq-aabma-cai",
155        governance: "umz53-fiaaa-aaaaq-aabmq-cai",
156        index:      "ux4b6-7qaaa-aaaaq-aaboa-cai",
157        ledger:     "uf2wh-taaaa-aaaaq-aabna-cai",
158    },
159
160    DecideAi {
161        root:       "x4kx5-ziaaa-aaaaq-aabeq-cai",
162        governance: "xvj4b-paaaa-aaaaq-aabfa-cai",
163        index:      "xaonm-oiaaa-aaaaq-aabgq-cai",
164        ledger:     "xsi2v-cyaaa-aaaaq-aabfq-cai",
165    },
166
167    Dragginz {
168        root:       "zxeu2-7aaaa-aaaaq-aaafa-cai",
169        governance: "zqfso-syaaa-aaaaq-aaafq-cai",
170        index:      "zlaol-iaaaa-aaaaq-aaaha-cai",
171        ledger:     "zfcdd-tqaaa-aaaaq-aaaga-cai",
172    },
173
174    GoldDao {
175        root:       "tw2vt-hqaaa-aaaaq-aab6a-cai",
176        governance: "tr3th-kiaaa-aaaaq-aab6q-cai",
177        index:      "efv5g-kqaaa-aaaaq-aacaa-cai",
178        ledger:     "tyyy3-4aaaa-aaaaq-aab7a-cai",
179    },
180
181    Kinic {
182        root:       "7jkta-eyaaa-aaaaq-aaarq-cai",
183        governance: "74ncn-fqaaa-aaaaq-aaasa-cai",
184        index:      "7vojr-tyaaa-aaaaq-aaatq-cai",
185        ledger:     "73mez-iiaaa-aaaaq-aaasq-cai",
186    },
187
188    KongSwap {
189        root:       "ormnc-tiaaa-aaaaq-aadyq-cai",
190        governance: "oypg6-faaaa-aaaaq-aadza-cai",
191        index:      "onixt-eiaaa-aaaaq-aad2q-cai",
192        ledger:     "o7oak-iyaaa-aaaaq-aadzq-cai",
193    },
194
195    Mimic {
196        root:       "4m6il-zqaaa-aaaaq-aaa2a-cai",
197        governance: "4l7o7-uiaaa-aaaaq-aaa2q-cai",
198        index:      "ks7eq-3yaaa-aaaaq-aaddq-cai",
199        ledger:     "4c4fd-caaaa-aaaaq-aaa3a-cai",
200    },
201
202    Motoko {
203        root:       "ko36b-myaaa-aaaaq-aadbq-cai",
204        governance: "k34pm-nqaaa-aaaaq-aadca-cai",
205        index:      "5ithz-aqaaa-aaaaq-aaa4a-cai",
206        ledger:     "k45jy-aiaaa-aaaaq-aadcq-cai",
207    },
208
209    Neutrinite {
210        root:       "extk7-gaaaa-aaaaq-aacda-cai",
211        governance: "eqsml-lyaaa-aaaaq-aacdq-cai",
212        index:      "ft6fn-7aaaa-aaaaq-aacfa-cai",
213        ledger:     "f54if-eqaaa-aaaaq-aacea-cai",
214    },
215
216    Nuance {
217        root:       "rzbmc-yiaaa-aaaaq-aabsq-cai",
218        governance: "rqch6-oaaaa-aaaaq-aabta-cai",
219        index:      "q5mdq-biaaa-aaaaq-aabuq-cai",
220        ledger:     "rxdbk-dyaaa-aaaaq-aabtq-cai",
221    },
222
223    OpenChat {
224        root:       "3e3x2-xyaaa-aaaaq-aaalq-cai",
225        governance: "2jvtu-yqaaa-aaaaq-aaama-cai",
226        index:      "2awyi-oyaaa-aaaaq-aaanq-cai",
227        ledger:     "2ouva-viaaa-aaaaq-aaamq-cai",
228    },
229
230    PokedBots {
231        root:       "nb7he-piaaa-aaaaq-aadqq-cai",
232        governance: "ni4my-zaaaa-aaaaq-aadra-cai",
233        index:      "n535v-yiaaa-aaaaq-aadsq-cai",
234        ledger:     "np5km-uyaaa-aaaaq-aadrq-cai",
235    },
236
237    Sneed {
238        root:       "fp274-iaaaa-aaaaq-aacha-cai",
239        governance: "fi3zi-fyaaa-aaaaq-aachq-cai",
240        index:      "h3e2i-naaaa-aaaaq-aacja-cai",
241        ledger:     "hvgxa-wqaaa-aaaaq-aacia-cai",
242    },
243
244    Swampies {
245        root:       "l7ra6-uqaaa-aaaaq-aadea-cai",
246        governance: "lyqgk-ziaaa-aaaaq-aadeq-cai",
247        index:      "ldv2p-dqaaa-aaaaq-aadga-cai",
248        ledger:     "lrtnw-paaaa-aaaaq-aadfa-cai",
249    },
250
251    TacoDao {
252        root:       "lacdn-3iaaa-aaaaq-aae3a-cai",
253        governance: "lhdfz-wqaaa-aaaaq-aae3q-cai",
254        index:      "kepm7-ciaaa-aaaaq-aae5a-cai",
255        ledger:     "kknbx-zyaaa-aaaaq-aae4a-cai",
256    },
257
258    Tendies {
259        root:       "kwj3g-oyaaa-aaaaq-aae6a-cai",
260        governance: "kri5s-daaaa-aaaaq-aae6q-cai",
261        index:      "bb4ce-dyaaa-aaaaq-aafaa-cai",
262        ledger:     "kylwo-viaaa-aaaaq-aae7a-cai",
263    },
264
265    Trax {
266        root:       "ecu3s-hiaaa-aaaaq-aacaq-cai",
267        governance: "elxqo-raaaa-aaaaq-aacba-cai",
268        index:      "e6qbd-qiaaa-aaaaq-aaccq-cai",
269        ledger:     "emww2-4yaaa-aaaaq-aacbq-cai",
270    },
271}