@@ -13,6 +13,8 @@ import {Strings} from "../Strings.sol";
1313 * specifications.
1414 */
1515library MessageHashUtils {
16+ error ERC5267ExtensionsNotSupported ();
17+
1618 /**
1719 * @dev Returns the keccak256 digest of an ERC-191 signed data with version
1820 * `0x45` (`personal_sign` messages).
@@ -96,4 +98,131 @@ library MessageHashUtils {
9698 digest := keccak256 (ptr, 0x42 )
9799 }
98100 }
101+
102+ /**
103+ * @dev Returns the EIP-712 domain separator constructed from an `eip712Domain`. See {IERC5267-eip712Domain}
104+ *
105+ * This function dynamically constructs the domain separator based on which fields are present in the
106+ * `fields` parameter. It contains flags that indicate which domain fields are present:
107+ *
108+ * * Bit 0 (0x01): name
109+ * * Bit 1 (0x02): version
110+ * * Bit 2 (0x04): chainId
111+ * * Bit 3 (0x08): verifyingContract
112+ * * Bit 4 (0x10): salt
113+ *
114+ * Arguments that correspond to fields which are not present in `fields` are ignored. For example, if `fields` is
115+ * `0x0f` (`0b01111`), then the `salt` parameter is ignored.
116+ */
117+ function toDomainSeparator (
118+ bytes1 fields ,
119+ string memory name ,
120+ string memory version ,
121+ uint256 chainId ,
122+ address verifyingContract ,
123+ bytes32 salt
124+ ) internal pure returns (bytes32 hash ) {
125+ return
126+ toDomainSeparator (
127+ fields,
128+ keccak256 (bytes (name)),
129+ keccak256 (bytes (version)),
130+ chainId,
131+ verifyingContract,
132+ salt
133+ );
134+ }
135+
136+ /// @dev Variant of {toDomainSeparator-bytes1-string-string-uint256-address-bytes32} that uses hashed name and version.
137+ function toDomainSeparator (
138+ bytes1 fields ,
139+ bytes32 nameHash ,
140+ bytes32 versionHash ,
141+ uint256 chainId ,
142+ address verifyingContract ,
143+ bytes32 salt
144+ ) internal pure returns (bytes32 hash ) {
145+ bytes32 domainTypeHash = toDomainTypeHash (fields);
146+
147+ assembly ("memory-safe" ) {
148+ // align fields to the right for easy processing
149+ fields := shr (248 , fields)
150+
151+ // FMP used as scratch space
152+ let fmp := mload (0x40 )
153+ mstore (fmp, domainTypeHash)
154+
155+ let ptr := add (fmp, 0x20 )
156+ if and (fields, 0x01 ) {
157+ mstore (ptr, nameHash)
158+ ptr := add (ptr, 0x20 )
159+ }
160+ if and (fields, 0x02 ) {
161+ mstore (ptr, versionHash)
162+ ptr := add (ptr, 0x20 )
163+ }
164+ if and (fields, 0x04 ) {
165+ mstore (ptr, chainId)
166+ ptr := add (ptr, 0x20 )
167+ }
168+ if and (fields, 0x08 ) {
169+ mstore (ptr, verifyingContract)
170+ ptr := add (ptr, 0x20 )
171+ }
172+ if and (fields, 0x10 ) {
173+ mstore (ptr, salt)
174+ ptr := add (ptr, 0x20 )
175+ }
176+
177+ hash := keccak256 (fmp, sub (ptr, fmp))
178+ }
179+ }
180+
181+ /// @dev Builds an EIP-712 domain type hash depending on the `fields` provided, following https://eips.ethereum.org/EIPS/eip-5267[ERC-5267]
182+ function toDomainTypeHash (bytes1 fields ) internal pure returns (bytes32 hash ) {
183+ if (fields & 0x20 == 0x20 ) revert ERC5267ExtensionsNotSupported ();
184+
185+ assembly ("memory-safe" ) {
186+ // align fields to the right for easy processing
187+ fields := shr (248 , fields)
188+
189+ // FMP used as scratch space
190+ let fmp := mload (0x40 )
191+ mstore (fmp, "EIP712Domain( " )
192+
193+ let ptr := add (fmp, 0x0d )
194+ // name field
195+ if and (fields, 0x01 ) {
196+ mstore (ptr, "string name, " )
197+ ptr := add (ptr, 0x0c )
198+ }
199+ // version field
200+ if and (fields, 0x02 ) {
201+ mstore (ptr, "string version, " )
202+ ptr := add (ptr, 0x0f )
203+ }
204+ // chainId field
205+ if and (fields, 0x04 ) {
206+ mstore (ptr, "uint256 chainId, " )
207+ ptr := add (ptr, 0x10 )
208+ }
209+ // verifyingContract field
210+ if and (fields, 0x08 ) {
211+ mstore (ptr, "address verifyingContract, " )
212+ ptr := add (ptr, 0x1a )
213+ }
214+ // salt field
215+ if and (fields, 0x10 ) {
216+ mstore (ptr, "bytes32 salt, " )
217+ ptr := add (ptr, 0x0d )
218+ }
219+ // if any field is enabled, remove the trailing comma
220+ ptr := sub (ptr, iszero (iszero (and (fields, 0x1f ))))
221+ // add the closing brace
222+ mstore8 (ptr, 0x29 ) // add closing brace
223+ ptr := add (ptr, 1 )
224+
225+ hash := keccak256 (fmp, sub (ptr, fmp))
226+ }
227+ }
99228}
0 commit comments