-
Notifications
You must be signed in to change notification settings - Fork 1.8k
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
feat: more flexible JSON parsing #8345
Conversation
Advantage of using EIP-712 is that we can reuse some of existing alloy structures for it and it's also pretty much human readable and compact. However, it has some limitations
/// @param number forge-json: hex
/// @param inner forge-json: flatten
struct SomeStruct {
uint256 number;
AnotherStruct inner;
} However, all of this can be added in the future by just allowing more formats for |
Added cheatcode for serialization and Example output for project with 1 struct. In case of multiple structs and duplicating names, those are getting resolved correctly pragma solidity >=0.6.2 <0.9.0;
pragma experimental ABIEncoderV2;
import {Vm} from "forge-std/Vm.sol";
import {SomeStruct} from "src/SomeStruct.sol";
library JsonBindings {
Vm constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code")))));
string constant schema_SomeStruct = "SomeStruct(uint256 a,uint256 b)";
function serialize(SomeStruct memory value) internal pure returns (string memory) {
return vm.serializeJsonType(schema_SomeStruct, abi.encode(value));
}
function deserializeSomeStruct(string memory json) public pure returns (SomeStruct memory) {
return abi.decode(vm.parseJsonType(json, schema_SomeStruct), (SomeStruct));
}
function deserializeSomeStruct(string memory json, string memory path) public pure returns (SomeStruct memory) {
return abi.decode(vm.parseJsonType(json, path, schema_SomeStruct), (SomeStruct));
}
function deserializeSomeStructArray(string memory json, string memory path) public pure returns (SomeStruct[] memory) {
return abi.decode(vm.parseJsonTypeArray(json, path, schema_SomeStruct), (SomeStruct[]));
}
}
bindings are written to import "../utils/JsonBindings.sol";
import "forge-std/Test.sol";
struct SomeStruct {
uint256 a;
uint256 b;
}
contract SomeStructJsonTest is Test {
using JsonBindings for *;
function test() public {
SomeStruct memory s = SomeStruct(1, 2);
string memory json = s.serialize();
SomeStruct memory s2 = json.deserializeSomeStruct();
assertEq(keccak256(abi.encode(s)), keccak256(abi.encode(s2)));
}
} The command itself is pretty straightforward - just walk the AST and write the methods, the only non-trivial part is preprocessing which we need to obtain valid AST from solc even when bindings are outdated and would cause compiler to fail normally. We rely on solang parser for this. I think it might make sense to add more configuration for this, maybe even a config section. Currently for large codebases bindings file is ending up pretty large and there are also some issues with multi-version projects which I've addressed by only generating bindings for a single compiler input |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice, this is pretty cool!
still need to take a closer look but I believe this should work, and the helper commands should make this convenient to use.
if possible, we def want some forgetest! for this as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice work!
only have a few questions/suggestions
Added config section:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm pending @mattsse
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
gg!
Motivation
closes #8326
Solution
Adds
parseJsonType
set of cheatcodes which accepts stringified Solidity type as guidance for the parser, allowing to express complex types without the need to resort to parsing of them in multipleparseJson*
calls.Example usage of such cheatcode:
For simpler creation of type strings for structs added
forge eip712 <path>
command which creates EIP-712encodeType
string for all structs in the given file.Example output:
This might also be useful for #4818 later
Will be working on serialization next