1use ark_ff::{BigInteger, PrimeField};
2use serde::{Deserialize, Deserializer, Serialize, Serializer};
3use std::collections::BTreeMap;
4use std::fmt;
5
6use crate::hash::poseidon2_hash_bytes;
7use crate::types::Fr;
8use crate::Error;
9
10fn strip_0x(s: &str) -> &str {
11 s.strip_prefix("0x").unwrap_or(s)
12}
13
14fn decode_selector_hex(s: &str) -> Result<[u8; 4], Error> {
15 let raw = strip_0x(s);
16 if raw.len() > 8 {
17 return Err(Error::InvalidData(
18 "function selector must fit in 4 bytes".to_owned(),
19 ));
20 }
21 let padded = format!("{raw:0>8}");
22 let bytes = hex::decode(padded).map_err(|e| Error::InvalidData(e.to_string()))?;
23 let mut out = [0u8; 4];
24 out.copy_from_slice(&bytes);
25 Ok(out)
26}
27
28fn field_to_selector_bytes(field: Fr) -> [u8; 4] {
29 let raw = field.0.into_bigint().to_bytes_be();
30 let mut padded = [0u8; 32];
31 padded[32 - raw.len()..].copy_from_slice(&raw);
32 let mut out = [0u8; 4];
33 out.copy_from_slice(&padded[28..]);
34 out
35}
36
37fn selector_bytes_to_field(bytes: [u8; 4]) -> Fr {
38 Fr::from(u64::from(u32::from_be_bytes(bytes)))
39}
40
41fn selector_from_signature(signature: &str) -> [u8; 4] {
42 let hash = poseidon2_hash_bytes(signature.as_bytes());
43 field_to_selector_bytes(hash)
44}
45
46pub fn abi_type_signature(typ: &AbiType) -> String {
48 match typ {
49 AbiType::Field => "Field".to_owned(),
50 AbiType::Boolean => "bool".to_owned(),
51 AbiType::Integer { sign, width } => {
52 let prefix = if sign == "signed" { "i" } else { "u" };
53 format!("{prefix}{width}")
54 }
55 AbiType::Array { element, length } => {
56 format!("[{};{length}]", abi_type_signature(element))
57 }
58 AbiType::String { length } => format!("str<{length}>"),
59 AbiType::Struct { fields, .. } => {
60 let inner: Vec<String> = fields.iter().map(|f| abi_type_signature(&f.typ)).collect();
61 format!("({})", inner.join(","))
62 }
63 AbiType::Tuple { elements } => {
64 let inner: Vec<String> = elements.iter().map(abi_type_signature).collect();
65 format!("({})", inner.join(","))
66 }
67 }
68}
69
70#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
72pub struct FunctionSelector(pub [u8; 4]);
73
74impl FunctionSelector {
75 pub fn from_hex(value: &str) -> Result<Self, Error> {
77 Ok(Self(decode_selector_hex(value)?))
78 }
79
80 pub fn from_field(field: Fr) -> Self {
82 Self(field_to_selector_bytes(field))
83 }
84
85 pub fn from_signature(signature: &str) -> Self {
96 Self(selector_from_signature(signature))
97 }
98
99 pub fn from_name_and_parameters(name: &str, params: &[AbiParameter]) -> Self {
104 let param_sigs: Vec<String> = params.iter().map(|p| abi_type_signature(&p.typ)).collect();
105 let sig = format!("{}({})", name, param_sigs.join(","));
106 Self::from_signature(&sig)
107 }
108
109 pub fn to_field(self) -> Fr {
111 selector_bytes_to_field(self.0)
112 }
113}
114
115impl fmt::Display for FunctionSelector {
116 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117 write!(f, "0x{}", hex::encode(self.0))
118 }
119}
120
121impl Serialize for FunctionSelector {
122 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
123 where
124 S: Serializer,
125 {
126 serializer.serialize_str(&self.to_string())
127 }
128}
129
130impl<'de> Deserialize<'de> for FunctionSelector {
131 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
132 where
133 D: Deserializer<'de>,
134 {
135 let s = String::deserialize(deserializer)?;
136 Self::from_hex(&s).map_err(serde::de::Error::custom)
137 }
138}
139
140#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
142pub struct AuthorizationSelector(pub [u8; 4]);
143
144impl AuthorizationSelector {
145 pub fn from_hex(value: &str) -> Result<Self, Error> {
147 Ok(Self(decode_selector_hex(value)?))
148 }
149
150 pub fn from_field(field: Fr) -> Self {
152 Self(field_to_selector_bytes(field))
153 }
154
155 pub fn from_signature(signature: &str) -> Self {
157 Self(selector_from_signature(signature))
158 }
159
160 pub fn to_field(self) -> Fr {
162 selector_bytes_to_field(self.0)
163 }
164}
165
166impl fmt::Display for AuthorizationSelector {
167 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
168 write!(f, "0x{}", hex::encode(self.0))
169 }
170}
171
172impl Serialize for AuthorizationSelector {
173 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
174 where
175 S: Serializer,
176 {
177 serializer.serialize_str(&self.to_string())
178 }
179}
180
181impl<'de> Deserialize<'de> for AuthorizationSelector {
182 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
183 where
184 D: Deserializer<'de>,
185 {
186 let s = String::deserialize(deserializer)?;
187 Self::from_hex(&s).map_err(serde::de::Error::custom)
188 }
189}
190
191#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
193pub struct EventSelector(pub Fr);
194
195#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
197#[serde(rename_all = "lowercase")]
198pub enum FunctionType {
199 Private,
201 Public,
203 Utility,
205}
206
207#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
209#[serde(tag = "kind", rename_all = "snake_case")]
210pub enum AbiType {
211 Field,
213 Boolean,
215 Integer {
217 sign: String,
219 width: u16,
221 },
222 Array {
224 #[serde(rename = "type", alias = "element")]
226 element: Box<Self>,
227 length: usize,
229 },
230 String {
232 length: usize,
234 },
235 Struct {
237 #[serde(alias = "path")]
239 name: String,
240 fields: Vec<AbiParameter>,
242 },
243 Tuple {
245 #[serde(alias = "fields")]
247 elements: Vec<Self>,
248 },
249}
250
251#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
253#[serde(tag = "kind", content = "value", rename_all = "snake_case")]
254pub enum AbiValue {
255 Field(Fr),
257 Boolean(bool),
259 Integer(i128),
261 Array(Vec<Self>),
263 String(String),
265 Struct(BTreeMap<String, Self>),
267 Tuple(Vec<Self>),
269}
270
271#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
273pub struct AbiParameter {
274 pub name: String,
276 #[serde(rename = "type")]
278 pub typ: AbiType,
279 #[serde(default)]
281 pub visibility: Option<String>,
282}
283
284#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
286pub struct FunctionArtifact {
287 pub name: String,
289 #[serde(alias = "functionType")]
291 pub function_type: FunctionType,
292 #[serde(default, alias = "isInitializer")]
294 pub is_initializer: bool,
295 #[serde(default, alias = "isStatic")]
297 pub is_static: bool,
298 #[serde(default, skip_serializing_if = "Option::is_none", alias = "isOnlySelf")]
300 pub is_only_self: Option<bool>,
301 #[serde(default)]
303 pub parameters: Vec<AbiParameter>,
304 #[serde(default, alias = "returnTypes")]
306 pub return_types: Vec<AbiType>,
307 #[serde(default, skip_serializing_if = "Option::is_none", alias = "errorTypes")]
309 pub error_types: Option<serde_json::Value>,
310 #[serde(default)]
312 pub selector: Option<FunctionSelector>,
313 #[serde(default, skip_serializing_if = "Option::is_none")]
315 pub bytecode: Option<String>,
316 #[serde(default, skip_serializing_if = "Option::is_none")]
318 pub verification_key_hash: Option<Fr>,
319 #[serde(default, skip_serializing_if = "Option::is_none")]
321 pub verification_key: Option<String>,
322 #[serde(
324 default,
325 skip_serializing_if = "Option::is_none",
326 alias = "customAttributes"
327 )]
328 pub custom_attributes: Option<Vec<String>>,
329 #[serde(
331 default,
332 skip_serializing_if = "Option::is_none",
333 alias = "isUnconstrained"
334 )]
335 pub is_unconstrained: Option<bool>,
336 #[serde(
338 default,
339 skip_serializing_if = "Option::is_none",
340 alias = "debugSymbols"
341 )]
342 pub debug_symbols: Option<serde_json::Value>,
343}
344
345#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
347pub struct ContractArtifact {
348 pub name: String,
350 #[serde(default)]
352 pub functions: Vec<FunctionArtifact>,
353 #[serde(default, skip_serializing_if = "Option::is_none")]
355 pub outputs: Option<serde_json::Value>,
356 #[serde(default, skip_serializing_if = "Option::is_none", alias = "fileMap")]
358 pub file_map: Option<serde_json::Value>,
359}
360
361impl ContractArtifact {
362 pub fn from_json(json: &str) -> Result<Self, Error> {
364 serde_json::from_str(json).map_err(Error::from)
365 }
366
367 pub fn find_function(&self, name: &str) -> Result<&FunctionArtifact, Error> {
369 self.functions
370 .iter()
371 .find(|f| f.name == name)
372 .ok_or_else(|| {
373 Error::Abi(format!(
374 "function '{}' not found in artifact '{}'",
375 name, self.name
376 ))
377 })
378 }
379
380 pub fn find_function_by_type(
382 &self,
383 name: &str,
384 function_type: &FunctionType,
385 ) -> Result<&FunctionArtifact, Error> {
386 self.functions
387 .iter()
388 .find(|f| f.name == name && &f.function_type == function_type)
389 .ok_or_else(|| {
390 Error::Abi(format!(
391 "{:?} function '{}' not found in artifact '{}'",
392 function_type, name, self.name
393 ))
394 })
395 }
396}
397
398pub fn encode_arguments(function: &FunctionArtifact, args: &[AbiValue]) -> Result<Vec<Fr>, Error> {
400 if function.parameters.len() != args.len() {
401 return Err(Error::Abi(format!(
402 "function '{}' expects {} argument(s), got {}",
403 function.name,
404 function.parameters.len(),
405 args.len()
406 )));
407 }
408
409 let mut out = Vec::new();
410 for (param, value) in function.parameters.iter().zip(args) {
411 encode_value(¶m.typ, value, &mut out)?;
412 }
413 Ok(out)
414}
415
416fn encode_value(typ: &AbiType, value: &AbiValue, out: &mut Vec<Fr>) -> Result<(), Error> {
417 match (typ, value) {
418 (AbiType::Field, AbiValue::Field(field)) => {
419 out.push(*field);
420 Ok(())
421 }
422 (AbiType::Boolean, AbiValue::Boolean(boolean)) => {
423 out.push(if *boolean { Fr::one() } else { Fr::zero() });
424 Ok(())
425 }
426 (AbiType::Integer { .. }, AbiValue::Integer(integer)) => {
427 out.push(Fr::from(*integer as u64));
428 Ok(())
429 }
430 (AbiType::Array { element, length }, AbiValue::Array(items)) => {
431 if items.len() != *length {
432 return Err(Error::Abi(format!(
433 "expected array of length {}, got {}",
434 length,
435 items.len()
436 )));
437 }
438 for item in items {
439 encode_value(element, item, out)?;
440 }
441 Ok(())
442 }
443 (AbiType::String { length }, AbiValue::String(string)) => {
444 let bytes = string.as_bytes();
445 if bytes.len() > *length {
446 return Err(Error::Abi(format!(
447 "string exceeds fixed ABI length {}",
448 length
449 )));
450 }
451 for byte in bytes {
452 out.push(Fr::from(u64::from(*byte)));
453 }
454 for _ in bytes.len()..*length {
455 out.push(Fr::zero());
456 }
457 Ok(())
458 }
459 (AbiType::Struct { fields, .. }, AbiValue::Struct(values)) => {
460 for field in fields {
461 let field_value = values
462 .get(&field.name)
463 .ok_or_else(|| Error::Abi(format!("missing struct field '{}'", field.name)))?;
464 encode_value(&field.typ, field_value, out)?;
465 }
466 Ok(())
467 }
468 (AbiType::Tuple { elements }, AbiValue::Tuple(values)) => {
469 if elements.len() != values.len() {
470 return Err(Error::Abi(format!(
471 "expected tuple of length {}, got {}",
472 elements.len(),
473 values.len()
474 )));
475 }
476 for (element, value) in elements.iter().zip(values) {
477 encode_value(element, value, out)?;
478 }
479 Ok(())
480 }
481 _ => Err(Error::Abi("argument type/value mismatch".to_owned())),
482 }
483}
484
485#[cfg(test)]
486#[allow(clippy::expect_used, clippy::panic)]
487mod tests {
488 use super::*;
489
490 const MINIMAL_ARTIFACT: &str = r#"
491 {
492 "name": "TestContract",
493 "functions": [
494 {
495 "name": "increment",
496 "function_type": "public",
497 "is_initializer": false,
498 "is_static": false,
499 "parameters": [
500 { "name": "value", "type": { "kind": "field" } }
501 ],
502 "return_types": []
503 }
504 ]
505 }
506 "#;
507
508 const MULTI_FUNCTION_ARTIFACT: &str = r#"
509 {
510 "name": "TokenContract",
511 "functions": [
512 {
513 "name": "constructor",
514 "function_type": "private",
515 "is_initializer": true,
516 "is_static": false,
517 "parameters": [
518 { "name": "admin", "type": { "kind": "field" } },
519 { "name": "name", "type": { "kind": "string", "length": 31 } }
520 ],
521 "return_types": []
522 },
523 {
524 "name": "transfer",
525 "function_type": "private",
526 "is_initializer": false,
527 "is_static": false,
528 "parameters": [
529 { "name": "from", "type": { "kind": "field" } },
530 { "name": "to", "type": { "kind": "field" } },
531 { "name": "amount", "type": { "kind": "integer", "sign": "unsigned", "width": 64 } }
532 ],
533 "return_types": []
534 },
535 {
536 "name": "balance_of",
537 "function_type": "utility",
538 "is_initializer": false,
539 "is_static": true,
540 "parameters": [
541 { "name": "owner", "type": { "kind": "field" } }
542 ],
543 "return_types": [
544 { "kind": "integer", "sign": "unsigned", "width": 64 }
545 ]
546 },
547 {
548 "name": "total_supply",
549 "function_type": "public",
550 "is_initializer": false,
551 "is_static": true,
552 "parameters": [],
553 "return_types": [
554 { "kind": "integer", "sign": "unsigned", "width": 64 }
555 ]
556 }
557 ]
558 }
559 "#;
560
561 #[test]
562 fn function_type_roundtrip() {
563 for (ft, expected) in [
564 (FunctionType::Private, "\"private\""),
565 (FunctionType::Public, "\"public\""),
566 (FunctionType::Utility, "\"utility\""),
567 ] {
568 let json = serde_json::to_string(&ft).expect("serialize FunctionType");
569 assert_eq!(json, expected);
570 let decoded: FunctionType =
571 serde_json::from_str(&json).expect("deserialize FunctionType");
572 assert_eq!(decoded, ft);
573 }
574 }
575
576 #[test]
577 fn function_selector_hex_roundtrip() {
578 let selector = FunctionSelector::from_hex("0xaabbccdd").expect("valid hex");
579 assert_eq!(selector.0, [0xaa, 0xbb, 0xcc, 0xdd]);
580 assert_eq!(selector.to_string(), "0xaabbccdd");
581
582 let json = serde_json::to_string(&selector).expect("serialize selector");
583 let decoded: FunctionSelector = serde_json::from_str(&json).expect("deserialize selector");
584 assert_eq!(decoded, selector);
585 }
586
587 #[test]
588 fn authorization_selector_hex_roundtrip() {
589 let selector = AuthorizationSelector::from_hex("0x01020304").expect("valid hex");
590 assert_eq!(selector.0, [0x01, 0x02, 0x03, 0x04]);
591 assert_eq!(selector.to_string(), "0x01020304");
592
593 let json = serde_json::to_string(&selector).expect("serialize selector");
594 let decoded: AuthorizationSelector =
595 serde_json::from_str(&json).expect("deserialize selector");
596 assert_eq!(decoded, selector);
597 }
598
599 #[test]
600 fn function_selector_rejects_too_long() {
601 let result = FunctionSelector::from_hex("0xaabbccddee");
602 assert!(result.is_err());
603 }
604
605 #[test]
606 fn event_selector_roundtrip() {
607 let selector = EventSelector(Fr::from(42u64));
608 let json = serde_json::to_string(&selector).expect("serialize EventSelector");
609 let decoded: EventSelector =
610 serde_json::from_str(&json).expect("deserialize EventSelector");
611 assert_eq!(decoded, selector);
612 }
613
614 #[test]
615 fn load_minimal_artifact() {
616 let artifact = ContractArtifact::from_json(MINIMAL_ARTIFACT).expect("parse artifact");
617 assert_eq!(artifact.name, "TestContract");
618 assert_eq!(artifact.functions.len(), 1);
619 assert_eq!(artifact.functions[0].name, "increment");
620 assert_eq!(artifact.functions[0].function_type, FunctionType::Public);
621 assert!(!artifact.functions[0].is_initializer);
622 assert_eq!(artifact.functions[0].parameters.len(), 1);
623 assert_eq!(artifact.functions[0].parameters[0].name, "value");
624 }
625
626 #[test]
627 fn load_multi_function_artifact() {
628 let artifact =
629 ContractArtifact::from_json(MULTI_FUNCTION_ARTIFACT).expect("parse artifact");
630 assert_eq!(artifact.name, "TokenContract");
631 assert_eq!(artifact.functions.len(), 4);
632
633 let constructor = &artifact.functions[0];
634 assert_eq!(constructor.name, "constructor");
635 assert_eq!(constructor.function_type, FunctionType::Private);
636 assert!(constructor.is_initializer);
637 assert_eq!(constructor.parameters.len(), 2);
638
639 let transfer = &artifact.functions[1];
640 assert_eq!(transfer.name, "transfer");
641 assert_eq!(transfer.function_type, FunctionType::Private);
642 assert!(!transfer.is_static);
643
644 let balance = &artifact.functions[2];
645 assert_eq!(balance.name, "balance_of");
646 assert_eq!(balance.function_type, FunctionType::Utility);
647 assert!(balance.is_static);
648 assert_eq!(balance.return_types.len(), 1);
649
650 let supply = &artifact.functions[3];
651 assert_eq!(supply.name, "total_supply");
652 assert_eq!(supply.function_type, FunctionType::Public);
653 assert!(supply.is_static);
654 }
655
656 #[test]
657 fn find_function_by_name() {
658 let artifact =
659 ContractArtifact::from_json(MULTI_FUNCTION_ARTIFACT).expect("parse artifact");
660
661 let transfer = artifact.find_function("transfer").expect("find transfer");
662 assert_eq!(transfer.name, "transfer");
663 assert_eq!(transfer.function_type, FunctionType::Private);
664 }
665
666 #[test]
667 fn find_function_not_found() {
668 let artifact =
669 ContractArtifact::from_json(MULTI_FUNCTION_ARTIFACT).expect("parse artifact");
670
671 let result = artifact.find_function("nonexistent");
672 assert!(result.is_err());
673 }
674
675 #[test]
676 fn find_function_by_type() {
677 let artifact =
678 ContractArtifact::from_json(MULTI_FUNCTION_ARTIFACT).expect("parse artifact");
679
680 let balance = artifact
681 .find_function_by_type("balance_of", &FunctionType::Utility)
682 .expect("find balance_of as utility");
683 assert_eq!(balance.name, "balance_of");
684
685 let wrong_type = artifact.find_function_by_type("balance_of", &FunctionType::Public);
686 assert!(wrong_type.is_err());
687 }
688
689 #[test]
690 fn abi_value_field_roundtrip() {
691 let value = AbiValue::Field(Fr::from(1u64));
692 let json = serde_json::to_string(&value).expect("serialize AbiValue::Field");
693 assert!(json.contains("field"));
694 let decoded: AbiValue = serde_json::from_str(&json).expect("deserialize AbiValue");
695 assert_eq!(decoded, value);
696 }
697
698 #[test]
699 fn abi_value_boolean_roundtrip() {
700 let value = AbiValue::Boolean(true);
701 let json = serde_json::to_string(&value).expect("serialize");
702 let decoded: AbiValue = serde_json::from_str(&json).expect("deserialize");
703 assert_eq!(decoded, value);
704 }
705
706 #[test]
707 fn abi_value_integer_roundtrip() {
708 let value = AbiValue::Integer(42);
709 let json = serde_json::to_string(&value).expect("serialize");
710 let decoded: AbiValue = serde_json::from_str(&json).expect("deserialize");
711 assert_eq!(decoded, value);
712 }
713
714 #[test]
715 fn abi_value_array_roundtrip() {
716 let value = AbiValue::Array(vec![
717 AbiValue::Field(Fr::from(1u64)),
718 AbiValue::Field(Fr::from(2u64)),
719 ]);
720 let json = serde_json::to_string(&value).expect("serialize");
721 let decoded: AbiValue = serde_json::from_str(&json).expect("deserialize");
722 assert_eq!(decoded, value);
723 }
724
725 #[test]
726 fn abi_value_struct_roundtrip() {
727 let mut fields = BTreeMap::new();
728 fields.insert("x".to_owned(), AbiValue::Field(Fr::from(1u64)));
729 fields.insert("y".to_owned(), AbiValue::Integer(2));
730 let value = AbiValue::Struct(fields);
731 let json = serde_json::to_string(&value).expect("serialize");
732 let decoded: AbiValue = serde_json::from_str(&json).expect("deserialize");
733 assert_eq!(decoded, value);
734 }
735
736 #[test]
737 fn abi_type_struct_roundtrip() {
738 let typ = AbiType::Struct {
739 name: "Point".to_owned(),
740 fields: vec![
741 AbiParameter {
742 name: "x".to_owned(),
743 typ: AbiType::Field,
744 visibility: None,
745 },
746 AbiParameter {
747 name: "y".to_owned(),
748 typ: AbiType::Field,
749 visibility: None,
750 },
751 ],
752 };
753 let json = serde_json::to_string(&typ).expect("serialize AbiType::Struct");
754 let decoded: AbiType = serde_json::from_str(&json).expect("deserialize AbiType::Struct");
755 assert_eq!(decoded, typ);
756 }
757
758 #[test]
759 fn abi_type_array_roundtrip() {
760 let typ = AbiType::Array {
761 element: Box::new(AbiType::Field),
762 length: 10,
763 };
764 let json = serde_json::to_string(&typ).expect("serialize");
765 let decoded: AbiType = serde_json::from_str(&json).expect("deserialize");
766 assert_eq!(decoded, typ);
767 }
768
769 #[test]
770 fn artifact_from_invalid_json_fails() {
771 let result = ContractArtifact::from_json("not json");
772 assert!(result.is_err());
773 }
774
775 #[test]
776 fn from_signature_is_deterministic() {
777 let a = FunctionSelector::from_signature("sponsor_unconditionally()");
778 let b = FunctionSelector::from_signature("sponsor_unconditionally()");
779 assert_eq!(a, b);
780 }
781
782 #[test]
783 fn from_signature_different_inputs_differ() {
784 let a = FunctionSelector::from_signature("sponsor_unconditionally()");
785 let b = FunctionSelector::from_signature("claim_and_end_setup((Field),u128,Field,Field)");
786 assert_ne!(a, b);
787 }
788
789 #[test]
790 fn from_signature_empty_string() {
791 let a = FunctionSelector::from_signature("");
793 let b = FunctionSelector::from_signature("");
794 assert_eq!(a, b);
795 }
796
797 #[test]
798 fn from_signature_produces_4_bytes() {
799 let selector = FunctionSelector::from_signature("transfer(Field,Field,u64)");
800 assert_eq!(selector.0.len(), 4);
801 }
802
803 #[test]
804 fn function_selector_roundtrips_through_field() {
805 let selector = FunctionSelector::from_signature("set_authorized(Field,bool)");
806 assert_eq!(FunctionSelector::from_field(selector.to_field()), selector);
807 }
808
809 #[test]
810 fn authorization_selector_roundtrips_through_field() {
811 let selector =
812 AuthorizationSelector::from_signature("CallAuthorization((Field),(u32),Field)");
813 assert_eq!(
814 AuthorizationSelector::from_field(selector.to_field()),
815 selector
816 );
817 }
818}