|
| 1 | +// SPDX-License-Identifier: MIT AND Apache-2.0 |
| 2 | +pragma solidity 0.8.23; |
| 3 | + |
| 4 | +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; |
| 5 | + |
| 6 | +import { CaveatEnforcer } from "./CaveatEnforcer.sol"; |
| 7 | +import { ModeCode } from "../utils/Types.sol"; |
| 8 | + |
| 9 | +/** |
| 10 | + * @title MetaSwapParamsEnforcer |
| 11 | + * @notice Enforces that the output token of a swap (tokenTo) is in the root delegator's allowed list. |
| 12 | + * @dev Used by DelegationMetaSwapAdapter. Terms = abi.encode(IERC20[] allowedTokens, address recipient, uint256 |
| 13 | + * maxSlippagePercent). |
| 14 | + * recipient and maxSlippagePercent are read by the adapter only; this enforcer only validates tokenTo. |
| 15 | + * Slippage format: 100e18 = 100%, 10e18 = 10%, 0 = no per-delegation check. |
| 16 | + * Args = abi.encode(IERC20 tokenTo), set by the adapter before redeem. |
| 17 | + * allowedTokens must not be empty. Use a single element ANY_TOKEN to allow any output token; otherwise tokenTo must be in |
| 18 | + * allowedTokens. |
| 19 | + */ |
| 20 | +contract MetaSwapParamsEnforcer is CaveatEnforcer { |
| 21 | + ////////////////////////////// Constants ////////////////////////////// |
| 22 | + |
| 23 | + /// @dev Special token value. When allowedTokens has length 1 and this value, any tokenTo is allowed. |
| 24 | + address public constant ANY_TOKEN = address(0xa11); |
| 25 | + |
| 26 | + /// @dev Maximum allowed slippage: 100% in 18-decimal fixed point (100e18). Values must be in [0, PERCENT_100]. |
| 27 | + uint256 public constant PERCENT_100 = 100e18; |
| 28 | + |
| 29 | + ////////////////////////////// External Functions ////////////////////////////// |
| 30 | + |
| 31 | + /** |
| 32 | + * @notice Decodes the terms used in this CaveatEnforcer. |
| 33 | + * @param _terms abi.encode(IERC20[] allowedTokens, address recipient, uint256 maxSlippagePercent) |
| 34 | + * @return allowedTokens_ Output tokens permitted by the root delegator (length >= 1; use [ANY_TOKEN] for any). |
| 35 | + * @return recipient_ Address to receive swap output (address(0) = root delegator). |
| 36 | + * @return maxSlippagePercent_ Max slippage (100e18 = 100%, 10e18 = 10%; 0 = no check; must be <= PERCENT_100). |
| 37 | + */ |
| 38 | + function getTermsInfo(bytes calldata _terms) |
| 39 | + public |
| 40 | + pure |
| 41 | + returns (IERC20[] memory allowedTokens_, address recipient_, uint256 maxSlippagePercent_) |
| 42 | + { |
| 43 | + (allowedTokens_, recipient_, maxSlippagePercent_) = abi.decode(_terms, (IERC20[], address, uint256)); |
| 44 | + require(allowedTokens_.length != 0, "MetaSwapParamsEnforcer:invalid-empty-allowed-tokens"); |
| 45 | + require(maxSlippagePercent_ <= PERCENT_100, "MetaSwapParamsEnforcer:invalid-max-slippage"); |
| 46 | + } |
| 47 | + |
| 48 | + /** |
| 49 | + * @notice Enforces that the swap output token (args) is in the allowed list (terms). |
| 50 | + * @param _terms abi.encode(IERC20[] allowedTokens, address recipient, uint256 maxSlippagePercent) |
| 51 | + * @param _args abi.encode(IERC20 tokenTo) — the output token from the swap execution |
| 52 | + */ |
| 53 | + function beforeHook( |
| 54 | + bytes calldata _terms, |
| 55 | + bytes calldata _args, |
| 56 | + ModeCode _mode, |
| 57 | + bytes calldata, |
| 58 | + bytes32, |
| 59 | + address, |
| 60 | + address |
| 61 | + ) |
| 62 | + public |
| 63 | + pure |
| 64 | + override |
| 65 | + onlyDefaultExecutionMode(_mode) |
| 66 | + { |
| 67 | + (IERC20[] memory allowedTokens_,,) = getTermsInfo(_terms); |
| 68 | + IERC20 tokenTo_ = abi.decode(_args, (IERC20)); |
| 69 | + |
| 70 | + if (allowedTokens_.length == 1 && address(allowedTokens_[0]) == ANY_TOKEN) return; |
| 71 | + |
| 72 | + for (uint256 i = 0; i < allowedTokens_.length; ++i) { |
| 73 | + if (address(allowedTokens_[i]) == address(tokenTo_)) return; |
| 74 | + } |
| 75 | + revert("MetaSwapParamsEnforcer:token-to-not-allowed"); |
| 76 | + } |
| 77 | +} |
0 commit comments