Skip to content

Commit 4e81e48

Browse files
authored
⚡️ Optimize EIP712 (#322)
1 parent 8def786 commit 4e81e48

File tree

2 files changed

+34
-38
lines changed

2 files changed

+34
-38
lines changed

.gas-snapshot

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -104,15 +104,15 @@ ECDSATest:testTryRecoverWithValidSignature() (gas: 5262)
104104
ECDSATest:testTryRecoverWithWrongSigner() (gas: 5255)
105105
ECDSATest:test__codesize() (gas: 11541)
106106
EIP712Test:testDomainSeparator() (gas: 5787)
107-
EIP712Test:testDomainSeparatorOnClone() (gas: 8897)
108-
EIP712Test:testDomainSeparatorOnCloneWithChainIdChange() (gas: 13511)
109-
EIP712Test:testDomainSeparatorWithChainIdChange() (gas: 10163)
110-
EIP712Test:testEIP5267() (gas: 35332)
107+
EIP712Test:testDomainSeparatorOnClone() (gas: 8537)
108+
EIP712Test:testDomainSeparatorOnCloneWithChainIdChange() (gas: 12791)
109+
EIP712Test:testDomainSeparatorWithChainIdChange() (gas: 9803)
110+
EIP712Test:testEIP5267() (gas: 35284)
111111
EIP712Test:testHashTypedData() (gas: 37972)
112-
EIP712Test:testHashTypedDataOnClone() (gas: 41571)
113-
EIP712Test:testHashTypedDataOnCloneWithChaindIdChange() (gas: 51900)
114-
EIP712Test:testHashTypedDataWithChaindIdChange() (gas: 48107)
115-
EIP712Test:test__codesize() (gas: 9515)
112+
EIP712Test:testHashTypedDataOnClone() (gas: 40851)
113+
EIP712Test:testHashTypedDataOnCloneWithChaindIdChange() (gas: 50460)
114+
EIP712Test:testHashTypedDataWithChaindIdChange() (gas: 47387)
115+
EIP712Test:test__codesize() (gas: 9350)
116116
FixedPointMathLibTest:testAbs() (gas: 577)
117117
FixedPointMathLibTest:testAbs(int256) (runs: 256, μ: 515, ~: 484)
118118
FixedPointMathLibTest:testAbsEdgeCases() (gas: 433)

src/utils/EIP712.sol

Lines changed: 26 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,40 @@ abstract contract EIP712 {
1616

1717
address private immutable _cachedThis;
1818
uint256 private immutable _cachedChainId;
19+
bytes32 private immutable _cachedNameHash;
20+
bytes32 private immutable _cachedVersionHash;
1921
bytes32 private immutable _cachedDomainSeparator;
2022

2123
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
2224
/* CONSTRUCTOR */
2325
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
2426

25-
/// @dev Cache the domain separator for cheaper runtime gas costs in
26-
/// non-upgradeable contracts. In the case of upgradeable contracts
27-
/// (i.e. proxies), or if the chain id changes due to a hard fork,
27+
/// @dev Cache the hashes for cheaper runtime gas costs.
28+
/// In the case of upgradeable contracts (i.e. proxies),
29+
/// or if the chain id changes due to a hard fork,
2830
/// the domain separator will be seamlessly calculated on-the-fly.
2931
constructor() {
3032
_cachedThis = address(this);
3133
_cachedChainId = block.chainid;
32-
_cachedDomainSeparator = _buildDomainSeparator();
34+
35+
(string memory name, string memory version) = _domainNameAndVersion();
36+
bytes32 nameHash = keccak256(bytes(name));
37+
bytes32 versionHash = keccak256(bytes(version));
38+
_cachedNameHash = nameHash;
39+
_cachedVersionHash = versionHash;
40+
41+
bytes32 separator;
42+
/// @solidity memory-safe-assembly
43+
assembly {
44+
let m := mload(0x40) // Load the free memory pointer.
45+
mstore(m, _DOMAIN_TYPEHASH)
46+
mstore(add(m, 0x20), nameHash)
47+
mstore(add(m, 0x40), versionHash)
48+
mstore(add(m, 0x60), chainid())
49+
mstore(add(m, 0x80), address())
50+
separator := keccak256(m, 0xa0)
51+
}
52+
_cachedDomainSeparator = separator;
3353
}
3454

3555
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
@@ -54,31 +74,6 @@ abstract contract EIP712 {
5474
virtual
5575
returns (string memory name, string memory version);
5676

57-
/// @dev You may override this function to directly return the `nameHash`
58-
/// and the `versionHash` for gas savings if the domain separator has to be
59-
/// computed on-the-fly (in upgradeable contracts, or if chain id changes).
60-
/// ```
61-
/// function _domainNameAndVersionHashes()
62-
/// internal
63-
/// pure
64-
/// virtual
65-
/// returns (bytes32 nameHash, bytes32 versionHash)
66-
/// {
67-
/// nameHash = keccak256(bytes("Solady"));
68-
/// versionHash = keccak256(bytes("1"));
69-
/// }
70-
/// ```
71-
function _domainNameAndVersionHashes()
72-
internal
73-
pure
74-
virtual
75-
returns (bytes32 nameHash, bytes32 versionHash)
76-
{
77-
(string memory name, string memory version) = _domainNameAndVersion();
78-
nameHash = keccak256(bytes(name));
79-
versionHash = keccak256(bytes(version));
80-
}
81-
8277
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
8378
/* HASHING OPERATIONS */
8479
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
@@ -154,7 +149,8 @@ abstract contract EIP712 {
154149

155150
/// @dev Returns the EIP-712 domain separator.
156151
function _buildDomainSeparator() private view returns (bytes32 separator) {
157-
(bytes32 nameHash, bytes32 versionHash) = _domainNameAndVersionHashes();
152+
bytes32 nameHash = _cachedNameHash;
153+
bytes32 versionHash = _cachedVersionHash;
158154
/// @solidity memory-safe-assembly
159155
assembly {
160156
let m := mload(0x40) // Load the free memory pointer.

0 commit comments

Comments
 (0)