|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
| 2 | + |
| 3 | +pragma solidity ^0.8.0; |
| 4 | + |
| 5 | +import {BaseHookTarget} from "./BaseHookTarget.sol"; |
| 6 | +import {SelectorAccessControl} from "../AccessControl/SelectorAccessControl.sol"; |
| 7 | + |
| 8 | +interface IKeyringCredentials { |
| 9 | + function checkCredential(address, uint32) external view returns (bool); |
| 10 | +} |
| 11 | + |
| 12 | +/// @title HookTargetAccessControlKeyring |
| 13 | +/// @custom:security-contact security@euler.xyz |
| 14 | +/// @author Euler Labs (https://www.eulerlabs.com/) |
| 15 | +/// @notice Hook target contract that allows specific functions on the vault to be called only by authorized callers. |
| 16 | +contract HookTargetAccessControlKeyring is BaseHookTarget, SelectorAccessControl { |
| 17 | + /// @notice The Keyring contract used for credential checking |
| 18 | + IKeyringCredentials public immutable keyring; |
| 19 | + |
| 20 | + /// @notice The policy ID used when checking credentials against the Keyring contract |
| 21 | + uint32 public immutable policyId; |
| 22 | + |
| 23 | + /// @notice Initializes the HookTargetAccessControlKeyring contract |
| 24 | + /// @param _evc The address of the EVC. |
| 25 | + /// @param _admin The address to be granted the DEFAULT_ADMIN_ROLE. |
| 26 | + /// @param _eVaultFactory The address of the EVault factory. |
| 27 | + /// @param _keyring The address of the Keyring contract. |
| 28 | + /// @param _policyId The policy ID to be used for credential checking. |
| 29 | + constructor(address _evc, address _admin, address _eVaultFactory, address _keyring, uint32 _policyId) |
| 30 | + BaseHookTarget(_eVaultFactory) |
| 31 | + SelectorAccessControl(_evc, _admin) |
| 32 | + { |
| 33 | + keyring = IKeyringCredentials(_keyring); |
| 34 | + policyId = _policyId; |
| 35 | + } |
| 36 | + |
| 37 | + /// @notice Fallback function to revert if the address is not whitelisted to call the selector. |
| 38 | + fallback() external { |
| 39 | + _authenticateCaller(); |
| 40 | + } |
| 41 | + |
| 42 | + /// @notice Intercepts EVault deposit operations to authenticate the caller and the receiver |
| 43 | + /// @param receiver The address that will receive shares |
| 44 | + function deposit(uint256, address receiver) external view { |
| 45 | + _authenticateCallerAndAccount(receiver); |
| 46 | + } |
| 47 | + |
| 48 | + /// @notice Intercepts EVault mint operations to authenticate the caller and the receiver |
| 49 | + /// @param receiver The address that will receive shares |
| 50 | + function mint(uint256, address receiver) external view { |
| 51 | + _authenticateCallerAndAccount(receiver); |
| 52 | + } |
| 53 | + |
| 54 | + /// @notice Intercepts EVault withdraw operations to authenticate the caller and the owner |
| 55 | + /// @param owner The address whose balance will change |
| 56 | + function withdraw(uint256, address, address owner) external view { |
| 57 | + _authenticateCallerAndAccount(owner); |
| 58 | + } |
| 59 | + |
| 60 | + /// @notice Intercepts EVault redeem operations to authenticate the caller and the owner |
| 61 | + /// @param owner The address whose balance will change |
| 62 | + function redeem(uint256, address, address owner) external view { |
| 63 | + _authenticateCallerAndAccount(owner); |
| 64 | + } |
| 65 | + |
| 66 | + /// @notice Intercepts EVault skim operations to authenticate the caller and the receiver |
| 67 | + /// @param receiver The address that will receive shares |
| 68 | + function skim(uint256, address receiver) external view { |
| 69 | + _authenticateCallerAndAccount(receiver); |
| 70 | + } |
| 71 | + |
| 72 | + /// @notice Intercepts EVault borrow operations to authenticate the caller and the receiver |
| 73 | + /// @param receiver The address that will receive borrowed assets |
| 74 | + function borrow(uint256, address receiver) external view { |
| 75 | + _authenticateCallerAndAccount(receiver); |
| 76 | + } |
| 77 | + |
| 78 | + /// @notice Intercepts EVault repay operations to authenticate the caller and the receiver |
| 79 | + /// @param receiver The address that will receive the repaid assets |
| 80 | + function repay(uint256, address receiver) external view { |
| 81 | + _authenticateCallerAndAccount(receiver); |
| 82 | + } |
| 83 | + |
| 84 | + /// @notice Intercepts EVault repayWithShares operations to authenticate the caller and the receiver |
| 85 | + /// @param receiver The address that will receive the repaid assets |
| 86 | + function repayWithShares(uint256, address receiver) external view { |
| 87 | + _authenticateCallerAndAccount(receiver); |
| 88 | + } |
| 89 | + |
| 90 | + /// @notice Intercepts EVault pullDebt operations to authenticate the caller and the from address |
| 91 | + /// @param from The address from which debt is being pulled |
| 92 | + function pullDebt(uint256, address from) external view { |
| 93 | + _authenticateCallerAndAccount(from); |
| 94 | + } |
| 95 | + |
| 96 | + /// @notice Checks if the EVC owner of the account has valid Keyring credential |
| 97 | + /// @dev If the EVC owner is not registered yet, the account is assumed to be the owner |
| 98 | + /// @param account The address to check credential for |
| 99 | + /// @return bool True if the EVC owner of the account has valid Keyring credential |
| 100 | + function checkKeyringCredential(address account) external view returns (bool) { |
| 101 | + address owner = evc.getAccountOwner(account); |
| 102 | + return keyring.checkCredential(owner == address(0) ? account : owner, policyId); |
| 103 | + } |
| 104 | + |
| 105 | + /// @notice Authenticates both the caller and the specified account for access control |
| 106 | + /// @dev This function checks if either the caller or the account owner are authorized to call the function |
| 107 | + /// @param account The account to be authenticated |
| 108 | + function _authenticateCallerAndAccount(address account) internal view { |
| 109 | + address caller = _msgSender(); |
| 110 | + |
| 111 | + // Skip Keyring authentication if caller has wildcard role or specific function selector role |
| 112 | + if (hasRole(WILD_CARD, caller) || hasRole(msg.sig, caller)) return; |
| 113 | + |
| 114 | + address owner = evc.getAccountOwner(caller); |
| 115 | + |
| 116 | + // If the EVC owner is not registered yet, assume the caller is the owner |
| 117 | + if (owner == address(0)) owner = caller; |
| 118 | + if (!keyring.checkCredential(owner, policyId)) revert NotAuthorized(); |
| 119 | + |
| 120 | + // If caller and account don't share the same EVC owner, authenticate the account separately |
| 121 | + if (!_haveCommonOwner(owner, account)) { |
| 122 | + owner = evc.getAccountOwner(account); |
| 123 | + |
| 124 | + // If the EVC owner is not registered yet, assume the account is the owner |
| 125 | + if (owner == address(0)) owner = account; |
| 126 | + if (!keyring.checkCredential(owner, policyId)) revert NotAuthorized(); |
| 127 | + } |
| 128 | + } |
| 129 | + |
| 130 | + /// @notice Retrieves the message sender in the context of the EVC or calling vault. |
| 131 | + /// @dev If the caller is a vault deployed by the recognized EVault factory, this function extracts the real |
| 132 | + /// caller address from the calldata. Otherwise, this function returns the account on behalf of which the current |
| 133 | + /// operation is being performed, which is either msg.sender or the account authenticated by the EVC. |
| 134 | + /// @return The address of the message sender. |
| 135 | + function _msgSender() internal view virtual override (BaseHookTarget, SelectorAccessControl) returns (address) { |
| 136 | + address msgSender = BaseHookTarget._msgSender(); |
| 137 | + return msg.sender == msgSender ? SelectorAccessControl._msgSender() : msgSender; |
| 138 | + } |
| 139 | +} |
0 commit comments