1use crate::{DynSolType, DynSolValue, Error, Result, Word};
2use alloc::{
3 string::{String, ToString},
4 vec::Vec,
5};
6use alloy_primitives::{Address, Function, I256, U256};
7
8impl DynSolType {
9 pub fn coerce_json(&self, value: &serde_json::Value) -> Result<DynSolValue> {
11 let err = || Error::eip712_coerce(self, value);
12 match self {
13 Self::Bool
14 | Self::Int(_)
15 | Self::Uint(_)
16 | Self::FixedBytes(_)
17 | Self::Address
18 | Self::Function
19 | Self::String
20 | Self::Bytes => self.coerce_json_simple(value).ok_or_else(err),
21
22 Self::Array(inner) => array(inner, value)
23 .ok_or_else(err)
24 .and_then(core::convert::identity)
25 .map(DynSolValue::Array),
26 Self::FixedArray(inner, n) => fixed_array(inner, *n, value)
27 .ok_or_else(err)
28 .and_then(core::convert::identity)
29 .map(DynSolValue::FixedArray),
30 Self::Tuple(inner) => tuple(inner, value)
31 .ok_or_else(err)
32 .and_then(core::convert::identity)
33 .map(DynSolValue::Tuple),
34 Self::CustomStruct { name, prop_names, tuple } => {
35 custom_struct(name, prop_names, tuple, value)
36 }
37 }
38 }
39
40 fn coerce_json_simple(&self, value: &serde_json::Value) -> Option<DynSolValue> {
41 match self {
42 Self::Bool => bool(value).map(DynSolValue::Bool),
43 &Self::Int(n) => int(n, value).map(|x| DynSolValue::Int(x, n)),
44 &Self::Uint(n) => uint(n, value).map(|x| DynSolValue::Uint(x, n)),
45 &Self::FixedBytes(n) => fixed_bytes(n, value).map(|x| DynSolValue::FixedBytes(x, n)),
46 Self::Address => address(value).map(DynSolValue::Address),
47 Self::Function => function(value).map(DynSolValue::Function),
48 Self::String => string(value).map(DynSolValue::String),
49 Self::Bytes => bytes(value).map(DynSolValue::Bytes),
50 _ => unreachable!(),
51 }
52 }
53}
54
55fn bool(value: &serde_json::Value) -> Option<bool> {
56 value.as_bool().or_else(|| value.as_str().and_then(|s| s.parse().ok()))
57}
58
59fn int(n: usize, value: &serde_json::Value) -> Option<I256> {
60 (|| {
61 if let Some(num) = value.as_i64() {
62 return Some(I256::try_from(num).unwrap());
63 }
64 value.as_str().and_then(|s| s.parse().ok())
65 })()
66 .and_then(|x| (x.bits() <= n as u32).then_some(x))
67}
68
69fn uint(n: usize, value: &serde_json::Value) -> Option<U256> {
70 (|| {
71 if let Some(num) = value.as_u64() {
72 return Some(U256::from(num));
73 }
74 value.as_str().and_then(|s| s.parse().ok())
75 })()
76 .and_then(|x| (x.bit_len() <= n).then_some(x))
77}
78
79fn fixed_bytes(n: usize, value: &serde_json::Value) -> Option<Word> {
80 if let Some(Ok(buf)) = value.as_str().map(hex::decode) {
81 let mut word = Word::ZERO;
82 let min = n.min(buf.len());
83 if min <= 32 {
84 word[..min].copy_from_slice(&buf[..min]);
85 return Some(word);
86 }
87 }
88 None
89}
90
91fn address(value: &serde_json::Value) -> Option<Address> {
92 value.as_str().and_then(|s| s.parse().ok())
93}
94
95fn function(value: &serde_json::Value) -> Option<Function> {
96 value.as_str().and_then(|s| s.parse().ok())
97}
98
99fn string(value: &serde_json::Value) -> Option<String> {
100 value.as_str().map(|s| s.to_string())
101}
102
103fn bytes(value: &serde_json::Value) -> Option<Vec<u8>> {
104 if let Some(s) = value.as_str() {
105 return hex::decode(s).ok();
106 }
107
108 let arr = value.as_array()?;
109 let mut vec = Vec::with_capacity(arr.len());
110 for elem in arr.iter() {
111 vec.push(elem.as_u64()?.try_into().ok()?);
112 }
113 Some(vec)
114}
115
116fn tuple(inner: &[DynSolType], value: &serde_json::Value) -> Option<Result<Vec<DynSolValue>>> {
117 if let Some(arr) = value.as_array() {
118 if inner.len() == arr.len() {
119 return Some(core::iter::zip(arr, inner).map(|(v, t)| t.coerce_json(v)).collect());
120 }
121 }
122 None
123}
124
125fn array(inner: &DynSolType, value: &serde_json::Value) -> Option<Result<Vec<DynSolValue>>> {
126 if let Some(arr) = value.as_array() {
127 return Some(arr.iter().map(|v| inner.coerce_json(v)).collect());
128 }
129 None
130}
131
132fn fixed_array(
133 inner: &DynSolType,
134 n: usize,
135 value: &serde_json::Value,
136) -> Option<Result<Vec<DynSolValue>>> {
137 if let Some(arr) = value.as_array() {
138 if arr.len() == n {
139 return Some(arr.iter().map(|v| inner.coerce_json(v)).collect());
140 }
141 }
142 None
143}
144
145pub(crate) fn custom_struct(
146 name: &str,
147 prop_names: &[String],
148 inner: &[DynSolType],
149 value: &serde_json::Value,
150) -> Result<DynSolValue> {
151 if let Some(map) = value.as_object() {
152 let mut tuple = vec![];
153 for (name, ty) in core::iter::zip(prop_names, inner) {
154 if let Some(v) = map.get(name) {
155 tuple.push(ty.coerce_json(v)?);
156 } else {
157 return Err(Error::eip712_coerce(
158 &DynSolType::CustomStruct {
159 name: name.to_string(),
160 prop_names: prop_names.to_vec(),
161 tuple: inner.to_vec(),
162 },
163 value,
164 ));
165 }
166 }
167 return Ok(DynSolValue::CustomStruct {
168 name: name.to_string(),
169 prop_names: prop_names.to_vec(),
170 tuple,
171 });
172 }
173
174 Err(Error::eip712_coerce(
175 &DynSolType::CustomStruct {
176 name: name.to_string(),
177 prop_names: prop_names.to_vec(),
178 tuple: inner.to_vec(),
179 },
180 value,
181 ))
182}
183
184#[cfg(test)]
185mod tests {
186 use super::*;
187 use alloc::{borrow::ToOwned, string::ToString};
188 use serde_json::json;
189
190 #[test]
191 fn test_bytes_num_array() {
192 let ty = DynSolType::Bytes;
193 let j = json!([1, 2, 3, 4]);
194 assert_eq!(ty.coerce_json(&j), Ok(DynSolValue::Bytes(vec![1, 2, 3, 4])));
195 }
196
197 #[test]
198 fn it_coerces() {
199 let j = json!({
200 "message": {
201 "contents": "Hello, Bob!",
202 "attachedMoneyInEth": 4.2,
203 "from": {
204 "name": "Cow",
205 "wallets": [
206 "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826",
207 "0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF",
208 ]
209 },
210 "to": [{
211 "name": "Bob",
212 "wallets": [
213 "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB",
214 "0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57",
215 "0xB0B0b0b0b0b0B000000000000000000000000000",
216 ]
217 }]
218 }
219 });
220
221 let ty = DynSolType::CustomStruct {
222 name: "Message".to_owned(),
223 prop_names: vec!["contents".to_string(), "from".to_string(), "to".to_string()],
224 tuple: vec![
225 DynSolType::String,
226 DynSolType::CustomStruct {
227 name: "Person".to_owned(),
228 prop_names: vec!["name".to_string(), "wallets".to_string()],
229 tuple: vec![
230 DynSolType::String,
231 DynSolType::Array(Box::new(DynSolType::Address)),
232 ],
233 },
234 DynSolType::Array(Box::new(DynSolType::CustomStruct {
235 name: "Person".to_owned(),
236 prop_names: vec!["name".to_string(), "wallets".to_string()],
237 tuple: vec![
238 DynSolType::String,
239 DynSolType::Array(Box::new(DynSolType::Address)),
240 ],
241 })),
242 ],
243 };
244 let top = j.as_object().unwrap().get("message").unwrap();
245
246 assert_eq!(
247 ty.coerce_json(top),
248 Ok(DynSolValue::CustomStruct {
249 name: "Message".to_owned(),
250 prop_names: vec!["contents".to_string(), "from".to_string(), "to".to_string()],
251 tuple: vec![
252 DynSolValue::String("Hello, Bob!".to_string()),
253 DynSolValue::CustomStruct {
254 name: "Person".to_owned(),
255 prop_names: vec!["name".to_string(), "wallets".to_string()],
256 tuple: vec![
257 DynSolValue::String("Cow".to_string()),
258 vec![
259 DynSolValue::Address(
260 "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826".parse().unwrap()
261 ),
262 DynSolValue::Address(
263 "0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF".parse().unwrap()
264 ),
265 ]
266 .into()
267 ]
268 },
269 vec![DynSolValue::CustomStruct {
270 name: "Person".to_owned(),
271 prop_names: vec!["name".to_string(), "wallets".to_string()],
272 tuple: vec![
273 DynSolValue::String("Bob".to_string()),
274 vec![
275 DynSolValue::Address(
276 "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB".parse().unwrap()
277 ),
278 DynSolValue::Address(
279 "0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57".parse().unwrap()
280 ),
281 DynSolValue::Address(
282 "0xB0B0b0b0b0b0B000000000000000000000000000".parse().unwrap()
283 ),
284 ]
285 .into()
286 ]
287 }]
288 .into()
289 ]
290 })
291 );
292 }
293}