From 83fd46ce9743c11e03bb6dc4ffa85a63ef95163a Mon Sep 17 00:00:00 2001 From: fourlen Date: Tue, 7 May 2024 17:15:02 +0300 Subject: [PATCH] [Plugin] add farming module --- .../interfaces/plugins/IFarmingPlugin.sol | 4 - .../contracts/modules/DynamicFeeModule.sol | 1 + .../contracts/modules/FarmingModule.sol | 90 +++++++++++++++++++ 3 files changed, 91 insertions(+), 4 deletions(-) create mode 100644 src/plugin/contracts/modules/FarmingModule.sol diff --git a/src/plugin/contracts/interfaces/plugins/IFarmingPlugin.sol b/src/plugin/contracts/interfaces/plugins/IFarmingPlugin.sol index a9fceb33a..a5e43882f 100644 --- a/src/plugin/contracts/interfaces/plugins/IFarmingPlugin.sol +++ b/src/plugin/contracts/interfaces/plugins/IFarmingPlugin.sol @@ -8,10 +8,6 @@ interface IFarmingPlugin { /// @param newIncentive The address of the new incentive event Incentive(address newIncentive); - /// @notice Returns the address of the pool the plugin is created for - /// @return address of the pool - function pool() external view returns (address); - /// @notice Connects or disconnects an incentive. /// @dev Only farming can connect incentives. /// The one who connected it and the current farming has the right to disconnect the incentive. diff --git a/src/plugin/contracts/modules/DynamicFeeModule.sol b/src/plugin/contracts/modules/DynamicFeeModule.sol index 8af64827e..e53ec5908 100644 --- a/src/plugin/contracts/modules/DynamicFeeModule.sol +++ b/src/plugin/contracts/modules/DynamicFeeModule.sol @@ -6,6 +6,7 @@ import '@cryptoalgebra/integral-core/contracts/interfaces/IAlgebraFactory.sol'; import '@cryptoalgebra/integral-core/contracts/base/common/Timestamp.sol'; import '@cryptoalgebra/integral-core/contracts/libraries/Plugins.sol'; import '@cryptoalgebra/integral-core/contracts/interfaces/IAlgebraPool.sol'; + import '@cryptoalgebra/algebra-modular-hub/contracts/base/AlgebraModule.sol'; import '@cryptoalgebra/algebra-modular-hub/contracts/interfaces/IAlgebraModularHub.sol'; import '@cryptoalgebra/algebra-modular-hub/contracts/types/HookParams.sol'; diff --git a/src/plugin/contracts/modules/FarmingModule.sol b/src/plugin/contracts/modules/FarmingModule.sol new file mode 100644 index 000000000..663b1227d --- /dev/null +++ b/src/plugin/contracts/modules/FarmingModule.sol @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity =0.8.20; + +import '@cryptoalgebra/integral-core/contracts/interfaces/pool/IAlgebraPoolState.sol'; +import '@cryptoalgebra/integral-core/contracts/base/common/Timestamp.sol'; +import '@cryptoalgebra/integral-core/contracts/libraries/Plugins.sol'; + +import '@cryptoalgebra/algebra-modular-hub/contracts/base/AlgebraModule.sol'; +import '@cryptoalgebra/algebra-modular-hub/contracts/interfaces/IAlgebraModularHub.sol'; +import '@cryptoalgebra/algebra-modular-hub/contracts/types/HookParams.sol'; + +import '../interfaces/plugins/IFarmingPlugin.sol'; +import '../interfaces/IBasePluginV1Factory.sol'; +import '../interfaces/IAlgebraVirtualPool.sol'; + +contract FarmingModule is AlgebraModule, IFarmingPlugin, Timestamp { + using Plugins for uint8; + + /// @inheritdoc IFarmingPlugin + address public override incentive; + + address public immutable modularHub; + + /// @dev the address which connected the last incentive. Needed so that he can disconnect it + address private _lastIncentiveOwner; + + address private immutable pluginFactory; + + constructor(address _modularHub, address _pluginFactory) { + (modularHub, pluginFactory) = (_modularHub, _pluginFactory); + } + + function setIncentive(address newIncentive) external override { + bool toConnect = newIncentive != address(0); + bool accessAllowed; + if (toConnect) { + accessAllowed = msg.sender == IBasePluginV1Factory(pluginFactory).farmingAddress(); + } else { + // we allow the one who connected the incentive to disconnect it, + // even if he no longer has the rights to connect incentives + if (_lastIncentiveOwner != address(0)) accessAllowed = msg.sender == _lastIncentiveOwner; + if (!accessAllowed) accessAllowed = msg.sender == IBasePluginV1Factory(pluginFactory).farmingAddress(); + } + require(accessAllowed, 'Not allowed to set incentive'); + + bool isPluginConnected = IAlgebraModularHub(modularHub).moduleAddressToIndex(address(this)) != 0; + if (toConnect) require(isPluginConnected, 'Plugin not attached'); + + address currentIncentive = incentive; + require(currentIncentive != newIncentive, 'Already active'); + if (toConnect) require(currentIncentive == address(0), 'Has active incentive'); + + incentive = newIncentive; + emit Incentive(newIncentive); + + if (toConnect) { + _lastIncentiveOwner = msg.sender; // write creator of this incentive + } else { + _lastIncentiveOwner = address(0); + } + } + + /// @inheritdoc IFarmingPlugin + function isIncentiveConnected(address targetIncentive) external view override returns (bool) { + if (incentive != targetIncentive) return false; + if (IAlgebraModularHub(modularHub).moduleAddressToIndex(address(this)) == 0) return false; + address pool = IAlgebraModularHub(modularHub).pool(); + (, , , uint8 pluginConfig) = _getPoolState(pool); + if (!pluginConfig.hasFlag(Plugins.AFTER_SWAP_FLAG)) return false; + + return true; + } + + function _getPoolState(address pool) internal view returns (uint160 price, int24 tick, uint16 fee, uint8 pluginConfig) { + (price, tick, fee, pluginConfig, , ) = IAlgebraPoolState(pool).globalState(); + } + + function _afterSwap( + bytes memory params , + uint16 /* poolFeeCache */ + ) internal virtual override { + AfterSwapParams memory decodedParams = abi.decode(params, (AfterSwapParams)); + + address _incentive = incentive; + if (_incentive != address(0)) { + (, int24 tick, , ) = _getPoolState(decodedParams.pool); + IAlgebraVirtualPool(_incentive).crossTo(tick, decodedParams.zeroToOne); + } + } +}