Skip to content

Commit

Permalink
after rebase
Browse files Browse the repository at this point in the history
  • Loading branch information
fourlen committed May 23, 2024
1 parent 2cc825d commit 03c46f4
Show file tree
Hide file tree
Showing 33 changed files with 1,506 additions and 439 deletions.
25 changes: 21 additions & 4 deletions src/plugin/contracts/AlgebraBasePluginV1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ contract AlgebraBasePluginV1 is IAlgebraBasePluginV1, Timestamp, IAlgebraPlugin
/// @inheritdoc IAlgebraPlugin
uint8 public constant override defaultPluginConfig = uint8(Plugins.AFTER_INIT_FLAG | Plugins.BEFORE_SWAP_FLAG | Plugins.DYNAMIC_FEE);

/// @inheritdoc IFarmingPlugin
address public immutable override pool;
address public immutable pool;
address private immutable factory;
address private immutable pluginFactory;

Expand Down Expand Up @@ -183,12 +182,12 @@ contract AlgebraBasePluginV1 is IAlgebraBasePluginV1, Timestamp, IAlgebraPlugin
bool toConnect = newIncentive != address(0);
bool accessAllowed;
if (toConnect) {
accessAllowed = msg.sender == IBasePluginV1Factory(pluginFactory).farmingAddress();
// 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();
// if (!accessAllowed) accessAllowed = msg.sender == IBasePluginV1Factory(pluginFactory).farmingAddress();
}
require(accessAllowed, 'Not allowed to set incentive');

Expand Down Expand Up @@ -317,4 +316,22 @@ contract AlgebraBasePluginV1 is IAlgebraBasePluginV1, Timestamp, IAlgebraPlugin
IAlgebraPool(pool).setFee(newFee);
}
}

function getCurrentAverageVolatility() external view override returns (uint88) {
uint16 lastIndex = timepointIndex;

uint16 oldestIndex = timepoints.getOldestIndex(lastIndex);
(, int24 tick, , ) = _getPoolState();

return timepoints.getAverageVolatility(_blockTimestamp(), tick, lastIndex, oldestIndex);
}

function getAverageVolatilityAtLastTimepoint(
uint32 currentTime,
int24 tick,
uint16 lastIndex,
uint16 oldestIndex
) external view override returns (uint88) {
return timepoints.getAverageVolatility(currentTime, tick, lastIndex, oldestIndex);
}
}
58 changes: 33 additions & 25 deletions src/plugin/contracts/BasePluginV1Factory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@
pragma solidity =0.8.20;

import './interfaces/IBasePluginV1Factory.sol';
import './interfaces/IAlgebraModuleFactory.sol';
import './base/AlgebraModuleFactory.sol';
import './libraries/AdaptiveFee.sol';
import './AlgebraBasePluginV1.sol';

import '@cryptoalgebra/algebra-modular-hub-v0.8.20/contracts/AlgebraModularHub.sol';
import '@cryptoalgebra/integral-core/contracts/interfaces/IAlgebraPool.sol';
import '@cryptoalgebra/integral-core/contracts/libraries/Plugins.sol';

