From 4a1a5770d893345c37fb4e903c83f951dde1d31b Mon Sep 17 00:00:00 2001 From: ZD Hu Date: Fri, 5 May 2023 14:39:06 +0800 Subject: [PATCH] Add natspec --- src/Agent.sol | 10 ++- src/AgentImplementation.sol | 21 +++++- src/Router.sol | 68 +++++++++++++++++-- src/callbacks/AaveV2FlashLoanCallback.sol | 13 ++-- src/callbacks/AaveV3FlashLoanCallback.sol | 13 ++-- src/callbacks/BalancerV2FlashLoanCallback.sol | 3 +- src/fees/AaveBorrowFeeCalculator.sol | 1 + src/fees/AaveFlashLoanFeeCalculator.sol | 1 + src/fees/BalancerFlashLoanFeeCalculator.sol | 1 + src/fees/FeeCalculatorBase.sol | 23 ++++++- src/fees/FeeVerifier.sol | 8 +++ src/fees/MakerDrawFeeCalculator.sol | 1 + src/fees/NativeFeeCalculator.sol | 1 + src/fees/Permit2FeeCalculator.sol | 1 + src/fees/TransferFromFeeCalculator.sol | 3 +- src/interfaces/IParam.sol | 45 ++++++------ src/libraries/LogicHash.sol | 2 + src/utilities/MakerUtility.sol | 2 + 18 files changed, 170 insertions(+), 47 deletions(-) diff --git a/src/Agent.sol b/src/Agent.sol index 3c7683db..f7696a41 100644 --- a/src/Agent.sol +++ b/src/Agent.sol @@ -1,10 +1,12 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; -/// @title Agent executes arbitrary logics +/// @title A user's agent contract created by the router +/// @notice A proxy for delegating calls to the immutable agent implementation contract contract Agent { address internal immutable _implementation; + /// @dev Create an initialized agent constructor(address implementation) { _implementation = implementation; (bool ok, ) = implementation.delegatecall(abi.encodeWithSignature('initialize()')); @@ -13,12 +15,14 @@ contract Agent { receive() external payable {} - /// @notice All the function will be delegated to `_implementation` + /// @notice Delegate all function calls to `_implementation` fallback() external payable { _delegate(_implementation); } - /// @notice Referenced from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.8.1/contracts/proxy/Proxy.sol#L22 + /// @notice Delegate the call to `_implementation` + /// @dev Referenced from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.8.1/contracts/proxy/Proxy.sol#L22 + /// @param implementation The address of the implementation contract that this agent delegates calls to function _delegate(address implementation) internal { assembly { // Copy msg.data. We take full control of memory in this inline assembly diff --git a/src/AgentImplementation.sol b/src/AgentImplementation.sol index 2fe6a8e6..d26d6ebc 100644 --- a/src/AgentImplementation.sol +++ b/src/AgentImplementation.sol @@ -10,19 +10,29 @@ import {IRouter} from './interfaces/IRouter.sol'; import {IWrappedNative} from './interfaces/IWrappedNative.sol'; import {ApproveHelper} from './libraries/ApproveHelper.sol'; -/// @title Implemtation contract of agent logics +/// @title Agent implementation contract +/// @notice Delegated by all users' agents contract AgentImplementation is IAgent, ERC721Holder, ERC1155Holder { using SafeERC20 for IERC20; using Address for address; using Address for address payable; + /// @dev Flag for identifying the native address such as ETH on Ethereum address internal constant _NATIVE = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + + /// @dev Denominator for calculating basis points uint256 internal constant _BPS_BASE = 10_000; - uint256 internal constant _SKIP = 0x8000000000000000000000000000000000000000000000000000000000000000; // Equivalent to 1<<255, i.e. a singular 1 in the most significant bit. + /// @dev Flag for indicating a skipped value by setting the most significant bit to 1 (1<<255) + uint256 internal constant _SKIP = 0x8000000000000000000000000000000000000000000000000000000000000000; + + /// @notice Immutable address for recording the router address address public immutable router; + + /// @notice Immutable address for recording wrapped native address such as WETH on Ethereum address public immutable wrappedNative; + /// @notice Transient address for recording a valid caller which should be the router address after each execution address internal _caller; modifier checkCaller() { @@ -37,17 +47,22 @@ contract AgentImplementation is IAgent, ERC721Holder, ERC1155Holder { _; } + /// @dev Create the agent implementation contract constructor(address wrappedNative_) { router = msg.sender; wrappedNative = wrappedNative_; } + /// @notice Initialize user's agent and can only be called once. function initialize() external { if (_caller != address(0)) revert Initialized(); _caller = router; } - /// @notice Execute logics and return tokens to the current user + /// @notice Execute arbitrary logics + /// @param logics Array of logics to be executed + /// @param fees Array of fees + /// @param tokensReturn Array of ERC-20 tokens to be returned to the current user function execute( IParam.Logic[] calldata logics, IParam.Fee[] calldata fees, diff --git a/src/Router.sol b/src/Router.sol index 221b740b..3dac7766 100644 --- a/src/Router.sol +++ b/src/Router.sol @@ -11,23 +11,40 @@ import {IParam} from './interfaces/IParam.sol'; import {IRouter} from './interfaces/IRouter.sol'; import {LogicHash} from './libraries/LogicHash.sol'; -/// @title Router executes arbitrary logics +/// @title Entry point for Composable Router contract Router is IRouter, EIP712, FeeVerifier { using SafeERC20 for IERC20; using LogicHash for IParam.LogicBatch; using SignatureChecker for address; + /// @dev Flag for reducing gas cost when reset `currentUser` address internal constant _INIT_USER = address(1); + + /// @dev Flag for identifying an invalid pauser address address internal constant _INVALID_PAUSER = address(0); + + /// @dev Flag for identifying an invalid fee collector address address internal constant _INVALID_FEE_COLLECTOR = address(0); + /// @notice Immutable implementation contract for all users' agents address public immutable agentImplementation; + /// @notice Mapping for recording exclusive agent contract to each user mapping(address user => IAgent agent) public agents; + + /// @notice Mapping for recording valid signers mapping(address signer => bool valid) public signers; + + /// @notice Transient address for recording `msg.sender` which resets to `_INIT_USER` after each execution address public currentUser; + + /// @notice Address for receiving collected fees address public feeCollector; + + /// @notice Address for invoking pause address public pauser; + + /// @notice Flag for indicating pasue bool public paused; modifier checkCaller() { @@ -50,6 +67,7 @@ contract Router is IRouter, EIP712, FeeVerifier { _; } + /// @dev Create the router with EIP-712 and the agent implementation contracts constructor(address wrappedNative, address pauser_, address feeCollector_) EIP712('Composable Router', '1') { currentUser = _INIT_USER; agentImplementation = address(new AgentImplementation(wrappedNative)); @@ -57,23 +75,36 @@ contract Router is IRouter, EIP712, FeeVerifier { _setFeeCollector(feeCollector_); } + /// @notice Get owner address + /// @return The owner address function owner() public view override(IRouter, Ownable) returns (address) { return super.owner(); } + /// @notice Get domain separator used for EIP-712 + /// @return The domain separator of Composable Router function domainSeparator() external view returns (bytes32) { return _domainSeparatorV4(); } + /// @notice Get agent address of an user + /// @param user The user address + /// @return The agent address of the user function getAgent(address user) external view returns (address) { return address(agents[user]); } + /// @notice Get user and agent addresses of the current user + /// @return The user address + /// @return The agent address function getUserAgent() external view returns (address, address) { address user = currentUser; return (user, address(agents[user])); } + /// @notice Calculate agent address for an user using the CREATE2 formula + /// @param user The user address + /// @return The calculated agent address for the user function calcAgent(address user) external view returns (address) { address result = address( uint160( @@ -92,16 +123,22 @@ contract Router is IRouter, EIP712, FeeVerifier { return result; } + /// @notice Add a signer whose signature can pass the validation in `executeWithSignature` by owner + /// @param signer The signer address to be added function addSigner(address signer) external onlyOwner { signers[signer] = true; emit SignerAdded(signer); } + /// @notice Remove a signer by owner + /// @param signer The signer address to be removed function removeSigner(address signer) external onlyOwner { delete signers[signer]; emit SignerRemoved(signer); } + /// @notice Set the fee collector address that collects fees from each user's agent by owner + /// @param feeCollector_ The fee collector address function setFeeCollector(address feeCollector_) external onlyOwner { _setFeeCollector(feeCollector_); } @@ -112,6 +149,8 @@ contract Router is IRouter, EIP712, FeeVerifier { emit FeeCollectorSet(feeCollector_); } + /// @notice Set the pauser address that can pause `execute` and `executeWithSignature` by owner + /// @param pauser_ The pauser address function setPauser(address pauser_) external onlyOwner { _setPauser(pauser_); } @@ -122,21 +161,32 @@ contract Router is IRouter, EIP712, FeeVerifier { emit PauserSet(pauser_); } + /// @notice Rescue ERC-20 tokens in case of stuck tokens by owner + /// @param token The token address + /// @param receiver The receiver address + /// @param amount The amount of tokens to be rescued function rescue(address token, address receiver, uint256 amount) external onlyOwner { IERC20(token).safeTransfer(receiver, amount); } + /// @notice Pause `execute` and `executeWithSignature` by pauser function pause() external onlyPauser { paused = true; emit Paused(); } + /// @notice Resume `execute` and `executeWithSignature` by pauser function resume() external onlyPauser { paused = false; emit Resumed(); } - /// @notice Execute logics through current user's agent. Create agent for user if not created. + /// @notice Execute arbitrary logics through the current user's agent. Creates an agent for users if not created. + /// Charge fees based on the scenarios defined in the FeeVerifier contract, which calculates fees on-chain. + /// @param logics Array of logics to be executed + /// @param fees Array of fees + /// @param tokensReturn Array of ERC-20 tokens to be returned to the current user + /// @param referralCode Referral code function execute( IParam.Logic[] calldata logics, IParam.Fee[] calldata fees, @@ -156,7 +206,14 @@ contract Router is IRouter, EIP712, FeeVerifier { agent.execute{value: msg.value}(logics, fees, tokensReturn); } - /// @notice Execute logics with signer's signature. + /// @notice Execute arbitrary logics through the current user's agent using a signer's signature. Creates an agent + /// for users if not created. The fees in logicBatch are off-chain encoded, instead of being calculated by + /// the FeeVerifier contract. + /// @param logicBatch A struct containing logics, fees and deadline, signed by a signer using EIP-712 + /// @param signer The signer address + /// @param signature The signer's signature bytes + /// @param tokensReturn Array of ERC-20 tokens to be returned to the current user + /// @param referralCode Referral code function executeWithSignature( IParam.LogicBatch calldata logicBatch, address signer, @@ -182,11 +239,14 @@ contract Router is IRouter, EIP712, FeeVerifier { } /// @notice Create an agent for `msg.sender` + /// @return The newly created agent address function newAgent() external returns (address payable) { return newAgent(msg.sender); } - /// @notice Create an agent for `user` + /// @notice Create an agent for the user + /// @param user The user address + /// @return The newly created agent address function newAgent(address user) public returns (address payable) { if (address(agents[user]) != address(0)) { revert AgentAlreadyCreated(); diff --git a/src/callbacks/AaveV2FlashLoanCallback.sol b/src/callbacks/AaveV2FlashLoanCallback.sol index 42dbc72b..f5c42c97 100644 --- a/src/callbacks/AaveV2FlashLoanCallback.sol +++ b/src/callbacks/AaveV2FlashLoanCallback.sol @@ -9,6 +9,7 @@ import {IAaveV2Provider} from '../interfaces/aaveV2/IAaveV2Provider.sol'; import {ApproveHelper} from '../libraries/ApproveHelper.sol'; /// @title Aave V2 flash loan callback +/// @notice Invoked by Aave V2 pool to call the current user's agent contract AaveV2FlashLoanCallback is IAaveV2FlashLoanCallback { using SafeERC20 for IERC20; using Address for address; @@ -21,11 +22,11 @@ contract AaveV2FlashLoanCallback is IAaveV2FlashLoanCallback { aaveV2Provider = aaveV2Provider_; } - /// @dev No need to check whether `initiator` is Agent as it's certain when the below conditions are satisfied: - /// 1. `to` in Agent is Aave Pool, i.e, user signed a correct `to` - /// 2. `_callback` in Agent is set to this callback, i.e, user signed a correct `callback` - /// 3. `msg.sender` of this callback is Aave Pool - /// 4. Aave Pool contract is benign + /// @dev No need to check if `initiator` is the agent as it's certain when the below conditions are satisfied: + /// 1. The `to` address used in agent is Aave Pool, i.e, the user signed a correct `to` + /// 2. The `_callback` address set in agent is this callback, i.e, the user signed a correct `callback` + /// 3. The `msg.sender` of this callback is Aave Pool + /// 4. The Aave pool is benign function executeOperation( address[] calldata assets, uint256[] calldata amounts, @@ -38,7 +39,7 @@ contract AaveV2FlashLoanCallback is IAaveV2FlashLoanCallback { if (msg.sender != pool) revert InvalidCaller(); (, address agent) = IRouter(router).getUserAgent(); - // Transfer assets to Agent and record initial balances + // Transfer assets to the agent and record initial balances uint256 assetsLength = assets.length; uint256[] memory initBalances = new uint256[](assetsLength); for (uint256 i = 0; i < assetsLength; ) { diff --git a/src/callbacks/AaveV3FlashLoanCallback.sol b/src/callbacks/AaveV3FlashLoanCallback.sol index a69c4cf2..56168c0a 100644 --- a/src/callbacks/AaveV3FlashLoanCallback.sol +++ b/src/callbacks/AaveV3FlashLoanCallback.sol @@ -9,6 +9,7 @@ import {IAaveV3Provider} from '../interfaces/aaveV3/IAaveV3Provider.sol'; import {ApproveHelper} from '../libraries/ApproveHelper.sol'; /// @title Aave V3 flash loan callback +/// @notice Invoked by Aave V3 pool to call the current user's agent contract AaveV3FlashLoanCallback is IAaveV3FlashLoanCallback { using SafeERC20 for IERC20; using Address for address; @@ -21,11 +22,11 @@ contract AaveV3FlashLoanCallback is IAaveV3FlashLoanCallback { aaveV3Provider = aaveV3Provider_; } - /// @dev No need to check whether `initiator` is Agent as it's certain when the below conditions are satisfied: - /// 1. `to` in Agent is Aave Pool, i.e, user signed a correct `to` - /// 2. `_callback` in Agent is set to this callback, i.e, user signed a correct `callback` - /// 3. `msg.sender` of this callback is Aave Pool - /// 4. Aave Pool contract is benign + /// @dev No need to check if `initiator` is the agent as it's certain when the below conditions are satisfied: + /// 1. The `to` address used in agent is Aave Pool, i.e, the user signed a correct `to` + /// 2. The `_callback` address set in agent is this callback, i.e, the user signed a correct `callback` + /// 3. The `msg.sender` of this callback is Aave Pool + /// 4. The Aave pool is benign function executeOperation( address[] calldata assets, uint256[] calldata amounts, @@ -38,7 +39,7 @@ contract AaveV3FlashLoanCallback is IAaveV3FlashLoanCallback { if (msg.sender != pool) revert InvalidCaller(); (, address agent) = IRouter(router).getUserAgent(); - // Transfer assets to Agent and record initial balances + // Transfer assets to the agent and record initial balances uint256 assetsLength = assets.length; uint256[] memory initBalances = new uint256[](assetsLength); for (uint256 i = 0; i < assetsLength; ) { diff --git a/src/callbacks/BalancerV2FlashLoanCallback.sol b/src/callbacks/BalancerV2FlashLoanCallback.sol index 49b7924c..6762b8d6 100644 --- a/src/callbacks/BalancerV2FlashLoanCallback.sol +++ b/src/callbacks/BalancerV2FlashLoanCallback.sol @@ -7,6 +7,7 @@ import {IRouter} from '../interfaces/IRouter.sol'; import {IBalancerV2FlashLoanCallback} from '../interfaces/IBalancerV2FlashLoanCallback.sol'; /// @title Balancer V2 flash loan callback +/// @notice Invoked by Balancer V2 vault to call the current user's agent contract BalancerV2FlashLoanCallback is IBalancerV2FlashLoanCallback { using SafeERC20 for IERC20; using Address for address; @@ -28,7 +29,7 @@ contract BalancerV2FlashLoanCallback is IBalancerV2FlashLoanCallback { if (msg.sender != balancerV2Vault) revert InvalidCaller(); (, address agent) = IRouter(router).getUserAgent(); - // Transfer tokens to Router and record initial balances + // Transfer assets to the agent and record initial balances uint256 tokensLength = tokens.length; uint256[] memory initBalances = new uint256[](tokensLength); for (uint256 i = 0; i < tokensLength; ) { diff --git a/src/fees/AaveBorrowFeeCalculator.sol b/src/fees/AaveBorrowFeeCalculator.sol index 1a3d10d3..dfecc2dd 100644 --- a/src/fees/AaveBorrowFeeCalculator.sol +++ b/src/fees/AaveBorrowFeeCalculator.sol @@ -6,6 +6,7 @@ import {IAaveV3Provider} from '../interfaces/aaveV3/IAaveV3Provider.sol'; import {IFeeCalculator} from '../interfaces/IFeeCalculator.sol'; import {IParam} from '../interfaces/IParam.sol'; +/// @title Aave borrow fee calculator contract AaveBorrowFeeCalculator is IFeeCalculator, FeeCalculatorBase { bytes32 internal constant _V2_BORROW_META_DATA = bytes32(bytes('aave-v2:borrow')); bytes32 internal constant _V3_BORROW_META_DATA = bytes32(bytes('aave-v3:borrow')); diff --git a/src/fees/AaveFlashLoanFeeCalculator.sol b/src/fees/AaveFlashLoanFeeCalculator.sol index 558d2f1f..3d07cbce 100644 --- a/src/fees/AaveFlashLoanFeeCalculator.sol +++ b/src/fees/AaveFlashLoanFeeCalculator.sol @@ -7,6 +7,7 @@ import {IAaveV3Provider} from '../interfaces/aaveV3/IAaveV3Provider.sol'; import {IFeeCalculator} from '../interfaces/IFeeCalculator.sol'; import {IParam} from '../interfaces/IParam.sol'; +/// @title Aave flash loan fee calculator contract AaveFlashLoanFeeCalculator is IFeeCalculator, FeeCalculatorBase { bytes32 internal constant _V2_FLASHLOAN_META_DATA = bytes32(bytes('aave-v2:flash-loan')); bytes32 internal constant _V3_FLASHLOAN_META_DATA = bytes32(bytes('aave-v3:flash-loan')); diff --git a/src/fees/BalancerFlashLoanFeeCalculator.sol b/src/fees/BalancerFlashLoanFeeCalculator.sol index d1245ff8..a6c67250 100644 --- a/src/fees/BalancerFlashLoanFeeCalculator.sol +++ b/src/fees/BalancerFlashLoanFeeCalculator.sol @@ -6,6 +6,7 @@ import {Router} from '../Router.sol'; import {IFeeCalculator} from '../interfaces/IFeeCalculator.sol'; import {IParam} from '../interfaces/IParam.sol'; +/// @title Balancer flash loan fee calculator contract BalancerFlashLoanFeeCalculator is IFeeCalculator, FeeCalculatorBase { bytes32 internal constant _META_DATA = bytes32(bytes('balancer-v2:flash-loan')); diff --git a/src/fees/FeeCalculatorBase.sol b/src/fees/FeeCalculatorBase.sol index 20421451..eb04c4b4 100644 --- a/src/fees/FeeCalculatorBase.sol +++ b/src/fees/FeeCalculatorBase.sol @@ -3,24 +3,37 @@ pragma solidity ^0.8.0; import {IRouter} from '../interfaces/IRouter.sol'; +/// @title Fee calculator base +/// @notice An abstract contract that provides basic functionality for calculating fees abstract contract FeeCalculatorBase { error InvalidSender(); error InvalidRate(); + /// @dev Denominator for calculating basis points uint256 internal constant _BPS_BASE = 10_000; + + /// @notice Immutable address for recording the router address address public immutable router; - uint256 public feeRate; // In bps, 20 means 0.2% + /// @notice Fee rate in basis points with 20 representing 0.2% + uint256 public feeRate; + /// @dev Initialize the router address and fee rate constructor(address router_, uint256 feeRate_) { router = router_; feeRate = feeRate_; } + /// @notice Calculate the fee for the given amount + /// @param amount The amount for which the fee needs to be calculated + /// @return The calculated fee function calculateFee(uint256 amount) public view returns (uint256) { return (amount * feeRate) / (_BPS_BASE + feeRate); } + /// @notice Calculate the fees for the given array of amounts + /// @param amounts An array of amounts for which fees need to be calculated + /// @return An array of calculated fees function calculateFee(uint256[] memory amounts) public view returns (uint256[] memory) { for (uint256 i = 0; i < amounts.length; ) { amounts[i] = (amounts[i] * feeRate) / (_BPS_BASE + feeRate); @@ -31,10 +44,16 @@ abstract contract FeeCalculatorBase { return amounts; } + /// @notice Calculate the amount with the fee included for the given amount + /// @param amount The amount to calculate the total with the fee included + /// @return The total amount with the fee included function calculateAmountWithFee(uint256 amount) public view returns (uint256) { return (amount * (_BPS_BASE + feeRate)) / _BPS_BASE; } + /// @notice Calculate the amounts with the fees included for the given array of amounts + /// @param amounts An array of amounts to calculate the totals with the fees included + /// @return An array of the total amounts with the fees included function calculateAmountWithFee(uint256[] memory amounts) public view returns (uint256[] memory) { for (uint256 i = 0; i < amounts.length; ) { amounts[i] = (amounts[i] * (_BPS_BASE + feeRate)) / _BPS_BASE; @@ -45,6 +64,8 @@ abstract contract FeeCalculatorBase { return amounts; } + /// @notice Set a new fee rate by router owner + /// @param feeRate_ The new fee rate in basis points function setFeeRate(uint256 feeRate_) external { if (msg.sender != IRouter(router).owner()) revert InvalidSender(); if (feeRate_ >= _BPS_BASE) revert InvalidRate(); diff --git a/src/fees/FeeVerifier.sol b/src/fees/FeeVerifier.sol index 57706eda..71f79b01 100644 --- a/src/fees/FeeVerifier.sol +++ b/src/fees/FeeVerifier.sol @@ -5,15 +5,23 @@ import {Ownable} from 'openzeppelin-contracts/contracts/access/Ownable.sol'; import {IParam} from '../interfaces/IParam.sol'; import {IFeeCalculator} from '../interfaces/IFeeCalculator.sol'; +/// @title Fee verifier +/// @notice An abstruct contract that verifies and calculates fees on-chain abstract contract FeeVerifier is Ownable { error LengthMismatch(); event FeeCalculatorSet(bytes4 indexed selector, address indexed to, address indexed feeCalculator); + /// @dev Flag for identifying the native address such as ETH on Ethereum address internal constant _NATIVE = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + + /// @dev Flag for identifying any to address in `feeCalculators` address internal constant _DUMMY_TO_ADDRESS = address(0); + + /// @dev Flag for identifying the native fee calculator bytes4 internal constant _NATIVE_FEE_SELECTOR = 0xeeeeeeee; + /// @notice Mapping for storing fee calculators for each combination of selector and to address mapping(bytes4 selector => mapping(address to => address feeCalculator)) public feeCalculators; /// @notice Get logics, msg.value and fees that contains fee diff --git a/src/fees/MakerDrawFeeCalculator.sol b/src/fees/MakerDrawFeeCalculator.sol index d8563504..4e32fd7d 100644 --- a/src/fees/MakerDrawFeeCalculator.sol +++ b/src/fees/MakerDrawFeeCalculator.sol @@ -5,6 +5,7 @@ import {FeeCalculatorBase} from './FeeCalculatorBase.sol'; import {IFeeCalculator} from '../interfaces/IFeeCalculator.sol'; import {IParam} from '../interfaces/IParam.sol'; +/// @title Maker draw fee calculator contract MakerDrawFeeCalculator is IFeeCalculator, FeeCalculatorBase { bytes32 internal constant _META_DATA = bytes32(bytes('maker:borrow')); bytes4 internal constant _DRAW_FUNCTION_SELECTOR = diff --git a/src/fees/NativeFeeCalculator.sol b/src/fees/NativeFeeCalculator.sol index 9dd37cf8..53e48422 100644 --- a/src/fees/NativeFeeCalculator.sol +++ b/src/fees/NativeFeeCalculator.sol @@ -5,6 +5,7 @@ import {FeeCalculatorBase} from './FeeCalculatorBase.sol'; import {IFeeCalculator} from '../interfaces/IFeeCalculator.sol'; import {IParam} from '../interfaces/IParam.sol'; +/// @title Native fee calculator contract NativeFeeCalculator is IFeeCalculator, FeeCalculatorBase { bytes32 internal constant _META_DATA = bytes32(bytes('native-token')); address internal constant _NATIVE = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; diff --git a/src/fees/Permit2FeeCalculator.sol b/src/fees/Permit2FeeCalculator.sol index 2999cd29..9ca1c29d 100644 --- a/src/fees/Permit2FeeCalculator.sol +++ b/src/fees/Permit2FeeCalculator.sol @@ -5,6 +5,7 @@ import {FeeCalculatorBase} from './FeeCalculatorBase.sol'; import {IFeeCalculator} from '../interfaces/IFeeCalculator.sol'; import {IParam} from '../interfaces/IParam.sol'; +/// @title Permit2 fee calculator contract Permit2FeeCalculator is IFeeCalculator, FeeCalculatorBase { bytes32 internal constant _META_DATA = bytes32(bytes('permit2:pull-token')); diff --git a/src/fees/TransferFromFeeCalculator.sol b/src/fees/TransferFromFeeCalculator.sol index 2e759f1d..5026346b 100644 --- a/src/fees/TransferFromFeeCalculator.sol +++ b/src/fees/TransferFromFeeCalculator.sol @@ -5,7 +5,8 @@ import {FeeCalculatorBase} from './FeeCalculatorBase.sol'; import {IFeeCalculator} from '../interfaces/IFeeCalculator.sol'; import {IParam} from '../interfaces/IParam.sol'; -/// @notice Fee calculator for ERC20::transferFrom action. This will also cause ERC721::transferFrom being executed and fail in transaction. +/// @title TransferFrom fee calculator for ERC20::transferFrom +/// @dev Cause a failed transaction when the rarely used ERC721::transferFrom is executed contract TransferFromFeeCalculator is IFeeCalculator, FeeCalculatorBase { bytes32 internal constant _META_DATA = bytes32(bytes('erc20:transfer-from')); diff --git a/src/interfaces/IParam.sol b/src/interfaces/IParam.sol index 2cba86e8..2e6837dc 100644 --- a/src/interfaces/IParam.sol +++ b/src/interfaces/IParam.sol @@ -2,40 +2,41 @@ pragma solidity ^0.8.0; interface IParam { + /// @notice LogicBatch is signed by a signer using EIP-712 struct LogicBatch { - Logic[] logics; - Fee[] fees; - uint256 deadline; + Logic[] logics; // An array of logics to be executed + Fee[] fees; // An array of fees to be charged + uint256 deadline; // The deadline for a signer's signature } + /// @notice Logic represents a single action to be executed struct Logic { - address to; - bytes data; - Input[] inputs; - WrapMode wrapMode; - // Approve to another contract instead of `to` since some protocols use spender contract to pull tokens from user - address approveTo; - address callback; + address to; // The target address for the execution + bytes data; // Encoded function call data + Input[] inputs; // An array of inputs for amount calculation and token approval + WrapMode wrapMode; // Determines if wrap or unwrap native + address approveTo; // The address to approve tokens for if different from `to` such as a spender contract + address callback; // The address allowed to make a one-time call to the agent } + /// @notice Input represents a single input for token amount calculation and approval struct Input { - address token; - // 7_000 means the replacing amount is 70% of token balance. Set type(uint256).max to skip bps calculation so simply use amountOrOffset as amount - uint256 amountBps; - // If amountBps is skip, can simply read amountOrOffset as amount - // If amountBps is not skip, amountOrOffset is byte offset of amount in Logic.data used for replacement. Set type(uint256).max to skip if don't need to replace. - uint256 amountOrOffset; + address token; // Token address + uint256 amountBps; // Basis points for calculating the amount, set to _SKIP to use amountOrOffset as amount + uint256 amountOrOffset; // Read as amount if amountBps is _SKIP; otherwise, read as byte offset of amount in Logic.data for replacement } + /// @notice Fee represents a fee to be charged struct Fee { - address token; - uint256 amount; - bytes32 metadata; + address token; // The token address + uint256 amount; // The fee amount + bytes32 metadata; // Metadata related to the fee } + /// @notice WrapMode determines how to handle native during execution enum WrapMode { - NONE, - WRAP_BEFORE, - UNWRAP_AFTER + NONE, // No wrapping or unwrapping of native + WRAP_BEFORE, // Wrap native before calling `Logic.to` + UNWRAP_AFTER // Unwrap native after calling `Logic.to` } } diff --git a/src/libraries/LogicHash.sol b/src/libraries/LogicHash.sol index a5cf5c6e..b06b5a15 100644 --- a/src/libraries/LogicHash.sol +++ b/src/libraries/LogicHash.sol @@ -3,6 +3,8 @@ pragma solidity ^0.8.0; import {IParam} from '../interfaces/IParam.sol'; +/// @title Library for EIP-712 encode +/// @notice Contains typehash constants and hash functions for structs library LogicHash { bytes32 internal constant _FEE_TYPEHASH = keccak256('Fee(address token,uint256 amount,bytes32 metadata)'); bytes32 internal constant _INPUT_TYPEHASH = diff --git a/src/utilities/MakerUtility.sol b/src/utilities/MakerUtility.sol index 723b8664..c155fdd7 100644 --- a/src/utilities/MakerUtility.sol +++ b/src/utilities/MakerUtility.sol @@ -8,6 +8,8 @@ import {IDSProxy, IDSProxyRegistry} from '../interfaces/maker/IDSProxy.sol'; import {IMakerGemJoin} from '../interfaces/maker/IMaker.sol'; import {ApproveHelper} from '../libraries/ApproveHelper.sol'; +/// @title Maker utility contract +/// @notice Perform additional actions after interacting with Maker contract MakerUtility is IMakerUtility { using SafeERC20 for IERC20;