1use serde::{Deserialize, Deserializer, Serialize, Serializer};
2use std::collections::BTreeMap;
3use std::fmt;
4
5use crate::types::Fr;
6use crate::Error;
7
8fn strip_0x(s: &str) -> &str {
9 s.strip_prefix("0x").unwrap_or(s)
10}
11
12fn decode_selector_hex(s: &str) -> Result<[u8; 4], Error> {
13 let raw = strip_0x(s);
14 if raw.len() > 8 {
15 return Err(Error::InvalidData(
16 "function selector must fit in 4 bytes".to_owned(),
17 ));
18 }
19 let padded = format!("{raw:0>8}");
20 let bytes = hex::decode(padded).map_err(|e| Error::InvalidData(e.to_string()))?;
21 let mut out = [0u8; 4];
22 out.copy_from_slice(&bytes);
23 Ok(out)
24}
25
26#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
28pub struct FunctionSelector(pub [u8; 4]);
29
30impl FunctionSelector {
31 pub fn from_hex(value: &str) -> Result<Self, Error> {
33 Ok(Self(decode_selector_hex(value)?))
34 }
35
36 pub fn from_signature(signature: &str) -> Self {
46 use sha3::{Digest, Keccak256};
47 let hash = Keccak256::digest(signature.as_bytes());
48 let mut bytes = [0u8; 4];
49 bytes.copy_from_slice(&hash[..4]);
50 Self(bytes)
51 }
52
53 pub fn from_name(_name: &str) -> Result<Self, Error> {
57 Err(Error::Abi(
58 "function selector derivation is not implemented yet".to_owned(),
59 ))
60 }
61}
62
63impl fmt::Display for FunctionSelector {
64 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65 write!(f, "0x{}", hex::encode(self.0))
66 }
67}
68
69impl Serialize for FunctionSelector {
70 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
71 where
72 S: Serializer,
73 {
74 serializer.serialize_str(&self.to_string())
75 }
76}
77
78impl<'de> Deserialize<'de> for FunctionSelector {
79 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
80 where
81 D: Deserializer<'de>,
82 {
83 let s = String::deserialize(deserializer)?;
84 Self::from_hex(&s).map_err(serde::de::Error::custom)
85 }
86}
87
88#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
90pub struct EventSelector(pub Fr);
91
92#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
94#[serde(rename_all = "lowercase")]
95pub enum FunctionType {
96 Private,
98 Public,
100 Utility,
102}
103
104#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
106#[serde(tag = "kind", rename_all = "snake_case")]
107pub enum AbiType {
108 Field,
110 Boolean,
112 Integer {
114 sign: String,
116 width: u16,
118 },
119 Array {
121 element: Box<Self>,
123 length: usize,
125 },
126 String {
128 length: usize,
130 },
131 Struct {
133 name: String,
135 fields: Vec<AbiParameter>,
137 },
138 Tuple {
140 elements: Vec<Self>,
142 },
143}
144
145#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
147#[serde(tag = "kind", content = "value", rename_all = "snake_case")]
148pub enum AbiValue {
149 Field(Fr),
151 Boolean(bool),
153 Integer(i128),
155 Array(Vec<Self>),
157 String(String),
159 Struct(BTreeMap<String, Self>),
161 Tuple(Vec<Self>),
163}
164
165#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
167pub struct AbiParameter {
168 pub name: String,
170 #[serde(rename = "type")]
172 pub typ: AbiType,
173 #[serde(default)]
175 pub visibility: Option<String>,
176}
177
178#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
180pub struct FunctionArtifact {
181 pub name: String,
183 pub function_type: FunctionType,
185 #[serde(default)]
187 pub is_initializer: bool,
188 #[serde(default)]
190 pub is_static: bool,
191 #[serde(default)]
193 pub parameters: Vec<AbiParameter>,
194 #[serde(default)]
196 pub return_types: Vec<AbiType>,
197 #[serde(default)]
199 pub selector: Option<FunctionSelector>,
200}
201
202#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
204pub struct ContractArtifact {
205 pub name: String,
207 #[serde(default)]
209 pub functions: Vec<FunctionArtifact>,
210}
211
212impl ContractArtifact {
213 pub fn from_json(json: &str) -> Result<Self, Error> {
215 serde_json::from_str(json).map_err(Error::from)
216 }
217
218 pub fn find_function(&self, name: &str) -> Result<&FunctionArtifact, Error> {
220 self.functions
221 .iter()
222 .find(|f| f.name == name)
223 .ok_or_else(|| {
224 Error::Abi(format!(
225 "function '{}' not found in artifact '{}'",
226 name, self.name
227 ))
228 })
229 }
230
231 pub fn find_function_by_type(
233 &self,
234 name: &str,
235 function_type: &FunctionType,
236 ) -> Result<&FunctionArtifact, Error> {
237 self.functions
238 .iter()
239 .find(|f| f.name == name && &f.function_type == function_type)
240 .ok_or_else(|| {
241 Error::Abi(format!(
242 "{:?} function '{}' not found in artifact '{}'",
243 function_type, name, self.name
244 ))
245 })
246 }
247}
248
249#[cfg(test)]
250#[allow(clippy::expect_used, clippy::panic)]
251mod tests {
252 use super::*;
253
254 const MINIMAL_ARTIFACT: &str = r#"
255 {
256 "name": "TestContract",
257 "functions": [
258 {
259 "name": "increment",
260 "function_type": "public",
261 "is_initializer": false,
262 "is_static": false,
263 "parameters": [
264 { "name": "value", "type": { "kind": "field" } }
265 ],
266 "return_types": []
267 }
268 ]
269 }
270 "#;
271
272 const MULTI_FUNCTION_ARTIFACT: &str = r#"
273 {
274 "name": "TokenContract",
275 "functions": [
276 {
277 "name": "constructor",
278 "function_type": "private",
279 "is_initializer": true,
280 "is_static": false,
281 "parameters": [
282 { "name": "admin", "type": { "kind": "field" } },
283 { "name": "name", "type": { "kind": "string", "length": 31 } }
284 ],
285 "return_types": []
286 },
287 {
288 "name": "transfer",
289 "function_type": "private",
290 "is_initializer": false,
291 "is_static": false,
292 "parameters": [
293 { "name": "from", "type": { "kind": "field" } },
294 { "name": "to", "type": { "kind": "field" } },
295 { "name": "amount", "type": { "kind": "integer", "sign": "unsigned", "width": 64 } }
296 ],
297 "return_types": []
298 },
299 {
300 "name": "balance_of",
301 "function_type": "utility",
302 "is_initializer": false,
303 "is_static": true,
304 "parameters": [
305 { "name": "owner", "type": { "kind": "field" } }
306 ],
307 "return_types": [
308 { "kind": "integer", "sign": "unsigned", "width": 64 }
309 ]
310 },
311 {
312 "name": "total_supply",
313 "function_type": "public",
314 "is_initializer": false,
315 "is_static": true,
316 "parameters": [],
317 "return_types": [
318 { "kind": "integer", "sign": "unsigned", "width": 64 }
319 ]
320 }
321 ]
322 }
323 "#;
324
325 #[test]
326 fn function_type_roundtrip() {
327 for (ft, expected) in [
328 (FunctionType::Private, "\"private\""),
329 (FunctionType::Public, "\"public\""),
330 (FunctionType::Utility, "\"utility\""),
331 ] {
332 let json = serde_json::to_string(&ft).expect("serialize FunctionType");
333 assert_eq!(json, expected);
334 let decoded: FunctionType =
335 serde_json::from_str(&json).expect("deserialize FunctionType");
336 assert_eq!(decoded, ft);
337 }
338 }
339
340 #[test]
341 fn function_selector_hex_roundtrip() {
342 let selector = FunctionSelector::from_hex("0xaabbccdd").expect("valid hex");
343 assert_eq!(selector.0, [0xaa, 0xbb, 0xcc, 0xdd]);
344 assert_eq!(selector.to_string(), "0xaabbccdd");
345
346 let json = serde_json::to_string(&selector).expect("serialize selector");
347 let decoded: FunctionSelector = serde_json::from_str(&json).expect("deserialize selector");
348 assert_eq!(decoded, selector);
349 }
350
351 #[test]
352 fn function_selector_rejects_too_long() {
353 let result = FunctionSelector::from_hex("0xaabbccddee");
354 assert!(result.is_err());
355 }
356
357 #[test]
358 fn event_selector_roundtrip() {
359 let selector = EventSelector(Fr::from(42u64));
360 let json = serde_json::to_string(&selector).expect("serialize EventSelector");
361 let decoded: EventSelector =
362 serde_json::from_str(&json).expect("deserialize EventSelector");
363 assert_eq!(decoded, selector);
364 }
365
366 #[test]
367 fn load_minimal_artifact() {
368 let artifact = ContractArtifact::from_json(MINIMAL_ARTIFACT).expect("parse artifact");
369 assert_eq!(artifact.name, "TestContract");
370 assert_eq!(artifact.functions.len(), 1);
371 assert_eq!(artifact.functions[0].name, "increment");
372 assert_eq!(artifact.functions[0].function_type, FunctionType::Public);
373 assert!(!artifact.functions[0].is_initializer);
374 assert_eq!(artifact.functions[0].parameters.len(), 1);
375 assert_eq!(artifact.functions[0].parameters[0].name, "value");
376 }
377
378 #[test]
379 fn load_multi_function_artifact() {
380 let artifact =
381 ContractArtifact::from_json(MULTI_FUNCTION_ARTIFACT).expect("parse artifact");
382 assert_eq!(artifact.name, "TokenContract");
383 assert_eq!(artifact.functions.len(), 4);
384
385 let constructor = &artifact.functions[0];
386 assert_eq!(constructor.name, "constructor");
387 assert_eq!(constructor.function_type, FunctionType::Private);
388 assert!(constructor.is_initializer);
389 assert_eq!(constructor.parameters.len(), 2);
390
391 let transfer = &artifact.functions[1];
392 assert_eq!(transfer.name, "transfer");
393 assert_eq!(transfer.function_type, FunctionType::Private);
394 assert!(!transfer.is_static);
395
396 let balance = &artifact.functions[2];
397 assert_eq!(balance.name, "balance_of");
398 assert_eq!(balance.function_type, FunctionType::Utility);
399 assert!(balance.is_static);
400 assert_eq!(balance.return_types.len(), 1);
401
402 let supply = &artifact.functions[3];
403 assert_eq!(supply.name, "total_supply");
404 assert_eq!(supply.function_type, FunctionType::Public);
405 assert!(supply.is_static);
406 }
407
408 #[test]
409 fn find_function_by_name() {
410 let artifact =
411 ContractArtifact::from_json(MULTI_FUNCTION_ARTIFACT).expect("parse artifact");
412
413 let transfer = artifact.find_function("transfer").expect("find transfer");
414 assert_eq!(transfer.name, "transfer");
415 assert_eq!(transfer.function_type, FunctionType::Private);
416 }
417
418 #[test]
419 fn find_function_not_found() {
420 let artifact =
421 ContractArtifact::from_json(MULTI_FUNCTION_ARTIFACT).expect("parse artifact");
422
423 let result = artifact.find_function("nonexistent");
424 assert!(result.is_err());
425 }
426
427 #[test]
428 fn find_function_by_type() {
429 let artifact =
430 ContractArtifact::from_json(MULTI_FUNCTION_ARTIFACT).expect("parse artifact");
431
432 let balance = artifact
433 .find_function_by_type("balance_of", &FunctionType::Utility)
434 .expect("find balance_of as utility");
435 assert_eq!(balance.name, "balance_of");
436
437 let wrong_type = artifact.find_function_by_type("balance_of", &FunctionType::Public);
438 assert!(wrong_type.is_err());
439 }
440
441 #[test]
442 fn abi_value_field_roundtrip() {
443 let value = AbiValue::Field(Fr::from(1u64));
444 let json = serde_json::to_string(&value).expect("serialize AbiValue::Field");
445 assert!(json.contains("field"));
446 let decoded: AbiValue = serde_json::from_str(&json).expect("deserialize AbiValue");
447 assert_eq!(decoded, value);
448 }
449
450 #[test]
451 fn abi_value_boolean_roundtrip() {
452 let value = AbiValue::Boolean(true);
453 let json = serde_json::to_string(&value).expect("serialize");
454 let decoded: AbiValue = serde_json::from_str(&json).expect("deserialize");
455 assert_eq!(decoded, value);
456 }
457
458 #[test]
459 fn abi_value_integer_roundtrip() {
460 let value = AbiValue::Integer(42);
461 let json = serde_json::to_string(&value).expect("serialize");
462 let decoded: AbiValue = serde_json::from_str(&json).expect("deserialize");
463 assert_eq!(decoded, value);
464 }
465
466 #[test]
467 fn abi_value_array_roundtrip() {
468 let value = AbiValue::Array(vec![
469 AbiValue::Field(Fr::from(1u64)),
470 AbiValue::Field(Fr::from(2u64)),
471 ]);
472 let json = serde_json::to_string(&value).expect("serialize");
473 let decoded: AbiValue = serde_json::from_str(&json).expect("deserialize");
474 assert_eq!(decoded, value);
475 }
476
477 #[test]
478 fn abi_value_struct_roundtrip() {
479 let mut fields = BTreeMap::new();
480 fields.insert("x".to_owned(), AbiValue::Field(Fr::from(1u64)));
481 fields.insert("y".to_owned(), AbiValue::Integer(2));
482 let value = AbiValue::Struct(fields);
483 let json = serde_json::to_string(&value).expect("serialize");
484 let decoded: AbiValue = serde_json::from_str(&json).expect("deserialize");
485 assert_eq!(decoded, value);
486 }
487
488 #[test]
489 fn abi_type_struct_roundtrip() {
490 let typ = AbiType::Struct {
491 name: "Point".to_owned(),
492 fields: vec![
493 AbiParameter {
494 name: "x".to_owned(),
495 typ: AbiType::Field,
496 visibility: None,
497 },
498 AbiParameter {
499 name: "y".to_owned(),
500 typ: AbiType::Field,
501 visibility: None,
502 },
503 ],
504 };
505 let json = serde_json::to_string(&typ).expect("serialize AbiType::Struct");
506 let decoded: AbiType = serde_json::from_str(&json).expect("deserialize AbiType::Struct");
507 assert_eq!(decoded, typ);
508 }
509
510 #[test]
511 fn abi_type_array_roundtrip() {
512 let typ = AbiType::Array {
513 element: Box::new(AbiType::Field),
514 length: 10,
515 };
516 let json = serde_json::to_string(&typ).expect("serialize");
517 let decoded: AbiType = serde_json::from_str(&json).expect("deserialize");
518 assert_eq!(decoded, typ);
519 }
520
521 #[test]
522 fn artifact_from_invalid_json_fails() {
523 let result = ContractArtifact::from_json("not json");
524 assert!(result.is_err());
525 }
526
527 #[test]
528 fn from_signature_is_deterministic() {
529 let a = FunctionSelector::from_signature("sponsor_unconditionally()");
530 let b = FunctionSelector::from_signature("sponsor_unconditionally()");
531 assert_eq!(a, b);
532 }
533
534 #[test]
535 fn from_signature_different_inputs_differ() {
536 let a = FunctionSelector::from_signature("sponsor_unconditionally()");
537 let b = FunctionSelector::from_signature("claim_and_end_setup((Field),u128,Field,Field)");
538 assert_ne!(a, b);
539 }
540
541 #[test]
542 fn from_signature_empty_string() {
543 let a = FunctionSelector::from_signature("");
545 let b = FunctionSelector::from_signature("");
546 assert_eq!(a, b);
547 }
548
549 #[test]
550 fn from_signature_produces_4_bytes() {
551 let selector = FunctionSelector::from_signature("transfer(Field,Field,u64)");
552 assert_eq!(selector.0.len(), 4);
553 }
554}