@@ -10,6 +10,7 @@ import {ECDSA} from "solady/utils/ECDSA.sol";
1010import {SignatureCheckerLib} from "solady/utils/SignatureCheckerLib.sol " ;
1111import {P256} from "solady/utils/P256.sol " ;
1212import {WebAuthn} from "solady/utils/WebAuthn.sol " ;
13+ import {LibStorage} from "solady/utils/LibStorage.sol " ;
1314import {EnumerableSetLib} from "solady/utils/EnumerableSetLib.sol " ;
1415import {GuardedExecutor} from "./GuardedExecutor.sol " ;
1516import {TokenTransferLib} from "./TokenTransferLib.sol " ;
@@ -21,6 +22,7 @@ contract Delegation is EIP712, GuardedExecutor {
2122 using EnumerableSetLib for * ;
2223 using LibBytes for LibBytes.BytesStorage;
2324 using LibBitmap for LibBitmap.Bitmap;
25+ using LibStorage for LibStorage.Bump;
2426
2527 ////////////////////////////////////////////////////////////////////////
2628 // Data Structures
@@ -51,6 +53,13 @@ contract Delegation is EIP712, GuardedExecutor {
5153 // Storage
5254 ////////////////////////////////////////////////////////////////////////
5355
56+ /// @dev This struct contains extra data for a given key hash.
57+ struct KeyExtraStorage {
58+ /// @dev The `msg.senders` that can use `isValidSignature`
59+ /// to successfully validate a signature for a given key hash.
60+ EnumerableSetLib.AddressSet checkers;
61+ }
62+
5463 /// @dev Holds the storage.
5564 struct DelegationStorage {
5665 /// @dev The label.
@@ -63,6 +72,8 @@ contract Delegation is EIP712, GuardedExecutor {
6372 EnumerableSetLib.Bytes32Set keyHashes;
6473 /// @dev Mapping of key hash to the key in encoded form.
6574 mapping (bytes32 => LibBytes.BytesStorage) keyStorage;
75+ /// @dev Mapping of key hash to the key's extra storage.
76+ mapping (bytes32 => LibStorage.Bump) keyExtraStorage;
6677 /// @dev Set of approved implementations for delegate calls.
6778 EnumerableSetLib.AddressSet approvedImplementations;
6879 }
@@ -76,6 +87,18 @@ contract Delegation is EIP712, GuardedExecutor {
7687 }
7788 }
7889
90+ /// @dev Returns the storage pointer.
91+ function _getKeyExtraStorage (bytes32 keyHash )
92+ internal
93+ view
94+ returns (KeyExtraStorage storage $)
95+ {
96+ bytes32 s = _getDelegationStorage ().keyExtraStorage[keyHash].slot ();
97+ assembly ("memory-safe" ) {
98+ $.slot := s
99+ }
100+ }
101+
79102 ////////////////////////////////////////////////////////////////////////
80103 // Errors
81104 ////////////////////////////////////////////////////////////////////////
@@ -92,6 +115,9 @@ contract Delegation is EIP712, GuardedExecutor {
92115 /// @dev There are too many approved implementations.
93116 error ExceededApprovedImplementationsCapacity ();
94117
118+ /// @dev There are too many signature checkers.
119+ error ExceededSignatureCheckersCapacity ();
120+
95121 ////////////////////////////////////////////////////////////////////////
96122 // Events
97123 ////////////////////////////////////////////////////////////////////////
@@ -114,12 +140,17 @@ contract Delegation is EIP712, GuardedExecutor {
114140 /// @dev The nonce salt has been incremented to `newNonceSalt`.
115141 event NonceSaltIncremented (uint256 newNonceSalt );
116142
143+ /// @dev The `checker` has been authorized to use `isValidSignature` for `keyHash`.
144+ event SignatureCheckerApprovalSet (
145+ bytes32 indexed keyHash , address indexed checker , bool isApproved
146+ );
147+
117148 ////////////////////////////////////////////////////////////////////////
118149 // Constants
119150 ////////////////////////////////////////////////////////////////////////
120151
121152 /// @dev The entry point address.
122- address public constant ENTRY_POINT = 0x00000000aC830f1181F6aAb6862E71EDc248941C ;
153+ address public constant ENTRY_POINT = 0x307AF7d28AfEE82092aA95D35644898311CA5360 ;
123154
124155 /// @dev For EIP712 signature digest calculation for the `execute` function.
125156 bytes32 public constant EXECUTE_TYPEHASH = keccak256 (
@@ -148,7 +179,11 @@ contract Delegation is EIP712, GuardedExecutor {
148179 virtual
149180 returns (bytes4 )
150181 {
151- (bool isValid ,) = _unwrapAndValidateSignature (digest, signature, true );
182+ (bool isValid , bytes32 keyHash ) = _unwrapAndValidateSignature (digest, signature);
183+ if (LibBit.and (keyHash != 0 , isValid)) {
184+ isValid = getKey (keyHash).isSuperAdmin
185+ || _getKeyExtraStorage (keyHash).checkers.contains (msg .sender );
186+ }
152187 // `bytes4(keccak256("isValidSignature(bytes32,bytes)")) = 0x1626ba7e`.
153188 // We use `0xffffffff` for invalid, in convention with the reference implementation.
154189 return bytes4 (isValid ? 0x1626ba7e : 0xffffffff );
@@ -196,6 +231,23 @@ contract Delegation is EIP712, GuardedExecutor {
196231 emit ImplementationApprovalSet (implementation, isApproved);
197232 }
198233
234+ /// @dev Sets whether `checker` can use `isValidSignature` to successfully validate
235+ /// a signature for a given key hash.
236+ function setSignatureCheckerApproval (bytes32 keyHash , address checker , bool isApproved )
237+ public
238+ virtual
239+ onlyThis
240+ {
241+ EnumerableSetLib.AddressSet storage checkers = _getKeyExtraStorage (keyHash).checkers;
242+ if (isApproved) {
243+ checkers.add (checker);
244+ if (checkers.length () > 512 ) revert ExceededSignatureCheckersCapacity ();
245+ } else {
246+ checkers.remove (checker);
247+ }
248+ emit SignatureCheckerApprovalSet (keyHash, checker, isApproved);
249+ }
250+
199251 /// @dev Invalidates the nonce.
200252 function invalidateNonce (uint256 nonce ) public virtual onlyThis {
201253 _invalidateNonce (nonce);
@@ -263,16 +315,21 @@ contract Delegation is EIP712, GuardedExecutor {
263315 return EfficientHashLib.hash (uint8 (key.keyType), uint256 (keccak256 (key.publicKey)));
264316 }
265317
266- /// @dev Returns whether `implementation` is approved.
267- function implementationIsApproved (address implementation ) public view virtual returns (bool ) {
268- return _getDelegationStorage ().approvedImplementations.contains (implementation);
269- }
270-
271318 /// @dev Returns the list of approved implementations.
272319 function approvedImplementations () public view virtual returns (address [] memory ) {
273320 return _getDelegationStorage ().approvedImplementations.values ();
274321 }
275322
323+ /// @dev Returns the list of approved signature checkers for `keyHash`.
324+ function approvedSignatureCheckers (bytes32 keyHash )
325+ public
326+ view
327+ virtual
328+ returns (address [] memory )
329+ {
330+ return _getKeyExtraStorage (keyHash).checkers.values ();
331+ }
332+
276333 /// @dev Computes the EIP712 digest for `calls`, with `nonceSalt` from storage.
277334 /// If the nonce is odd, the digest will be computed without the chain ID.
278335 /// Otherwise, the digest will be computed with the chain ID.
@@ -336,6 +393,7 @@ contract Delegation is EIP712, GuardedExecutor {
336393 function _removeKey (bytes32 keyHash ) internal virtual {
337394 DelegationStorage storage $ = _getDelegationStorage ();
338395 $.keyStorage[keyHash].clear ();
396+ $.keyExtraStorage[keyHash].invalidate ();
339397 if (! $.keyHashes.remove (keyHash)) revert KeyDoesNotExist ();
340398 }
341399
@@ -359,17 +417,18 @@ contract Delegation is EIP712, GuardedExecutor {
359417 virtual
360418 returns (bool isValid , bytes32 keyHash )
361419 {
362- return _unwrapAndValidateSignature (digest, signature, false );
420+ return _unwrapAndValidateSignature (digest, signature);
363421 }
364422
365423 /// @dev Returns if the signature is valid, along with its `keyHash`.
366424 /// The `signature` is a wrapped signature, given by
367425 /// `abi.encodePacked(bytes(innerSignature), bytes32(keyHash), bool(prehash))`.
368- function _unwrapAndValidateSignature (
369- bytes32 digest ,
370- bytes calldata signature ,
371- bool requireSuperAdmin
372- ) internal view virtual returns (bool isValid , bytes32 keyHash ) {
426+ function _unwrapAndValidateSignature (bytes32 digest , bytes calldata signature )
427+ internal
428+ view
429+ virtual
430+ returns (bool isValid , bytes32 keyHash )
431+ {
373432 // If the signature's length is 64 or 65, treat it like an secp256k1 signature.
374433 if (LibBit.or (signature.length == 64 , signature.length == 65 )) {
375434 return (ECDSA.recoverCalldata (digest, signature) == address (this ), 0 );
@@ -389,8 +448,6 @@ contract Delegation is EIP712, GuardedExecutor {
389448 }
390449 Key memory key = getKey (keyHash);
391450
392- if (LibBit.and (requireSuperAdmin, ! key.isSuperAdmin)) return (false , keyHash);
393-
394451 // Early return if the key has expired.
395452 if (LibBit.and (key.expiry != 0 , block .timestamp > key.expiry)) return (false , keyHash);
396453
0 commit comments