Skip to content

Commit

Permalink
T
Browse files Browse the repository at this point in the history
  • Loading branch information
Vectorized committed Jan 31, 2025
1 parent c360dee commit e75c353
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 61 deletions.
13 changes: 9 additions & 4 deletions src/utils/EIP712.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ abstract contract EIP712 {
0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;

/// @dev `keccak256("EIP712Domain(string name,string version,address verifyingContract)")`.
/// This is only used in `_hashTypedDataWithoutChainId`.
bytes32 internal constant _DOMAIN_TYPEHASH_WITHOUT_CHAINID =
/// This is only used in `_hashTypedDataSansChainId`.
bytes32 internal constant _DOMAIN_TYPEHASH_SANS_CHAIN_ID =
0x91ab3d17e3a50a9d89e63fd30b92be7f5336b03b287bb946787a83a9d62a2766;

uint256 private immutable _cachedThis;
Expand Down Expand Up @@ -148,12 +148,17 @@ abstract contract EIP712 {

/// @dev Variant that excludes the chain ID.
/// Optimized for smaller bytecode size over runtime gas, as it is intended to be used sparingly.
function _hashTypedDataWithoutChainId(bytes32 structHash) internal view virtual returns (bytes32 digest) {
function _hashTypedDataSansChainId(bytes32 structHash)
internal
view
virtual
returns (bytes32 digest)
{
(string memory name, string memory version) = _domainNameAndVersion();
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(0x00, _DOMAIN_TYPEHASH_WITHOUT_CHAINID)
mstore(0x00, _DOMAIN_TYPEHASH_SANS_CHAIN_ID)
mstore(0x20, keccak256(add(name, 0x20), mload(name)))
mstore(0x40, keccak256(add(version, 0x20), mload(version)))
mstore(0x60, address())
Expand Down
134 changes: 77 additions & 57 deletions test/EIP712.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,6 @@ contract EIP712Test is SoladyTest {
_testHashTypedDataOnClone(MockEIP712(address(mockDynamicClone)));
}

function testHashTypedDataWithOutChainId() public {
_testHashTypedDataOnClone(MockEIP712(address(mockWithoutChainId)));
}

function testHashTypedDataOnCloneDynamicWithOutChainId() public {
_testHashTypedDataOnClone(MockEIP712(address(mockWithoutChainIdClone)));
}

function testHashTypedDataWithChaindIdChange() public {
_testHashTypedDataOnClone(mock);
vm.chainId(32123);
Expand All @@ -67,25 +59,40 @@ contract EIP712Test is SoladyTest {
_testHashTypedDataOnClone(MockEIP712(address(mockDynamicClone)));
}

struct _TestTemps {
string name;
string version;
address signer;
address to;
uint256 privateKey;
bytes32 structHash;
bytes32 expectedDigest;
string message;
uint8 v;
bytes32 r;
bytes32 s;
address recoveredAddress;
}

function _testHashTypedDataOnClone(MockEIP712 mockToTest) internal {
(address signer, uint256 privateKey) = _randomSigner();
_TestTemps memory t;
(t.signer, t.privateKey) = _randomSigner();

(address to,) = _randomSigner();
(t.to,) = _randomSigner();

string memory message = "Hello Milady!";
t.message = "Hello Milady!";

bytes32 structHash =
keccak256(abi.encode("Message(address to,string message)", to, message));
bytes32 expectedDigest =
keccak256(abi.encodePacked("\x19\x01", mockToTest.DOMAIN_SEPARATOR(), structHash));
t.structHash = keccak256(abi.encode("Message(address to,string message)", t.to, t.message));
t.expectedDigest =
keccak256(abi.encodePacked("\x19\x01", mockToTest.DOMAIN_SEPARATOR(), t.structHash));

assertEq(mockToTest.hashTypedData(structHash), expectedDigest);
assertEq(mockToTest.hashTypedData(t.structHash), t.expectedDigest);

(uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, expectedDigest);
(t.v, t.r, t.s) = vm.sign(t.privateKey, t.expectedDigest);

address recoveredAddress = ecrecover(expectedDigest, v, r, s);
t.recoveredAddress = ecrecover(t.expectedDigest, t.v, t.r, t.s);

assertEq(recoveredAddress, signer);
assertEq(t.recoveredAddress, t.signer);
}

function testDomainSeparator() public {
Expand Down Expand Up @@ -114,14 +121,6 @@ contract EIP712Test is SoladyTest {
_testDomainSeparator(MockEIP712(address(mockDynamic)));
mockDynamic.setDomainNameAndVersion("Remilio", "2");
_testDomainSeparator(MockEIP712(address(mockDynamic)), "Remilio", "2");

mockWithoutChainId.setDomainNameAndVersion("Remilio", "2");
_testDomainSeparatorWithoutChainId(MockEIP712(address(mockWithoutChainId)), "Remilio", "2");

mockWithoutChainIdClone.setDomainNameAndVersion("Remilio", "2");
_testDomainSeparatorWithoutChainId(
MockEIP712(address(mockWithoutChainIdClone)), "Remilio", "2"
);
}

function testDomainSeparatorOnCloneDynamicWithChainIdChange() public {
Expand Down Expand Up @@ -151,23 +150,6 @@ contract EIP712Test is SoladyTest {
assertEq(mockToTest.DOMAIN_SEPARATOR(), expectedDomainSeparator);
}

function _testDomainSeparatorWithoutChainId(
MockEIP712 mockToTest,
bytes memory name,
bytes memory version
) internal {
bytes32 expectedDomainSeparator = keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,address verifyingContract)"),
keccak256(name),
keccak256(version),
address(mockToTest)
)
);

assertEq(mockToTest.DOMAIN_SEPARATOR(), expectedDomainSeparator);
}

function _testDomainSeparator(MockEIP712 mockToTest) internal {
_testDomainSeparator(mockToTest, "Milady", "1");
}
Expand All @@ -178,9 +160,6 @@ contract EIP712Test is SoladyTest {
vm.chainId(32123);
_testEIP5267(mock);
_testEIP5267(mockClone);
_testEIP5267WithoutChainId(MockEIP712(address(mockWithoutChainId)));
mockWithoutChainIdClone.setDomainNameAndVersion("Milady", "1");
_testEIP5267WithoutChainId(MockEIP712(address(mockWithoutChainIdClone)));
}

struct _testEIP5267Variables {
Expand All @@ -207,15 +186,56 @@ contract EIP712Test is SoladyTest {
assertEq(t.extensions, new uint256[](0));
}

function _testEIP5267WithoutChainId(MockEIP712 mockToTest) public {
_testEIP5267Variables memory t;
(t.fields, t.name, t.version, t.chainId, t.verifyingContract, t.salt, t.extensions) =
mockToTest.eip712Domain();
assertEq(t.fields, hex"0b");
assertEq(t.name, "Milady");
assertEq(t.version, "1");
assertEq(t.verifyingContract, address(mockToTest));
assertEq(t.salt, bytes32(0));
assertEq(t.extensions, new uint256[](0));
function _testHashTypedDataSansChainId(MockEIP712 mockToTest) public {
_TestTemps memory t;
(, t.name, t.version,,,,) = mockToTest.eip712Domain();

(t.signer, t.privateKey) = _randomSigner();

(t.to,) = _randomSigner();

t.message = "Hello Milady!";

t.structHash = keccak256(abi.encode("Message(address to,string message)", t.to, t.message));
t.expectedDigest = keccak256(
abi.encodePacked(
"\x19\x01",
keccak256(
abi.encode(
keccak256(
"EIP712Domain(string name,string version,address verifyingContract)"
),
keccak256(bytes(t.name)),
keccak256(bytes(t.version)),
address(mockToTest)
)
),
t.structHash
)
);

assertEq(mockToTest.hashTypedDataSansChainId(t.structHash), t.expectedDigest);

(t.v, t.r, t.s) = vm.sign(t.privateKey, t.expectedDigest);

t.recoveredAddress = ecrecover(t.expectedDigest, t.v, t.r, t.s);

assertEq(t.recoveredAddress, t.signer);
}

function testHashTypedDataSansChainId() public {
_testHashTypedDataSansChainId(mock);
}

function testHashTypedDataSansChainIdOnClone() public {
_testHashTypedDataSansChainId(mockClone);
}

function testHashTypedDataSansChainIdOnDynamic() public {
_testHashTypedDataSansChainId(MockEIP712(address(mockDynamic)));
}

function testHashTypedDataSansChainIdOnDynamicClone() public {
_testHashTypedDataSansChainId(MockEIP712(address(mockDynamicClone)));
}
}
4 changes: 4 additions & 0 deletions test/utils/mocks/MockEIP712.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,8 @@ contract MockEIP712 is EIP712 {
function DOMAIN_SEPARATOR() external view returns (bytes32) {
return _domainSeparator();
}

function hashTypedDataSansChainId(bytes32 structHash) external view returns (bytes32) {
return _hashTypedDataSansChainId(structHash);
}
}
4 changes: 4 additions & 0 deletions test/utils/mocks/MockEIP712Dynamic.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,8 @@ contract MockEIP712Dynamic is EIP712 {
function DOMAIN_SEPARATOR() external view returns (bytes32) {
return _domainSeparator();
}

function hashTypedDataSansChainId(bytes32 structHash) external view returns (bytes32) {
return _hashTypedDataSansChainId(structHash);
}
}

0 comments on commit e75c353

Please sign in to comment.