/// @title Algebra Integral 1.1 default plugin factory
/// @notice This contract creates Algebra default plugins for Algebra liquidity pools
/// @dev This plugin factory can only be used for Algebra base pools
Expand All @@ -15,24 +21,25 @@ contract BasePluginV1Factory is IBasePluginV1Factory {
/// @inheritdoc IBasePluginV1Factory
address public immutable override algebraFactory;

/// @inheritdoc IBasePluginV1Factory
AlgebraFeeConfiguration public override defaultFeeConfiguration; // values of constants for sigmoids in fee calculation formula

/// @inheritdoc IBasePluginV1Factory
address public override farmingAddress;

/// @inheritdoc IBasePluginV1Factory
mapping(address poolAddress => address pluginAddress) public override pluginByPool;

mapping(uint256 factoryIndex => address factoryAddress) public factoryByIndex;
uint256 public factoriesCounter;

modifier onlyAdministrator() {
require(IAlgebraFactory(algebraFactory).hasRoleOrOwner(ALGEBRA_BASE_PLUGIN_FACTORY_ADMINISTRATOR, msg.sender), 'Only administrator');
_;
}

constructor(address _algebraFactory) {
constructor(address _algebraFactory, address _dynamicFeeModuleFactory, address _farmingModuleFactory, address _oracleModuleFactory) {
algebraFactory = _algebraFactory;
defaultFeeConfiguration = AdaptiveFee.initialFeeConfiguration();
emit DefaultFeeConfiguration(defaultFeeConfiguration);

factoryByIndex[0] = _oracleModuleFactory;
factoryByIndex[1] = _dynamicFeeModuleFactory;
factoryByIndex[2] = _farmingModuleFactory;

factoriesCounter = 3;
}

/// @inheritdoc IAlgebraPluginFactory
Expand All @@ -54,23 +61,24 @@ contract BasePluginV1Factory is IBasePluginV1Factory {

function _createPlugin(address pool) internal returns (address) {
require(pluginByPool[pool] == address(0), 'Already created');
IAlgebraBasePluginV1 volatilityOracle = new AlgebraBasePluginV1(pool, algebraFactory, address(this));
volatilityOracle.changeFeeConfiguration(defaultFeeConfiguration);
pluginByPool[pool] = address(volatilityOracle);
return address(volatilityOracle);
}

/// @inheritdoc IBasePluginV1Factory
function setDefaultFeeConfiguration(AlgebraFeeConfiguration calldata newConfig) external override onlyAdministrator {
AdaptiveFee.validateFeeConfiguration(newConfig);
defaultFeeConfiguration = newConfig;
emit DefaultFeeConfiguration(newConfig);
}
AlgebraModularHub modularHub = new AlgebraModularHub(pool, algebraFactory);

/// @inheritdoc IBasePluginV1Factory
function setFarmingAddress(address newFarmingAddress) external override onlyAdministrator {
require(farmingAddress != newFarmingAddress);
farmingAddress = newFarmingAddress;
emit FarmingAddress(newFarmingAddress);
IAlgebraPool(pool).setPlugin(address(modularHub));

for (uint256 i = 0; i < factoriesCounter; ++i) {
address moduleFactoryAddress = factoryByIndex[i];
address moduleAddress = IAlgebraModuleFactory(moduleFactoryAddress).deploy(address(modularHub));

uint256 globalModuleIndex = modularHub.registerModule(moduleAddress);
InsertModuleParams[] memory insertModuleParams = IAlgebraModuleFactory(moduleFactoryAddress).getInsertModuleParams(globalModuleIndex);

modularHub.insertModulesToHookLists(insertModuleParams);
}

IAlgebraPool(pool).setPluginConfig(uint8(Plugins.DYNAMIC_FEE));

pluginByPool[pool] = address(modularHub);
return address(modularHub);
}
}
22 changes: 22 additions & 0 deletions src/plugin/contracts/base/AlgebraModuleFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;

import '../libraries/AdaptiveFee.sol';
import '../interfaces/IAlgebraModuleFactory.sol';

abstract contract AlgebraModuleFactory is IAlgebraModuleFactory {
bytes32 public constant ALGEBRA_BASE_PLUGIN_FACTORY_ADMINISTRATOR = keccak256('ALGEBRA_BASE_PLUGIN_FACTORY_ADMINISTRATOR');

address public immutable algebraFactory;

mapping(address pool => address pluginAddress) public poolToPlugin;

modifier onlyAdministrator() {
require(IAlgebraFactory(algebraFactory).hasRoleOrOwner(ALGEBRA_BASE_PLUGIN_FACTORY_ADMINISTRATOR, msg.sender), 'Only administrator');
_;
}

constructor(address _algebraFactory) {
algebraFactory = _algebraFactory;
}
}
18 changes: 18 additions & 0 deletions src/plugin/contracts/interfaces/IAlgebraFarmingModuleFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;

import './IAlgebraModuleFactory.sol';

interface IAlgebraFarmingModuleFactory is IAlgebraModuleFactory {
/// @notice Emitted when the farming address is changed
/// @param newFarmingAddress The farming address after the address was changed
event FarmingAddress(address newFarmingAddress);

/// @notice Returns current farming address
/// @return The farming contract address
function farmingAddress() external view returns (address);

/// @dev updates farmings manager address on the factory
/// @param newFarmingAddress The new tokenomics contract address
function setFarmingAddress(address newFarmingAddress) external;
}
13 changes: 13 additions & 0 deletions src/plugin/contracts/interfaces/IAlgebraModuleFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;

import '@cryptoalgebra/integral-core/contracts/interfaces/IAlgebraFactory.sol';
import '@cryptoalgebra/algebra-modular-hub-v0.8.20/contracts/types/InsertModuleParams.sol';

interface IAlgebraModuleFactory {
function algebraFactory() external returns (address);

function deploy(address modularHub) external returns (address);

function getInsertModuleParams(uint256 moduleGlobalIndex) external pure returns (InsertModuleParams[] memory);
}
22 changes: 0 additions & 22 deletions src/plugin/contracts/interfaces/IBasePluginV1Factory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,6 @@ interface IBasePluginV1Factory is IAlgebraPluginFactory {
/// @return The AlgebraFactory contract address
function algebraFactory() external view returns (address);

/// @notice Current default dynamic fee configuration
/// @dev See the AdaptiveFee struct for more details about params.
/// This value is set by default in new plugins
function defaultFeeConfiguration()
external
view
returns (uint16 alpha1, uint16 alpha2, uint32 beta1, uint32 beta2, uint16 gamma1, uint16 gamma2, uint16 baseFee);

/// @notice Returns current farming address
/// @return The farming contract address
function farmingAddress() external view returns (address);

/// @notice Returns address of plugin created for given AlgebraPool
/// @param pool The address of AlgebraPool
/// @return The address of corresponding plugin
Expand All @@ -48,14 +36,4 @@ interface IBasePluginV1Factory is IAlgebraPluginFactory {
/// @param token1 The address of second token in pool
/// @return The address of created plugin
function createPluginForExistingPool(address token0, address token1) external returns (address);

/// @notice Changes initial fee configuration for new pools
/// @dev changes coefficients for sigmoids: α / (1 + e^( (β-x) / γ))
/// alpha1 + alpha2 + baseFee (max possible fee) must be <= type(uint16).max and gammas must be > 0
/// @param newConfig new default fee configuration. See the #AdaptiveFee.sol library for details
function setDefaultFeeConfiguration(AlgebraFeeConfiguration calldata newConfig) external;

/// @dev updates farmings manager address on the factory
/// @param newFarmingAddress The new tokenomics contract address
function setFarmingAddress(address newFarmingAddress) external;
}
4 changes: 0 additions & 4 deletions src/plugin/contracts/interfaces/plugins/IFarmingPlugin.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
5 changes: 5 additions & 0 deletions src/plugin/contracts/interfaces/plugins/IVolatilityOracle.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

import '../../libraries/VolatilityOracle.sol';

/// @title The interface for the Algebra volatility oracle
/// @dev This contract stores timepoints and calculates statistical averages
interface IVolatilityOracle {
Expand Down Expand Up @@ -64,4 +66,7 @@ interface IVolatilityOracle {
/// @param startIndex The start index, must be not initialized
/// @param amount of slots to fill, startIndex + amount must be <= type(uint16).max
function prepayTimepointsStorageSlots(uint16 startIndex, uint16 amount) external;

function getCurrentAverageVolatility() external view returns (uint88);
function getAverageVolatilityAtLastTimepoint(uint32 currentTime, int24 tick, uint16 lastIndex, uint16 oldestIndex) external view returns (uint88);
}
132 changes: 132 additions & 0 deletions src/plugin/contracts/modules/DynamicFeeModule.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// 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-v0.8.20/contracts/base/AlgebraModule.sol';
import '@cryptoalgebra/algebra-modular-hub-v0.8.20/contracts/interfaces/IAlgebraModularHub.sol';
import '@cryptoalgebra/algebra-modular-hub-v0.8.20/contracts/types/HookParams.sol';
import '@cryptoalgebra/algebra-modular-hub-v0.8.20/contracts/libraries/ModuleUtils.sol';

import '../types/AlgebraFeeConfigurationU144.sol';
import '../interfaces/plugins/IDynamicFeeManager.sol';
import '../interfaces/plugins/IVolatilityOracle.sol';
import '../libraries/AdaptiveFee.sol';
import '../libraries/VolatilityOracle.sol';

// DEV
import 'hardhat/console.sol';
import './OracleModule.sol';

contract DynamicFeeModule is AlgebraModule, IDynamicFeeManager, Timestamp {
string public override constant MODULE_NAME = 'Dynamic Fee';

using Plugins for uint8;
using AlgebraFeeConfigurationU144Lib for AlgebraFeeConfiguration;

uint256 internal constant UINT16_MODULO = 65536;
using VolatilityOracle for VolatilityOracle.Timepoint[UINT16_MODULO];

uint8 public override constant DEFAULT_PLUGIN_CONFIG = uint8(Plugins.AFTER_INIT_FLAG | Plugins.BEFORE_SWAP_FLAG | Plugins.DYNAMIC_FEE);

/// @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;

constructor(address _factory, address _pluginFactory, address _oracleModule, address _modularHub) AlgebraModule(_modularHub, IAlgebraModule(_modularHub).pool()) {
(factory, pluginFactory, oracleModule) = (_factory, _pluginFactory, _oracleModule);
}

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 view override {
ModuleUtils.returnDynamicFeeResult(_feeConfig.baseFee(), false);
}

function _beforeSwap(
bytes memory params,
uint16 /* poolFeeCache */
) internal view override {
BeforeSwapParams memory decodedParams = abi.decode(params, (BeforeSwapParams));
uint16 newFee = _getNewFee(decodedParams.pool);
ModuleUtils.returnDynamicFeeResult(newFee, false);
}

function _getNewFee(address pool) internal view returns (uint16 newFee) {
uint16 lastTimepointIndex = IVolatilityOracle(oracleModule).timepointIndex();

uint16 lastTimepointIndexOutsideCurrentBlock;
unchecked {
lastTimepointIndexOutsideCurrentBlock = lastTimepointIndex - 1;
}

uint16 oldestTimepointIndex; /* ❗❗❗ вот тут еще подумать, точно ли правильно логика написана ❗❗❗ */
unchecked {
// overflow is desired
oldestTimepointIndex = lastTimepointIndex + 1;
}

(bool initialized, , , , , , ) = IVolatilityOracle(oracleModule).timepoints(oldestTimepointIndex);
(, uint32 lastTimepointTimestampOutsideCurrentBlock, , , , ,) = IVolatilityOracle(oracleModule).timepoints(lastTimepointIndexOutsideCurrentBlock);

oldestTimepointIndex = initialized ? oldestTimepointIndex : 0;

(, int24 tick, uint16 fee, ) = _getPoolState(pool);

newFee = lastTimepointTimestampOutsideCurrentBlock != _blockTimestamp() ? _getFeeAtLastTimepoint(lastTimepointIndex, oldestTimepointIndex, tick, _feeConfig) : fee;
}
}
Loading

0 comments on commit 03c46f4

Please sign in to comment.