Skip to content

Commit c14e6a8

Browse files
committed
fix(cast): consistent serialization of Uint/Ints depending on actual type
The current implementation dynamically tries to determine if the runtime value can fit in 64 bits, but this leads to inconsistent serialization. For instance if you were decoding an `uint[]`, some of the values that fit in 64 bits will serialize as number while others serialize as string making it require special handling on the user that is consuming the json. This change makes it so it uses the type information to determine the serialization. So the user will always know that specific types will always serialize to a number or a string depending on the number of bits that type uses.
1 parent 9b46351 commit c14e6a8

File tree

8 files changed

+22
-26
lines changed

8 files changed

+22
-26
lines changed

crates/cast/tests/cli/selectors.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ casttest!(event_decode_with_sig, |_prj, cmd| {
140140

141141
cmd.args(["--json"]).assert_success().stdout_eq(str![[r#"
142142
[
143-
78,
143+
"78",
144144
"0x0000000000000000000000000000000000D0004F"
145145
]
146146
@@ -168,7 +168,7 @@ casttest!(error_decode_with_sig, |_prj, cmd| {
168168

169169
cmd.args(["--json"]).assert_success().stdout_eq(str![[r#"
170170
[
171-
101,
171+
"101",
172172
"0x0000000000000000000000000000000000D0004F"
173173
]
174174

crates/cheatcodes/src/json.rs

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -590,10 +590,10 @@ fn _json_value_to_token(value: &Value, defs: &StructDefinitions) -> Result<DynSo
590590
}
591591

592592
if let Ok(n) = s.parse() {
593-
return Ok(DynSolValue::Uint(n, 256));
593+
return Ok(DynSolValue::Uint(n, 64));
594594
}
595595
if let Ok(n) = s.parse() {
596-
return Ok(DynSolValue::Int(n, 256));
596+
return Ok(DynSolValue::Int(n, 64));
597597
}
598598
}
599599
}
@@ -624,13 +624,9 @@ fn _json_value_to_token(value: &Value, defs: &StructDefinitions) -> Result<DynSo
624624
// capacity of a 64-bit integer.
625625
// Note that number-like strings that *could* fit in an `i64`/`u64` will fall through
626626
// and be treated as literal strings.
627-
if let Ok(n) = string.parse::<I256>()
628-
&& i64::try_from(n).is_err()
629-
{
627+
if let Ok(n) = string.parse::<I256>() {
630628
return Ok(DynSolValue::Int(n, 256));
631-
} else if let Ok(n) = string.parse::<U256>()
632-
&& u64::try_from(n).is_err()
633-
{
629+
} else if let Ok(n) = string.parse::<U256>() {
634630
return Ok(DynSolValue::Uint(n, 256));
635631
}
636632

@@ -1064,7 +1060,7 @@ mod tests {
10641060
// Serialize the value to JSON and verify that the order is preserved.
10651061
let json_value = serialize_value_as_json(item_struct, Some(&struct_defs.into())).unwrap();
10661062
let json_string = serde_json::to_string(&json_value).unwrap();
1067-
assert_eq!(json_string, r#"{"name":"Test Item","id":123,"active":true}"#);
1063+
assert_eq!(json_string, r#"{"name":"Test Item","id":"123","active":true}"#);
10681064
}
10691065

10701066
#[test]
@@ -1100,7 +1096,7 @@ mod tests {
11001096
let json_string = serde_json::to_string(&json_value).unwrap();
11011097
assert_eq!(
11021098
json_string,
1103-
format!(r#"{{"owner":"{owner_address}","balance":5000,"id":"{wallet_id}"}}"#)
1099+
format!(r#"{{"owner":"{owner_address}","balance":"5000","id":"{wallet_id}"}}"#)
11041100
);
11051101

11061102
// Resolve the type, which should also respect the struct definition order.

crates/common/fmt/src/dynamic.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -175,20 +175,20 @@ fn _serialize_value_as_json(value: DynSolValue, defs: &StructDefinitions) -> Res
175175
}
176176
DynSolValue::Bytes(b) => Ok(Value::String(hex::encode_prefixed(b))),
177177
DynSolValue::FixedBytes(b, size) => Ok(Value::String(hex::encode_prefixed(&b[..size]))),
178-
DynSolValue::Int(i, _) => {
179-
if let Ok(n) = i64::try_from(i) {
178+
DynSolValue::Int(i, bits) => {
179+
if bits <= 64 {
180180
// Use `serde_json::Number` if the number can be accurately represented.
181-
Ok(Value::Number(n.into()))
181+
Ok(Value::Number(TryInto::<i64>::try_into(i)?.into()))
182182
} else {
183183
// Otherwise, fallback to its string representation to preserve precision and ensure
184184
// compatibility with alloy's `DynSolType` coercion.
185185
Ok(Value::String(i.to_string()))
186186
}
187187
}
188-
DynSolValue::Uint(i, _) => {
189-
if let Ok(n) = u64::try_from(i) {
188+
DynSolValue::Uint(i, bits) => {
189+
if bits <= 64 {
190190
// Use `serde_json::Number` if the number can be accurately represented.
191-
Ok(Value::Number(n.into()))
191+
Ok(Value::Number(TryInto::<u64>::try_into(i)?.into()))
192192
} else {
193193
// Otherwise, fallback to its string representation to preserve precision and ensure
194194
// compatibility with alloy's `DynSolType` coercion.

testdata/default/cheats/Toml.t.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ contract WriteTomlTest is DSTest {
412412
function test_writeToml() public {
413413
string memory json3 = "json3";
414414
string memory path = "fixtures/Toml/write_test.toml";
415-
vm.serializeUint(json3, "a", uint256(123));
415+
vm.serializeUint(json3, "a", uint64(123));
416416
string memory finalJson = vm.serializeString(json3, "b", "test");
417417
vm.writeToml(finalJson, path);
418418

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
2-
"a": 123,
2+
"a": "123",
33
"b": "test",
44
"c": {
5-
"a": 123,
5+
"a": "123",
66
"b": "test"
77
}
88
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
2-
"a": 123,
2+
"a": "123",
33
"b": "0x000000000000000000000000000000000000bEEF"
4-
}
4+
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
a = 123
1+
a = "123"
22
b = "test"
33

44
[c]
5-
a = 123
5+
a = "123"
66
b = "test"
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
a = 123
1+
a = "123"
22
b = "0x000000000000000000000000000000000000bEEF"

0 commit comments

Comments
 (0)