From f8984131b74ea07ab647b72f4e43001734f91253 Mon Sep 17 00:00:00 2001 From: fourlen Date: Mon, 6 May 2024 20:38:12 +0300 Subject: [PATCH] [Plugin] add DynamicFeeModule --- .../contracts/modules/DynamicFeeModule.sol | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 src/plugin/contracts/modules/DynamicFeeModule.sol diff --git a/src/plugin/contracts/modules/DynamicFeeModule.sol b/src/plugin/contracts/modules/DynamicFeeModule.sol new file mode 100644 index 000000000..f18268123 --- /dev/null +++ b/src/plugin/contracts/modules/DynamicFeeModule.sol @@ -0,0 +1,107 @@ +// 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/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'; + +import '../types/AlgebraFeeConfigurationU144.sol'; +import '../interfaces/plugins/IDynamicFeeManager.sol'; +import '../interfaces/plugins/IVolatilityOracle.sol'; +import '../libraries/AdaptiveFee.sol'; +import '../libraries/VolatilityOracle.sol'; + +contract DynamicFeeModule is AlgebraModule, IDynamicFeeManager, Timestamp { + using Plugins for uint8; + using AlgebraFeeConfigurationU144Lib for AlgebraFeeConfiguration; + + uint256 internal constant UINT16_MODULO = 65536; + using VolatilityOracle for VolatilityOracle.Timepoint[UINT16_MODULO]; + + + /// @dev The role can be granted in AlgebraFactory + bytes32 public constant ALGEBRA_BASE_PLUGIN_MANAGER = keccak256('ALGEBRA_BASE_PLUGIN_MANAGER'); + + address private immutable factory; + address private immutable pluginFactory; + + /// @dev AlgebraFeeConfiguration struct packed in uint144 + AlgebraFeeConfigurationU144 private _feeConfig; + + address public immutable oracleModule; + address public immutable modularHub; + + constructor(address _factory, address _pluginFactory, address _oracleModule, address _modularHub) { + (factory, pluginFactory, oracleModule, modularHub) = (_factory, _pluginFactory, _oracleModule, _modularHub); + } + + function changeFeeConfiguration(AlgebraFeeConfiguration calldata _config) external override { + require(msg.sender == pluginFactory || IAlgebraFactory(factory).hasRoleOrOwner(ALGEBRA_BASE_PLUGIN_MANAGER, msg.sender)); + AdaptiveFee.validateFeeConfiguration(_config); + + _feeConfig = _config.pack(); // pack struct to uint144 and write in storage + emit IDynamicFeeManager.FeeConfiguration(_config); + } + + /// @inheritdoc IAlgebraDynamicFeePlugin + function getCurrentFee() external view override returns (uint16 fee) { + uint88 volatilityAverage = IVolatilityOracle(oracleModule).getCurrentAverageVolatility(); + return AdaptiveFee.getFee(volatilityAverage, _feeConfig); + } + + function _getFeeAtLastTimepoint( + uint16 lastTimepointIndex, + uint16 oldestTimepointIndex, + int24 currentTick, + AlgebraFeeConfigurationU144 feeConfig_ + ) internal view returns (uint16 fee) { + if (feeConfig_.alpha1() | feeConfig_.alpha2() == 0) return feeConfig_.baseFee(); + + uint88 volatilityAverage = IVolatilityOracle(oracleModule).getAverageVolatilityAtLastTimepoint(_blockTimestamp(), currentTick, lastTimepointIndex, oldestTimepointIndex); + return AdaptiveFee.getFee(volatilityAverage, feeConfig_); + } + + function feeConfig() + external + view + override + returns (uint16 alpha1, uint16 alpha2, uint32 beta1, uint32 beta2, uint16 gamma1, uint16 gamma2, uint16 baseFee) + { + (alpha1, alpha2) = (_feeConfig.alpha1(), _feeConfig.alpha2()); + (beta1, beta2) = (_feeConfig.beta1(), _feeConfig.beta2()); + (gamma1, gamma2) = (_feeConfig.gamma1(), _feeConfig.gamma2()); + baseFee = _feeConfig.baseFee(); + } + + function _getPoolState(address pool) internal view returns (uint160 price, int24 tick, uint16 fee, uint8 pluginConfig) { + (price, tick, fee, pluginConfig, , ) = IAlgebraPoolState(pool).globalState(); + } + + function _afterInitialize( + bytes memory params, + uint16 /* poolFeeCache */ + ) internal virtual override { + AfterInitializeParams memory decodedParams = abi.decode(params, (AfterInitializeParams)); + IAlgebraPool(decodedParams.pool).setFee(_feeConfig.baseFee()); + } + + function updateFee( + address pool, + uint16 fee, + uint16 lastTimepointIndex, + uint16 oldestTimepointIndex, + int24 currentTick + ) external override { + require(msg.sender == oracleModule, 'Only oracle'); + + uint16 newFee = _getFeeAtLastTimepoint(lastTimepointIndex, oldestTimepointIndex, currentTick, _feeConfig); + if (newFee != fee) { + IAlgebraPool(pool).setFee(newFee); + } + } +}