Skip to content

Commit 9853008

Browse files
leovctrplusq
authored andcommitted
feat: implement parseTomlType cheats (foundry-rs#8911)
* feat: implement `parseTomlType` cheats * chore: `forge fmt` * revert: use json naming to indicate to users that they are operating on json data * chore: nit * chore: nit
1 parent d15f13e commit 9853008

File tree

8 files changed

+244
-16
lines changed

8 files changed

+244
-16
lines changed

crates/cheatcodes/assets/cheatcodes.json

Lines changed: 60 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/cheatcodes/spec/src/vm.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2134,6 +2134,19 @@ interface Vm {
21342134
pure
21352135
returns (bytes32[] memory);
21362136

2137+
/// Parses a string of TOML data and coerces it to type corresponding to `typeDescription`.
2138+
#[cheatcode(group = Toml)]
2139+
function parseTomlType(string calldata toml, string calldata typeDescription) external pure returns (bytes memory);
2140+
/// Parses a string of TOML data at `key` and coerces it to type corresponding to `typeDescription`.
2141+
#[cheatcode(group = Toml)]
2142+
function parseTomlType(string calldata toml, string calldata key, string calldata typeDescription) external pure returns (bytes memory);
2143+
/// Parses a string of TOML data at `key` and coerces it to type array corresponding to `typeDescription`.
2144+
#[cheatcode(group = Toml)]
2145+
function parseTomlTypeArray(string calldata toml, string calldata key, string calldata typeDescription)
2146+
external
2147+
pure
2148+
returns (bytes memory);
2149+
21372150
/// Returns an array of all the keys in a TOML table.
21382151
#[cheatcode(group = Toml)]
21392152
function parseTomlKeys(string calldata toml, string calldata key) external pure returns (string[] memory keys);

crates/cheatcodes/src/json.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -654,7 +654,7 @@ fn serialize_json(
654654
}
655655

656656
/// Resolves a [DynSolType] from user input.
657-
fn resolve_type(type_description: &str) -> Result<DynSolType> {
657+
pub(super) fn resolve_type(type_description: &str) -> Result<DynSolType> {
658658
if let Ok(ty) = DynSolType::parse(type_description) {
659659
return Ok(ty);
660660
};

crates/cheatcodes/src/toml.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@
33
use crate::{
44
json::{
55
canonicalize_json_path, check_json_key_exists, parse_json, parse_json_coerce,
6-
parse_json_keys,
6+
parse_json_keys, resolve_type,
77
},
88
Cheatcode, Cheatcodes, Result,
99
Vm::*,
1010
};
1111
use alloy_dyn_abi::DynSolType;
12+
use alloy_sol_types::SolValue;
1213
use foundry_common::fs;
1314
use foundry_config::fs_permissions::FsAccessKind;
1415
use serde_json::Value as JsonValue;
@@ -133,6 +134,28 @@ impl Cheatcode for parseTomlBytes32ArrayCall {
133134
}
134135
}
135136

137+
impl Cheatcode for parseTomlType_0Call {
138+
fn apply(&self, _state: &mut Cheatcodes) -> Result {
139+
let Self { toml, typeDescription } = self;
140+
parse_toml_coerce(toml, "$", &resolve_type(typeDescription)?).map(|v| v.abi_encode())
141+
}
142+
}
143+
144+
impl Cheatcode for parseTomlType_1Call {
145+
fn apply(&self, _state: &mut Cheatcodes) -> Result {
146+
let Self { toml, key, typeDescription } = self;
147+
parse_toml_coerce(toml, key, &resolve_type(typeDescription)?).map(|v| v.abi_encode())
148+
}
149+
}
150+
151+
impl Cheatcode for parseTomlTypeArrayCall {
152+
fn apply(&self, _state: &mut Cheatcodes) -> Result {
153+
let Self { toml, key, typeDescription } = self;
154+
let ty = resolve_type(typeDescription)?;
155+
parse_toml_coerce(toml, key, &DynSolType::Array(Box::new(ty))).map(|v| v.abi_encode())
156+
}
157+
}
158+
136159
impl Cheatcode for parseTomlKeysCall {
137160
fn apply(&self, _state: &mut Cheatcodes) -> Result {
138161
let Self { toml, key } = self;

testdata/cheats/Vm.sol

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

testdata/default/cheats/Toml.t.sol

Lines changed: 118 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,81 @@ import "ds-test/test.sol";
55
import "cheats/Vm.sol";
66
import "../logs/console.sol";
77

8+
library TomlStructs {
9+
address constant HEVM_ADDRESS = address(bytes20(uint160(uint256(keccak256("hevm cheat code")))));
10+
Vm constant vm = Vm(HEVM_ADDRESS);
11+
12+
// forge eip712 testdata/default/cheats/Toml.t.sol -R 'cheats=testdata/cheats' -R 'ds-test=testdata/lib/ds-test/src' | grep ^FlatToml
13+
string constant schema_FlatToml =
14+
"FlatToml(uint256 a,int24[][] arr,string str,bytes b,address addr,bytes32 fixedBytes)";
15+
16+
// forge eip712 testdata/default/cheats/Toml.t.sol -R 'cheats=testdata/cheats' -R 'ds-test=testdata/lib/ds-test/src' | grep ^NestedToml
17+
string constant schema_NestedToml =
18+
"NestedToml(FlatToml[] members,AnotherFlatToml inner,string name)AnotherFlatToml(bytes4 fixedBytes)FlatToml(uint256 a,int24[][] arr,string str,bytes b,address addr,bytes32 fixedBytes)";
19+
20+
function deserializeFlatToml(string memory toml) internal pure returns (ParseTomlTest.FlatToml memory) {
21+
return abi.decode(vm.parseTomlType(toml, schema_FlatToml), (ParseTomlTest.FlatToml));
22+
}
23+
24+
function deserializeFlatToml(string memory toml, string memory path)
25+
internal
26+
pure
27+
returns (ParseTomlTest.FlatToml memory)
28+
{
29+
return abi.decode(vm.parseTomlType(toml, path, schema_FlatToml), (ParseTomlTest.FlatToml));
30+
}
31+
32+
function deserializeFlatTomlArray(string memory toml, string memory path)
33+
internal
34+
pure
35+
returns (ParseTomlTest.FlatToml[] memory)
36+
{
37+
return abi.decode(vm.parseTomlTypeArray(toml, path, schema_FlatToml), (ParseTomlTest.FlatToml[]));
38+
}
39+
40+
function deserializeNestedToml(string memory toml) internal pure returns (ParseTomlTest.NestedToml memory) {
41+
return abi.decode(vm.parseTomlType(toml, schema_NestedToml), (ParseTomlTest.NestedToml));
42+
}
43+
44+
function deserializeNestedToml(string memory toml, string memory path)
45+
internal
46+
pure
47+
returns (ParseTomlTest.NestedToml memory)
48+
{
49+
return abi.decode(vm.parseTomlType(toml, path, schema_NestedToml), (ParseTomlTest.NestedToml));
50+
}
51+
52+
function deserializeNestedTomlArray(string memory toml, string memory path)
53+
internal
54+
pure
55+
returns (ParseTomlTest.NestedToml[] memory)
56+
{
57+
return abi.decode(vm.parseTomlType(toml, path, schema_NestedToml), (ParseTomlTest.NestedToml[]));
58+
}
59+
}
60+
861
contract ParseTomlTest is DSTest {
62+
using TomlStructs for *;
63+
64+
struct FlatToml {
65+
uint256 a;
66+
int24[][] arr;
67+
string str;
68+
bytes b;
69+
address addr;
70+
bytes32 fixedBytes;
71+
}
72+
73+
struct AnotherFlatToml {
74+
bytes4 fixedBytes;
75+
}
76+
77+
struct NestedToml {
78+
FlatToml[] members;
79+
AnotherFlatToml inner;
80+
string name;
81+
}
82+
983
Vm constant vm = Vm(HEVM_ADDRESS);
1084
string toml;
1185

@@ -169,20 +243,20 @@ contract ParseTomlTest is DSTest {
169243
assertEq(bytesArray[1], hex"02");
170244
}
171245

172-
struct Nested {
246+
struct NestedStruct {
173247
uint256 number;
174248
string str;
175249
}
176250

177251
function test_nestedObject() public {
178252
bytes memory data = vm.parseToml(toml, ".nestedObject");
179-
Nested memory nested = abi.decode(data, (Nested));
253+
NestedStruct memory nested = abi.decode(data, (NestedStruct));
180254
assertEq(nested.number, 9223372036854775807); // TOML is limited to 64-bit integers
181255
assertEq(nested.str, "NEST");
182256
}
183257

184-
function test_advancedJsonPath() public {
185-
bytes memory data = vm.parseToml(toml, ".advancedJsonPath[*].id");
258+
function test_advancedTomlPath() public {
259+
bytes memory data = vm.parseToml(toml, ".advancedTomlPath[*].id");
186260
uint256[] memory numbers = abi.decode(data, (uint256[]));
187261
assertEq(numbers[0], 1);
188262
assertEq(numbers[1], 2);
@@ -225,6 +299,36 @@ contract ParseTomlTest is DSTest {
225299
vm._expectCheatcodeRevert("key \".*\" must return exactly one JSON object");
226300
vm.parseTomlKeys(tomlString, ".*");
227301
}
302+
303+
// forge eip712 testdata/default/cheats/Toml.t.sol -R 'cheats=testdata/cheats' -R 'ds-test=testdata/lib/ds-test/src' | grep ^FlatToml
304+
string constant schema_FlatToml =
305+
"FlatToml(uint256 a,int24[][] arr,string str,bytes b,address addr,bytes32 fixedBytes)";
306+
307+
// forge eip712 testdata/default/cheats/Toml.t.sol -R 'cheats=testdata/cheats' -R 'ds-test=testdata/lib/ds-test/src' | grep ^NestedToml
308+
string constant schema_NestedToml =
309+
"NestedToml(FlatToml[] members,AnotherFlatToml inner,string name)AnotherFlatToml(bytes4 fixedBytes)FlatToml(uint256 a,int24[][] arr,string str,bytes b,address addr,bytes32 fixedBytes)";
310+
311+
function test_parseTomlType() public {
312+
string memory readToml = vm.readFile("fixtures/Toml/nested_toml_struct.toml");
313+
NestedToml memory data = readToml.deserializeNestedToml();
314+
assertEq(data.members.length, 2);
315+
316+
FlatToml memory expected = FlatToml({
317+
a: 200,
318+
arr: new int24[][](0),
319+
str: "some other string",
320+
b: hex"0000000000000000000000000000000000000000",
321+
addr: 0x167D91deaEEE3021161502873d3bcc6291081648,
322+
fixedBytes: 0xed1c7beb1f00feaaaec5636950d6edb25a8d4fedc8deb2711287b64c4d27719d
323+
});
324+
325+
assertEq(keccak256(abi.encode(data.members[1])), keccak256(abi.encode(expected)));
326+
assertEq(bytes32(data.inner.fixedBytes), bytes32(bytes4(0x12345678)));
327+
328+
FlatToml[] memory members = TomlStructs.deserializeFlatTomlArray(readToml, ".members");
329+
330+
assertEq(keccak256(abi.encode(members)), keccak256(abi.encode(data.members)));
331+
}
228332
}
229333

230334
contract WriteTomlTest is DSTest {
@@ -238,18 +342,18 @@ contract WriteTomlTest is DSTest {
238342
json2 = "example2";
239343
}
240344

241-
struct simpleJson {
345+
struct simpleStruct {
242346
uint256 a;
243347
string b;
244348
}
245349

246-
struct notSimpleJson {
350+
struct nestedStruct {
247351
uint256 a;
248352
string b;
249-
simpleJson c;
353+
simpleStruct c;
250354
}
251355

252-
function test_serializeNotSimpleToml() public {
356+
function test_serializeNestedStructToml() public {
253357
string memory json3 = "json3";
254358
string memory path = "fixtures/Toml/write_complex_test.toml";
255359
vm.serializeUint(json3, "a", uint256(123));
@@ -259,14 +363,16 @@ contract WriteTomlTest is DSTest {
259363
vm.writeToml(finalJson, path);
260364
string memory toml = vm.readFile(path);
261365
bytes memory data = vm.parseToml(toml);
262-
notSimpleJson memory decodedData = abi.decode(data, (notSimpleJson));
366+
nestedStruct memory decodedData = abi.decode(data, (nestedStruct));
367+
console.log(decodedData.a);
368+
assertEq(decodedData.a, 123);
263369
}
264370

265371
function test_retrieveEntireToml() public {
266372
string memory path = "fixtures/Toml/write_complex_test.toml";
267373
string memory toml = vm.readFile(path);
268374
bytes memory data = vm.parseToml(toml, ".");
269-
notSimpleJson memory decodedData = abi.decode(data, (notSimpleJson));
375+
nestedStruct memory decodedData = abi.decode(data, (nestedStruct));
270376
console.log(decodedData.a);
271377
assertEq(decodedData.a, 123);
272378
}
@@ -294,7 +400,7 @@ contract WriteTomlTest is DSTest {
294400

295401
string memory toml = vm.readFile(path);
296402
bytes memory data = vm.parseToml(toml);
297-
simpleJson memory decodedData = abi.decode(data, (simpleJson));
403+
simpleStruct memory decodedData = abi.decode(data, (simpleStruct));
298404
assertEq(decodedData.a, 123);
299405
assertEq(decodedData.b, "test");
300406

@@ -303,7 +409,7 @@ contract WriteTomlTest is DSTest {
303409
// read again
304410
toml = vm.readFile(path);
305411
data = vm.parseToml(toml, ".b");
306-
decodedData = abi.decode(data, (simpleJson));
412+
decodedData = abi.decode(data, (simpleStruct));
307413
assertEq(decodedData.a, 123);
308414
assertEq(decodedData.b, "test");
309415

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name = "test"
2+
3+
[[members]]
4+
a = 100
5+
arr = [
6+
[1, -2, -5],
7+
[1000, 2000, 0]
8+
]
9+
str = "some string"
10+
b = "0x"
11+
addr = "0x0000000000000000000000000000000000000000"
12+
fixedBytes = "0x8ae3fc6bd1b150a73ec4afe3ef136fa2f88e9c96131c883c5e4a4714811c1598"
13+
14+
[[members]]
15+
a = 200
16+
arr = []
17+
str = "some other string"
18+
b = "0x0000000000000000000000000000000000000000"
19+
addr = "0x167D91deaEEE3021161502873d3bcc6291081648"
20+
fixedBytes = "0xed1c7beb1f00feaaaec5636950d6edb25a8d4fedc8deb2711287b64c4d27719d"
21+
22+
[inner]
23+
fixedBytes = "0x12345678"

testdata/fixtures/Toml/test.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ bytesStringArray = ["0x01", "0x02"]
4343
number = 9223372036854775807 # TOML is limited to 64-bit integers
4444
str = "NEST"
4545

46-
[[advancedJsonPath]]
46+
[[advancedTomlPath]]
4747
id = 1
4848

49-
[[advancedJsonPath]]
49+
[[advancedTomlPath]]
5050
id = 2

0 commit comments

Comments
 (0)