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 2 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.

156 changes: 131 additions & 25 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 @@ -181,8 +255,8 @@ contract ParseTomlTest is DSTest {
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,48 +299,80 @@ 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 {
Vm constant vm = Vm(HEVM_ADDRESS);

string json1;
string json2;
string toml1;
string toml2;

function setUp() public {
json1 = "example";
json2 = "example2";
toml1 = "example";
toml2 = "example2";
}

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

struct notSimpleJson {
struct notSimpleToml {
uint256 a;
string b;
simpleJson c;
simpleToml c;
}

function test_serializeNotSimpleToml() public {
string memory json3 = "json3";
string memory toml3 = "toml3";
string memory path = "fixtures/Toml/write_complex_test.toml";
vm.serializeUint(json3, "a", uint256(123));
string memory semiFinal = vm.serializeString(json3, "b", "test");
string memory finalJson = vm.serializeString(json3, "c", semiFinal);
console.log(finalJson);
vm.writeToml(finalJson, path);
vm.serializeUint(toml3, "a", uint256(123));
string memory semiFinal = vm.serializeString(toml3, "b", "test");
string memory finalToml = vm.serializeString(toml3, "c", semiFinal);
console.log(finalToml);
vm.writeToml(finalToml, path);
string memory toml = vm.readFile(path);
bytes memory data = vm.parseToml(toml);
notSimpleJson memory decodedData = abi.decode(data, (notSimpleJson));
notSimpleToml memory decodedData = abi.decode(data, (notSimpleToml));
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));
notSimpleToml memory decodedData = abi.decode(data, (notSimpleToml));
console.log(decodedData.a);
assertEq(decodedData.a, 123);
}
Expand All @@ -286,24 +392,24 @@ contract WriteTomlTest is DSTest {
}

function test_writeToml() public {
string memory json3 = "json3";
string memory toml3 = "toml3";
string memory path = "fixtures/Toml/write_test.toml";
vm.serializeUint(json3, "a", uint256(123));
string memory finalJson = vm.serializeString(json3, "b", "test");
vm.writeToml(finalJson, path);
vm.serializeUint(toml3, "a", uint256(123));
string memory finalToml = vm.serializeString(toml3, "b", "test");
vm.writeToml(finalToml, path);

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

// write json3 to key b
vm.writeToml(finalJson, path, ".b");
// write toml3 to key b
vm.writeToml(finalToml, path, ".b");
// read again
toml = vm.readFile(path);
data = vm.parseToml(toml, ".b");
decodedData = abi.decode(data, (simpleJson));
decodedData = abi.decode(data, (simpleToml));
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"
Loading
Loading