Skip to main content

visualsign/
lib.rs

1use crate::errors::VisualSignError;
2use serde::ser::SerializeMap;
3use serde::{Deserialize, Serialize, Serializer};
4use serde_json::Value;
5pub mod encodings;
6pub mod errors;
7pub mod field_builders;
8pub mod registry;
9pub mod test_utils;
10pub mod vsptrait;
11
12// Marker trait to ensure types implement deterministic ordering in their serialization
13// Types that implement this trait guarantee their JSON serialization has a deterministic,
14// reproducible field order (currently implemented as alphabetical ordering)
15pub trait DeterministicOrdering: Serialize {
16    // This method can be used to verify at runtime that the implementation maintains
17    // deterministic ordering (currently alphabetical, but this is an implementation detail)
18    fn verify_deterministic_ordering(&self) -> Result<(), String> {
19        let json = serde_json::to_value(self).map_err(|e| e.to_string())?;
20        // Currently we use alphabetical ordering as our deterministic strategy
21        verify_json_deterministic(&json, "")
22    }
23}
24
25// This macro would ideally be a procedural macro that generates both Serialize impl
26// and DeterministicOrdering impl, ensuring they're always in sync
27// For now, this is a declarative macro that helps document the pattern
28#[macro_export]
29macro_rules! impl_deterministic_serialize {
30    ($type:ty) => {
31        // This would be where the procedural macro generates the Serialize impl
32        // with guaranteed deterministic ordering
33        impl DeterministicOrdering for $type {}
34    };
35}
36
37// Static assertion helper - this ensures at compile time that a type implements the trait
38pub struct StaticAssertDeterministic<T: DeterministicOrdering>(std::marker::PhantomData<T>);
39
40// Function that can be used in const contexts to verify trait implementation at compile time
41pub const fn assert_deterministic<T: DeterministicOrdering>() -> StaticAssertDeterministic<T> {
42    StaticAssertDeterministic(std::marker::PhantomData)
43}
44
45// Helper function to verify JSON has alphabetical ordering (current implementation of deterministic ordering)
46fn verify_json_deterministic(value: &serde_json::Value, path: &str) -> Result<(), String> {
47    match value {
48        serde_json::Value::Object(map) => {
49            let keys: Vec<_> = map.keys().cloned().collect();
50            let mut sorted_keys = keys.clone();
51            sorted_keys.sort();
52
53            if keys != sorted_keys {
54                return Err(format!(
55                    "Keys at path '{}' are not alphabetically ordered. Got: {:?}, Expected: {:?}",
56                    if path.is_empty() { "root" } else { path },
57                    keys,
58                    sorted_keys
59                ));
60            }
61
62            // Recursively check nested values
63            for (key, nested_value) in map {
64                let new_path = if path.is_empty() {
65                    key.clone()
66                } else {
67                    format!("{path}.{key}")
68                };
69                verify_json_deterministic(nested_value, &new_path)?;
70            }
71        }
72        serde_json::Value::Array(arr) => {
73            for (i, item) in arr.iter().enumerate() {
74                let new_path = format!("{path}[{i}]");
75                verify_json_deterministic(item, &new_path)?;
76            }
77        }
78        _ => {} // Leaf nodes don't need checking
79    }
80    Ok(())
81}
82
83// A function to check if a string is empty (used for skip_serializing_if)
84fn is_empty_string(s: &str) -> bool {
85    s.is_empty()
86}
87
88// A bare bones implementation of the SignablePayload struct and its associated methods
89// The fields are serialized alphabetically to ensure that default serialization works the same
90// and the canonical representation is done by simply sorting the fields first
91#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
92pub struct SignablePayload {
93    #[serde(rename = "Fields")]
94    pub fields: Vec<SignablePayloadField>,
95    #[serde(rename = "PayloadType", skip_serializing_if = "is_empty_string")]
96    pub payload_type: String,
97    #[serde(rename = "Subtitle", skip_serializing_if = "Option::is_none")]
98    pub subtitle: Option<String>,
99    #[serde(rename = "Title")]
100    pub title: String,
101    #[serde(rename = "Version")]
102    pub version: String,
103}
104
105// Common fields shared by all field types
106#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
107pub struct SignablePayloadFieldCommon {
108    #[serde(rename = "FallbackText")]
109    pub fallback_text: String,
110    #[serde(rename = "Label")]
111    pub label: String,
112}
113
114// Implement DeterministicOrdering for SignablePayloadFieldCommon
115impl DeterministicOrdering for SignablePayloadFieldCommon {}
116
117// Now SignablePayloadField is an enum with variants for each field type
118#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
119#[serde(tag = "Type")]
120pub enum SignablePayloadField {
121    #[serde(rename = "text")]
122    Text {
123        #[serde(flatten)]
124        common: SignablePayloadFieldCommon,
125        #[serde(rename = "Text")]
126        text: SignablePayloadFieldText,
127    },
128
129    #[serde(rename = "text_v2")]
130    TextV2 {
131        #[serde(flatten)]
132        common: SignablePayloadFieldCommon,
133        #[serde(rename = "TextV2")]
134        text_v2: SignablePayloadFieldTextV2,
135    },
136
137    #[serde(rename = "address")]
138    Address {
139        #[serde(flatten)]
140        common: SignablePayloadFieldCommon,
141        #[serde(rename = "Address")]
142        address: SignablePayloadFieldAddress,
143    },
144
145    #[serde(rename = "address_v2")]
146    AddressV2 {
147        #[serde(flatten)]
148        common: SignablePayloadFieldCommon,
149        #[serde(rename = "AddressV2")]
150        address_v2: SignablePayloadFieldAddressV2,
151    },
152
153    #[serde(rename = "number")]
154    Number {
155        #[serde(flatten)]
156        common: SignablePayloadFieldCommon,
157        #[serde(rename = "Number")]
158        number: SignablePayloadFieldNumber,
159    },
160
161    #[serde(rename = "amount")]
162    Amount {
163        #[serde(flatten)]
164        common: SignablePayloadFieldCommon,
165        #[serde(rename = "Amount")]
166        amount: SignablePayloadFieldAmount,
167    },
168
169    #[serde(rename = "amount_v2")]
170    AmountV2 {
171        #[serde(flatten)]
172        common: SignablePayloadFieldCommon,
173        #[serde(rename = "AmountV2")]
174        amount_v2: SignablePayloadFieldAmountV2,
175    },
176
177    #[serde(rename = "divider")]
178    Divider {
179        #[serde(flatten)]
180        common: SignablePayloadFieldCommon,
181        #[serde(rename = "Divider")]
182        divider: SignablePayloadFieldDivider,
183    },
184
185    #[serde(rename = "preview_layout")]
186    PreviewLayout {
187        #[serde(flatten)]
188        common: SignablePayloadFieldCommon,
189        #[serde(rename = "PreviewLayout")]
190        preview_layout: SignablePayloadFieldPreviewLayout,
191    },
192
193    #[serde(rename = "list_layout")]
194    ListLayout {
195        #[serde(flatten)]
196        common: SignablePayloadFieldCommon,
197        #[serde(rename = "ListLayout")]
198        list_layout: SignablePayloadFieldListLayout,
199    },
200
201    #[serde(rename = "unknown")]
202    Unknown {
203        #[serde(flatten)]
204        common: SignablePayloadFieldCommon,
205        #[serde(rename = "Unknown")]
206        unknown: SignablePayloadFieldUnknown,
207    },
208}
209
210// Trait to ensure all SignablePayloadField variants implement serialization correctly
211trait FieldSerializer {
212    fn serialize_to_map(
213        &self,
214    ) -> Result<std::collections::BTreeMap<String, serde_json::Value>, serde_json::Error>;
215    fn get_expected_fields(&self) -> Vec<&'static str>;
216}
217
218// Macro to help serialize field variants with alphabetical ordering and verification
219macro_rules! serialize_field_variant {
220    ($fields:expr, $variant_name:literal, $common:expr, $(($field_name:literal, $field_value:expr)),* $(,)?) => {
221        // Add common fields
222        $fields.insert("FallbackText".to_string(), serde_json::to_value(&$common.fallback_text).unwrap());
223        $fields.insert("Label".to_string(), serde_json::to_value(&$common.label).unwrap());
224        $fields.insert("Type".to_string(), serde_json::Value::String($variant_name.to_string()));
225
226        // Add variant-specific fields
227        $(
228            $fields.insert($field_name.to_string(), serde_json::to_value($field_value).unwrap());
229        )*
230    };
231}
232
233// Implementation of FieldSerializer for SignablePayloadField
234impl FieldSerializer for SignablePayloadField {
235    fn serialize_to_map(
236        &self,
237    ) -> Result<std::collections::BTreeMap<String, serde_json::Value>, serde_json::Error> {
238        let mut fields = std::collections::HashMap::new();
239
240        // Use the macro to serialize each variant - macro uses unwrap() internally
241        match self {
242            SignablePayloadField::Text { common, text } => {
243                serialize_field_variant!(fields, "text", common, ("Text", text));
244            }
245            SignablePayloadField::TextV2 { common, text_v2 } => {
246                serialize_field_variant!(fields, "text_v2", common, ("TextV2", text_v2));
247            }
248            SignablePayloadField::Address { common, address } => {
249                serialize_field_variant!(fields, "address", common, ("Address", address));
250            }
251            SignablePayloadField::AddressV2 { common, address_v2 } => {
252                serialize_field_variant!(fields, "address_v2", common, ("AddressV2", address_v2));
253            }
254            SignablePayloadField::Number { common, number } => {
255                serialize_field_variant!(fields, "number", common, ("Number", number));
256            }
257            SignablePayloadField::Amount { common, amount } => {
258                serialize_field_variant!(fields, "amount", common, ("Amount", amount));
259            }
260            SignablePayloadField::AmountV2 { common, amount_v2 } => {
261                serialize_field_variant!(fields, "amount_v2", common, ("AmountV2", amount_v2));
262            }
263            SignablePayloadField::Divider { common, divider } => {
264                serialize_field_variant!(fields, "divider", common, ("Divider", divider));
265            }
266            SignablePayloadField::PreviewLayout {
267                common,
268                preview_layout,
269            } => {
270                serialize_field_variant!(
271                    fields,
272                    "preview_layout",
273                    common,
274                    ("PreviewLayout", preview_layout)
275                );
276            }
277            SignablePayloadField::ListLayout {
278                common,
279                list_layout,
280            } => {
281                serialize_field_variant!(
282                    fields,
283                    "list_layout",
284                    common,
285                    ("ListLayout", list_layout)
286                );
287            }
288            SignablePayloadField::Unknown { common, unknown } => {
289                serialize_field_variant!(fields, "unknown", common, ("Unknown", unknown));
290            }
291        }
292
293        // Convert to BTreeMap for alphabetical ordering
294        Ok(fields.into_iter().collect())
295    }
296
297    fn get_expected_fields(&self) -> Vec<&'static str> {
298        let mut base_fields = vec!["FallbackText", "Label", "Type"];
299
300        match self {
301            SignablePayloadField::Text { .. } => base_fields.push("Text"),
302            SignablePayloadField::TextV2 { .. } => base_fields.push("TextV2"),
303            SignablePayloadField::Address { .. } => base_fields.push("Address"),
304            SignablePayloadField::AddressV2 { .. } => base_fields.push("AddressV2"),
305            SignablePayloadField::Number { .. } => base_fields.push("Number"),
306            SignablePayloadField::Amount { .. } => base_fields.push("Amount"),
307            SignablePayloadField::AmountV2 { .. } => base_fields.push("AmountV2"),
308            SignablePayloadField::Divider { .. } => base_fields.push("Divider"),
309            SignablePayloadField::PreviewLayout { .. } => base_fields.push("PreviewLayout"),
310            SignablePayloadField::ListLayout { .. } => base_fields.push("ListLayout"),
311            SignablePayloadField::Unknown { .. } => base_fields.push("Unknown"),
312        }
313
314        base_fields.sort();
315        base_fields
316    }
317}
318
319// Custom Serialize implementation to ensure alphabetical field ordering with verification
320impl Serialize for SignablePayloadField {
321    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
322    where
323        S: Serializer,
324    {
325        // Use the trait method to get serialized fields
326        let sorted_map = self.serialize_to_map().map_err(serde::ser::Error::custom)?;
327
328        // Verify that all expected fields are present
329        let expected_fields = self.get_expected_fields();
330        let actual_fields: Vec<_> = sorted_map.keys().map(|s| s.as_str()).collect();
331
332        // Check for missing fields
333        for expected in &expected_fields {
334            if !actual_fields.contains(expected) {
335                return Err(serde::ser::Error::custom(format!(
336                    "Missing expected field '{}' in serialization of {:?}. Expected fields: {:?}, Actual fields: {:?}",
337                    expected,
338                    std::mem::discriminant(self),
339                    expected_fields,
340                    actual_fields
341                )));
342            }
343        }
344
345        // Check for unexpected fields (fields that shouldn't be there)
346        for actual in &actual_fields {
347            if !expected_fields.contains(actual) {
348                return Err(serde::ser::Error::custom(format!(
349                    "Unexpected field '{}' found in serialization of {:?}. Expected fields: {:?}",
350                    actual,
351                    std::mem::discriminant(self),
352                    expected_fields
353                )));
354            }
355        }
356
357        // Serialize the verified, sorted map
358        let mut map_ser = serializer.serialize_map(Some(sorted_map.len()))?;
359        for (k, v) in sorted_map {
360            map_ser.serialize_entry(&k, &v)?;
361        }
362        map_ser.end()
363    }
364}
365
366// Implement DeterministicOrdering for SignablePayloadField since it has custom Serialize
367impl DeterministicOrdering for SignablePayloadField {}
368
369// Helper methods for the enum
370impl SignablePayloadField {
371    pub fn fallback_text(&self) -> &String {
372        match self {
373            SignablePayloadField::Text { common, .. } => &common.fallback_text,
374            SignablePayloadField::TextV2 { common, .. } => &common.fallback_text,
375            SignablePayloadField::Address { common, .. } => &common.fallback_text,
376            SignablePayloadField::AddressV2 { common, .. } => &common.fallback_text,
377            SignablePayloadField::Number { common, .. } => &common.fallback_text,
378            SignablePayloadField::Amount { common, .. } => &common.fallback_text,
379            SignablePayloadField::AmountV2 { common, .. } => &common.fallback_text,
380            SignablePayloadField::Divider { common, .. } => &common.fallback_text,
381            SignablePayloadField::PreviewLayout { common, .. } => &common.fallback_text,
382            SignablePayloadField::ListLayout { common, .. } => &common.fallback_text,
383            SignablePayloadField::Unknown { common, .. } => &common.fallback_text,
384        }
385    }
386
387    pub fn label(&self) -> &String {
388        match self {
389            SignablePayloadField::Text { common, .. } => &common.label,
390            SignablePayloadField::TextV2 { common, .. } => &common.label,
391            SignablePayloadField::Address { common, .. } => &common.label,
392            SignablePayloadField::AddressV2 { common, .. } => &common.label,
393            SignablePayloadField::Number { common, .. } => &common.label,
394            SignablePayloadField::Amount { common, .. } => &common.label,
395            SignablePayloadField::AmountV2 { common, .. } => &common.label,
396            SignablePayloadField::Divider { common, .. } => &common.label,
397            SignablePayloadField::PreviewLayout { common, .. } => &common.label,
398            SignablePayloadField::ListLayout { common, .. } => &common.label,
399            SignablePayloadField::Unknown { common, .. } => &common.label,
400        }
401    }
402
403    pub fn field_type(&self) -> &str {
404        match self {
405            SignablePayloadField::Text { .. } => "text",
406            SignablePayloadField::TextV2 { .. } => "text_v2",
407            SignablePayloadField::Address { .. } => "address",
408            SignablePayloadField::AddressV2 { .. } => "address_v2",
409            SignablePayloadField::Number { .. } => "number",
410            SignablePayloadField::Amount { .. } => "amount",
411            SignablePayloadField::AmountV2 { .. } => "amount_v2",
412            SignablePayloadField::Divider { .. } => "divider",
413            SignablePayloadField::PreviewLayout { .. } => "preview_layout",
414            SignablePayloadField::ListLayout { .. } => "list_layout",
415            SignablePayloadField::Unknown { .. } => "unknown",
416        }
417    }
418}
419
420// Update all struct definitions to use String instead of NormalString
421#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
422pub struct SignablePayloadFieldPreviewLayout {
423    #[serde(rename = "Title", skip_serializing_if = "Option::is_none")]
424    pub title: Option<SignablePayloadFieldTextV2>,
425    #[serde(rename = "Subtitle", skip_serializing_if = "Option::is_none")]
426    pub subtitle: Option<SignablePayloadFieldTextV2>,
427    #[serde(rename = "Condensed", skip_serializing_if = "Option::is_none")]
428    pub condensed: Option<SignablePayloadFieldListLayout>,
429    #[serde(rename = "Expanded", skip_serializing_if = "Option::is_none")]
430    pub expanded: Option<SignablePayloadFieldListLayout>,
431}
432
433// Implement DeterministicOrdering for SignablePayloadFieldPreviewLayout
434impl DeterministicOrdering for SignablePayloadFieldPreviewLayout {}
435
436// Custom Serialize implementation for SignablePayloadFieldPreviewLayout to ensure alphabetical ordering
437impl Serialize for SignablePayloadFieldPreviewLayout {
438    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
439    where
440        S: Serializer,
441    {
442        // Use BTreeMap to ensure alphabetical ordering
443        let mut map = std::collections::BTreeMap::new();
444
445        // Add fields in alphabetical order (BTreeMap will maintain this)
446        // Note: Order should be Condensed, Expanded, Subtitle, Title
447        if let Some(ref condensed) = self.condensed {
448            map.insert(
449                "Condensed",
450                serde_json::to_value(condensed).map_err(serde::ser::Error::custom)?,
451            );
452        }
453        if let Some(ref expanded) = self.expanded {
454            map.insert(
455                "Expanded",
456                serde_json::to_value(expanded).map_err(serde::ser::Error::custom)?,
457            );
458        }
459        if let Some(ref subtitle) = self.subtitle {
460            map.insert(
461                "Subtitle",
462                serde_json::to_value(subtitle).map_err(serde::ser::Error::custom)?,
463            );
464        }
465        if let Some(ref title) = self.title {
466            map.insert(
467                "Title",
468                serde_json::to_value(title).map_err(serde::ser::Error::custom)?,
469            );
470        }
471
472        // Serialize the map
473        let mut map_ser = serializer.serialize_map(Some(map.len()))?;
474        for (k, v) in map {
475            map_ser.serialize_entry(&k, &v)?;
476        }
477        map_ser.end()
478    }
479}
480
481#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
482pub struct SignablePayloadFieldListLayout {
483    #[serde(rename = "Fields")]
484    pub fields: Vec<AnnotatedPayloadField>,
485}
486
487// Implement DeterministicOrdering for SignablePayloadFieldListLayout
488impl DeterministicOrdering for SignablePayloadFieldListLayout {}
489
490#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
491pub struct SignablePayloadFieldText {
492    #[serde(rename = "Text")]
493    pub text: String,
494}
495
496// Implement DeterministicOrdering for SignablePayloadFieldText
497impl DeterministicOrdering for SignablePayloadFieldText {}
498
499#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
500pub struct SignablePayloadFieldTextV2 {
501    #[serde(rename = "Text")]
502    pub text: String,
503}
504
505// Implement DeterministicOrdering for SignablePayloadFieldTextV2
506impl DeterministicOrdering for SignablePayloadFieldTextV2 {}
507
508#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
509pub struct SignablePayloadFieldAddress {
510    #[serde(rename = "Address")]
511    pub address: String,
512    #[serde(rename = "Name")]
513    pub name: String,
514}
515
516// Implement DeterministicOrdering for SignablePayloadFieldAddress
517impl DeterministicOrdering for SignablePayloadFieldAddress {}
518
519#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
520pub struct SignablePayloadFieldAddressV2 {
521    #[serde(rename = "Address")]
522    pub address: String,
523    #[serde(rename = "Name", skip_serializing_if = "is_empty_string")]
524    pub name: String,
525    #[serde(rename = "Memo", skip_serializing_if = "Option::is_none")]
526    pub memo: Option<String>,
527    #[serde(rename = "AssetLabel", skip_serializing_if = "is_empty_string")]
528    pub asset_label: String,
529    #[serde(rename = "BadgeText", skip_serializing_if = "Option::is_none")]
530    pub badge_text: Option<String>,
531}
532
533// Implement DeterministicOrdering for SignablePayloadFieldAddressV2
534impl DeterministicOrdering for SignablePayloadFieldAddressV2 {}
535
536#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
537pub struct SignablePayloadFieldNumber {
538    #[serde(rename = "Number")]
539    pub number: String,
540}
541
542// Implement DeterministicOrdering for SignablePayloadFieldNumber
543impl DeterministicOrdering for SignablePayloadFieldNumber {}
544
545#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
546pub struct SignablePayloadFieldAmount {
547    #[serde(rename = "Amount")]
548    pub amount: String,
549    #[serde(rename = "Abbreviation", skip_serializing_if = "Option::is_none")]
550    pub abbreviation: Option<String>,
551}
552
553// Implement DeterministicOrdering for SignablePayloadFieldAmount
554impl DeterministicOrdering for SignablePayloadFieldAmount {}
555
556#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
557pub struct SignablePayloadFieldAmountV2 {
558    #[serde(rename = "Amount")]
559    pub amount: String,
560    #[serde(rename = "Abbreviation", skip_serializing_if = "Option::is_none")]
561    pub abbreviation: Option<String>,
562}
563
564impl Serialize for SignablePayloadFieldAmountV2 {
565    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
566    where
567        S: serde::Serializer,
568    {
569        use std::collections::BTreeMap;
570
571        let mut map = BTreeMap::new();
572        map.insert("Amount", &self.amount);
573        if let Some(ref abbreviation) = self.abbreviation {
574            map.insert("Abbreviation", abbreviation);
575        }
576        map.serialize(serializer)
577    }
578}
579
580// Implement DeterministicOrdering for SignablePayloadFieldAmountV2
581impl DeterministicOrdering for SignablePayloadFieldAmountV2 {}
582
583#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
584pub struct SignablePayloadFieldDivider {
585    #[serde(rename = "Style")]
586    pub style: DividerStyle,
587}
588
589// Implement DeterministicOrdering for SignablePayloadFieldDivider
590impl DeterministicOrdering for SignablePayloadFieldDivider {}
591
592#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
593pub struct SignablePayloadFieldUnknown {
594    #[serde(rename = "Data")]
595    pub data: String,
596    #[serde(rename = "Explanation")]
597    pub explanation: String,
598}
599
600// Implement DeterministicOrdering for SignablePayloadFieldUnknown
601impl DeterministicOrdering for SignablePayloadFieldUnknown {}
602
603#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
604pub struct SignablePayloadFieldStaticAnnotation {
605    #[serde(rename = "Text")]
606    pub text: String,
607}
608
609// Implement DeterministicOrdering for SignablePayloadFieldStaticAnnotation
610impl DeterministicOrdering for SignablePayloadFieldStaticAnnotation {}
611
612#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
613pub struct SignablePayloadFieldDynamicAnnotation {
614    #[serde(rename = "Type")]
615    pub field_type: String,
616    #[serde(rename = "ID")]
617    pub id: String,
618    #[serde(rename = "Params")]
619    pub params: Vec<String>,
620}
621
622impl Serialize for SignablePayloadFieldDynamicAnnotation {
623    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
624    where
625        S: serde::Serializer,
626    {
627        use serde::ser::SerializeMap;
628
629        let mut map = serializer.serialize_map(Some(3))?;
630        map.serialize_entry("ID", &self.id)?;
631        map.serialize_entry("Params", &self.params)?;
632        map.serialize_entry("Type", &self.field_type)?;
633        map.end()
634    }
635}
636
637// Implement DeterministicOrdering for SignablePayloadFieldDynamicAnnotation
638impl DeterministicOrdering for SignablePayloadFieldDynamicAnnotation {}
639
640#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
641pub struct AnnotatedPayload {
642    #[serde(rename = "Version")]
643    pub version: String,
644    #[serde(rename = "Title", skip_serializing_if = "Option::is_none")]
645    pub title: Option<String>,
646    #[serde(rename = "Subtitle", skip_serializing_if = "Option::is_none")]
647    pub subtitle: Option<String>,
648    #[serde(rename = "Fields", skip_serializing_if = "Option::is_none")]
649    pub fields: Option<Vec<AnnotatedPayloadField>>,
650}
651
652#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
653pub struct AnnotatedPayloadField {
654    #[serde(flatten)]
655    pub signable_payload_field: SignablePayloadField,
656    #[serde(rename = "StaticAnnotation", skip_serializing_if = "Option::is_none")]
657    pub static_annotation: Option<SignablePayloadFieldStaticAnnotation>,
658    #[serde(rename = "DynamicAnnotation", skip_serializing_if = "Option::is_none")]
659    pub dynamic_annotation: Option<SignablePayloadFieldDynamicAnnotation>,
660}
661
662// Implement DeterministicOrdering for AnnotatedPayloadField since it has custom Serialize
663impl DeterministicOrdering for AnnotatedPayloadField {}
664
665// Custom Serialize implementation for AnnotatedPayloadField to ensure alphabetical ordering
666impl Serialize for AnnotatedPayloadField {
667    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
668    where
669        S: Serializer,
670    {
671        // First, serialize the flattened SignablePayloadField to get its fields
672        let field_map = self
673            .signable_payload_field
674            .serialize_to_map()
675            .map_err(serde::ser::Error::custom)?;
676
677        // Create a BTreeMap to ensure alphabetical ordering
678        let mut sorted_map = std::collections::BTreeMap::new();
679
680        // Add all fields from the SignablePayloadField
681        for (key, value) in field_map {
682            sorted_map.insert(key, value);
683        }
684
685        // Add optional annotation fields if present
686        if let Some(ref static_annotation) = self.static_annotation {
687            sorted_map.insert(
688                "StaticAnnotation".to_string(),
689                serde_json::to_value(static_annotation).map_err(serde::ser::Error::custom)?,
690            );
691        }
692
693        if let Some(ref dynamic_annotation) = self.dynamic_annotation {
694            sorted_map.insert(
695                "DynamicAnnotation".to_string(),
696                serde_json::to_value(dynamic_annotation).map_err(serde::ser::Error::custom)?,
697            );
698        }
699
700        // Serialize the sorted map
701        let mut map_ser = serializer.serialize_map(Some(sorted_map.len()))?;
702        for (k, v) in sorted_map {
703            map_ser.serialize_entry(&k, &v)?;
704        }
705        map_ser.end()
706    }
707}
708
709#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
710pub struct UserIntent {
711    #[serde(rename = "Type")]
712    pub intent_type: String,
713    #[serde(rename = "Payload")]
714    pub payload: Value,
715}
716
717#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
718pub struct DividerStyle(String);
719
720impl DividerStyle {
721    pub const THIN: DividerStyle = DividerStyle(String::new());
722}
723
724// Implement DeterministicOrdering for SignablePayload
725impl DeterministicOrdering for SignablePayload {}
726
727impl SignablePayload {
728    pub fn new(
729        version: i64,
730        title: String,
731        subtitle: Option<String>,
732        fields: Vec<SignablePayloadField>,
733        payload_type: String,
734    ) -> Self {
735        SignablePayload {
736            version: version.to_string(),
737            title,
738            subtitle,
739            payload_type,
740            fields,
741        }
742    }
743
744    // This function enforces that fields must implement DeterministicOrdering at compile time
745    pub fn new_with_verified_fields<F>(
746        version: i64,
747        title: String,
748        subtitle: Option<String>,
749        fields: Vec<F>,
750        payload_type: String,
751    ) -> Self
752    where
753        F: Into<SignablePayloadField> + DeterministicOrdering,
754    {
755        SignablePayload {
756            version: version.to_string(),
757            title,
758            subtitle,
759            payload_type,
760            fields: fields.into_iter().map(Into::into).collect(),
761        }
762    }
763
764    // Helper function that ensures all nested types in a complex field structure implement DeterministicOrdering
765    pub fn verify_field_deterministic_ordering(field: &SignablePayloadField) -> Result<(), String> {
766        // This function compile-time enforces that all nested types implement DeterministicOrdering
767        // by calling verify_deterministic_ordering on each component
768        match field {
769            SignablePayloadField::PreviewLayout { preview_layout, .. } => {
770                preview_layout.verify_deterministic_ordering()?;
771                if let Some(ref condensed) = preview_layout.condensed {
772                    condensed.verify_deterministic_ordering()?;
773                }
774                if let Some(ref expanded) = preview_layout.expanded {
775                    expanded.verify_deterministic_ordering()?;
776                }
777            }
778            SignablePayloadField::ListLayout { list_layout, .. } => {
779                list_layout.verify_deterministic_ordering()?;
780            }
781            _ => {}
782        }
783        field.verify_deterministic_ordering()
784    }
785
786    pub fn to_json(&self) -> Result<String, Box<dyn std::error::Error>> {
787        // First convert to a standard JSON value
788        let value = serde_json::to_value(self)?;
789
790        // Convert to a completely new object with alphabetically sorted keys
791        let sorted_value = sort_json_alphabetically(value);
792
793        // Serialize without pretty-printing and without escape HTML
794        let mut buf = Vec::new();
795        let formatter = serde_json::ser::CompactFormatter;
796        let mut ser = serde_json::Serializer::with_formatter(&mut buf, formatter);
797        sorted_value.serialize(&mut ser)?;
798
799        // Convert bytes to string
800        Ok(String::from_utf8(buf)?)
801    }
802
803    // Add this method for debugging
804    pub fn to_pretty_json(&self) -> Result<String, Box<dyn std::error::Error>> {
805        let value = serde_json::to_value(self)?;
806        let sorted_value = sort_json_alphabetically(value);
807        Ok(serde_json::to_string_pretty(&sorted_value)?)
808    }
809}
810
811// Helper function to recursively sort JSON by keys alphabetically
812fn sort_json_alphabetically(value: serde_json::Value) -> serde_json::Value {
813    match value {
814        serde_json::Value::Object(map) => {
815            // Create a BTreeMap (which is sorted by keys)
816            let mut sorted_map = std::collections::BTreeMap::new();
817
818            // Insert all entries, recursively sorting nested objects
819            for (key, val) in map {
820                sorted_map.insert(key, sort_json_alphabetically(val));
821            }
822
823            // Convert back to serde_json::Value
824            serde_json::Value::Object(serde_json::Map::from_iter(sorted_map))
825        }
826        serde_json::Value::Array(arr) => {
827            // Recursively sort array elements (if they are objects)
828            serde_json::Value::Array(arr.into_iter().map(sort_json_alphabetically).collect())
829        }
830        // Other value types (string, number, boolean, null) don't need sorting
831        other => other,
832    }
833}
834
835impl SignablePayload {
836    /// Validates that the payload only contains safe ASCII characters to prevent unicode confusion
837    /// This should be called before returning any SignablePayload to ensure consistent character safety
838    /// I understand that this might be overly cautious, but it's better to be safe at launch and incrementally open up unicode support later
839    pub fn validate_charset(&self) -> Result<(), VisualSignError> {
840        let json_str = self.to_json().map_err(|e| {
841            VisualSignError::SerializationError(format!("Failed to serialize for validation: {e}"))
842        })?;
843
844        // Check for unicode escapes
845        if json_str.contains("\\u") {
846            return Err(VisualSignError::ValidationError(
847                "Restricted Characters Detected".to_string(),
848            ));
849        }
850
851        // Use Rust's built-in ASCII validation
852        if !json_str.is_ascii() {
853            return Err(VisualSignError::ValidationError(
854                "Restricted Characters Detected".to_string(),
855            ));
856        }
857
858        // Additional validation for printable characters
859        for (i, ch) in json_str.char_indices() {
860            if !ch.is_ascii_graphic() && !ch.is_ascii_whitespace() {
861                return Err(VisualSignError::ValidationError(format!(
862                    "JSON output contains non-printable character '{}' (U+{:02X}) at position {}",
863                    ch.escape_default(),
864                    ch as u32,
865                    i
866                )));
867            }
868        }
869
870        Ok(())
871    }
872
873    /// Validates and returns the JSON string, ensuring charset safety
874    pub fn to_validated_json(&self) -> Result<String, VisualSignError> {
875        self.validate_charset()?;
876        self.to_json()
877            .map_err(|e| VisualSignError::SerializationError(format!("Serialization failed: {e}")))
878    }
879}
880
881#[cfg(test)]
882mod tests {
883    use super::*;
884    use pretty_assertions::assert_eq;
885    use serde_json::json;
886
887    #[test]
888    fn test_signable_payload_to_json() {
889        let fields = vec![
890            SignablePayloadField::Text {
891                common: SignablePayloadFieldCommon {
892                    fallback_text: "FallbackText1".to_string(),
893                    label: "Label1".to_string(),
894                },
895                text: SignablePayloadFieldText {
896                    text: "Text1".to_string(),
897                },
898            },
899            SignablePayloadField::Text {
900                common: SignablePayloadFieldCommon {
901                    fallback_text: "FallbackText2".to_string(),
902                    label: "Label2".to_string(),
903                },
904                text: SignablePayloadFieldText {
905                    text: "Text2".to_string(),
906                },
907            },
908        ];
909
910        let payload = SignablePayload::new(
911            1,
912            "Test Title".to_string(),
913            Some("Test Subtitle".to_string()),
914            fields,
915            "Test Payload Type".to_string(),
916        );
917
918        let json = payload.to_json().unwrap();
919        println!("{json}");
920    }
921
922    #[test]
923    fn test_eth_user_intent_equivalence() {
924        // this is a relatively lazy attempt to keep this consistent with the Go implementation at
925        let from_address = "0xYourFromAddress";
926
927        let fields = vec![
928            SignablePayloadField::TextV2 {
929                common: SignablePayloadFieldCommon {
930                    fallback_text: "Ethereum Regnet".to_string(),
931                    label: "Network".to_string(),
932                },
933                text_v2: SignablePayloadFieldTextV2 {
934                    text: "Ethereum Regnet".to_string(),
935                },
936            },
937            SignablePayloadField::AddressV2 {
938                common: SignablePayloadFieldCommon {
939                    fallback_text: from_address.to_string(),
940                    label: "From".to_string(),
941                },
942                address_v2: SignablePayloadFieldAddressV2 {
943                    address: from_address.to_string(),
944                    name: "".to_string(),
945                    memo: None,
946                    asset_label: "".to_string(),
947                    badge_text: None,
948                },
949            },
950            SignablePayloadField::AddressV2 {
951                common: SignablePayloadFieldCommon {
952                    fallback_text: "0xb06E442b696513d54B05b5De58494E902E6e08Cb".to_string(),
953                    label: "Contract Address".to_string(),
954                },
955                address_v2: SignablePayloadFieldAddressV2 {
956                    address: "0xb06E442b696513d54B05b5De58494E902E6e08Cb".to_string(),
957                    name: "".to_string(),
958                    memo: None,
959                    asset_label: "".to_string(),
960                    badge_text: None,
961                },
962            },
963            SignablePayloadField::TextV2 {
964                common: SignablePayloadFieldCommon {
965                    fallback_text: "0x00".to_string(),
966                    label: "Data".to_string(),
967                },
968                text_v2: SignablePayloadFieldTextV2 {
969                    text: "0x00".to_string(),
970                },
971            },
972            SignablePayloadField::AmountV2 {
973                common: SignablePayloadFieldCommon {
974                    fallback_text: "0 ETH_R".to_string(),
975                    label: "Value".to_string(),
976                },
977                amount_v2: SignablePayloadFieldAmountV2 {
978                    amount: "0".to_string(),
979                    abbreviation: Some("ETH_R".to_string()),
980                },
981            },
982            SignablePayloadField::AmountV2 {
983                common: SignablePayloadFieldCommon {
984                    fallback_text: "0.000000000000000004 ETH_R".to_string(),
985                    label: "Max Fee".to_string(),
986                },
987                amount_v2: SignablePayloadFieldAmountV2 {
988                    amount: "0.000000000000000004".to_string(),
989                    abbreviation: Some("ETH_R".to_string()),
990                },
991            },
992        ];
993
994        let payload =
995            SignablePayload::new(15, "Withdraw".to_string(), None, fields, "".to_string());
996
997        let json = payload.to_json().unwrap();
998        println!("{json}");
999
1000        let expected_json = json!({
1001            "Version": "15",
1002            "Title": "Withdraw",
1003            "Fields": [
1004                {
1005                    "FallbackText": "Ethereum Regnet",
1006                    "Type": "text_v2",
1007                    "Label": "Network",
1008                    "TextV2": {
1009                        "Text": "Ethereum Regnet"
1010                    }
1011                },
1012                {
1013                    "FallbackText": "0xYourFromAddress",
1014                    "Type": "address_v2",
1015                    "Label": "From",
1016                    "AddressV2": {
1017                        "Address": "0xYourFromAddress"
1018                    }
1019                },
1020                {
1021                    "FallbackText": "0xb06E442b696513d54B05b5De58494E902E6e08Cb",
1022                    "Type": "address_v2",
1023                    "Label": "Contract Address",
1024                    "AddressV2": {
1025                        "Address": "0xb06E442b696513d54B05b5De58494E902E6e08Cb"
1026                    }
1027                },
1028                {
1029                    "FallbackText": "0x00",
1030                    "Type": "text_v2",
1031                    "Label": "Data",
1032                    "TextV2": {
1033                        "Text": "0x00"
1034                    }
1035                },
1036                {
1037                    "FallbackText": "0 ETH_R",
1038                    "Type": "amount_v2",
1039                    "Label": "Value",
1040                    "AmountV2": {
1041                        "Amount": "0",
1042                        "Abbreviation": "ETH_R"
1043                    }
1044                },
1045                {
1046                    "FallbackText": "0.000000000000000004 ETH_R",
1047                    "Type": "amount_v2",
1048                    "Label": "Max Fee",
1049                    "AmountV2": {
1050                        "Amount": "0.000000000000000004",
1051                        "Abbreviation": "ETH_R"
1052                    }
1053                }
1054            ]
1055        });
1056
1057        let generated_json: Value = serde_json::from_str(&json).unwrap();
1058        assert_eq!(generated_json, expected_json);
1059    }
1060
1061    #[test]
1062    fn test_extensibility_with_new_field_type_requires_deterministic_ordering() {
1063        // This test demonstrates that new field types MUST implement DeterministicOrdering
1064        // to be used in contexts requiring deterministic serialization
1065
1066        // Define new field type structs (these would normally be added to the main code)
1067        #[derive(Serialize, Debug, Clone, PartialEq, Eq)]
1068        struct TestCurrencyField {
1069            #[serde(rename = "CurrencyCode")]
1070            currency_code: String,
1071            #[serde(rename = "Symbol")]
1072            symbol: String,
1073        }
1074
1075        // Create a test enum that extends SignablePayloadField with a new Currency variant
1076        #[derive(Debug, Clone, PartialEq, Eq)]
1077        enum ExtendedSignablePayloadField {
1078            // Include an existing variant to test alongside the new one
1079            TextV2 {
1080                common: SignablePayloadFieldCommon,
1081                text_v2: SignablePayloadFieldTextV2,
1082            },
1083            // New Currency variant - this shows how easy it is to add
1084            Currency {
1085                common: SignablePayloadFieldCommon,
1086                currency: TestCurrencyField,
1087            },
1088        }
1089
1090        // Implement Serialize for the extended enum using our macro
1091        impl Serialize for ExtendedSignablePayloadField {
1092            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1093            where
1094                S: Serializer,
1095            {
1096                let mut fields = std::collections::HashMap::new();
1097
1098                match self {
1099                    ExtendedSignablePayloadField::TextV2 { common, text_v2 } => {
1100                        serialize_field_variant!(fields, "text_v2", common, ("TextV2", text_v2));
1101                    }
1102                    // Adding the new Currency variant is just one line!
1103                    ExtendedSignablePayloadField::Currency { common, currency } => {
1104                        serialize_field_variant!(
1105                            fields,
1106                            "currency",
1107                            common,
1108                            ("Currency", currency)
1109                        );
1110                    }
1111                }
1112
1113                let sorted_map: std::collections::BTreeMap<String, serde_json::Value> =
1114                    fields.into_iter().collect();
1115                let mut map_ser = serializer.serialize_map(Some(sorted_map.len()))?;
1116                for (k, v) in sorted_map {
1117                    map_ser.serialize_entry(&k, &v)?;
1118                }
1119                map_ser.end()
1120            }
1121        }
1122
1123        // CRITICAL: To use this new type with deterministic ordering, we MUST implement the trait
1124        impl DeterministicOrdering for ExtendedSignablePayloadField {}
1125
1126        // This function requires DeterministicOrdering - it won't compile without the impl above
1127        fn require_deterministic<T: DeterministicOrdering>(field: &T) -> Result<(), String> {
1128            field.verify_deterministic_ordering()
1129        }
1130
1131        // Test the new Currency field type
1132        let currency_field = ExtendedSignablePayloadField::Currency {
1133            common: SignablePayloadFieldCommon {
1134                fallback_text: "USD ($)".to_string(),
1135                label: "Payment Currency".to_string(),
1136            },
1137            currency: TestCurrencyField {
1138                currency_code: "USD".to_string(),
1139                symbol: "$".to_string(),
1140            },
1141        };
1142
1143        // This line will compile because ExtendedSignablePayloadField implements DeterministicOrdering
1144        require_deterministic(&currency_field).unwrap();
1145
1146        let json = serde_json::to_string(&currency_field).unwrap();
1147        println!("New Currency Field JSON: {json}");
1148
1149        // Verify alphabetical ordering
1150        let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1151        if let serde_json::Value::Object(map) = value {
1152            let keys: Vec<_> = map.keys().cloned().collect();
1153            println!("Currency Field Keys in order: {keys:?}");
1154
1155            // Expected order: Currency, FallbackText, Label, Type
1156            assert_eq!(keys, vec!["Currency", "FallbackText", "Label", "Type"]);
1157        } else {
1158            panic!("Expected JSON object");
1159        }
1160
1161        // Test that TextV2 still works correctly alongside the new field
1162        let text_field = ExtendedSignablePayloadField::TextV2 {
1163            common: SignablePayloadFieldCommon {
1164                fallback_text: "Test Text".to_string(),
1165                label: "Test Label".to_string(),
1166            },
1167            text_v2: SignablePayloadFieldTextV2 {
1168                text: "Hello World".to_string(),
1169            },
1170        };
1171
1172        let json2 = serde_json::to_string(&text_field).unwrap();
1173        println!("TextV2 Field JSON: {json2}");
1174
1175        let value2: serde_json::Value = serde_json::from_str(&json2).unwrap();
1176        if let serde_json::Value::Object(map) = value2 {
1177            let keys: Vec<_> = map.keys().cloned().collect();
1178            println!("TextV2 Field Keys in order: {keys:?}");
1179
1180            // Expected order: FallbackText, Label, TextV2, Type
1181            assert_eq!(keys, vec!["FallbackText", "Label", "TextV2", "Type"]);
1182        } else {
1183            panic!("Expected JSON object");
1184        }
1185
1186        println!("✅ Successfully demonstrated adding new field type with automatic alphabetical ordering!");
1187        println!("✅ New field type MUST implement DeterministicOrdering to be used in deterministic contexts!");
1188    }
1189
1190    #[test]
1191    fn test_compile_time_error_without_deterministic_ordering() {
1192        // This test demonstrates that forgetting to implement DeterministicOrdering
1193        // will cause a compile-time error when trying to use the type in a deterministic context
1194
1195        // Define a BAD field type that uses default Serialize (no alphabetical ordering)
1196        #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
1197        struct BadFieldType {
1198            z_field: String, // Note: fields are named to be out of alphabetical order
1199            a_field: String,
1200            m_field: String,
1201        }
1202
1203        // Create an enum variant with the bad field type
1204        #[derive(Serialize, Debug, Clone, PartialEq, Eq)]
1205        enum BadSignablePayloadField {
1206            BadVariant {
1207                common: SignablePayloadFieldCommon,
1208                bad_field: BadFieldType,
1209            },
1210        }
1211
1212        // NOTE: We intentionally DO NOT implement DeterministicOrdering for BadSignablePayloadField
1213        // impl DeterministicOrdering for BadSignablePayloadField {} // MISSING!
1214
1215        // This function requires DeterministicOrdering - used to demonstrate compile-time checking
1216        // It's intentionally never called because calling it with BadSignablePayloadField would cause a compile error
1217        #[allow(dead_code)]
1218        fn process_field<T: DeterministicOrdering>(_field: &T) -> String {
1219            "processed".to_string()
1220        }
1221
1222        // Create an instance of the bad field
1223        let bad_field = BadSignablePayloadField::BadVariant {
1224            common: SignablePayloadFieldCommon {
1225                fallback_text: "bad".to_string(),
1226                label: "Bad Field".to_string(),
1227            },
1228            bad_field: BadFieldType {
1229                z_field: "z".to_string(),
1230                a_field: "a".to_string(),
1231                m_field: "m".to_string(),
1232            },
1233        };
1234
1235        // The following lines are COMMENTED OUT because they would cause a compile error:
1236        // process_field(&bad_field);  // COMPILE ERROR: BadSignablePayloadField doesn't implement DeterministicOrdering
1237
1238        // This demonstrates the compile-time safety:
1239        // 1. If you forget to implement DeterministicOrdering, you can't use the type where it's required
1240        // 2. The compiler will tell you exactly what's wrong
1241        // 3. You're forced to implement proper deterministic ordering before the code will compile
1242
1243        // Let's verify the bad field actually has non-deterministic ordering:
1244        let json = serde_json::to_string(&bad_field).unwrap();
1245        let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1246
1247        // Check that the fields are NOT alphabetically ordered (this is the problem we're preventing)
1248        if let serde_json::Value::Object(map) = value {
1249            if let Some(serde_json::Value::Object(bad_field_obj)) = map.get("bad_field") {
1250                let keys: Vec<_> = bad_field_obj.keys().cloned().collect();
1251                // Default serde ordering follows struct field order, not alphabetical
1252                // So we expect: ["z_field", "a_field", "m_field"] not ["a_field", "m_field", "z_field"]
1253                println!("Bad field keys (not alphabetical): {keys:?}");
1254                assert_ne!(
1255                    keys,
1256                    vec!["a_field", "m_field", "z_field"],
1257                    "Fields should NOT be alphabetically ordered without custom Serialize"
1258                );
1259            }
1260        }
1261
1262        println!("✅ Demonstrated that types without DeterministicOrdering can't be used in deterministic contexts!");
1263    }
1264
1265    #[test]
1266    fn test_new_field_type_workflow_with_deterministic_ordering() {
1267        // This test shows the complete workflow for adding a new field type with deterministic ordering
1268
1269        // Step 1: Define the new field type's data structure
1270        #[derive(Deserialize, Debug, Clone, PartialEq)]
1271        struct GeoLocationField {
1272            #[serde(rename = "Latitude")]
1273            latitude: f64,
1274            #[serde(rename = "Longitude")]
1275            longitude: f64,
1276            #[serde(rename = "Accuracy")]
1277            accuracy: Option<f64>,
1278        }
1279
1280        impl Serialize for GeoLocationField {
1281            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1282            where
1283                S: Serializer,
1284            {
1285                use serde::ser::SerializeMap;
1286
1287                let mut map = serializer.serialize_map(Some(3))?;
1288                if let Some(ref accuracy) = self.accuracy {
1289                    map.serialize_entry("Accuracy", accuracy)?;
1290                }
1291                map.serialize_entry("Latitude", &self.latitude)?;
1292                map.serialize_entry("Longitude", &self.longitude)?;
1293                map.end()
1294            }
1295        }
1296
1297        // Step 2: Create the new variant for SignablePayloadField
1298        #[derive(Debug, Clone, PartialEq)]
1299        enum NewFieldVariant {
1300            GeoLocation {
1301                common: SignablePayloadFieldCommon,
1302                location: GeoLocationField,
1303            },
1304        }
1305
1306        // Step 3: Implement custom Serialize with deterministic (alphabetical) ordering
1307        impl Serialize for NewFieldVariant {
1308            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1309            where
1310                S: Serializer,
1311            {
1312                match self {
1313                    NewFieldVariant::GeoLocation { common, location } => {
1314                        // Use BTreeMap to ensure alphabetical ordering
1315                        let mut map = std::collections::BTreeMap::new();
1316
1317                        // Add common fields
1318                        map.insert(
1319                            "FallbackText",
1320                            serde_json::to_value(&common.fallback_text).unwrap(),
1321                        );
1322                        map.insert("GeoLocation", serde_json::to_value(location).unwrap());
1323                        map.insert("Label", serde_json::to_value(&common.label).unwrap());
1324                        map.insert(
1325                            "Type",
1326                            serde_json::Value::String("geo_location".to_string()),
1327                        );
1328
1329                        // Serialize with guaranteed alphabetical ordering
1330                        let mut map_ser = serializer.serialize_map(Some(map.len()))?;
1331                        for (k, v) in map {
1332                            map_ser.serialize_entry(&k, &v)?;
1333                        }
1334                        map_ser.end()
1335                    }
1336                }
1337            }
1338        }
1339
1340        // Step 4: Implement DeterministicOrdering trait
1341        impl DeterministicOrdering for NewFieldVariant {}
1342
1343        // Step 5: Create a function that requires deterministic ordering (simulating real usage)
1344        fn add_to_payload<T: DeterministicOrdering>(field: T) -> Result<String, String> {
1345            // This function can only accept types with deterministic ordering
1346            field.verify_deterministic_ordering()?;
1347            Ok("Field added to payload".to_string())
1348        }
1349
1350        // Step 6: Test the new field type
1351        let geo_field = NewFieldVariant::GeoLocation {
1352            common: SignablePayloadFieldCommon {
1353                fallback_text: "37.7749° N, 122.4194° W".to_string(),
1354                label: "Location".to_string(),
1355            },
1356            location: GeoLocationField {
1357                latitude: 37.7749,
1358                longitude: -122.4194,
1359                accuracy: Some(10.0),
1360            },
1361        };
1362
1363        // This compiles and runs because NewFieldVariant implements DeterministicOrdering
1364        let result = add_to_payload(geo_field.clone());
1365        assert!(result.is_ok());
1366
1367        // Verify the JSON has alphabetical ordering
1368        let json = serde_json::to_string(&geo_field).unwrap();
1369        let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1370
1371        if let serde_json::Value::Object(map) = value {
1372            let keys: Vec<_> = map.keys().cloned().collect();
1373            assert_eq!(
1374                keys,
1375                vec!["FallbackText", "GeoLocation", "Label", "Type"],
1376                "Fields must be in alphabetical order"
1377            );
1378
1379            // Also check nested GeoLocation fields are alphabetical
1380            if let Some(serde_json::Value::Object(geo_obj)) = map.get("GeoLocation") {
1381                let geo_keys: Vec<_> = geo_obj.keys().cloned().collect();
1382                assert_eq!(
1383                    geo_keys,
1384                    vec!["Accuracy", "Latitude", "Longitude"],
1385                    "Nested fields must also be alphabetical"
1386                );
1387            }
1388        }
1389
1390        println!("✅ Complete workflow demonstrated:");
1391        println!("   1. Define new field type structure");
1392        println!("   2. Create enum variant");
1393        println!("   3. Implement custom Serialize with deterministic ordering");
1394        println!("   4. Implement DeterministicOrdering trait");
1395        println!("   5. Use in functions requiring deterministic ordering");
1396        println!("   6. Verify JSON output has correct ordering");
1397    }
1398
1399    #[test]
1400    fn test_multiple_new_field_types_extensibility() {
1401        // This test demonstrates adding multiple new field types at once
1402        // showing how the macro approach scales easily
1403
1404        #[derive(Serialize, Debug, Clone, PartialEq, Eq)]
1405        struct TestDateTimeField {
1406            #[serde(rename = "DateTime")]
1407            date_time: String,
1408            #[serde(rename = "Format")]
1409            format: String,
1410        }
1411
1412        #[derive(Serialize, Debug, Clone, PartialEq, Eq)]
1413        struct TestPercentageField {
1414            #[serde(rename = "Value")]
1415            value: String,
1416            #[serde(rename = "Precision")]
1417            precision: u32,
1418        }
1419
1420        #[derive(Serialize, Debug, Clone, PartialEq, Eq)]
1421        struct TestLocationField {
1422            #[serde(rename = "Latitude")]
1423            latitude: String, // Use string to avoid float comparison issues in tests
1424            #[serde(rename = "Longitude")]
1425            longitude: String,
1426            #[serde(rename = "Address")]
1427            address: String,
1428        }
1429
1430        // Extended enum with multiple new field types
1431        #[derive(Debug, Clone, PartialEq, Eq)]
1432        enum MultiExtendedSignablePayloadField {
1433            DateTime {
1434                common: SignablePayloadFieldCommon,
1435                date_time: TestDateTimeField,
1436            },
1437            Percentage {
1438                common: SignablePayloadFieldCommon,
1439                percentage: TestPercentageField,
1440            },
1441            Location {
1442                common: SignablePayloadFieldCommon,
1443                location: TestLocationField,
1444            },
1445        }
1446
1447        impl Serialize for MultiExtendedSignablePayloadField {
1448            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1449            where
1450                S: Serializer,
1451            {
1452                let mut fields = std::collections::HashMap::new();
1453
1454                match self {
1455                    // Each new field type is just one line using the macro!
1456                    MultiExtendedSignablePayloadField::DateTime { common, date_time } => {
1457                        serialize_field_variant!(
1458                            fields,
1459                            "date_time",
1460                            common,
1461                            ("DateTime", date_time)
1462                        );
1463                    }
1464                    MultiExtendedSignablePayloadField::Percentage { common, percentage } => {
1465                        serialize_field_variant!(
1466                            fields,
1467                            "percentage",
1468                            common,
1469                            ("Percentage", percentage)
1470                        );
1471                    }
1472                    MultiExtendedSignablePayloadField::Location { common, location } => {
1473                        serialize_field_variant!(
1474                            fields,
1475                            "location",
1476                            common,
1477                            ("Location", location)
1478                        );
1479                    }
1480                }
1481
1482                let sorted_map: std::collections::BTreeMap<String, serde_json::Value> =
1483                    fields.into_iter().collect();
1484                let mut map_ser = serializer.serialize_map(Some(sorted_map.len()))?;
1485                for (k, v) in sorted_map {
1486                    map_ser.serialize_entry(&k, &v)?;
1487                }
1488                map_ser.end()
1489            }
1490        }
1491
1492        // Test DateTime field
1493        let datetime_field = MultiExtendedSignablePayloadField::DateTime {
1494            common: SignablePayloadFieldCommon {
1495                fallback_text: "2024-01-15 14:30:00 UTC".to_string(),
1496                label: "Transaction Time".to_string(),
1497            },
1498            date_time: TestDateTimeField {
1499                date_time: "2024-01-15T14:30:00Z".to_string(),
1500                format: "ISO8601".to_string(),
1501            },
1502        };
1503
1504        let json = serde_json::to_string(&datetime_field).unwrap();
1505        let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1506        if let serde_json::Value::Object(map) = value {
1507            let keys: Vec<_> = map.keys().cloned().collect();
1508            // Expected order: DateTime, FallbackText, Label, Type
1509            assert_eq!(keys, vec!["DateTime", "FallbackText", "Label", "Type"]);
1510        }
1511
1512        // Test Percentage field
1513        let percentage_field = MultiExtendedSignablePayloadField::Percentage {
1514            common: SignablePayloadFieldCommon {
1515                fallback_text: "15.50%".to_string(),
1516                label: "Fee Rate".to_string(),
1517            },
1518            percentage: TestPercentageField {
1519                value: "15.50".to_string(),
1520                precision: 2,
1521            },
1522        };
1523
1524        let json2 = serde_json::to_string(&percentage_field).unwrap();
1525        let value2: serde_json::Value = serde_json::from_str(&json2).unwrap();
1526        if let serde_json::Value::Object(map) = value2 {
1527            let keys: Vec<_> = map.keys().cloned().collect();
1528            // Expected order: FallbackText, Label, Percentage, Type
1529            assert_eq!(keys, vec!["FallbackText", "Label", "Percentage", "Type"]);
1530        }
1531
1532        // Test Location field
1533        let location_field = MultiExtendedSignablePayloadField::Location {
1534            common: SignablePayloadFieldCommon {
1535                fallback_text: "New York, NY (40.7128, -74.0060)".to_string(),
1536                label: "Transaction Location".to_string(),
1537            },
1538            location: TestLocationField {
1539                latitude: "40.7128".to_string(),
1540                longitude: "-74.0060".to_string(),
1541                address: "New York, NY".to_string(),
1542            },
1543        };
1544
1545        let json3 = serde_json::to_string(&location_field).unwrap();
1546        let value3: serde_json::Value = serde_json::from_str(&json3).unwrap();
1547        if let serde_json::Value::Object(map) = value3 {
1548            let keys: Vec<_> = map.keys().cloned().collect();
1549            // Expected order: FallbackText, Label, Location, Type
1550            assert_eq!(keys, vec!["FallbackText", "Label", "Location", "Type"]);
1551        }
1552
1553        println!("✅ Successfully demonstrated adding multiple new field types easily!");
1554        println!("   - DateTime field: automatic alphabetical ordering");
1555        println!("   - Percentage field: automatic alphabetical ordering");
1556        println!("   - Location field: automatic alphabetical ordering");
1557        println!("   - Each new type required only ONE line of macro code!");
1558    }
1559
1560    #[test]
1561    fn test_field_completeness_verification() {
1562        // This test demonstrates that the new approach catches missing or incorrect field serialization
1563
1564        // Create a test enum with intentionally incomplete serialization to show error detection
1565        #[derive(Debug, Clone, PartialEq, Eq)]
1566        enum IncompleteTestField {
1567            TestVariant {
1568                common: SignablePayloadFieldCommon,
1569                test_data: String,
1570            },
1571        }
1572
1573        // Implement trait with MISSING field on purpose
1574        impl FieldSerializer for IncompleteTestField {
1575            fn serialize_to_map(
1576                &self,
1577            ) -> Result<std::collections::BTreeMap<String, serde_json::Value>, serde_json::Error>
1578            {
1579                let mut fields = std::collections::HashMap::new();
1580                match self {
1581                    IncompleteTestField::TestVariant {
1582                        common,
1583                        test_data: _,
1584                    } => {
1585                        // Intentionally FORGET to serialize test_data to demonstrate detection
1586                        fields.insert(
1587                            "FallbackText".to_string(),
1588                            serde_json::to_value(&common.fallback_text).unwrap(),
1589                        );
1590                        fields.insert(
1591                            "Label".to_string(),
1592                            serde_json::to_value(&common.label).unwrap(),
1593                        );
1594                        fields.insert(
1595                            "Type".to_string(),
1596                            serde_json::Value::String("test".to_string()),
1597                        );
1598                        // Missing: "TestData" field!
1599                    }
1600                }
1601                Ok(fields.into_iter().collect())
1602            }
1603
1604            fn get_expected_fields(&self) -> Vec<&'static str> {
1605                vec!["FallbackText", "Label", "TestData", "Type"] // Expects TestData but we didn't serialize it
1606            }
1607        }
1608
1609        // Implement Serialize using the verification logic
1610        impl Serialize for IncompleteTestField {
1611            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1612            where
1613                S: Serializer,
1614            {
1615                let sorted_map = self.serialize_to_map().map_err(serde::ser::Error::custom)?;
1616                let expected_fields = self.get_expected_fields();
1617                let actual_fields: Vec<_> = sorted_map.keys().map(|s| s.as_str()).collect();
1618
1619                // Check for missing fields
1620                for expected in &expected_fields {
1621                    if !actual_fields.contains(expected) {
1622                        return Err(serde::ser::Error::custom(format!(
1623                            "Missing expected field '{expected}'. Expected: {expected_fields:?}, Actual: {actual_fields:?}",
1624                        )));
1625                    }
1626                }
1627
1628                let mut map_ser = serializer.serialize_map(Some(sorted_map.len()))?;
1629                for (k, v) in sorted_map {
1630                    map_ser.serialize_entry(&k, &v)?;
1631                }
1632                map_ser.end()
1633            }
1634        }
1635
1636        // Test that serialization fails when fields are missing
1637        let incomplete_field = IncompleteTestField::TestVariant {
1638            common: SignablePayloadFieldCommon {
1639                fallback_text: "Test".to_string(),
1640                label: "Test Label".to_string(),
1641            },
1642            test_data: "This should be serialized but isn't".to_string(),
1643        };
1644
1645        let result = serde_json::to_string(&incomplete_field);
1646
1647        // This should FAIL because we forgot to serialize TestData
1648        assert!(
1649            result.is_err(),
1650            "Expected serialization to fail due to missing TestData field"
1651        );
1652
1653        let error_msg = result.unwrap_err().to_string();
1654        assert!(
1655            error_msg.contains("Missing expected field 'TestData'"),
1656            "Error should mention missing TestData field, got: {error_msg}",
1657        );
1658
1659        println!("✅ Successfully caught missing field serialization!");
1660        println!("   Error: {error_msg}");
1661    }
1662
1663    #[test]
1664    fn test_field_completeness_verification_with_correct_implementation() {
1665        // This test shows a CORRECT implementation that passes verification
1666
1667        #[derive(Serialize, Debug, Clone, PartialEq, Eq)]
1668        struct TestDataStruct {
1669            #[serde(rename = "Data")]
1670            data: String,
1671        }
1672
1673        #[derive(Debug, Clone, PartialEq, Eq)]
1674        enum CompleteTestField {
1675            TestVariant {
1676                common: SignablePayloadFieldCommon,
1677                test_data: TestDataStruct,
1678            },
1679        }
1680
1681        // Implement trait with ALL required fields
1682        impl FieldSerializer for CompleteTestField {
1683            fn serialize_to_map(
1684                &self,
1685            ) -> Result<std::collections::BTreeMap<String, serde_json::Value>, serde_json::Error>
1686            {
1687                let mut fields = std::collections::HashMap::new();
1688                match self {
1689                    CompleteTestField::TestVariant { common, test_data } => {
1690                        // Correctly serialize ALL fields
1691                        serialize_field_variant!(fields, "test", common, ("TestData", test_data));
1692                    }
1693                }
1694                Ok(fields.into_iter().collect())
1695            }
1696
1697            fn get_expected_fields(&self) -> Vec<&'static str> {
1698                vec!["FallbackText", "Label", "TestData", "Type"] // Matches what we actually serialize
1699            }
1700        }
1701
1702        // Implement Serialize using the verification logic
1703        impl Serialize for CompleteTestField {
1704            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1705            where
1706                S: Serializer,
1707            {
1708                let sorted_map = self.serialize_to_map().map_err(serde::ser::Error::custom)?;
1709                let expected_fields = self.get_expected_fields();
1710                let actual_fields: Vec<_> = sorted_map.keys().map(|s| s.as_str()).collect();
1711
1712                for expected in &expected_fields {
1713                    if !actual_fields.contains(expected) {
1714                        return Err(serde::ser::Error::custom(format!(
1715                            "Missing expected field '{expected}'. Expected: {expected_fields:?}, Actual: {actual_fields:?}",
1716                        )));
1717                    }
1718                }
1719
1720                let mut map_ser = serializer.serialize_map(Some(sorted_map.len()))?;
1721                for (k, v) in sorted_map {
1722                    map_ser.serialize_entry(&k, &v)?;
1723                }
1724                map_ser.end()
1725            }
1726        }
1727
1728        // Test that serialization succeeds when all fields are present
1729        let complete_field = CompleteTestField::TestVariant {
1730            common: SignablePayloadFieldCommon {
1731                fallback_text: "Test".to_string(),
1732                label: "Test Label".to_string(),
1733            },
1734            test_data: TestDataStruct {
1735                data: "This is properly serialized".to_string(),
1736            },
1737        };
1738
1739        let result = serde_json::to_string(&complete_field);
1740        assert!(
1741            result.is_ok(),
1742            "Expected serialization to succeed: {result:?}",
1743        );
1744
1745        let json = result.unwrap();
1746        let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1747        if let serde_json::Value::Object(map) = value {
1748            let keys: Vec<_> = map.keys().cloned().collect();
1749            // Verify alphabetical ordering: FallbackText, Label, TestData, Type
1750            assert_eq!(keys, vec!["FallbackText", "Label", "TestData", "Type"]);
1751        }
1752
1753        println!("✅ Correctly implemented field serialization passed verification!");
1754        println!("   JSON: {json}");
1755    }
1756
1757    #[test]
1758    fn test_original_signable_payload_field_verification() {
1759        // Test that the original SignablePayloadField enum passes all verification
1760        // This confirms our refactoring maintains correctness and adds verification
1761
1762        let test_fields = vec![
1763            // TextV2
1764            SignablePayloadField::TextV2 {
1765                common: SignablePayloadFieldCommon {
1766                    fallback_text: "Test Text".to_string(),
1767                    label: "Text Field".to_string(),
1768                },
1769                text_v2: SignablePayloadFieldTextV2 {
1770                    text: "Hello World".to_string(),
1771                },
1772            },
1773            // AmountV2
1774            SignablePayloadField::AmountV2 {
1775                common: SignablePayloadFieldCommon {
1776                    fallback_text: "100 USD".to_string(),
1777                    label: "Amount Field".to_string(),
1778                },
1779                amount_v2: SignablePayloadFieldAmountV2 {
1780                    amount: "100".to_string(),
1781                    abbreviation: Some("USD".to_string()),
1782                },
1783            },
1784            // Address
1785            SignablePayloadField::Address {
1786                common: SignablePayloadFieldCommon {
1787                    fallback_text: "0x123...abc".to_string(),
1788                    label: "Address Field".to_string(),
1789                },
1790                address: SignablePayloadFieldAddress {
1791                    address: "0x123abc".to_string(),
1792                    name: "Test Address".to_string(),
1793                },
1794            },
1795        ];
1796
1797        for (i, field) in test_fields.iter().enumerate() {
1798            // Verify each field type serializes correctly with verification
1799            let result = serde_json::to_string(field);
1800            assert!(
1801                result.is_ok(),
1802                "Field {i} should serialize successfully: {result:?}",
1803            );
1804
1805            let json = result.unwrap();
1806            let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1807
1808            // Verify alphabetical ordering
1809            if let serde_json::Value::Object(map) = value {
1810                let keys: Vec<_> = map.keys().cloned().collect();
1811                let mut expected_keys = keys.clone();
1812                expected_keys.sort();
1813
1814                assert_eq!(
1815                    keys, expected_keys,
1816                    "Fields should be in alphabetical order for field {}: got {:?}",
1817                    i, keys
1818                );
1819
1820                // Verify all expected fields are present
1821                let expected_field_count = field.get_expected_fields().len();
1822                assert_eq!(
1823                    keys.len(),
1824                    expected_field_count,
1825                    "Field {} should have exactly {} fields: {:?}",
1826                    i,
1827                    expected_field_count,
1828                    keys
1829                );
1830
1831                println!(
1832                    "✅ Field {} verified: {} fields in alphabetical order: {:?}",
1833                    i,
1834                    keys.len(),
1835                    keys
1836                );
1837            }
1838        }
1839
1840        println!(
1841            "✅ All SignablePayloadField variants pass verification with alphabetical ordering!"
1842        );
1843    }
1844
1845    #[test]
1846    fn test_field_alphabetical_ordering() {
1847        // Test that fields within SignablePayloadField are ordered alphabetically
1848
1849        // Test TextV2 field
1850        let field = SignablePayloadField::TextV2 {
1851            common: SignablePayloadFieldCommon {
1852                fallback_text: "Test Fallback".to_string(),
1853                label: "Test Label".to_string(),
1854            },
1855            text_v2: SignablePayloadFieldTextV2 {
1856                text: "Test Text".to_string(),
1857            },
1858        };
1859
1860        let json = serde_json::to_string(&field).unwrap();
1861        println!("TextV2 Field JSON: {json}");
1862
1863        // Parse back to check field order
1864        let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1865        if let serde_json::Value::Object(map) = value {
1866            let keys: Vec<_> = map.keys().cloned().collect();
1867            println!("Keys in order: {keys:?}");
1868
1869            // Expected order: FallbackText, Label, TextV2, Type
1870            assert_eq!(keys, vec!["FallbackText", "Label", "TextV2", "Type"]);
1871        } else {
1872            panic!("Expected JSON object");
1873        }
1874
1875        // Test AmountV2 field
1876        let field2 = SignablePayloadField::AmountV2 {
1877            common: SignablePayloadFieldCommon {
1878                fallback_text: "0 ETH".to_string(),
1879                label: "Value".to_string(),
1880            },
1881            amount_v2: SignablePayloadFieldAmountV2 {
1882                amount: "0".to_string(),
1883                abbreviation: Some("ETH".to_string()),
1884            },
1885        };
1886
1887        let json2 = serde_json::to_string(&field2).unwrap();
1888        println!("AmountV2 Field JSON: {json2}");
1889
1890        let value2: serde_json::Value = serde_json::from_str(&json2).unwrap();
1891        if let serde_json::Value::Object(map) = value2 {
1892            let keys: Vec<_> = map.keys().cloned().collect();
1893            println!("Keys in order: {keys:?}");
1894
1895            // Expected order: AmountV2, FallbackText, Label, Type
1896            assert_eq!(keys, vec!["AmountV2", "FallbackText", "Label", "Type"]);
1897        } else {
1898            panic!("Expected JSON object");
1899        }
1900    }
1901
1902    #[test]
1903    fn test_alphabetical_sorting() {
1904        let payload = SignablePayload::new(
1905            1,
1906            "Z_Title".to_string(),          // Starts with Z
1907            Some("A_Subtitle".to_string()), // Starts with A
1908            vec![],                         // Empty fields
1909            "M_PayloadType".to_string(),    // Starts with M
1910        );
1911
1912        let json = payload.to_json().unwrap();
1913        assert_sorted_alphabetically(json);
1914
1915        // Lets try using serde_json to serialize the payload and ensure that ordering is still same
1916        // this is more to ensure that engineer isn't changing the order of fields
1917        let serde_default_json = serde_json::to_string(&payload).unwrap();
1918        assert_sorted_alphabetically(serde_default_json);
1919    }
1920
1921    #[test]
1922    fn test_compile_time_deterministic_ordering_enforcement() {
1923        // This test verifies that our key types implement DeterministicOrdering trait
1924        // If any type is missing the implementation, this will fail at compile time
1925
1926        fn assert_deterministic_ordering<T: DeterministicOrdering>(_: &T) {}
1927
1928        // Test all leaf types that make up SignablePayloadField values
1929        let text_v2 = SignablePayloadFieldTextV2 {
1930            text: "Value".to_string(),
1931        };
1932        assert_deterministic_ordering(&text_v2);
1933
1934        let address = SignablePayloadFieldAddress {
1935            address: "0x123".to_string(),
1936            name: "Test".to_string(),
1937        };
1938        assert_deterministic_ordering(&address);
1939
1940        let amount_v2 = SignablePayloadFieldAmountV2 {
1941            amount: "100".to_string(),
1942            abbreviation: Some("USD".to_string()),
1943        };
1944        assert_deterministic_ordering(&amount_v2);
1945
1946        // Test layout types
1947        let preview_layout = SignablePayloadFieldPreviewLayout {
1948            title: Some(text_v2.clone()),
1949            subtitle: None,
1950            condensed: Some(SignablePayloadFieldListLayout { fields: vec![] }),
1951            expanded: None,
1952        };
1953        assert_deterministic_ordering(&preview_layout);
1954
1955        let list_layout = SignablePayloadFieldListLayout { fields: vec![] };
1956        assert_deterministic_ordering(&list_layout);
1957
1958        // Test annotation types
1959        let static_annotation = SignablePayloadFieldStaticAnnotation {
1960            text: "Note".to_string(),
1961        };
1962        assert_deterministic_ordering(&static_annotation);
1963
1964        let dynamic_annotation = SignablePayloadFieldDynamicAnnotation {
1965            field_type: "type".to_string(),
1966            id: "id".to_string(),
1967            params: vec![],
1968        };
1969        assert_deterministic_ordering(&dynamic_annotation);
1970
1971        // Test SignablePayloadField
1972        let field = SignablePayloadField::TextV2 {
1973            common: SignablePayloadFieldCommon {
1974                fallback_text: "Test".to_string(),
1975                label: "Label".to_string(),
1976            },
1977            text_v2: text_v2.clone(),
1978        };
1979        assert_deterministic_ordering(&field);
1980
1981        // Test AnnotatedPayloadField
1982        let annotated = AnnotatedPayloadField {
1983            signable_payload_field: field.clone(),
1984            static_annotation: Some(static_annotation),
1985            dynamic_annotation: Some(dynamic_annotation),
1986        };
1987        assert_deterministic_ordering(&annotated);
1988
1989        // Test SignablePayload
1990        let payload = SignablePayload::new(
1991            1,
1992            "Title".to_string(),
1993            None,
1994            vec![field.clone()],
1995            "Type".to_string(),
1996        );
1997        assert_deterministic_ordering(&payload);
1998
1999        // Verify runtime deterministic ordering for complex nested structure
2000        let complex_field = SignablePayloadField::PreviewLayout {
2001            common: SignablePayloadFieldCommon {
2002                fallback_text: "Preview".to_string(),
2003                label: "Complex".to_string(),
2004            },
2005            preview_layout,
2006        };
2007        assert_deterministic_ordering(&complex_field);
2008        assert!(complex_field.verify_deterministic_ordering().is_ok());
2009    }
2010
2011    #[test]
2012    fn test_annotated_payload_field_alphabetical_ordering() {
2013        // Test that AnnotatedPayloadField maintains alphabetical ordering of all its fields
2014        // including when SignablePayloadField is flattened
2015
2016        // Test 1: AnnotatedPayloadField with all annotations
2017        let annotated_field = AnnotatedPayloadField {
2018            signable_payload_field: SignablePayloadField::AmountV2 {
2019                common: SignablePayloadFieldCommon {
2020                    fallback_text: "100 USD".to_string(),
2021                    label: "Amount".to_string(),
2022                },
2023                amount_v2: SignablePayloadFieldAmountV2 {
2024                    amount: "100".to_string(),
2025                    abbreviation: Some("USD".to_string()),
2026                },
2027            },
2028            static_annotation: Some(SignablePayloadFieldStaticAnnotation {
2029                text: "Note: This is a test".to_string(),
2030            }),
2031            dynamic_annotation: Some(SignablePayloadFieldDynamicAnnotation {
2032                field_type: "test_type".to_string(),
2033                id: "test_id".to_string(),
2034                params: vec!["param1".to_string()],
2035            }),
2036        };
2037
2038        let json = serde_json::to_string(&annotated_field).unwrap();
2039        let value: serde_json::Value = serde_json::from_str(&json).unwrap();
2040
2041        assert_json_keys_alphabetical(&value, "AnnotatedPayloadField with all annotations");
2042
2043        // Test 2: AnnotatedPayloadField without annotations
2044        let annotated_field_no_annotations = AnnotatedPayloadField {
2045            signable_payload_field: SignablePayloadField::TextV2 {
2046                common: SignablePayloadFieldCommon {
2047                    fallback_text: "Test Text".to_string(),
2048                    label: "Test Label".to_string(),
2049                },
2050                text_v2: SignablePayloadFieldTextV2 {
2051                    text: "Hello World".to_string(),
2052                },
2053            },
2054            static_annotation: None,
2055            dynamic_annotation: None,
2056        };
2057
2058        let json2 = serde_json::to_string(&annotated_field_no_annotations).unwrap();
2059        let value2: serde_json::Value = serde_json::from_str(&json2).unwrap();
2060
2061        assert_json_keys_alphabetical(&value2, "AnnotatedPayloadField without annotations");
2062
2063        // Test 3: AnnotatedPayloadField with only static annotation
2064        let annotated_field_static_only = AnnotatedPayloadField {
2065            signable_payload_field: SignablePayloadField::Address {
2066                common: SignablePayloadFieldCommon {
2067                    fallback_text: "0x123".to_string(),
2068                    label: "Address".to_string(),
2069                },
2070                address: SignablePayloadFieldAddress {
2071                    address: "0x123456".to_string(),
2072                    name: "Test Address".to_string(),
2073                },
2074            },
2075            static_annotation: Some(SignablePayloadFieldStaticAnnotation {
2076                text: "Static annotation only".to_string(),
2077            }),
2078            dynamic_annotation: None,
2079        };
2080
2081        let json3 = serde_json::to_string(&annotated_field_static_only).unwrap();
2082        let value3: serde_json::Value = serde_json::from_str(&json3).unwrap();
2083
2084        assert_json_keys_alphabetical(&value3, "AnnotatedPayloadField with static annotation only");
2085    }
2086
2087    #[test]
2088    fn test_preview_layout_field_alphabetical_ordering() {
2089        // Test that SignablePayloadFieldPreviewLayout maintains alphabetical ordering of its fields
2090
2091        // Create a PreviewLayout with all optional fields populated
2092        let preview_layout = SignablePayloadFieldPreviewLayout {
2093            title: Some(SignablePayloadFieldTextV2 {
2094                text: "Test Title".to_string(),
2095            }),
2096            subtitle: Some(SignablePayloadFieldTextV2 {
2097                text: "Test Subtitle".to_string(),
2098            }),
2099            condensed: Some(SignablePayloadFieldListLayout { fields: vec![] }),
2100            expanded: Some(SignablePayloadFieldListLayout { fields: vec![] }),
2101        };
2102
2103        // Serialize and check ordering
2104        let json = serde_json::to_string(&preview_layout).unwrap();
2105        let value: serde_json::Value = serde_json::from_str(&json).unwrap();
2106
2107        if let serde_json::Value::Object(map) = value {
2108            let keys: Vec<_> = map.keys().cloned().collect();
2109
2110            // Expected alphabetical order: Condensed, Expanded, Subtitle, Title
2111            assert_eq!(
2112                keys,
2113                vec!["Condensed", "Expanded", "Subtitle", "Title"],
2114                "PreviewLayout fields should be in alphabetical order"
2115            );
2116        }
2117
2118        // Test with only some fields populated
2119        let partial_preview = SignablePayloadFieldPreviewLayout {
2120            title: Some(SignablePayloadFieldTextV2 {
2121                text: "Title Only".to_string(),
2122            }),
2123            subtitle: None,
2124            condensed: Some(SignablePayloadFieldListLayout { fields: vec![] }),
2125            expanded: None,
2126        };
2127
2128        let json2 = serde_json::to_string(&partial_preview).unwrap();
2129        let value2: serde_json::Value = serde_json::from_str(&json2).unwrap();
2130
2131        if let serde_json::Value::Object(map) = value2 {
2132            let keys: Vec<_> = map.keys().cloned().collect();
2133
2134            // Should only have Condensed and Title, still alphabetical
2135            assert_eq!(
2136                keys,
2137                vec!["Condensed", "Title"],
2138                "Partial PreviewLayout fields should be in alphabetical order"
2139            );
2140        }
2141
2142        // Test PreviewLayout field within SignablePayloadField
2143        let preview_field = SignablePayloadField::PreviewLayout {
2144            common: SignablePayloadFieldCommon {
2145                fallback_text: "Preview".to_string(),
2146                label: "Preview Field".to_string(),
2147            },
2148            preview_layout,
2149        };
2150
2151        let json3 = serde_json::to_string(&preview_field).unwrap();
2152        let value3: serde_json::Value = serde_json::from_str(&json3).unwrap();
2153
2154        // Check the outer field is alphabetical
2155        if let serde_json::Value::Object(map) = &value3 {
2156            let keys: Vec<_> = map.keys().cloned().collect();
2157            let mut expected_keys = keys.clone();
2158            expected_keys.sort();
2159            assert_eq!(
2160                keys, expected_keys,
2161                "SignablePayloadField with PreviewLayout should have alphabetical keys"
2162            );
2163
2164            // Check the inner PreviewLayout is alphabetical
2165            if let Some(serde_json::Value::Object(preview_map)) = map.get("PreviewLayout") {
2166                let inner_keys: Vec<_> = preview_map.keys().cloned().collect();
2167                assert_eq!(
2168                    inner_keys,
2169                    vec!["Condensed", "Expanded", "Subtitle", "Title"],
2170                    "Inner PreviewLayout should have alphabetical field ordering"
2171                );
2172            }
2173        }
2174    }
2175
2176    #[test]
2177    fn test_annotated_payload_field_in_condensed_recursive_ordering() {
2178        // Test that AnnotatedPayloadField maintains alphabetical ordering when nested
2179        // within Condensed/Expanded sections of PreviewLayout
2180
2181        let condensed_fields = vec![
2182            AnnotatedPayloadField {
2183                signable_payload_field: SignablePayloadField::AmountV2 {
2184                    common: SignablePayloadFieldCommon {
2185                        fallback_text: "10 SOL".to_string(),
2186                        label: "Transfer Amount".to_string(),
2187                    },
2188                    amount_v2: SignablePayloadFieldAmountV2 {
2189                        amount: "10000000000".to_string(),
2190                        abbreviation: Some("lamports".to_string()),
2191                    },
2192                },
2193                static_annotation: Some(SignablePayloadFieldStaticAnnotation {
2194                    text: "Fee warning".to_string(),
2195                }),
2196                dynamic_annotation: None,
2197            },
2198            AnnotatedPayloadField {
2199                signable_payload_field: SignablePayloadField::TextV2 {
2200                    common: SignablePayloadFieldCommon {
2201                        fallback_text: "Test Text".to_string(),
2202                        label: "Test Label".to_string(),
2203                    },
2204                    text_v2: SignablePayloadFieldTextV2 {
2205                        text: "Hello World".to_string(),
2206                    },
2207                },
2208                static_annotation: None,
2209                dynamic_annotation: Some(SignablePayloadFieldDynamicAnnotation {
2210                    field_type: "dynamic".to_string(),
2211                    id: "id123".to_string(),
2212                    params: vec![],
2213                }),
2214            },
2215        ];
2216
2217        let preview_field = SignablePayloadField::PreviewLayout {
2218            common: SignablePayloadFieldCommon {
2219                fallback_text: "Preview".to_string(),
2220                label: "Preview Field".to_string(),
2221            },
2222            preview_layout: SignablePayloadFieldPreviewLayout {
2223                title: Some(SignablePayloadFieldTextV2 {
2224                    text: "Title Text".to_string(),
2225                }),
2226                subtitle: None,
2227                condensed: Some(SignablePayloadFieldListLayout {
2228                    fields: condensed_fields.clone(),
2229                }),
2230                expanded: Some(SignablePayloadFieldListLayout {
2231                    fields: vec![AnnotatedPayloadField {
2232                        signable_payload_field: SignablePayloadField::Number {
2233                            common: SignablePayloadFieldCommon {
2234                                fallback_text: "42".to_string(),
2235                                label: "Number Field".to_string(),
2236                            },
2237                            number: SignablePayloadFieldNumber {
2238                                number: "42".to_string(),
2239                            },
2240                        },
2241                        static_annotation: None,
2242                        dynamic_annotation: None,
2243                    }],
2244                }),
2245            },
2246        };
2247
2248        let json = serde_json::to_string(&preview_field).unwrap();
2249        let value: serde_json::Value = serde_json::from_str(&json).unwrap();
2250
2251        // Recursively check all JSON objects for alphabetical key ordering
2252        assert_json_recursive_alphabetical(&value, "");
2253    }
2254
2255    // Helper function to check if JSON object keys are alphabetically ordered
2256    fn assert_json_keys_alphabetical(value: &serde_json::Value, context: &str) {
2257        if let serde_json::Value::Object(map) = value {
2258            let keys: Vec<_> = map.keys().cloned().collect();
2259            let mut expected_keys = keys.clone();
2260            expected_keys.sort();
2261
2262            assert_eq!(
2263                keys, expected_keys,
2264                "{} should have alphabetically ordered keys. Got: {:?}, Expected: {:?}",
2265                context, keys, expected_keys
2266            );
2267        }
2268    }
2269
2270    // Helper function to recursively check all JSON objects for alphabetical key ordering
2271    fn assert_json_recursive_alphabetical(value: &serde_json::Value, path: &str) {
2272        match value {
2273            serde_json::Value::Object(map) => {
2274                // Check current object's keys
2275                let keys: Vec<_> = map.keys().cloned().collect();
2276                let mut expected_keys = keys.clone();
2277                expected_keys.sort();
2278
2279                assert_eq!(
2280                    keys, expected_keys,
2281                    "Object at path '{}' should have alphabetically ordered keys. Got: {:?}, Expected: {:?}",
2282                    if path.is_empty() { "root" } else { path },
2283                    keys, expected_keys
2284                );
2285
2286                // Recursively check nested values
2287                for (key, nested_value) in map {
2288                    let new_path = if path.is_empty() {
2289                        key.clone()
2290                    } else {
2291                        format!("{path}.{key}")
2292                    };
2293                    assert_json_recursive_alphabetical(nested_value, &new_path);
2294                }
2295            }
2296            serde_json::Value::Array(arr) => {
2297                // Recursively check array elements
2298                for (i, item) in arr.iter().enumerate() {
2299                    let new_path = format!("{path}[{i}]");
2300                    assert_json_recursive_alphabetical(item, &new_path);
2301                }
2302            }
2303            _ => {} // Leaf nodes (strings, numbers, etc.) don't need checking
2304        }
2305    }
2306
2307    #[test]
2308    fn test_deterministic_ordering_consistency() {
2309        // This test verifies that ALL types in the system implement DeterministicOrdering consistently
2310        // and that complex nested structures maintain deterministic ordering throughout
2311
2312        // Create a complex nested structure using all field types
2313        let preview_field = SignablePayloadField::PreviewLayout {
2314            common: SignablePayloadFieldCommon {
2315                fallback_text: "Complex Preview".to_string(),
2316                label: "Preview".to_string(),
2317            },
2318            preview_layout: SignablePayloadFieldPreviewLayout {
2319                title: Some(SignablePayloadFieldTextV2 {
2320                    text: "Title".to_string(),
2321                }),
2322                subtitle: Some(SignablePayloadFieldTextV2 {
2323                    text: "Subtitle".to_string(),
2324                }),
2325                condensed: Some(SignablePayloadFieldListLayout {
2326                    fields: vec![AnnotatedPayloadField {
2327                        signable_payload_field: SignablePayloadField::AmountV2 {
2328                            common: SignablePayloadFieldCommon {
2329                                fallback_text: "100 USD".to_string(),
2330                                label: "Amount".to_string(),
2331                            },
2332                            amount_v2: SignablePayloadFieldAmountV2 {
2333                                amount: "100".to_string(),
2334                                abbreviation: Some("USD".to_string()),
2335                            },
2336                        },
2337                        static_annotation: Some(SignablePayloadFieldStaticAnnotation {
2338                            text: "Fee".to_string(),
2339                        }),
2340                        dynamic_annotation: None,
2341                    }],
2342                }),
2343                expanded: Some(SignablePayloadFieldListLayout {
2344                    fields: vec![AnnotatedPayloadField {
2345                        signable_payload_field: SignablePayloadField::AddressV2 {
2346                            common: SignablePayloadFieldCommon {
2347                                fallback_text: "0x123".to_string(),
2348                                label: "Address".to_string(),
2349                            },
2350                            address_v2: SignablePayloadFieldAddressV2 {
2351                                address: "0x123456".to_string(),
2352                                name: "".to_string(),
2353                                memo: None,
2354                                asset_label: "".to_string(),
2355                                badge_text: Some("Verified".to_string()),
2356                            },
2357                        },
2358                        static_annotation: None,
2359                        dynamic_annotation: Some(SignablePayloadFieldDynamicAnnotation {
2360                            field_type: "address".to_string(),
2361                            id: "addr_1".to_string(),
2362                            params: vec!["param1".to_string()],
2363                        }),
2364                    }],
2365                }),
2366            },
2367        };
2368
2369        // Verify the field and all its nested components implement DeterministicOrdering
2370        let result = SignablePayload::verify_field_deterministic_ordering(&preview_field);
2371        assert!(
2372            result.is_ok(),
2373            "Complex field should verify deterministic ordering"
2374        );
2375
2376        // Serialize and verify the entire structure maintains alphabetical ordering
2377        let json = serde_json::to_string(&preview_field).unwrap();
2378        let value: serde_json::Value = serde_json::from_str(&json).unwrap();
2379
2380        // Use recursive verification to ensure ALL nested objects are alphabetically ordered
2381        assert_json_recursive_alphabetical(&value, "");
2382
2383        // Create a payload with the complex field
2384        let payload = SignablePayload::new(
2385            1,
2386            "Test Payload".to_string(),
2387            Some("With complex fields".to_string()),
2388            vec![preview_field],
2389            "test".to_string(),
2390        );
2391
2392        // Verify the entire payload maintains deterministic ordering
2393        assert!(payload.verify_deterministic_ordering().is_ok());
2394
2395        // Compile-time verification that all types can be used in deterministic contexts
2396        fn require_deterministic<T: DeterministicOrdering>(_: &T) {}
2397
2398        // These will all compile because ALL types implement DeterministicOrdering
2399        require_deterministic(&SignablePayloadFieldTextV2 {
2400            text: "".to_string(),
2401        });
2402        require_deterministic(&SignablePayloadFieldAmountV2 {
2403            amount: "".to_string(),
2404            abbreviation: None,
2405        });
2406        require_deterministic(&SignablePayloadFieldPreviewLayout {
2407            title: None,
2408            subtitle: None,
2409            condensed: None,
2410            expanded: None,
2411        });
2412        require_deterministic(&SignablePayloadFieldListLayout { fields: vec![] });
2413        require_deterministic(&payload);
2414
2415        println!("✅ All types consistently implement DeterministicOrdering!");
2416    }
2417
2418    fn assert_sorted_alphabetically(json: String) {
2419        println!("Sorted JSON: {json}");
2420        // ensure that ordering si preserved when using to_json()
2421        let pos_fields = json.find("Fields").unwrap_or(0);
2422        let pos_payload = json.find("PayloadType").unwrap_or(0);
2423        let pos_subtitle = json.find("Subtitle").unwrap_or(0);
2424        let pos_title = json.find("Title").unwrap_or(0);
2425        let pos_version = json.find("Version").unwrap_or(0);
2426
2427        assert!(
2428            pos_fields < pos_payload,
2429            "Fields should come before PayloadType"
2430        );
2431        assert!(
2432            pos_payload < pos_subtitle,
2433            "PayloadType should come before Subtitle"
2434        );
2435        assert!(
2436            pos_subtitle < pos_title,
2437            "Subtitle should come before Title"
2438        );
2439        assert!(pos_title < pos_version, "Title should come before Version");
2440    }
2441}