Skip to content

feat: implement parseTomlType cheats #8911

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions crates/cheatcodes/assets/cheatcodes.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions crates/cheatcodes/spec/src/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2134,6 +2134,19 @@ interface Vm {
pure
returns (bytes32[] memory);

/// Parses a string of TOML data and coerces it to type corresponding to `typeDescription`.
#[cheatcode(group = Toml)]
function parseTomlType(string calldata toml, string calldata typeDescription) external pure returns (bytes memory);
/// Parses a string of TOML data at `key` and coerces it to type corresponding to `typeDescription`.
#[cheatcode(group = Toml)]
function parseTomlType(string calldata toml, string calldata key, string calldata typeDescription) external pure returns (bytes memory);
/// Parses a string of TOML data at `key` and coerces it to type array corresponding to `typeDescription`.
#[cheatcode(group = Toml)]
function parseTomlTypeArray(string calldata toml, string calldata key, string calldata typeDescription)
external
pure
returns (bytes memory);

/// Returns an array of all the keys in a TOML table.
#[cheatcode(group = Toml)]
function parseTomlKeys(string calldata toml, string calldata key) external pure returns (string[] memory keys);
Expand Down
2 changes: 1 addition & 1 deletion crates/cheatcodes/src/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -654,7 +654,7 @@ fn serialize_json(
}

/// Resolves a [DynSolType] from user input.
fn resolve_type(type_description: &str) -> Result<DynSolType> {
pub(super) fn resolve_type(type_description: &str) -> Result<DynSolType> {
if let Ok(ty) = DynSolType::parse(type_description) {
return Ok(ty);
};
Expand Down
25 changes: 24 additions & 1 deletion crates/cheatcodes/src/toml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
use crate::{
json::{
canonicalize_json_path, check_json_key_exists, parse_json, parse_json_coerce,
parse_json_keys,
parse_json_keys, resolve_type,
},
Cheatcode, Cheatcodes, Result,
Vm::*,
};
use alloy_dyn_abi::DynSolType;
use alloy_sol_types::SolValue;
use foundry_common::fs;
use foundry_config::fs_permissions::FsAccessKind;
use serde_json::Value as JsonValue;
Expand Down Expand Up @@ -133,6 +134,28 @@ impl Cheatcode for parseTomlBytes32ArrayCall {
}
}

impl Cheatcode for parseTomlType_0Call {
fn apply(&self, _state: &mut Cheatcodes) -> Result {
let Self { toml, typeDescription } = self;
parse_toml_coerce(toml, "$", &resolve_type(typeDescription)?).map(|v| v.abi_encode())
}
}

impl Cheatcode for parseTomlType_1Call {
fn apply(&self, _state: &mut Cheatcodes) -> Result {
let Self { toml, key, typeDescription } = self;
parse_toml_coerce(toml, key, &resolve_type(typeDescription)?).map(|v| v.abi_encode())
}
}

impl Cheatcode for parseTomlTypeArrayCall {
fn apply(&self, _state: &mut Cheatcodes) -> Result {
let Self { toml, key, typeDescription } = self;
let ty = resolve_type(typeDescription)?;
parse_toml_coerce(toml, key, &DynSolType::Array(Box::new(ty))).map(|v| v.abi_encode())
}
}

impl Cheatcode for parseTomlKeysCall {
fn apply(&self, _state: &mut Cheatcodes) -> Result {
let Self { toml, key } = self;
Expand Down
3 changes: 3 additions & 0 deletions testdata/cheats/Vm.sol

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

130 changes: 118 additions & 12 deletions testdata/default/cheats/Toml.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,81 @@ import "ds-test/test.sol";
import "cheats/Vm.sol";
import "../logs/console.sol";

library TomlStructs {
address constant HEVM_ADDRESS = address(bytes20(uint160(uint256(keccak256("hevm cheat code")))));
Vm constant vm = Vm(HEVM_ADDRESS);

// forge eip712 testdata/default/cheats/Toml.t.sol -R 'cheats=testdata/cheats' -R 'ds-test=testdata/lib/ds-test/src' | grep ^FlatToml
string constant schema_FlatToml =
"FlatToml(uint256 a,int24[][] arr,string str,bytes b,address addr,bytes32 fixedBytes)";

// forge eip712 testdata/default/cheats/Toml.t.sol -R 'cheats=testdata/cheats' -R 'ds-test=testdata/lib/ds-test/src' | grep ^NestedToml
string constant schema_NestedToml =
"NestedToml(FlatToml[] members,AnotherFlatToml inner,string name)AnotherFlatToml(bytes4 fixedBytes)FlatToml(uint256 a,int24[][] arr,string str,bytes b,address addr,bytes32 fixedBytes)";

function deserializeFlatToml(string memory toml) internal pure returns (ParseTomlTest.FlatToml memory) {
return abi.decode(vm.parseTomlType(toml, schema_FlatToml), (ParseTomlTest.FlatToml));
}

function deserializeFlatToml(string memory toml, string memory path)
internal
pure
returns (ParseTomlTest.FlatToml memory)
{
return abi.decode(vm.parseTomlType(toml, path, schema_FlatToml), (ParseTomlTest.FlatToml));
}

function deserializeFlatTomlArray(string memory toml, string memory path)
internal
pure
returns (ParseTomlTest.FlatToml[] memory)
{
return abi.decode(vm.parseTomlTypeArray(toml, path, schema_FlatToml), (ParseTomlTest.FlatToml[]));
}

function deserializeNestedToml(string memory toml) internal pure returns (ParseTomlTest.NestedToml memory) {
return abi.decode(vm.parseTomlType(toml, schema_NestedToml), (ParseTomlTest.NestedToml));
}

function deserializeNestedToml(string memory toml, string memory path)
internal
pure
returns (ParseTomlTest.NestedToml memory)
{
return abi.decode(vm.parseTomlType(toml, path, schema_NestedToml), (ParseTomlTest.NestedToml));
}

function deserializeNestedTomlArray(string memory toml, string memory path)
internal
pure
returns (ParseTomlTest.NestedToml[] memory)
{
return abi.decode(vm.parseTomlType(toml, path, schema_NestedToml), (ParseTomlTest.NestedToml[]));
}
}

contract ParseTomlTest is DSTest {
using TomlStructs for *;

struct FlatToml {
uint256 a;
int24[][] arr;
string str;
bytes b;
address addr;
bytes32 fixedBytes;
}

struct AnotherFlatToml {
bytes4 fixedBytes;
}

struct NestedToml {
FlatToml[] members;
AnotherFlatToml inner;
string name;
}

Vm constant vm = Vm(HEVM_ADDRESS);
string toml;

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

struct Nested {
struct NestedStruct {
uint256 number;
string str;
}

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

function test_advancedJsonPath() public {
bytes memory data = vm.parseToml(toml, ".advancedJsonPath[*].id");
function test_advancedTomlPath() public {
bytes memory data = vm.parseToml(toml, ".advancedTomlPath[*].id");
uint256[] memory numbers = abi.decode(data, (uint256[]));
assertEq(numbers[0], 1);
assertEq(numbers[1], 2);
Expand Down Expand Up @@ -225,6 +299,36 @@ contract ParseTomlTest is DSTest {
vm._expectCheatcodeRevert("key \".*\" must return exactly one JSON object");
vm.parseTomlKeys(tomlString, ".*");
}

// forge eip712 testdata/default/cheats/Toml.t.sol -R 'cheats=testdata/cheats' -R 'ds-test=testdata/lib/ds-test/src' | grep ^FlatToml
string constant schema_FlatToml =
"FlatToml(uint256 a,int24[][] arr,string str,bytes b,address addr,bytes32 fixedBytes)";

// forge eip712 testdata/default/cheats/Toml.t.sol -R 'cheats=testdata/cheats' -R 'ds-test=testdata/lib/ds-test/src' | grep ^NestedToml
string constant schema_NestedToml =
"NestedToml(FlatToml[] members,AnotherFlatToml inner,string name)AnotherFlatToml(bytes4 fixedBytes)FlatToml(uint256 a,int24[][] arr,string str,bytes b,address addr,bytes32 fixedBytes)";

function test_parseTomlType() public {
string memory readToml = vm.readFile("fixtures/Toml/nested_toml_struct.toml");
NestedToml memory data = readToml.deserializeNestedToml();
assertEq(data.members.length, 2);

FlatToml memory expected = FlatToml({
a: 200,
arr: new int24[][](0),
str: "some other string",
b: hex"0000000000000000000000000000000000000000",
addr: 0x167D91deaEEE3021161502873d3bcc6291081648,
fixedBytes: 0xed1c7beb1f00feaaaec5636950d6edb25a8d4fedc8deb2711287b64c4d27719d
});

assertEq(keccak256(abi.encode(data.members[1])), keccak256(abi.encode(expected)));
assertEq(bytes32(data.inner.fixedBytes), bytes32(bytes4(0x12345678)));

FlatToml[] memory members = TomlStructs.deserializeFlatTomlArray(readToml, ".members");

assertEq(keccak256(abi.encode(members)), keccak256(abi.encode(data.members)));
}
}

contract WriteTomlTest is DSTest {
Expand All @@ -238,18 +342,18 @@ contract WriteTomlTest is DSTest {
json2 = "example2";
}

struct simpleJson {
struct simpleStruct {
uint256 a;
string b;
}

struct notSimpleJson {
struct nestedStruct {
uint256 a;
string b;
simpleJson c;
simpleStruct c;
}

function test_serializeNotSimpleToml() public {
function test_serializeNestedStructToml() public {
string memory json3 = "json3";
string memory path = "fixtures/Toml/write_complex_test.toml";
vm.serializeUint(json3, "a", uint256(123));
Expand All @@ -259,14 +363,16 @@ contract WriteTomlTest is DSTest {
vm.writeToml(finalJson, path);
string memory toml = vm.readFile(path);
bytes memory data = vm.parseToml(toml);
notSimpleJson memory decodedData = abi.decode(data, (notSimpleJson));
nestedStruct memory decodedData = abi.decode(data, (nestedStruct));
console.log(decodedData.a);
assertEq(decodedData.a, 123);
}

function test_retrieveEntireToml() public {
string memory path = "fixtures/Toml/write_complex_test.toml";
string memory toml = vm.readFile(path);
bytes memory data = vm.parseToml(toml, ".");
notSimpleJson memory decodedData = abi.decode(data, (notSimpleJson));
nestedStruct memory decodedData = abi.decode(data, (nestedStruct));
console.log(decodedData.a);
assertEq(decodedData.a, 123);
}
Expand Down Expand Up @@ -294,7 +400,7 @@ contract WriteTomlTest is DSTest {

string memory toml = vm.readFile(path);
bytes memory data = vm.parseToml(toml);
simpleJson memory decodedData = abi.decode(data, (simpleJson));
simpleStruct memory decodedData = abi.decode(data, (simpleStruct));
assertEq(decodedData.a, 123);
assertEq(decodedData.b, "test");

Expand All @@ -303,7 +409,7 @@ contract WriteTomlTest is DSTest {
// read again
toml = vm.readFile(path);
data = vm.parseToml(toml, ".b");
decodedData = abi.decode(data, (simpleJson));
decodedData = abi.decode(data, (simpleStruct));
assertEq(decodedData.a, 123);
assertEq(decodedData.b, "test");

Expand Down
23 changes: 23 additions & 0 deletions testdata/fixtures/Toml/nested_toml_struct.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name = "test"

[[members]]
a = 100
arr = [
[1, -2, -5],
[1000, 2000, 0]
]
str = "some string"
b = "0x"
addr = "0x0000000000000000000000000000000000000000"
fixedBytes = "0x8ae3fc6bd1b150a73ec4afe3ef136fa2f88e9c96131c883c5e4a4714811c1598"

[[members]]
a = 200
arr = []
str = "some other string"
b = "0x0000000000000000000000000000000000000000"
addr = "0x167D91deaEEE3021161502873d3bcc6291081648"
fixedBytes = "0xed1c7beb1f00feaaaec5636950d6edb25a8d4fedc8deb2711287b64c4d27719d"

[inner]
fixedBytes = "0x12345678"
4 changes: 2 additions & 2 deletions testdata/fixtures/Toml/test.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ bytesStringArray = ["0x01", "0x02"]
number = 9223372036854775807 # TOML is limited to 64-bit integers
str = "NEST"

[[advancedJsonPath]]
[[advancedTomlPath]]
id = 1

[[advancedJsonPath]]
[[advancedTomlPath]]
id = 2
Loading