-
Notifications
You must be signed in to change notification settings - Fork 102
feat: Add FeeSharesMinter contract
#1216
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 28 commits
75e9f55
9ec1d11
4dc1448
0f6dab4
4f0492e
d9bfd8d
cf0e864
ba91946
729a05f
89d75b4
521c61c
3a54457
05bcd6c
aba7d8c
55f3330
60d2395
01fc11f
d6c0ac0
9a0ce16
36d151c
976361e
e0244e0
6e155aa
39c7c5a
56fbaf0
7bb20ec
55f1f82
c0c322d
8bb19cc
98c4cc4
c85cb6f
9010c1f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| // Imported from https://github.com/smartcontractkit/chainlink/blob/v2.22.0/contracts/src/v0.8/automation/interfaces/AutomationCompatibleInterface.sol | ||
| pragma solidity ^0.8.0; | ||
|
|
||
| // solhint-disable-next-line interface-starts-with-i | ||
| interface AutomationCompatibleInterface { | ||
| /** | ||
| * @notice method that is simulated by the keepers to see if any work actually | ||
| * needs to be performed. This method does does not actually need to be | ||
|
CheyenneAtapour marked this conversation as resolved.
Outdated
|
||
| * executable, and since it is only ever simulated it can consume lots of gas. | ||
| * @dev To ensure that it is never called, you may want to add the | ||
| * cannotExecute modifier from KeeperBase to your implementation of this | ||
| * method. | ||
| * @param checkData specified in the upkeep registration so it is always the | ||
| * same for a registered upkeep. This can easily be broken down into specific | ||
| * arguments using `abi.decode`, so multiple upkeeps can be registered on the | ||
| * same contract and easily differentiated by the contract. | ||
| * @return upkeepNeeded boolean to indicate whether the keeper should call | ||
| * performUpkeep or not. | ||
| * @return performData bytes that the keeper should call performUpkeep with, if | ||
| * upkeep is needed. If you would like to encode data to decode later, try | ||
| * `abi.encode`. | ||
| */ | ||
| function checkUpkeep( | ||
| bytes calldata checkData | ||
| ) external returns (bool upkeepNeeded, bytes memory performData); | ||
|
|
||
| /** | ||
| * @notice method that is actually executed by the keepers, via the registry. | ||
| * The data returned by the checkUpkeep simulation will be passed into | ||
| * this method to actually be executed. | ||
| * @dev The input to this method should not be trusted, and the caller of the | ||
| * method should not even be restricted to any single registry. Anyone should | ||
| * be able call it, and the input should be validated, there is no guarantee | ||
| * that the data passed in is the performData returned from checkUpkeep. This | ||
| * could happen due to malicious keepers, racing keepers, or simply a state | ||
| * change while the performUpkeep transaction is waiting for confirmation. | ||
| * Always validate the data passed in. | ||
| * @param performData is the data which was passed back from the checkData | ||
| * simulation. If it is encoded, it can easily be decoded into other types by | ||
| * calling `abi.decode`. This data should not be trusted, and should be | ||
| * validated against the contract's current state. | ||
| */ | ||
| function performUpkeep( | ||
| bytes calldata performData | ||
| ) external; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| // SPDX-License-Identifier: LicenseRef-BUSL | ||
| pragma solidity ^0.8.0; | ||
|
|
||
| import {BatchReports} from 'src/deployments/libraries/BatchReports.sol'; | ||
| import {AaveV4FeeSharesMinterDeployProcedure} from 'src/deployments/procedures/deploy/utils/AaveV4FeeSharesMinterDeployProcedure.sol'; | ||
|
|
||
| /// @title AaveV4FeeSharesMinterBatch | ||
| /// @author Aave Labs | ||
| /// @notice Deploys the FeeSharesMinter contract, producing a batch report. | ||
| contract AaveV4FeeSharesMinterBatch is AaveV4FeeSharesMinterDeployProcedure { | ||
| BatchReports.FeeSharesMinterBatchReport internal _report; | ||
|
|
||
| /// @dev Constructor. | ||
| /// @param owner_ The owner of the FeeSharesMinter. | ||
| /// @param salt_ The CREATE2 salt for deterministic deployment. | ||
| constructor(address owner_, bytes32 salt_) { | ||
| address feeSharesMinter = _deployFeeSharesMinter({owner: owner_, salt: salt_}); | ||
| _report = BatchReports.FeeSharesMinterBatchReport({feeSharesMinter: feeSharesMinter}); | ||
| } | ||
|
|
||
| /// @notice Returns the batch deployment report. | ||
| function getReport() external view returns (BatchReports.FeeSharesMinterBatchReport memory) { | ||
| return _report; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| // SPDX-License-Identifier: LicenseRef-BUSL | ||
| pragma solidity ^0.8.0; | ||
|
|
||
| import {AaveV4DeployProcedureBase} from 'src/deployments/procedures/AaveV4DeployProcedureBase.sol'; | ||
| import {Create2Utils} from 'src/deployments/utils/libraries/Create2Utils.sol'; | ||
| import {FeeSharesMinter} from 'src/utils/FeeSharesMinter.sol'; | ||
|
|
||
| /// @title AaveV4FeeSharesMinterDeployProcedure | ||
| /// @author Aave Labs | ||
| /// @notice Deploys the FeeSharesMinter contract. | ||
| contract AaveV4FeeSharesMinterDeployProcedure is AaveV4DeployProcedureBase { | ||
| /// @notice Deploys a new FeeSharesMinter instance via CREATE2. | ||
| /// @param owner The owner of the FeeSharesMinter. | ||
| /// @param salt The CREATE2 salt for deterministic deployment. | ||
| /// @return The address of the deployed FeeSharesMinter contract. | ||
| function _deployFeeSharesMinter(address owner, bytes32 salt) internal returns (address) { | ||
| require(owner != address(0), 'invalid owner'); | ||
| return | ||
| Create2Utils.create2Deploy({ | ||
| salt: salt, | ||
| bytecode: abi.encodePacked(type(FeeSharesMinter).creationCode, abi.encode(owner)) | ||
| }); | ||
| } | ||
| } |
|
DhairyaSethi marked this conversation as resolved.
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| // SPDX-License-Identifier: LicenseRef-BUSL | ||
| pragma solidity 0.8.28; | ||
|
|
||
| import {Ownable2Step, Ownable} from 'src/dependencies/openzeppelin/Ownable2Step.sol'; | ||
| import {PercentageMath} from 'src/libraries/math/PercentageMath.sol'; | ||
| import {Rescuable} from 'src/utils/Rescuable.sol'; | ||
| import {IFeeSharesMinter} from 'src/utils/IFeeSharesMinter.sol'; | ||
| import {IHub} from 'src/hub/interfaces/IHub.sol'; | ||
|
|
||
| /// @title FeeSharesMinter | ||
| /// @author Aave Labs | ||
| /// @notice Contract to mint fee shares on the Hub when specific conditions are met. | ||
| contract FeeSharesMinter is IFeeSharesMinter, Ownable2Step, Rescuable { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. who will own this contract? if EXECUTOR, then what I don't like is that we have no option to disable fee minting without an AIP wdyt?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. lets add OwnableWithGuardian ac and allow disable fee through guardian, or longer way to introduce AccessManager here |
||
| mapping(address hub => mapping(uint256 assetId => uint16)) internal _minAccruedFeesPercent; | ||
|
avniculae marked this conversation as resolved.
|
||
|
|
||
| /// @dev Constructor. | ||
| /// @param owner The owner of the contract. | ||
| constructor(address owner) Ownable(owner) {} | ||
|
|
||
| /// @inheritdoc IFeeSharesMinter | ||
| function setConfig( | ||
| address hub, | ||
| uint256 assetId, | ||
| uint16 minAccruedFeesPercent | ||
| ) external onlyOwner { | ||
| require(minAccruedFeesPercent <= PercentageMath.PERCENTAGE_FACTOR, InvalidConfig()); | ||
| _minAccruedFeesPercent[hub][assetId] = minAccruedFeesPercent; | ||
|
avniculae marked this conversation as resolved.
|
||
| emit ConfigUpdated(hub, assetId, minAccruedFeesPercent); | ||
| } | ||
|
|
||
| /// @inheritdoc IFeeSharesMinter | ||
| function performUpkeep(bytes calldata performData) external override { | ||
| (address hub, uint256 assetId) = abi.decode(performData, (address, uint256)); | ||
| _performUpkeep(hub, assetId); | ||
| } | ||
|
|
||
| /// @inheritdoc IFeeSharesMinter | ||
| function checkUpkeep( | ||
| bytes calldata checkData | ||
| ) external view override returns (bool, bytes memory) { | ||
|
Kogaroshi marked this conversation as resolved.
Outdated
|
||
| (address hub, uint256 assetId) = abi.decode(checkData, (address, uint256)); | ||
| bool upkeepNeeded = _checkUpkeep(hub, assetId); | ||
| bytes memory performData = checkData; | ||
|
avniculae marked this conversation as resolved.
Outdated
|
||
| return (upkeepNeeded, performData); | ||
| } | ||
|
|
||
| /// @inheritdoc IFeeSharesMinter | ||
| function getConfig(address hub, uint256 assetId) external view returns (uint16) { | ||
| return _minAccruedFeesPercent[hub][assetId]; | ||
| } | ||
|
|
||
| /// @dev Internal function to execute fee share minting. | ||
| /// @param hub The address of the hub. | ||
| /// @param assetId The identifier of the asset. | ||
| function _performUpkeep(address hub, uint256 assetId) internal virtual { | ||
| require(_checkUpkeep(hub, assetId), ConditionsNotMet()); | ||
|
|
||
| IHub(hub).mintFeeShares(assetId); | ||
| } | ||
|
|
||
| /// @dev Internal function to check execution conditions. | ||
| /// @param hub The address of the hub. | ||
| /// @param assetId The identifier of the asset. | ||
|
yan-man marked this conversation as resolved.
Outdated
|
||
| /// @return True if conditions are met, false otherwise. | ||
| function _checkUpkeep(address hub, uint256 assetId) internal view virtual returns (bool) { | ||
| uint16 minAccruedFeesPercent = _minAccruedFeesPercent[hub][assetId]; | ||
|
|
||
| IHub hubContract = IHub(hub); | ||
|
avniculae marked this conversation as resolved.
Outdated
|
||
| uint256 accruedFees = hubContract.getAssetAccruedFees(assetId); | ||
| uint256 totalAddedAssets = hubContract.getAddedAssets(assetId); | ||
|
|
||
| if (totalAddedAssets == 0) { | ||
| return false; | ||
| } | ||
| if (PercentageMath.percentDivDown(accruedFees, totalAddedAssets) < minAccruedFeesPercent) { | ||
|
yan-man marked this conversation as resolved.
Outdated
avniculae marked this conversation as resolved.
Outdated
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is bps enough precision here? |
||
| return false; | ||
| } | ||
|
|
||
| // Ensure at least 1 fee share would be minted | ||
| uint256 expectedShares = hubContract.previewAddByAssets(assetId, accruedFees); | ||
|
Kogaroshi marked this conversation as resolved.
Outdated
|
||
|
|
||
| return expectedShares > 0; | ||
| } | ||
|
|
||
| /// @inheritdoc Rescuable | ||
| function _rescueGuardian() internal view override returns (address) { | ||
| return owner(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| // SPDX-License-Identifier: LicenseRef-BUSL | ||
| pragma solidity 0.8.28; | ||
|
|
||
| import {AutomationCompatibleInterface} from 'src/dependencies/chainlink/AutomationCompatibleInterface.sol'; | ||
|
|
||
| /// @title IFeeSharesMinter | ||
| /// @author Aave Labs | ||
| /// @notice Interface for the FeeSharesMinter contract | ||
| interface IFeeSharesMinter is AutomationCompatibleInterface { | ||
| /// @notice Emitted when the configuration for an asset is updated. | ||
| /// @param hub The address of the hub. | ||
|
Kogaroshi marked this conversation as resolved.
Outdated
|
||
| /// @param assetId The identifier of the asset. | ||
| /// @param minAccruedFeesPercent The new minimum ratio of accrued fees to total added assets, in BPS. | ||
| event ConfigUpdated(address indexed hub, uint256 indexed assetId, uint16 minAccruedFeesPercent); | ||
|
|
||
| /// @notice Thrown upon minting when the required conditions are not met. | ||
| error ConditionsNotMet(); | ||
|
|
||
| /// @notice Thrown when `setConfig` is called with an invalid value. | ||
| error InvalidConfig(); | ||
|
avniculae marked this conversation as resolved.
Outdated
|
||
|
|
||
| /// @notice Sets the minimum accrued fees percent for a specific asset. | ||
| /// @param hub The address of the hub. | ||
|
Kogaroshi marked this conversation as resolved.
Outdated
|
||
| /// @param assetId The identifier of the asset. | ||
| /// @param minAccruedFeesPercent Minimum ratio of accrued fees to total added assets, in BPS. | ||
| function setConfig(address hub, uint256 assetId, uint16 minAccruedFeesPercent) external; | ||
|
|
||
| /// @notice Chainlink Automation on-chain execution entry point. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nbd but just need to align on onchain vs on-chain consistency
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we seem to use on-chain in other parts of the repo, so I'd keep as is.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i dont really mind either way but in JP's tech docs it's using onchain: https://github.com/aave/aave-v4/pull/1297/changes @CanonicalJP thoughts? |
||
| /// @dev performData must be abi.encoded as (address hub, uint256 assetId). | ||
| /// @inheritdoc AutomationCompatibleInterface | ||
| function performUpkeep(bytes calldata performData) external; | ||
|
avniculae marked this conversation as resolved.
Outdated
|
||
|
|
||
| /// @notice Chainlink Automation off-chain simulation check. | ||
| /// @dev checkData must be abi.encoded as (address hub, uint256 assetId). | ||
|
CheyenneAtapour marked this conversation as resolved.
Outdated
|
||
| /// @dev Returns whether upkeep is needed and the performData in bytes when conditions are met. | ||
| /// @inheritdoc AutomationCompatibleInterface | ||
| function checkUpkeep( | ||
| bytes calldata checkData | ||
| ) external view returns (bool upkeepNeeded, bytes memory performData); | ||
|
CheyenneAtapour marked this conversation as resolved.
Outdated
|
||
|
|
||
| /// @notice Returns the minimum accrued fees percent for a specific asset. | ||
| /// @param hub The address of the hub. | ||
|
Kogaroshi marked this conversation as resolved.
Outdated
|
||
| /// @param assetId The identifier of the asset. | ||
| /// @return The minimum ratio of accrued fees to total added assets, in BPS. | ||
| function getConfig(address hub, uint256 assetId) external view returns (uint16); | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.