world_id_primitives/
config.rs1use serde::{Deserialize, Serialize};
2
3use alloy_primitives::Address;
4use url::Url;
5
6use crate::PrimitiveError;
7
8const fn default_nullifier_oracle_threshold() -> usize {
9 2
10}
11
12#[derive(Clone, Debug, Serialize, Deserialize)]
18#[serde(tag = "type", rename_all = "snake_case")]
19pub enum ServiceEndpoint {
20 Direct {
22 url: String,
24 },
25 Ohttp {
27 url: String,
29 relay_url: String,
31 key_config_base64: String,
33 },
34}
35
36impl ServiceEndpoint {
37 #[must_use]
39 pub const fn direct(url: String) -> Self {
40 Self::Direct { url }
41 }
42
43 #[must_use]
45 pub const fn ohttp(url: String, relay_url: String, key_config_base64: String) -> Self {
46 Self::Ohttp {
47 url,
48 relay_url,
49 key_config_base64,
50 }
51 }
52
53 #[must_use]
55 pub fn url(&self) -> &str {
56 match self {
57 Self::Direct { url } | Self::Ohttp { url, .. } => url,
58 }
59 }
60}
61
62#[derive(Clone, Debug, Serialize, Deserialize)]
66pub struct Config {
67 rpc_url: Option<Url>,
71 chain_id: u64,
73 registry_address: Address,
75 indexer: ServiceEndpoint,
77 gateway: ServiceEndpoint,
79 nullifier_oracle_urls: Vec<String>,
81 #[serde(default = "default_nullifier_oracle_threshold")]
83 nullifier_oracle_threshold: usize,
84}
85
86impl Config {
87 pub fn new(
93 rpc_url: Option<String>,
94 chain_id: u64,
95 registry_address: Address,
96 indexer: ServiceEndpoint,
97 gateway: ServiceEndpoint,
98 nullifier_oracle_urls: Vec<String>,
99 nullifier_oracle_threshold: usize,
100 ) -> Result<Self, PrimitiveError> {
101 let rpc_url = rpc_url
102 .map(|url| {
103 Url::parse(&url).map_err(|e| PrimitiveError::InvalidInput {
104 reason: e.to_string(),
105 attribute: "rpc_url".to_string(),
106 })
107 })
108 .transpose()?;
109
110 Ok(Self {
111 rpc_url,
112 chain_id,
113 registry_address,
114 indexer,
115 gateway,
116 nullifier_oracle_urls,
117 nullifier_oracle_threshold,
118 })
119 }
120
121 pub fn from_json(json_str: &str) -> Result<Self, PrimitiveError> {
126 serde_json::from_str(json_str)
127 .map_err(|e| PrimitiveError::Serialization(format!("failed to parse config: {e}")))
128 }
129
130 #[must_use]
132 pub const fn rpc_url(&self) -> Option<&Url> {
133 self.rpc_url.as_ref()
134 }
135
136 #[must_use]
138 pub const fn chain_id(&self) -> u64 {
139 self.chain_id
140 }
141
142 #[must_use]
144 pub const fn registry_address(&self) -> &Address {
145 &self.registry_address
146 }
147
148 #[must_use]
151 pub const fn indexer(&self) -> &ServiceEndpoint {
152 &self.indexer
153 }
154
155 #[must_use]
158 pub const fn gateway(&self) -> &ServiceEndpoint {
159 &self.gateway
160 }
161
162 #[must_use]
164 pub fn indexer_url(&self) -> &str {
165 self.indexer.url()
166 }
167
168 #[must_use]
170 pub fn gateway_url(&self) -> &str {
171 self.gateway.url()
172 }
173
174 #[must_use]
176 pub const fn nullifier_oracle_urls(&self) -> &Vec<String> {
177 &self.nullifier_oracle_urls
178 }
179
180 #[must_use]
182 pub const fn nullifier_oracle_threshold(&self) -> usize {
183 self.nullifier_oracle_threshold
184 }
185}
186
187#[cfg(test)]
188mod tests {
189 use super::*;
190
191 #[test]
192 fn from_json_direct_endpoints() {
193 let json = serde_json::json!({
194 "chain_id": 480,
195 "registry_address": "0x0000000000000000000000000000000000000001",
196 "indexer": { "type": "direct", "url": "http://indexer.example.com" },
197 "gateway": { "type": "direct", "url": "http://gateway.example.com" },
198 "nullifier_oracle_urls": [],
199 "nullifier_oracle_threshold": 2
200 });
201
202 let config = Config::from_json(&json.to_string()).unwrap();
203 assert!(matches!(config.indexer(), ServiceEndpoint::Direct { .. }));
204 assert!(matches!(config.gateway(), ServiceEndpoint::Direct { .. }));
205 assert_eq!(config.indexer_url(), "http://indexer.example.com");
206 assert_eq!(config.gateway_url(), "http://gateway.example.com");
207 }
208
209 #[test]
210 fn from_json_ohttp_endpoints() {
211 let json = serde_json::json!({
212 "chain_id": 480,
213 "registry_address": "0x0000000000000000000000000000000000000001",
214 "indexer": {
215 "type": "ohttp",
216 "url": "http://indexer.example.com",
217 "relay_url": "https://relay.example.com/gateway",
218 "key_config_base64": "dGVzdC1rZXk="
219 },
220 "gateway": {
221 "type": "ohttp",
222 "url": "http://gateway.example.com",
223 "relay_url": "https://relay.example.com/gateway",
224 "key_config_base64": "dGVzdC1rZXk="
225 },
226 "nullifier_oracle_urls": [],
227 "nullifier_oracle_threshold": 2
228 });
229
230 let config = Config::from_json(&json.to_string()).unwrap();
231 match config.indexer() {
232 ServiceEndpoint::Ohttp {
233 url,
234 relay_url,
235 key_config_base64,
236 } => {
237 assert_eq!(url, "http://indexer.example.com");
238 assert_eq!(relay_url, "https://relay.example.com/gateway");
239 assert_eq!(key_config_base64, "dGVzdC1rZXk=");
240 }
241 other => panic!("expected Ohttp variant, got: {other:?}"),
242 }
243 match config.gateway() {
244 ServiceEndpoint::Ohttp {
245 url,
246 relay_url,
247 key_config_base64,
248 } => {
249 assert_eq!(url, "http://gateway.example.com");
250 assert_eq!(relay_url, "https://relay.example.com/gateway");
251 assert_eq!(key_config_base64, "dGVzdC1rZXk=");
252 }
253 other => panic!("expected Ohttp variant, got: {other:?}"),
254 }
255 }
256}