Skip to content

Commit

Permalink
✨ Added _domainSeparatorWithoutChainId()
Browse files Browse the repository at this point in the history
  • Loading branch information
atarpara committed Jan 31, 2025
1 parent 5909ddd commit d1b1f62
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 18 deletions.
69 changes: 51 additions & 18 deletions src/utils/EIP712.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ abstract contract EIP712 {
bytes32 internal constant _DOMAIN_TYPEHASH =
0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;

/// @dev `keccak256("EIP712Domain(string name,string version,address verifyingContract)")`.
bytes32 internal constant _DOMAIN_TYPEHASH_WITHOUT_CHAINID =
0x91ab3d17e3a50a9d89e63fd30b92be7f5336b03b287bb946787a83a9d62a2766;

uint256 private immutable _cachedThis;
uint256 private immutable _cachedChainId;
bytes32 private immutable _cachedNameHash;
Expand Down Expand Up @@ -50,15 +54,27 @@ abstract contract EIP712 {

bytes32 separator;
if (!_domainNameAndVersionMayChange()) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(m, _DOMAIN_TYPEHASH)
mstore(add(m, 0x20), nameHash)
mstore(add(m, 0x40), versionHash)
mstore(add(m, 0x60), chainid())
mstore(add(m, 0x80), address())
separator := keccak256(m, 0xa0)
if (!_domainSeparatorWithoutChainId()) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(m, _DOMAIN_TYPEHASH)
mstore(add(m, 0x20), nameHash)
mstore(add(m, 0x40), versionHash)
mstore(add(m, 0x60), chainid())
mstore(add(m, 0x80), address())
separator := keccak256(m, 0xa0)
}
} else {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(m, _DOMAIN_TYPEHASH_WITHOUT_CHAINID)
mstore(add(m, 0x20), nameHash)
mstore(add(m, 0x40), versionHash)
mstore(add(m, 0x80), address())
separator := keccak256(m, 0x80)
}
}
}
_cachedDomainSeparator = separator;
Expand Down Expand Up @@ -94,6 +110,10 @@ abstract contract EIP712 {
/// Default: false.
function _domainNameAndVersionMayChange() internal pure virtual returns (bool result) {}

/// @dev Returns if `_domainSeparator()` without chain Id.
/// Default: false.
function _domainSeparatorWithoutChainId() internal pure virtual returns (bool result) {}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HASHING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
Expand Down Expand Up @@ -161,6 +181,7 @@ abstract contract EIP712 {
)
{
fields = hex"0f"; // `0b01111`.
if (_domainSeparatorWithoutChainId()) fields = hex"0b"; // `0b01011`.
(name, version) = _domainNameAndVersion();
chainId = block.chainid;
verifyingContract = address(this);
Expand All @@ -184,15 +205,27 @@ abstract contract EIP712 {
separator = _cachedNameHash;
versionHash = _cachedVersionHash;
}
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(m, _DOMAIN_TYPEHASH)
mstore(add(m, 0x20), separator) // Name hash.
mstore(add(m, 0x40), versionHash)
mstore(add(m, 0x60), chainid())
mstore(add(m, 0x80), address())
separator := keccak256(m, 0xa0)
if (!_domainSeparatorWithoutChainId()) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(m, _DOMAIN_TYPEHASH)
mstore(add(m, 0x20), separator) // Name hash.
mstore(add(m, 0x40), versionHash)
mstore(add(m, 0x60), chainid())
mstore(add(m, 0x80), address())
separator := keccak256(m, 0xa0)
}
} else {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(m, _DOMAIN_TYPEHASH_WITHOUT_CHAINID)
mstore(add(m, 0x20), separator) // Name hash.
mstore(add(m, 0x40), versionHash)
mstore(add(m, 0x60), address())
separator := keccak256(m, 0x80)
}
}
}

Expand Down
54 changes: 54 additions & 0 deletions test/EIP712.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@ pragma solidity ^0.8.4;
import "./utils/SoladyTest.sol";
import {MockEIP712} from "./utils/mocks/MockEIP712.sol";
import {MockEIP712Dynamic} from "./utils/mocks/MockEIP712Dynamic.sol";
import {MockEIP712DyanmicWithoutChainId} from "./utils/mocks/MockEIP712DyanmicWithoutChainId.sol";
import {LibClone} from "../src/utils/LibClone.sol";

contract EIP712Test is SoladyTest {
MockEIP712 mock;
MockEIP712 mockClone;
MockEIP712DyanmicWithoutChainId mockWithoutChainId;
MockEIP712DyanmicWithoutChainId mockWithoutChainIdClone;
MockEIP712Dynamic mockDynamic;
MockEIP712Dynamic mockDynamicClone;

Expand All @@ -17,6 +20,9 @@ contract EIP712Test is SoladyTest {
mockClone = MockEIP712(LibClone.clone(address(mock)));
mockDynamic = new MockEIP712Dynamic("Milady", "1");
mockDynamicClone = MockEIP712Dynamic(LibClone.clone(address(mockDynamic)));
mockWithoutChainId = new MockEIP712DyanmicWithoutChainId("Milady", "1");
mockWithoutChainIdClone =
MockEIP712DyanmicWithoutChainId(LibClone.clone(address(mockWithoutChainId)));
}

function testHashTypedData() public {
Expand All @@ -35,6 +41,14 @@ contract EIP712Test is SoladyTest {
_testHashTypedDataOnClone(MockEIP712(address(mockDynamicClone)));
}

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

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

function testHashTypedDataWithChaindIdChange() public {
_testHashTypedDataOnClone(mock);
vm.chainId(32123);
Expand Down Expand Up @@ -106,6 +120,14 @@ 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 @@ -135,6 +157,23 @@ 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 @@ -145,6 +184,9 @@ 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 @@ -170,4 +212,16 @@ contract EIP712Test is SoladyTest {
assertEq(t.salt, bytes32(0));
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));
}
}
47 changes: 47 additions & 0 deletions test/utils/mocks/MockEIP712DyanmicWithoutChainId.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "../../../src/utils/EIP712.sol";

/// @dev WARNING! This mock is strictly intended for testing purposes only.
/// Do NOT copy anything here into production code unless you really know what you are doing.
contract MockEIP712DyanmicWithoutChainId is EIP712 {
string private _name;
string private _version;

constructor(string memory name, string memory version) {
_name = name;
_version = version;
}

function setDomainNameAndVersion(string memory name, string memory version) public {
_name = name;
_version = version;
}

function _domainNameAndVersion()
internal
view
override
returns (string memory name, string memory version)
{
name = _name;
version = _version;
}

function _domainNameAndVersionMayChange() internal pure override returns (bool) {
return true;
}

function hashTypedData(bytes32 structHash) external view returns (bytes32) {
return _hashTypedData(structHash);
}

function DOMAIN_SEPARATOR() external view returns (bytes32) {
return _domainSeparator();
}

function _domainSeparatorWithoutChainId() internal pure override returns (bool) {
return true;
}
}

0 comments on commit d1b1f62

Please sign in to comment.