whisky_pallas/utils/
data.rs

1use pallas_primitives::{BigInt, BoundedBytes, MaybeIndefArray, PlutusData};
2
3use serde_json::Value;
4use uplc::{Constr, KeyValuePairs};
5use whisky_common::WError;
6
7pub fn encode_json_str_to_plutus_datum(json: &str) -> Result<PlutusData, WError> {
8    let value: serde_json::Value = serde_json::from_str(json).map_err(WError::from_err(
9        "encode_json_str_to_plutus_datum - from_str",
10    ))?;
11
12    encode_json_value_to_plutus_datum(value)
13}
14
15pub fn encode_json_value_to_plutus_datum(value: Value) -> Result<PlutusData, WError> {
16    fn encode_number(x: serde_json::Number) -> Result<PlutusData, WError> {
17        if let Some(x) = x.as_u64() {
18            Ok(PlutusData::BigInt(BigInt::Int((x as i64).into())))
19        } else if let Some(x) = x.as_i64() {
20            Ok(PlutusData::BigInt(BigInt::Int(x.into())))
21        } else {
22            Err(WError::new(
23                "encode_number - ",
24                "floats not allowed in plutus datums",
25            ))
26        }
27    }
28
29    fn encode_string(s: &str, is_key: bool) -> Result<PlutusData, WError> {
30        if s.starts_with("0x") {
31            hex::decode(&s[2..])
32                .map(|bytes| PlutusData::BoundedBytes(BoundedBytes::from(bytes)))
33                .map_err(WError::from_err("encode_string - hex decode"))
34        } else if is_key {
35            // try first as integer
36            if let Ok(x) = s.parse::<u64>() {
37                Ok(PlutusData::BigInt(BigInt::Int((x as i64).into())))
38            } else if let Ok(x) = s.parse::<i64>() {
39                Ok(PlutusData::BigInt(BigInt::Int(x.into())))
40            } else {
41                // if not integer, encode as bytes
42                Ok(PlutusData::BoundedBytes(BoundedBytes::from(
43                    s.as_bytes().to_vec(),
44                )))
45            }
46        } else {
47            Ok(PlutusData::BoundedBytes(BoundedBytes::from(
48                s.as_bytes().to_vec(),
49            )))
50        }
51    }
52
53    fn encode_array(json_arr: Vec<Value>) -> Result<PlutusData, WError> {
54        let mut arr: Vec<PlutusData> = Vec::new();
55        for item in json_arr {
56            arr.push(encode_json_value_to_plutus_datum(item)?);
57        }
58        Ok(PlutusData::Array(MaybeIndefArray::Def(arr)))
59    }
60
61    match value {
62        Value::Object(obj) => {
63            if obj.len() == 1 {
64                let (k, v) = obj.into_iter().next().unwrap();
65                match k.as_str() {
66                    "int" => match v {
67                        Value::Number(x) => encode_number(x),
68                        _ => Err(WError::new(
69                            "encode_json_value_to_plutus_datum - int",
70                            "expected number for int type",
71                        )),
72                    },
73                    "bytes" => match v {
74                        Value::String(s) => encode_string(&s, false),
75                        _ => Err(WError::new(
76                            "encode_json_value_to_plutus_datum - bytes",
77                            "expected string for bytes type",
78                        )),
79                    },
80                    "list" => match v {
81                        Value::Array(arr) => encode_array(arr),
82                        _ => Err(WError::new(
83                            "encode_json_value_to_plutus_datum - list",
84                            "expected array for list type",
85                        )),
86                    },
87                    "map" => match v {
88                        Value::Array(map_vec) => {
89                            let mut map: Vec<(PlutusData, PlutusData)> = vec![];
90                            for entry in map_vec {
91                                match entry {
92                                    Value::Object(entry_obj) => {
93                                        let raw_key = entry_obj.get("k").ok_or_else(|| {
94                                            WError::new(
95                                                "encode_json_value_to_plutus_datum - map entry",
96                                                "missing key in map entry",
97                                            )
98                                        })?;
99                                        let raw_value = entry_obj.get("v").ok_or_else(|| {
100                                            WError::new(
101                                                "encode_json_value_to_plutus_datum - map entry",
102                                                "missing value in map entry",
103                                            )
104                                        })?;
105                                        let encoded_key =
106                                            encode_json_value_to_plutus_datum(raw_key.clone())?;
107                                        let encoded_value =
108                                            encode_json_value_to_plutus_datum(raw_value.clone())?;
109                                        map.push((encoded_key, encoded_value));
110                                    }
111                                    _ => {
112                                        return Err(WError::new(
113                                            "encode_json_value_to_plutus_datum - map entry",
114                                            "expected object for map entry",
115                                        ))
116                                    }
117                                }
118                            }
119                            Ok(PlutusData::Map(KeyValuePairs::from(map)))
120                        }
121                        _ => Err(WError::new(
122                            "encode_json_value_to_plutus_datum - map",
123                            "expected array for map type",
124                        )),
125                    },
126                    _ => Err(WError::new(
127                        "encode_json_value_to_plutus_datum",
128                        "unknown type key",
129                    )),
130                }
131            } else {
132                if obj.len() != 2 {
133                    return Err(WError::new(
134                        "encode_json_value_to_plutus_datum - constr",
135                        "expected object with single key for constr type",
136                    ));
137                }
138                let variant: u64 = obj
139                    .get("constructor")
140                    .ok_or_else(|| {
141                        WError::new(
142                            "encode_json_value_to_plutus_datum - constr",
143                            "missing constr key for constr type",
144                        )
145                    })?
146                    .as_u64()
147                    .ok_or_else(|| {
148                        WError::new(
149                            "encode_json_value_to_plutus_datum - constr",
150                            "expected unsigned integer for constr variant",
151                        )
152                    })?;
153                let fields_json = obj
154                    .get("fields")
155                    .ok_or_else(|| {
156                        WError::new(
157                            "encode_json_value_to_plutus_datum - constr",
158                            "missing fields key for constr type",
159                        )
160                    })?
161                    .as_array()
162                    .ok_or_else(|| {
163                        WError::new(
164                            "encode_json_value_to_plutus_datum - constr",
165                            "expected array for constr fields",
166                        )
167                    })?;
168                let mut fields: Vec<PlutusData> = Vec::new();
169                for field_json in fields_json {
170                    fields.push(encode_json_value_to_plutus_datum(field_json.clone())?);
171                }
172                return Ok(PlutusData::Constr(Constr {
173                    tag: variant + 121,
174                    any_constructor: None,
175                    fields: MaybeIndefArray::Def(fields),
176                }));
177            }
178        }
179        _ => {
180            return Err(WError::new(
181                "encode_json_value_to_plutus_datum",
182                "expected object with single key for typed value",
183            ))
184        }
185    }
186}