From 8c238e0a58fc2907ea7d376f7a010a02a11a118a Mon Sep 17 00:00:00 2001 From: IliaAzhel Date: Wed, 14 Aug 2024 17:54:56 +0300 Subject: [PATCH 01/21] [Plugin] split base plugin into abstract classes --- src/plugin/contracts/AlgebraBasePluginV1.sol | 290 +----------------- src/plugin/contracts/BasePluginV1Factory.sol | 2 +- src/plugin/contracts/base/BasePlugin.sol | 103 +++++++ .../interfaces/IAlgebraBasePluginV1.sol | 22 -- .../contracts/interfaces/IBasePlugin.sol | 18 ++ .../interfaces/plugins/IFarmingPlugin.sol | 4 - .../interfaces/plugins/IVolatilityOracle.sol | 4 + .../contracts/plugins/DynamicFeePlugin.sol | 76 +++++ .../contracts/plugins/FarmingProxyPlugin.sol | 83 +++++ .../contracts/plugins/SlidingFeePlugin.sol | 109 +++++++ .../plugins/VolatilityOraclePlugin.sol | 106 +++++++ src/plugin/contracts/test/MockObservable.sol | 4 + 12 files changed, 520 insertions(+), 301 deletions(-) create mode 100644 src/plugin/contracts/base/BasePlugin.sol delete mode 100644 src/plugin/contracts/interfaces/IAlgebraBasePluginV1.sol create mode 100644 src/plugin/contracts/interfaces/IBasePlugin.sol create mode 100644 src/plugin/contracts/plugins/DynamicFeePlugin.sol create mode 100644 src/plugin/contracts/plugins/FarmingProxyPlugin.sol create mode 100644 src/plugin/contracts/plugins/SlidingFeePlugin.sol create mode 100644 src/plugin/contracts/plugins/VolatilityOraclePlugin.sol diff --git a/src/plugin/contracts/AlgebraBasePluginV1.sol b/src/plugin/contracts/AlgebraBasePluginV1.sol index 8dde7e8b1..70fd8a3c1 100644 --- a/src/plugin/contracts/AlgebraBasePluginV1.sol +++ b/src/plugin/contracts/AlgebraBasePluginV1.sol @@ -1,253 +1,36 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.20; -import '@cryptoalgebra/integral-core/contracts/base/common/Timestamp.sol'; import '@cryptoalgebra/integral-core/contracts/libraries/Plugins.sol'; -import '@cryptoalgebra/integral-core/contracts/libraries/SafeTransfer.sol'; -import '@cryptoalgebra/integral-core/contracts/interfaces/IAlgebraFactory.sol'; import '@cryptoalgebra/integral-core/contracts/interfaces/plugin/IAlgebraPlugin.sol'; -import '@cryptoalgebra/integral-core/contracts/interfaces/pool/IAlgebraPoolState.sol'; -import '@cryptoalgebra/integral-core/contracts/interfaces/IAlgebraPool.sol'; -import './interfaces/IAlgebraBasePluginV1.sol'; -import './interfaces/IBasePluginV1Factory.sol'; -import './interfaces/IAlgebraVirtualPool.sol'; - -import './libraries/VolatilityOracle.sol'; -import './libraries/AdaptiveFee.sol'; -import './types/AlgebraFeeConfigurationU144.sol'; +import './plugins/DynamicFeePlugin.sol'; +import './plugins/FarmingProxyPlugin.sol'; +import './plugins/SlidingFeePlugin.sol'; +import './plugins/VolatilityOraclePlugin.sol'; /// @title Algebra Integral 1.1 default plugin /// @notice This contract stores timepoints and calculates adaptive fee and statistical averages -contract AlgebraBasePluginV1 is IAlgebraBasePluginV1, Timestamp, IAlgebraPlugin { +contract AlgebraBasePluginV1 is DynamicFeePlugin, FarmingProxyPlugin, VolatilityOraclePlugin { 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'); /// @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 private immutable factory; - address private immutable pluginFactory; - - /// @inheritdoc IVolatilityOracle - VolatilityOracle.Timepoint[UINT16_MODULO] public override timepoints; - - /// @inheritdoc IVolatilityOracle - uint16 public override timepointIndex; - - /// @inheritdoc IVolatilityOracle - uint32 public override lastTimepointTimestamp; - - /// @inheritdoc IVolatilityOracle - bool public override isInitialized; - - /// @dev AlgebraFeeConfiguration struct packed in uint144 - AlgebraFeeConfigurationU144 private _feeConfig; - - /// @inheritdoc IFarmingPlugin - address public override incentive; - - /// @dev the address which connected the last incentive. Needed so that he can disconnect it - address private _lastIncentiveOwner; - - modifier onlyPool() { - _checkIfFromPool(); - _; - } - - constructor(address _pool, address _factory, address _pluginFactory) { - (factory, pool, pluginFactory) = (_factory, _pool, _pluginFactory); - } - - /// @inheritdoc IDynamicFeeManager - 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 _checkIfFromPool() internal view { - require(msg.sender == pool, 'Only pool can call this'); - } - - function _getPoolState() internal view returns (uint160 price, int24 tick, uint16 fee, uint8 pluginConfig) { - (price, tick, fee, pluginConfig, , ) = IAlgebraPoolState(pool).globalState(); - } - - function _getPluginInPool() internal view returns (address plugin) { - return IAlgebraPool(pool).plugin(); - } - - /// @inheritdoc IAlgebraBasePluginV1 - function initialize() external override { - require(!isInitialized, 'Already initialized'); - require(_getPluginInPool() == address(this), 'Plugin not attached'); - (uint160 price, int24 tick, , ) = _getPoolState(); - require(price != 0, 'Pool is not initialized'); - - uint32 time = _blockTimestamp(); - timepoints.initialize(time, tick); - lastTimepointTimestamp = time; - isInitialized = true; - - _updatePluginConfigInPool(); - } - - /// @inheritdoc IAlgebraBasePluginV1 - function collectPluginFee(address token, uint256 amount, address recipient) external override { - require(msg.sender == pluginFactory || IAlgebraFactory(factory).hasRoleOrOwner(ALGEBRA_BASE_PLUGIN_MANAGER, msg.sender)); - SafeTransfer.safeTransfer(token, recipient, amount); - } - - /// @inheritdoc IAlgebraPlugin - function handlePluginFee(uint256, uint256) external view override onlyPool returns (bytes4) { - return IAlgebraPlugin.handlePluginFee.selector; - } - - // ###### Volatility and TWAP oracle ###### - - /// @inheritdoc IVolatilityOracle - function getSingleTimepoint(uint32 secondsAgo) external view override returns (int56 tickCumulative, uint88 volatilityCumulative) { - // `volatilityCumulative` values for timestamps after the last timepoint _should not_ be compared: they may differ due to interpolation errors - (, int24 tick, , ) = _getPoolState(); - uint16 lastTimepointIndex = timepointIndex; - uint16 oldestIndex = timepoints.getOldestIndex(lastTimepointIndex); - VolatilityOracle.Timepoint memory result = timepoints.getSingleTimepoint(_blockTimestamp(), secondsAgo, tick, lastTimepointIndex, oldestIndex); - (tickCumulative, volatilityCumulative) = (result.tickCumulative, result.volatilityCumulative); - } - - /// @inheritdoc IVolatilityOracle - function getTimepoints( - uint32[] memory secondsAgos - ) external view override returns (int56[] memory tickCumulatives, uint88[] memory volatilityCumulatives) { - // `volatilityCumulative` values for timestamps after the last timepoint _should not_ be compared: they may differ due to interpolation errors - (, int24 tick, , ) = _getPoolState(); - return timepoints.getTimepoints(_blockTimestamp(), secondsAgos, tick, timepointIndex); - } - - /// @inheritdoc IVolatilityOracle - function prepayTimepointsStorageSlots(uint16 startIndex, uint16 amount) external override { - require(!timepoints[startIndex].initialized); // if not initialized, then all subsequent ones too - require(amount > 0 && type(uint16).max - startIndex >= amount); - - unchecked { - for (uint256 i = startIndex; i < startIndex + amount; ++i) { - timepoints[i].blockTimestamp = 1; // will be overwritten - } - } - } - - // ###### Fee manager ###### - - /// @inheritdoc IDynamicFeeManager - 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 FeeConfiguration(_config); - } - - /// @inheritdoc IAlgebraDynamicFeePlugin - function getCurrentFee() external view override returns (uint16 fee) { - uint16 lastIndex = timepointIndex; - AlgebraFeeConfigurationU144 feeConfig_ = _feeConfig; - if (feeConfig_.alpha1() | feeConfig_.alpha2() == 0) return feeConfig_.baseFee(); - - uint16 oldestIndex = timepoints.getOldestIndex(lastIndex); - (, int24 tick, , ) = _getPoolState(); - - uint88 volatilityAverage = timepoints.getAverageVolatility(_blockTimestamp(), tick, lastIndex, oldestIndex); - 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 = timepoints.getAverageVolatility(_blockTimestamp(), currentTick, lastTimepointIndex, oldestTimepointIndex); - return AdaptiveFee.getFee(volatilityAverage, feeConfig_); - } - - // ###### Farming plugin ###### - - /// @inheritdoc IFarmingPlugin - 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 = _getPluginInPool() == address(this); - 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); - } - - if (isPluginConnected) { - _updatePluginConfigInPool(); - } - } - - /// @inheritdoc IFarmingPlugin - function isIncentiveConnected(address targetIncentive) external view override returns (bool) { - if (incentive != targetIncentive) return false; - if (_getPluginInPool() != address(this)) return false; - (, , , uint8 pluginConfig) = _getPoolState(); - if (!pluginConfig.hasFlag(Plugins.AFTER_SWAP_FLAG)) return false; - - return true; + constructor(address _pool, address _factory, address _pluginFactory) BasePlugin(_pool, _factory, _pluginFactory) { + } // ###### HOOKS ###### function beforeInitialize(address, uint160) external override onlyPool returns (bytes4) { - _updatePluginConfigInPool(); + _updatePluginConfigInPool(defaultPluginConfig); return IAlgebraPlugin.beforeInitialize.selector; } function afterInitialize(address, uint160, int24 tick) external override onlyPool returns (bytes4) { - uint32 _timestamp = _blockTimestamp(); - timepoints.initialize(_timestamp, tick); - - lastTimepointTimestamp = _timestamp; - isInitialized = true; + _initialize_TWAP(tick); IAlgebraPool(pool).setFee(_feeConfig.baseFee()); return IAlgebraPlugin.afterInitialize.selector; @@ -255,78 +38,37 @@ contract AlgebraBasePluginV1 is IAlgebraBasePluginV1, Timestamp, IAlgebraPlugin /// @dev unused function beforeModifyPosition(address, address, int24, int24, int128, bytes calldata) external override onlyPool returns (bytes4, uint24) { - _updatePluginConfigInPool(); // should not be called, reset config + _updatePluginConfigInPool(defaultPluginConfig); // should not be called, reset config return (IAlgebraPlugin.beforeModifyPosition.selector, 0); } /// @dev unused function afterModifyPosition(address, address, int24, int24, int128, uint256, uint256, bytes calldata) external override onlyPool returns (bytes4) { - _updatePluginConfigInPool(); // should not be called, reset config + _updatePluginConfigInPool(defaultPluginConfig); // should not be called, reset config return IAlgebraPlugin.afterModifyPosition.selector; } function beforeSwap(address, address, bool, int256, uint160, bool, bytes calldata) external override onlyPool returns (bytes4, uint24, uint24) { - _writeTimepointAndUpdateFee(); + uint88 volatilityAverage = _writeTimepoint(); + if(volatilityAverage != 0 ) _updateFee(volatilityAverage); return (IAlgebraPlugin.beforeSwap.selector, 0, 0); } function afterSwap(address, address, bool zeroToOne, int256, uint160, int256, int256, bytes calldata) external override onlyPool returns (bytes4) { - address _incentive = incentive; - if (_incentive != address(0)) { - (, int24 tick, , ) = _getPoolState(); - IAlgebraVirtualPool(_incentive).crossTo(tick, zeroToOne); - } else { - _updatePluginConfigInPool(); // should not be called, reset config - } - + _updateVirtualPoolTick(zeroToOne); return IAlgebraPlugin.afterSwap.selector; } /// @dev unused function beforeFlash(address, address, uint256, uint256, bytes calldata) external override onlyPool returns (bytes4) { - _updatePluginConfigInPool(); // should not be called, reset config + _updatePluginConfigInPool(defaultPluginConfig); // should not be called, reset config return IAlgebraPlugin.beforeFlash.selector; } /// @dev unused function afterFlash(address, address, uint256, uint256, uint256, uint256, bytes calldata) external override onlyPool returns (bytes4) { - _updatePluginConfigInPool(); // should not be called, reset config + _updatePluginConfigInPool(defaultPluginConfig); // should not be called, reset config return IAlgebraPlugin.afterFlash.selector; } - function _updatePluginConfigInPool() internal { - uint8 newPluginConfig = defaultPluginConfig; - if (incentive != address(0)) { - newPluginConfig |= uint8(Plugins.AFTER_SWAP_FLAG); - } - - (, , , uint8 currentPluginConfig) = _getPoolState(); - if (currentPluginConfig != newPluginConfig) { - IAlgebraPool(pool).setPluginConfig(newPluginConfig); - } - } - - function _writeTimepointAndUpdateFee() internal { - // single SLOAD - uint16 _lastIndex = timepointIndex; - uint32 _lastTimepointTimestamp = lastTimepointTimestamp; - AlgebraFeeConfigurationU144 feeConfig_ = _feeConfig; // struct packed in uint144 - bool _isInitialized = isInitialized; - require(_isInitialized, 'Not initialized'); - - uint32 currentTimestamp = _blockTimestamp(); - - if (_lastTimepointTimestamp == currentTimestamp) return; - - (, int24 tick, uint16 fee, ) = _getPoolState(); - (uint16 newLastIndex, uint16 newOldestIndex) = timepoints.write(_lastIndex, currentTimestamp, tick); - - timepointIndex = newLastIndex; - lastTimepointTimestamp = currentTimestamp; - - uint16 newFee = _getFeeAtLastTimepoint(newLastIndex, newOldestIndex, tick, feeConfig_); - if (newFee != fee) { - IAlgebraPool(pool).setFee(newFee); - } - } } diff --git a/src/plugin/contracts/BasePluginV1Factory.sol b/src/plugin/contracts/BasePluginV1Factory.sol index ad294e51b..d68331dec 100644 --- a/src/plugin/contracts/BasePluginV1Factory.sol +++ b/src/plugin/contracts/BasePluginV1Factory.sol @@ -59,7 +59,7 @@ 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)); + IDynamicFeeManager volatilityOracle = new AlgebraBasePluginV1(pool, algebraFactory, address(this)); volatilityOracle.changeFeeConfiguration(defaultFeeConfiguration); pluginByPool[pool] = address(volatilityOracle); return address(volatilityOracle); diff --git a/src/plugin/contracts/base/BasePlugin.sol b/src/plugin/contracts/base/BasePlugin.sol new file mode 100644 index 000000000..43622a087 --- /dev/null +++ b/src/plugin/contracts/base/BasePlugin.sol @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity =0.8.20; + +import '@cryptoalgebra/integral-core/contracts/base/common/Timestamp.sol'; +import '@cryptoalgebra/integral-core/contracts/libraries/Plugins.sol'; +import '@cryptoalgebra/integral-core/contracts/libraries/SafeTransfer.sol'; + +import '@cryptoalgebra/integral-core/contracts/interfaces/IAlgebraFactory.sol'; +import '@cryptoalgebra/integral-core/contracts/interfaces/pool/IAlgebraPoolState.sol'; +import '@cryptoalgebra/integral-core/contracts/interfaces/IAlgebraPool.sol'; + +import '../interfaces/IBasePlugin.sol'; + +/// @title Algebra Integral 1.1 default plugin +/// @notice This contract stores timepoints and calculates adaptive fee and statistical averages +abstract contract BasePlugin is IBasePlugin, Timestamp { + using Plugins for uint8; + + /// @dev The role can be granted in AlgebraFactory + bytes32 public constant ALGEBRA_BASE_PLUGIN_MANAGER = keccak256('ALGEBRA_BASE_PLUGIN_MANAGER'); + + uint8 private constant defaultPluginConfig = 0; + + address public immutable override pool; + address internal immutable factory; + address internal immutable pluginFactory; + + modifier onlyPool() { + _checkIfFromPool(); + _; + } + + constructor(address _pool, address _factory, address _pluginFactory) { + (factory, pool, pluginFactory) = (_factory, _pool, _pluginFactory); + } + + function _checkIfFromPool() internal view { + require(msg.sender == pool, 'Only pool can call this'); + } + + function _getPoolState() internal view returns (uint160 price, int24 tick, uint16 fee, uint8 pluginConfig) { + (price, tick, fee, pluginConfig, , ) = IAlgebraPoolState(pool).globalState(); + } + + function _getPluginInPool() internal view returns (address plugin) { + return IAlgebraPool(pool).plugin(); + } + + /// @inheritdoc IBasePlugin + function collectPluginFee(address token, uint256 amount, address recipient) external override { + require(msg.sender == pluginFactory || IAlgebraFactory(factory).hasRoleOrOwner(ALGEBRA_BASE_PLUGIN_MANAGER, msg.sender)); + SafeTransfer.safeTransfer(token, recipient, amount); + } + + /// @inheritdoc IAlgebraPlugin + function handlePluginFee(uint256, uint256) external view override onlyPool returns (bytes4) { + return IAlgebraPlugin.handlePluginFee.selector; + } + + // ###### HOOKS ###### + + function beforeInitialize(address, uint160) external override virtual onlyPool returns (bytes4) { + return IAlgebraPlugin.beforeInitialize.selector; + } + + function afterInitialize(address, uint160, int24) external override virtual onlyPool returns (bytes4) { + return IAlgebraPlugin.afterInitialize.selector; + } + + function beforeModifyPosition(address, address, int24, int24, int128, bytes calldata) external override virtual onlyPool returns (bytes4, uint24) { + return (IAlgebraPlugin.beforeModifyPosition.selector, 0); + } + + function afterModifyPosition(address, address, int24, int24, int128, uint256, uint256, bytes calldata) external override virtual onlyPool returns (bytes4) { + return IAlgebraPlugin.afterModifyPosition.selector; + } + + function beforeSwap(address, address, bool, int256, uint160, bool, bytes calldata) external override virtual onlyPool returns (bytes4, uint24, uint24) { + return (IAlgebraPlugin.beforeSwap.selector, 0, 0); + } + + function afterSwap(address, address, bool, int256, uint160, int256, int256, bytes calldata) external override virtual onlyPool returns (bytes4) { + return IAlgebraPlugin.afterSwap.selector; + } + + function beforeFlash(address, address, uint256, uint256, bytes calldata) external override virtual onlyPool returns (bytes4) { + return IAlgebraPlugin.beforeFlash.selector; + } + + function afterFlash(address, address, uint256, uint256, uint256, uint256, bytes calldata) external override virtual onlyPool returns (bytes4) { + return IAlgebraPlugin.afterFlash.selector; + } + + // TODO + function _updatePluginConfigInPool(uint8 newPluginConfig) internal { + + (, , , uint8 currentPluginConfig) = _getPoolState(); + if (currentPluginConfig != newPluginConfig) { + IAlgebraPool(pool).setPluginConfig(newPluginConfig); + } + } + +} diff --git a/src/plugin/contracts/interfaces/IAlgebraBasePluginV1.sol b/src/plugin/contracts/interfaces/IAlgebraBasePluginV1.sol deleted file mode 100644 index 84efe37a5..000000000 --- a/src/plugin/contracts/interfaces/IAlgebraBasePluginV1.sol +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; -pragma abicoder v2; - -import './plugins/IVolatilityOracle.sol'; -import './plugins/IDynamicFeeManager.sol'; -import './plugins/IFarmingPlugin.sol'; - -/// @title The interface for the AlgebraBasePluginV1 -/// @notice This contract combines the standard implementations of the volatility oracle and the dynamic fee manager -/// @dev This contract stores timepoints and calculates adaptive fee and statistical averages -interface IAlgebraBasePluginV1 is IVolatilityOracle, IDynamicFeeManager, IFarmingPlugin { - /// @notice Initialize the plugin externally - /// @dev This function allows to initialize the plugin if it was created after the pool was created - function initialize() external; - - /// @notice Claim plugin fee - /// @param token The token address - /// @param amount Amount of tokens - /// @param recipient Recipient address - function collectPluginFee(address token, uint256 amount, address recipient) external; -} diff --git a/src/plugin/contracts/interfaces/IBasePlugin.sol b/src/plugin/contracts/interfaces/IBasePlugin.sol new file mode 100644 index 000000000..4730809d0 --- /dev/null +++ b/src/plugin/contracts/interfaces/IBasePlugin.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; +pragma abicoder v2; + +import '@cryptoalgebra/integral-core/contracts/interfaces/plugin/IAlgebraPlugin.sol'; + +/// @title The interface for the BasePlugin +interface IBasePlugin is IAlgebraPlugin { + /// @notice Claim plugin fee + /// @param token The token address + /// @param amount Amount of tokens + /// @param recipient Recipient address + function collectPluginFee(address token, uint256 amount, address recipient) external; + + /// @notice Returns the address of the pool the plugin is created for + /// @return address of the pool + function pool() external view returns (address); +} 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/interfaces/plugins/IVolatilityOracle.sol b/src/plugin/contracts/interfaces/plugins/IVolatilityOracle.sol index f0ab2b9d0..265f937bf 100644 --- a/src/plugin/contracts/interfaces/plugins/IVolatilityOracle.sol +++ b/src/plugin/contracts/interfaces/plugins/IVolatilityOracle.sol @@ -33,6 +33,10 @@ interface IVolatilityOracle { /// @return index of the last timepoint written function timepointIndex() external view returns (uint16); + /// @notice Initialize the plugin externally + /// @dev This function allows to initialize the plugin if it was created after the pool was created + function initialize() external; + /// @notice Returns the timestamp of the last timepoint that was written. /// @return timestamp of the last timepoint function lastTimepointTimestamp() external view returns (uint32); diff --git a/src/plugin/contracts/plugins/DynamicFeePlugin.sol b/src/plugin/contracts/plugins/DynamicFeePlugin.sol new file mode 100644 index 000000000..b84b3d434 --- /dev/null +++ b/src/plugin/contracts/plugins/DynamicFeePlugin.sol @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity =0.8.20; + +import '@cryptoalgebra/integral-core/contracts/libraries/Plugins.sol'; + +import '@cryptoalgebra/integral-core/contracts/interfaces/IAlgebraFactory.sol'; +import '@cryptoalgebra/integral-core/contracts/interfaces/IAlgebraPool.sol'; + +import '../interfaces/IBasePluginV1Factory.sol'; +import '../interfaces/plugins/IDynamicFeeManager.sol'; + +import '../libraries/AdaptiveFee.sol'; +import '../types/AlgebraFeeConfigurationU144.sol'; +import '../base/BasePlugin.sol'; + +/// @title Algebra Integral 1.1 default plugin +/// @notice This contract stores timepoints and calculates adaptive fee and statistical averages +abstract contract DynamicFeePlugin is BasePlugin, IDynamicFeeManager { + using Plugins for uint8; + using AlgebraFeeConfigurationU144Lib for AlgebraFeeConfiguration; + + uint8 private constant defaultPluginConfig = uint8(Plugins.BEFORE_SWAP_FLAG | Plugins.DYNAMIC_FEE); + + /// @dev AlgebraFeeConfiguration struct packed in uint144 + AlgebraFeeConfigurationU144 internal _feeConfig; + + /// @inheritdoc IDynamicFeeManager + 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(); + } + + // ###### Fee manager ###### + + /// @inheritdoc IDynamicFeeManager + 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 FeeConfiguration(_config); + } + + // TODO + /// @inheritdoc IAlgebraDynamicFeePlugin + function getCurrentFee() external view override returns (uint16 fee) { + uint88 volatilityAverage; + AlgebraFeeConfigurationU144 feeConfig_ = _feeConfig; + if (feeConfig_.alpha1() | feeConfig_.alpha2() == 0) return feeConfig_.baseFee(); + + return AdaptiveFee.getFee(volatilityAverage, feeConfig_); + } + + function _updateFee(uint88 volatilityAverage) internal { + uint16 newFee; + AlgebraFeeConfigurationU144 feeConfig_ = _feeConfig; // struct packed in uint144 + + (, , uint16 fee, ) = _getPoolState(); + if (feeConfig_.alpha1() | feeConfig_.alpha2() == 0) { + newFee = feeConfig_.baseFee(); + } else { + newFee = AdaptiveFee.getFee(volatilityAverage, feeConfig_); + } + + if (newFee != fee) { + IAlgebraPool(pool).setFee(newFee); + } + } +} diff --git a/src/plugin/contracts/plugins/FarmingProxyPlugin.sol b/src/plugin/contracts/plugins/FarmingProxyPlugin.sol new file mode 100644 index 000000000..287a0a0a9 --- /dev/null +++ b/src/plugin/contracts/plugins/FarmingProxyPlugin.sol @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity =0.8.20; + +import '@cryptoalgebra/integral-core/contracts/libraries/Plugins.sol'; + +import '@cryptoalgebra/integral-core/contracts/interfaces/IAlgebraPool.sol'; + +import '../interfaces/IBasePluginV1Factory.sol'; +import '../interfaces/IAlgebraVirtualPool.sol'; +import '../interfaces/plugins/IFarmingPlugin.sol'; + +import '../base/BasePlugin.sol'; + +/// @title Algebra Integral 1.1 default plugin +/// @notice This contract stores timepoints and calculates adaptive fee and statistical averages +abstract contract FarmingProxyPlugin is BasePlugin, IFarmingPlugin { + using Plugins for uint8; + + uint8 private constant defaultPluginConfig = uint8(Plugins.AFTER_INIT_FLAG | Plugins.AFTER_SWAP_FLAG); + + /// @inheritdoc IFarmingPlugin + address public override incentive; + + /// @dev the address which connected the last incentive. Needed so that he can disconnect it + address private _lastIncentiveOwner; + + /// @inheritdoc IFarmingPlugin + 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 = _getPluginInPool() == address(this); + 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); + } + + if (isPluginConnected) { + _updatePluginConfigInPool(defaultPluginConfig); + } + } + + /// @inheritdoc IFarmingPlugin + function isIncentiveConnected(address targetIncentive) external view override returns (bool) { + if (incentive != targetIncentive) return false; + if (_getPluginInPool() != address(this)) return false; + (, , , uint8 pluginConfig) = _getPoolState(); + if (!pluginConfig.hasFlag(Plugins.AFTER_SWAP_FLAG)) return false; + + return true; + } + + function _updateVirtualPoolTick(bool zeroToOne) internal { + address _incentive = incentive; + if (_incentive != address(0)) { + (, int24 tick, , ) = _getPoolState(); + IAlgebraVirtualPool(_incentive).crossTo(tick, zeroToOne); + } else { + _updatePluginConfigInPool(defaultPluginConfig); // should not be called, reset config + } + + } + +} diff --git a/src/plugin/contracts/plugins/SlidingFeePlugin.sol b/src/plugin/contracts/plugins/SlidingFeePlugin.sol new file mode 100644 index 000000000..5556f41fb --- /dev/null +++ b/src/plugin/contracts/plugins/SlidingFeePlugin.sol @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: MIT +pragma solidity =0.8.20; + +import {IAlgebraPool} from '@cryptoalgebra/integral-core/contracts/interfaces/IAlgebraPool.sol'; +import {Timestamp} from '@cryptoalgebra/integral-core/contracts/base/common/Timestamp.sol'; +import {TickMath} from '@cryptoalgebra/integral-core/contracts/libraries/TickMath.sol'; + +import 'hardhat/console.sol'; + +abstract contract SlidingFeeModule is Timestamp { + struct FeeFactors { + uint128 zeroToOneFeeFactor; + uint128 oneToZeroFeeFactor; + } + + uint64 internal constant FEE_FACTOR_SHIFT = 96; + + FeeFactors public s_feeFactors; + + uint256 public s_priceChangeFactor = 1; + + event PriceChangeFactor(uint256 priceChangeFactor); + + constructor() { + FeeFactors memory feeFactors = FeeFactors( + uint128(1 << FEE_FACTOR_SHIFT), + uint128(1 << FEE_FACTOR_SHIFT) + ); + + s_feeFactors = feeFactors; + } + + function _getFeeAndUpdateFactors( + int24 currenTick, + int24 lastTick, + uint16 poolFee, + bool zeroToOne + ) internal returns (uint16) { + FeeFactors memory currentFeeFactors; + + // console.log('current price: ', currentPrice); + // console.log('last price: ', lastPrice); + // console.log('zero to one: ', zeroToOne); + + // ❗❗❗ + // раньше было currentPrice = 0, я так понял проверка на то, инициализирована ли была цена + // теперь currentTick = 0 - валидное значение, возможно стоит передавать доп аргумент + if (lastTick == 0) { + return poolFee; + } + + currentFeeFactors = _calculateFeeFactors(currenTick, lastTick); + + s_feeFactors = currentFeeFactors; + + uint16 adjustedFee = zeroToOne ? + uint16((poolFee * currentFeeFactors.zeroToOneFeeFactor) >> FEE_FACTOR_SHIFT) : + uint16((poolFee * currentFeeFactors.oneToZeroFeeFactor) >> FEE_FACTOR_SHIFT); + + return adjustedFee; + } + + function _calculateFeeFactors( + int24 currentTick, + int24 lastTick + ) internal view returns (FeeFactors memory feeFactors) { + console.log('currentTick: '); + console.logInt(int256(currentTick)); + console.log('lastTick: '); + console.logInt(int256(lastTick)); + // price change is positive after zeroToOne prevalence + int256 priceChangeRatio = int256(uint256(TickMath.getSqrtRatioAtTick(currentTick - lastTick))) - int256(1 << FEE_FACTOR_SHIFT); // (currentPrice - lastPrice) / lastPrice + console.log('priceChangeRatio: '); + console.logInt(priceChangeRatio); + int128 feeFactorImpact = int128(priceChangeRatio * int256(s_priceChangeFactor)); + + feeFactors = s_feeFactors; + + // if there were zeroToOne prevalence in the last price change, + // in result price has increased + // we need to increase zeroToOneFeeFactor + // and vice versa + int128 newZeroToOneFeeFactor = int128(feeFactors.zeroToOneFeeFactor) + feeFactorImpact; + + if ((int128(-2) << FEE_FACTOR_SHIFT) < newZeroToOneFeeFactor && newZeroToOneFeeFactor < int128(uint128(2) << FEE_FACTOR_SHIFT)) { + feeFactors = FeeFactors( + uint128(newZeroToOneFeeFactor), + uint128(int128(feeFactors.oneToZeroFeeFactor) - feeFactorImpact) + ); + } else if (newZeroToOneFeeFactor <= 0) { + // In this case price has decreased that much so newZeroToOneFeeFactor is less than 0 + // So we set it to the minimal value == 0 + // It means that there were too much oneToZero prevalence and we want to decrease it + // Basically price change is -100% + feeFactors = FeeFactors( + uint128(2 << FEE_FACTOR_SHIFT), + 0 + ); + } else { + // In this case priceChange is big enough that newZeroToOneFeeFactor is greater than 2 + // So we set it to the maximum value + // It means that there were too much zeroToOne prevalence and we want to decrease it + feeFactors = FeeFactors( + 0, + uint128(2 << FEE_FACTOR_SHIFT) + ); + } + } +} \ No newline at end of file diff --git a/src/plugin/contracts/plugins/VolatilityOraclePlugin.sol b/src/plugin/contracts/plugins/VolatilityOraclePlugin.sol new file mode 100644 index 000000000..e63e39ee6 --- /dev/null +++ b/src/plugin/contracts/plugins/VolatilityOraclePlugin.sol @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity =0.8.20; + +import '@cryptoalgebra/integral-core/contracts/libraries/Plugins.sol'; + +import '../interfaces/IBasePlugin.sol'; +import '../interfaces/IBasePluginV1Factory.sol'; +import '../interfaces/plugins/IVolatilityOracle.sol'; + +import '../libraries/VolatilityOracle.sol'; +import '../base/BasePlugin.sol'; + + +/// @title Algebra Integral 1.1 default plugin +/// @notice This contract stores timepoints and calculates adaptive fee and statistical averages +abstract contract VolatilityOraclePlugin is BasePlugin, IVolatilityOracle{ + using Plugins for uint8; + + uint256 internal constant UINT16_MODULO = 65536; + using VolatilityOracle for VolatilityOracle.Timepoint[UINT16_MODULO]; + + uint8 private constant defaultPluginConfig = uint8(Plugins.AFTER_INIT_FLAG | Plugins.BEFORE_SWAP_FLAG); + + /// @inheritdoc IVolatilityOracle + VolatilityOracle.Timepoint[UINT16_MODULO] public override timepoints; + + /// @inheritdoc IVolatilityOracle + uint16 public override timepointIndex; + + /// @inheritdoc IVolatilityOracle + uint32 public override lastTimepointTimestamp; + + /// @inheritdoc IVolatilityOracle + bool public override isInitialized; + + /// @inheritdoc IVolatilityOracle + function initialize() external override { + require(!isInitialized, 'Already initialized'); + require(_getPluginInPool() == address(this), 'Plugin not attached'); + (uint160 price, int24 tick, , ) = _getPoolState(); + require(price != 0, 'Pool is not initialized'); + _initialize_TWAP(tick); + + } + + function _initialize_TWAP(int24 tick) internal { + + uint32 time = _blockTimestamp(); + timepoints.initialize(time, tick); + lastTimepointTimestamp = time; + isInitialized = true; + + _updatePluginConfigInPool(defaultPluginConfig); + } + // ###### Volatility and TWAP oracle ###### + + /// @inheritdoc IVolatilityOracle + function getSingleTimepoint(uint32 secondsAgo) external view override returns (int56 tickCumulative, uint88 volatilityCumulative) { + // `volatilityCumulative` values for timestamps after the last timepoint _should not_ be compared: they may differ due to interpolation errors + (, int24 tick, , ) = _getPoolState(); + uint16 lastTimepointIndex = timepointIndex; + uint16 oldestIndex = timepoints.getOldestIndex(lastTimepointIndex); + VolatilityOracle.Timepoint memory result = timepoints.getSingleTimepoint(_blockTimestamp(), secondsAgo, tick, lastTimepointIndex, oldestIndex); + (tickCumulative, volatilityCumulative) = (result.tickCumulative, result.volatilityCumulative); + } + + /// @inheritdoc IVolatilityOracle + function getTimepoints( + uint32[] memory secondsAgos + ) external view override returns (int56[] memory tickCumulatives, uint88[] memory volatilityCumulatives) { + // `volatilityCumulative` values for timestamps after the last timepoint _should not_ be compared: they may differ due to interpolation errors + (, int24 tick, , ) = _getPoolState(); + return timepoints.getTimepoints(_blockTimestamp(), secondsAgos, tick, timepointIndex); + } + + /// @inheritdoc IVolatilityOracle + function prepayTimepointsStorageSlots(uint16 startIndex, uint16 amount) external override { + require(!timepoints[startIndex].initialized); // if not initialized, then all subsequent ones too + require(amount > 0 && type(uint16).max - startIndex >= amount); + + unchecked { + for (uint256 i = startIndex; i < startIndex + amount; ++i) { + timepoints[i].blockTimestamp = 1; // will be overwritten + } + } + } + + function _writeTimepoint() internal returns(uint88 volatilityAverage) { + // single SLOAD + uint16 _lastIndex = timepointIndex; + uint32 _lastTimepointTimestamp = lastTimepointTimestamp; + + bool _isInitialized = isInitialized; + require(_isInitialized, 'Not initialized'); + + uint32 currentTimestamp = _blockTimestamp(); + // TODO + if (_lastTimepointTimestamp == currentTimestamp) return 0; + + (, int24 tick, , ) = _getPoolState(); + (uint16 newLastIndex, uint16 newOldestIndex) = timepoints.write(_lastIndex, currentTimestamp, tick); + + volatilityAverage = timepoints.getAverageVolatility(currentTimestamp, tick, newLastIndex, newOldestIndex); + + } +} diff --git a/src/plugin/contracts/test/MockObservable.sol b/src/plugin/contracts/test/MockObservable.sol index 3521f21e8..75af06c26 100644 --- a/src/plugin/contracts/test/MockObservable.sol +++ b/src/plugin/contracts/test/MockObservable.sol @@ -64,4 +64,8 @@ contract MockVolatilityOracle is IVolatilityOracle { function getSingleTimepoint(uint32 secondsAgo) external view override returns (int56 tickCumulative, uint88 volatilityCumulative) {} function prepayTimepointsStorageSlots(uint16 startIndex, uint16 amount) external override {} + + function initialize() external { + + } } From f574d3c5e88c8ec560fe0fb11d819511407a6f0e Mon Sep 17 00:00:00 2001 From: IliaAzhel Date: Thu, 15 Aug 2024 14:43:24 +0300 Subject: [PATCH 02/21] [Plugin] add volatility getter to twap plugin --- src/plugin/contracts/AlgebraBasePluginV1.sol | 10 ++++++++-- .../contracts/plugins/DynamicFeePlugin.sol | 5 +---- .../plugins/VolatilityOraclePlugin.sol | 18 +++++++++++++----- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/plugin/contracts/AlgebraBasePluginV1.sol b/src/plugin/contracts/AlgebraBasePluginV1.sol index 70fd8a3c1..194075bca 100644 --- a/src/plugin/contracts/AlgebraBasePluginV1.sol +++ b/src/plugin/contracts/AlgebraBasePluginV1.sol @@ -49,8 +49,9 @@ contract AlgebraBasePluginV1 is DynamicFeePlugin, FarmingProxyPlugin, Volatility } function beforeSwap(address, address, bool, int256, uint160, bool, bytes calldata) external override onlyPool returns (bytes4, uint24, uint24) { - uint88 volatilityAverage = _writeTimepoint(); - if(volatilityAverage != 0 ) _updateFee(volatilityAverage); + _writeTimepoint(); + uint88 volatilityAverage = _getAverageVolatility(); + _updateFee(volatilityAverage); return (IAlgebraPlugin.beforeSwap.selector, 0, 0); } @@ -71,4 +72,9 @@ contract AlgebraBasePluginV1 is DynamicFeePlugin, FarmingProxyPlugin, Volatility return IAlgebraPlugin.afterFlash.selector; } + function getCurrentFee() external view override returns(uint16 fee) { + uint88 volatilityAverage = _getAverageVolatility(); + fee =_getCurrentFee(volatilityAverage); + } + } diff --git a/src/plugin/contracts/plugins/DynamicFeePlugin.sol b/src/plugin/contracts/plugins/DynamicFeePlugin.sol index b84b3d434..77b6ea4b9 100644 --- a/src/plugin/contracts/plugins/DynamicFeePlugin.sol +++ b/src/plugin/contracts/plugins/DynamicFeePlugin.sol @@ -48,10 +48,7 @@ abstract contract DynamicFeePlugin is BasePlugin, IDynamicFeeManager { emit FeeConfiguration(_config); } - // TODO - /// @inheritdoc IAlgebraDynamicFeePlugin - function getCurrentFee() external view override returns (uint16 fee) { - uint88 volatilityAverage; + function _getCurrentFee(uint88 volatilityAverage) internal view returns (uint16 fee) { AlgebraFeeConfigurationU144 feeConfig_ = _feeConfig; if (feeConfig_.alpha1() | feeConfig_.alpha2() == 0) return feeConfig_.baseFee(); diff --git a/src/plugin/contracts/plugins/VolatilityOraclePlugin.sol b/src/plugin/contracts/plugins/VolatilityOraclePlugin.sol index e63e39ee6..f67b8cd50 100644 --- a/src/plugin/contracts/plugins/VolatilityOraclePlugin.sol +++ b/src/plugin/contracts/plugins/VolatilityOraclePlugin.sol @@ -85,7 +85,7 @@ abstract contract VolatilityOraclePlugin is BasePlugin, IVolatilityOracle{ } } - function _writeTimepoint() internal returns(uint88 volatilityAverage) { + function _writeTimepoint() internal { // single SLOAD uint16 _lastIndex = timepointIndex; uint32 _lastTimepointTimestamp = lastTimepointTimestamp; @@ -94,13 +94,21 @@ abstract contract VolatilityOraclePlugin is BasePlugin, IVolatilityOracle{ require(_isInitialized, 'Not initialized'); uint32 currentTimestamp = _blockTimestamp(); - // TODO - if (_lastTimepointTimestamp == currentTimestamp) return 0; + if (_lastTimepointTimestamp == currentTimestamp) return; (, int24 tick, , ) = _getPoolState(); - (uint16 newLastIndex, uint16 newOldestIndex) = timepoints.write(_lastIndex, currentTimestamp, tick); + timepoints.write(_lastIndex, currentTimestamp, tick); - volatilityAverage = timepoints.getAverageVolatility(currentTimestamp, tick, newLastIndex, newOldestIndex); + } + + function _getAverageVolatility() internal view returns (uint88 volatilityAverage) { + + uint32 currentTimestamp = _blockTimestamp(); + (, int24 tick, , ) = _getPoolState(); + + uint16 lastTimepointIndex = timepointIndex; + uint16 oldestIndex = timepoints.getOldestIndex(lastTimepointIndex); + volatilityAverage = timepoints.getAverageVolatility(currentTimestamp, tick, lastTimepointIndex, oldestIndex); } } From 008f30e6f90831b4a0a2c4cefd8ab67d780de128 Mon Sep 17 00:00:00 2001 From: IliaAzhel Date: Thu, 15 Aug 2024 14:45:18 +0300 Subject: [PATCH 03/21] [Plugin] fix farming plugin default config --- src/plugin/contracts/plugins/FarmingProxyPlugin.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugin/contracts/plugins/FarmingProxyPlugin.sol b/src/plugin/contracts/plugins/FarmingProxyPlugin.sol index 287a0a0a9..3c4ec065b 100644 --- a/src/plugin/contracts/plugins/FarmingProxyPlugin.sol +++ b/src/plugin/contracts/plugins/FarmingProxyPlugin.sol @@ -16,7 +16,7 @@ import '../base/BasePlugin.sol'; abstract contract FarmingProxyPlugin is BasePlugin, IFarmingPlugin { using Plugins for uint8; - uint8 private constant defaultPluginConfig = uint8(Plugins.AFTER_INIT_FLAG | Plugins.AFTER_SWAP_FLAG); + uint8 private constant defaultPluginConfig = uint8(Plugins.AFTER_SWAP_FLAG); /// @inheritdoc IFarmingPlugin address public override incentive; From 6edd5920c8653dea1c054d0189b4befdfbf0b9f1 Mon Sep 17 00:00:00 2001 From: IliaAzhel Date: Thu, 15 Aug 2024 17:27:07 +0300 Subject: [PATCH 04/21] [Plugin] change default plugin config --- src/plugin/contracts/AlgebraBasePluginV1.sol | 6 +++--- src/plugin/contracts/base/BasePlugin.sol | 17 ++++++++++++++++- .../contracts/plugins/FarmingProxyPlugin.sol | 4 ++-- .../plugins/VolatilityOraclePlugin.sol | 6 ++---- 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/plugin/contracts/AlgebraBasePluginV1.sol b/src/plugin/contracts/AlgebraBasePluginV1.sol index 194075bca..53d71da42 100644 --- a/src/plugin/contracts/AlgebraBasePluginV1.sol +++ b/src/plugin/contracts/AlgebraBasePluginV1.sol @@ -16,7 +16,7 @@ contract AlgebraBasePluginV1 is DynamicFeePlugin, FarmingProxyPlugin, Volatility using Plugins for uint8; /// @inheritdoc IAlgebraPlugin - uint8 public constant override defaultPluginConfig = uint8(Plugins.AFTER_INIT_FLAG | Plugins.BEFORE_SWAP_FLAG | Plugins.DYNAMIC_FEE); + uint8 public constant override defaultPluginConfig = uint8(Plugins.AFTER_INIT_FLAG | Plugins.BEFORE_SWAP_FLAG | Plugins.AFTER_SWAP_FLAG | Plugins.DYNAMIC_FEE); constructor(address _pool, address _factory, address _pluginFactory) BasePlugin(_pool, _factory, _pluginFactory) { @@ -50,7 +50,7 @@ contract AlgebraBasePluginV1 is DynamicFeePlugin, FarmingProxyPlugin, Volatility function beforeSwap(address, address, bool, int256, uint160, bool, bytes calldata) external override onlyPool returns (bytes4, uint24, uint24) { _writeTimepoint(); - uint88 volatilityAverage = _getAverageVolatility(); + uint88 volatilityAverage = _getAverageVolatilityLast(); _updateFee(volatilityAverage); return (IAlgebraPlugin.beforeSwap.selector, 0, 0); } @@ -73,7 +73,7 @@ contract AlgebraBasePluginV1 is DynamicFeePlugin, FarmingProxyPlugin, Volatility } function getCurrentFee() external view override returns(uint16 fee) { - uint88 volatilityAverage = _getAverageVolatility(); + uint88 volatilityAverage = _getAverageVolatilityLast(); fee =_getCurrentFee(volatilityAverage); } diff --git a/src/plugin/contracts/base/BasePlugin.sol b/src/plugin/contracts/base/BasePlugin.sol index 43622a087..93850a00c 100644 --- a/src/plugin/contracts/base/BasePlugin.sol +++ b/src/plugin/contracts/base/BasePlugin.sol @@ -91,7 +91,6 @@ abstract contract BasePlugin is IBasePlugin, Timestamp { return IAlgebraPlugin.afterFlash.selector; } - // TODO function _updatePluginConfigInPool(uint8 newPluginConfig) internal { (, , , uint8 currentPluginConfig) = _getPoolState(); @@ -100,4 +99,20 @@ abstract contract BasePlugin is IBasePlugin, Timestamp { } } + function _disablePluginFlags(uint8 config) internal { + (, , , uint8 currentPluginConfig) = _getPoolState(); + uint8 newPluginConfig = currentPluginConfig & ~config; + if (currentPluginConfig != newPluginConfig) { + IAlgebraPool(pool).setPluginConfig(newPluginConfig); + } + } + + function _enablePluginFlags(uint8 config) internal { + (, , , uint8 currentPluginConfig) = _getPoolState(); + uint8 newPluginConfig = currentPluginConfig | config; + if (currentPluginConfig != newPluginConfig) { + IAlgebraPool(pool).setPluginConfig(newPluginConfig); + } + } + } diff --git a/src/plugin/contracts/plugins/FarmingProxyPlugin.sol b/src/plugin/contracts/plugins/FarmingProxyPlugin.sol index 3c4ec065b..4e54da36f 100644 --- a/src/plugin/contracts/plugins/FarmingProxyPlugin.sol +++ b/src/plugin/contracts/plugins/FarmingProxyPlugin.sol @@ -55,7 +55,7 @@ abstract contract FarmingProxyPlugin is BasePlugin, IFarmingPlugin { } if (isPluginConnected) { - _updatePluginConfigInPool(defaultPluginConfig); + _enablePluginFlags(defaultPluginConfig); } } @@ -75,7 +75,7 @@ abstract contract FarmingProxyPlugin is BasePlugin, IFarmingPlugin { (, int24 tick, , ) = _getPoolState(); IAlgebraVirtualPool(_incentive).crossTo(tick, zeroToOne); } else { - _updatePluginConfigInPool(defaultPluginConfig); // should not be called, reset config + _disablePluginFlags(defaultPluginConfig); // should not be called, reset config } } diff --git a/src/plugin/contracts/plugins/VolatilityOraclePlugin.sol b/src/plugin/contracts/plugins/VolatilityOraclePlugin.sol index f67b8cd50..e7b65827f 100644 --- a/src/plugin/contracts/plugins/VolatilityOraclePlugin.sol +++ b/src/plugin/contracts/plugins/VolatilityOraclePlugin.sol @@ -10,7 +10,6 @@ import '../interfaces/plugins/IVolatilityOracle.sol'; import '../libraries/VolatilityOracle.sol'; import '../base/BasePlugin.sol'; - /// @title Algebra Integral 1.1 default plugin /// @notice This contract stores timepoints and calculates adaptive fee and statistical averages abstract contract VolatilityOraclePlugin is BasePlugin, IVolatilityOracle{ @@ -50,7 +49,7 @@ abstract contract VolatilityOraclePlugin is BasePlugin, IVolatilityOracle{ lastTimepointTimestamp = time; isInitialized = true; - _updatePluginConfigInPool(defaultPluginConfig); + _enablePluginFlags(defaultPluginConfig); } // ###### Volatility and TWAP oracle ###### @@ -98,10 +97,9 @@ abstract contract VolatilityOraclePlugin is BasePlugin, IVolatilityOracle{ (, int24 tick, , ) = _getPoolState(); timepoints.write(_lastIndex, currentTimestamp, tick); - } - function _getAverageVolatility() internal view returns (uint88 volatilityAverage) { + function _getAverageVolatilityLast() internal view returns (uint88 volatilityAverage) { uint32 currentTimestamp = _blockTimestamp(); (, int24 tick, , ) = _getPoolState(); From 542fe2f37d7084a162a8e3b18f1ea5f011a568a9 Mon Sep 17 00:00:00 2001 From: IliaAzhel Date: Thu, 15 Aug 2024 17:40:43 +0300 Subject: [PATCH 05/21] [Plugin] fix volatility oracle plugin --- src/plugin/contracts/plugins/VolatilityOraclePlugin.sol | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/plugin/contracts/plugins/VolatilityOraclePlugin.sol b/src/plugin/contracts/plugins/VolatilityOraclePlugin.sol index e7b65827f..97ed661a4 100644 --- a/src/plugin/contracts/plugins/VolatilityOraclePlugin.sol +++ b/src/plugin/contracts/plugins/VolatilityOraclePlugin.sol @@ -96,7 +96,10 @@ abstract contract VolatilityOraclePlugin is BasePlugin, IVolatilityOracle{ if (_lastTimepointTimestamp == currentTimestamp) return; (, int24 tick, , ) = _getPoolState(); - timepoints.write(_lastIndex, currentTimestamp, tick); + (uint16 newLastIndex, ) = timepoints.write(_lastIndex, currentTimestamp, tick); + + timepointIndex = newLastIndex; + lastTimepointTimestamp = currentTimestamp; } function _getAverageVolatilityLast() internal view returns (uint88 volatilityAverage) { From 3596dc76803ed2c9381f8f096869677d82a55fc3 Mon Sep 17 00:00:00 2001 From: IliaAzhel Date: Thu, 15 Aug 2024 19:05:45 +0300 Subject: [PATCH 06/21] [Plugin] update snapshots --- .../AlgebraBasePluginV1.spec.ts.snap | 2 +- .../AlgebraPool.gas.spec.ts.snap | 100 +++++++++--------- 2 files changed, 51 insertions(+), 51 deletions(-) diff --git a/src/plugin/test/__snapshots__/AlgebraBasePluginV1.spec.ts.snap b/src/plugin/test/__snapshots__/AlgebraBasePluginV1.spec.ts.snap index 3a2ab609a..369d2135e 100644 --- a/src/plugin/test/__snapshots__/AlgebraBasePluginV1.spec.ts.snap +++ b/src/plugin/test/__snapshots__/AlgebraBasePluginV1.spec.ts.snap @@ -120,4 +120,4 @@ Array [ ] `; -exports[`AlgebraBasePluginV1 AlgebraBasePluginV1 external methods #changeFeeConfiguration feeConfig getter gas cost [ @skip-on-coverage ] 1`] = `23779`; +exports[`AlgebraBasePluginV1 AlgebraBasePluginV1 external methods #changeFeeConfiguration feeConfig getter gas cost [ @skip-on-coverage ] 1`] = `23774`; diff --git a/src/plugin/test/__snapshots__/AlgebraPool.gas.spec.ts.snap b/src/plugin/test/__snapshots__/AlgebraPool.gas.spec.ts.snap index 13a477198..ef3c1b4a2 100644 --- a/src/plugin/test/__snapshots__/AlgebraPool.gas.spec.ts.snap +++ b/src/plugin/test/__snapshots__/AlgebraPool.gas.spec.ts.snap @@ -1,24 +1,24 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps dynamic fee large swap crossing several initialized ticks 1`] = `208344`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps dynamic fee large swap crossing several initialized ticks 1`] = `216020`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps dynamic fee small swap with filled volatilityOracle 1`] = `160359`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps dynamic fee small swap with filled volatilityOracle 1`] = `168035`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps dynamic fee small swap with filled volatilityOracle after 4h 1`] = `195797`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps dynamic fee small swap with filled volatilityOracle after 4h 1`] = `203473`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps dynamic fee small swap with filled volatilityOracle after 8h 1`] = `188583`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps dynamic fee small swap with filled volatilityOracle after 8h 1`] = `196259`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps dynamic fee small swap with filled volatilityOracle after 24h 1`] = `156851`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps dynamic fee small swap with filled volatilityOracle after 24h 1`] = `164527`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps static fee large swap crossing several initialized ticks 1`] = `205458`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps static fee large swap crossing several initialized ticks 1`] = `214790`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps static fee small swap with filled volatilityOracle 1`] = `155205`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps static fee small swap with filled volatilityOracle 1`] = `164537`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps static fee small swap with filled volatilityOracle after 4h 1`] = `188670`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps static fee small swap with filled volatilityOracle after 4h 1`] = `198002`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps static fee small swap with filled volatilityOracle after 8h 1`] = `199604`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps static fee small swap with filled volatilityOracle after 8h 1`] = `208936`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps static fee small swap with filled volatilityOracle after 24h 1`] = `154677`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps static fee small swap with filled volatilityOracle after 24h 1`] = `164009`; exports[`AlgebraPool gas tests [ @skip-on-coverage ] Positions #burn above current price burn when only position using ticks 1`] = `117065`; @@ -62,82 +62,82 @@ exports[`AlgebraPool gas tests [ @skip-on-coverage ] Positions #mint below curre exports[`AlgebraPool gas tests [ @skip-on-coverage ] Positions #poke best case 1`] = `63711`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 first swap in block moves tick, no initialized crossings 1`] = `149772`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 first swap in block moves tick, no initialized crossings 1`] = `157448`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 first swap in block with no tick movement 1`] = `149741`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 first swap in block with no tick movement 1`] = `157417`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 first swap in block with no tick movement, static fee 1`] = `150465`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 first swap in block with no tick movement, static fee 1`] = `159202`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 first swap in block, large swap crossing a single initialized tick 1`] = `166332`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 first swap in block, large swap crossing a single initialized tick 1`] = `174008`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 first swap in block, large swap crossing several initialized ticks 1`] = `199882`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 first swap in block, large swap crossing several initialized ticks 1`] = `207558`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 first swap in block, large swap, no initialized crossings 1`] = `149839`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 first swap in block, large swap, no initialized crossings 1`] = `157515`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 large swap crossing several initialized ticks after some time passes 1`] = `199882`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 large swap crossing several initialized ticks after some time passes 1`] = `207558`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 large swap crossing several initialized ticks second time after some time passes 1`] = `219082`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 large swap crossing several initialized ticks second time after some time passes 1`] = `226758`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 second swap in block moves tick, no initialized crossings 1`] = `114239`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 second swap in block moves tick, no initialized crossings 1`] = `127681`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 second swap in block with no tick movement 1`] = `114203`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 second swap in block with no tick movement 1`] = `127645`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 second swap in block, large swap crossing a single initialized tick 1`] = `129984`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 second swap in block, large swap crossing a single initialized tick 1`] = `143426`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 second swap in block, large swap crossing several initialized ticks 1`] = `164355`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 second swap in block, large swap crossing several initialized ticks 1`] = `177797`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 several large swaps with pauses 1`] = `226844`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 several large swaps with pauses 1`] = `234520`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 small swap after several large swaps with pauses 1`] = `157372`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 small swap after several large swaps with pauses 1`] = `165048`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact1For0 first swap in block moves tick, no initialized crossings 1`] = `149833`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact1For0 first swap in block moves tick, no initialized crossings 1`] = `157509`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact1For0 first swap in block with no tick movement 1`] = `149781`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact1For0 first swap in block with no tick movement 1`] = `157457`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact1For0 second swap in block with no tick movement 1`] = `114264`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact1For0 second swap in block with no tick movement 1`] = `127706`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap farming connected first swap in block moves tick, no initialized crossings 1`] = `181166`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap farming connected first swap in block moves tick, no initialized crossings 1`] = `188859`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap farming connected first swap in block with no tick movement 1`] = `181114`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap farming connected first swap in block with no tick movement 1`] = `188807`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap farming connected second swap in block with no tick movement 1`] = `128497`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap farming connected second swap in block with no tick movement 1`] = `141956`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 first swap in block moves tick, no initialized crossings 1`] = `160259`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 first swap in block moves tick, no initialized crossings 1`] = `167935`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 first swap in block with no tick movement 1`] = `160228`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 first swap in block with no tick movement 1`] = `167904`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 first swap in block with no tick movement, static fee 1`] = `150690`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 first swap in block with no tick movement, static fee 1`] = `159427`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 first swap in block, large swap crossing a single initialized tick 1`] = `177056`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 first swap in block, large swap crossing a single initialized tick 1`] = `184732`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 first swap in block, large swap crossing several initialized ticks 1`] = `211317`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 first swap in block, large swap crossing several initialized ticks 1`] = `218993`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 first swap in block, large swap, no initialized crossings 1`] = `160326`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 first swap in block, large swap, no initialized crossings 1`] = `168002`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 large swap crossing several initialized ticks after some time passes 1`] = `211317`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 large swap crossing several initialized ticks after some time passes 1`] = `218993`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 large swap crossing several initialized ticks second time after some time passes 1`] = `230517`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 large swap crossing several initialized ticks second time after some time passes 1`] = `238193`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 second swap in block moves tick, no initialized crossings 1`] = `124726`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 second swap in block moves tick, no initialized crossings 1`] = `138168`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 second swap in block with no tick movement 1`] = `124690`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 second swap in block with no tick movement 1`] = `138132`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 second swap in block, large swap crossing a single initialized tick 1`] = `140708`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 second swap in block, large swap crossing a single initialized tick 1`] = `154150`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 second swap in block, large swap crossing several initialized ticks 1`] = `175790`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 second swap in block, large swap crossing several initialized ticks 1`] = `189232`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 several large swaps with pauses 1`] = `238279`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 several large swaps with pauses 1`] = `245955`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 small swap after several large swaps with pauses 1`] = `157597`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 small swap after several large swaps with pauses 1`] = `165273`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact1For0 first swap in block moves tick, no initialized crossings 1`] = `160331`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact1For0 first swap in block moves tick, no initialized crossings 1`] = `168007`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact1For0 first swap in block with no tick movement 1`] = `160279`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact1For0 first swap in block with no tick movement 1`] = `167955`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact1For0 second swap in block with no tick movement 1`] = `124762`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact1For0 second swap in block with no tick movement 1`] = `138204`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap farming connected first swap in block moves tick, no initialized crossings 1`] = `191664`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap farming connected first swap in block moves tick, no initialized crossings 1`] = `199357`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap farming connected first swap in block with no tick movement 1`] = `191612`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap farming connected first swap in block with no tick movement 1`] = `199305`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap farming connected second swap in block with no tick movement 1`] = `138995`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap farming connected second swap in block with no tick movement 1`] = `152454`; From 2d221db0f764c37c81c8ab243b1ff78e1310387f Mon Sep 17 00:00:00 2001 From: IliaAzhel Date: Fri, 23 Aug 2024 20:02:21 +0300 Subject: [PATCH 07/21] [Plugin] add elasticFee plugin and basePluginV2 --- src/plugin/contracts/AlgebraBasePluginV2.sol | 82 +++++++++++++++++++ .../contracts/plugins/SlidingFeePlugin.sol | 29 ++----- .../plugins/VolatilityOraclePlugin.sol | 5 ++ 3 files changed, 93 insertions(+), 23 deletions(-) create mode 100644 src/plugin/contracts/AlgebraBasePluginV2.sol diff --git a/src/plugin/contracts/AlgebraBasePluginV2.sol b/src/plugin/contracts/AlgebraBasePluginV2.sol new file mode 100644 index 000000000..ae1a9bbdf --- /dev/null +++ b/src/plugin/contracts/AlgebraBasePluginV2.sol @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity =0.8.20; + +import '@cryptoalgebra/integral-core/contracts/libraries/Plugins.sol'; + +import '@cryptoalgebra/integral-core/contracts/interfaces/plugin/IAlgebraPlugin.sol'; + +import './plugins/FarmingProxyPlugin.sol'; +import './plugins/SlidingFeePlugin.sol'; +import './plugins/VolatilityOraclePlugin.sol'; + +/// @title Algebra Integral 1.1 default plugin +/// @notice This contract stores timepoints and calculates adaptive fee and statistical averages +contract AlgebraBasePluginV1 is SlidingFeePlugin, FarmingProxyPlugin, VolatilityOraclePlugin { + using Plugins for uint8; + + /// @inheritdoc IAlgebraPlugin + uint8 public constant override defaultPluginConfig = uint8(Plugins.AFTER_INIT_FLAG | Plugins.BEFORE_SWAP_FLAG | Plugins.AFTER_SWAP_FLAG | Plugins.DYNAMIC_FEE); + + constructor(address _pool, address _factory, address _pluginFactory) BasePlugin(_pool, _factory, _pluginFactory) { + + } + + // ###### HOOKS ###### + + function beforeInitialize(address, uint160) external override onlyPool returns (bytes4) { + _updatePluginConfigInPool(defaultPluginConfig); + return IAlgebraPlugin.beforeInitialize.selector; + } + + function afterInitialize(address, uint160, int24 tick) external override onlyPool returns (bytes4) { + _initialize_TWAP(tick); + + return IAlgebraPlugin.afterInitialize.selector; + } + + /// @dev unused + function beforeModifyPosition(address, address, int24, int24, int128, bytes calldata) external override onlyPool returns (bytes4, uint24) { + _updatePluginConfigInPool(defaultPluginConfig); // should not be called, reset config + return (IAlgebraPlugin.beforeModifyPosition.selector, 0); + } + + /// @dev unused + function afterModifyPosition(address, address, int24, int24, int128, uint256, uint256, bytes calldata) external override onlyPool returns (bytes4) { + _updatePluginConfigInPool(defaultPluginConfig); // should not be called, reset config + return IAlgebraPlugin.afterModifyPosition.selector; + } + + function beforeSwap(address, address, bool zeroToOne, int256, uint160, bool, bytes calldata) external override onlyPool returns (bytes4, uint24, uint24) { + ( , int24 currentTick, uint16 fee, ) = _getPoolState(); + int24 lastTick = _getLastTick(); + uint16 newFee = _getFeeAndUpdateFactors(zeroToOne, currentTick, lastTick, fee); + if (newFee != fee) { + IAlgebraPool(pool).setFee(newFee); + } + + _writeTimepoint(); + return (IAlgebraPlugin.beforeSwap.selector, 0, 0); + } + + function afterSwap(address, address, bool zeroToOne, int256, uint160, int256, int256, bytes calldata) external override onlyPool returns (bytes4) { + _updateVirtualPoolTick(zeroToOne); + return IAlgebraPlugin.afterSwap.selector; + } + + /// @dev unused + function beforeFlash(address, address, uint256, uint256, bytes calldata) external override onlyPool returns (bytes4) { + _updatePluginConfigInPool(defaultPluginConfig); // should not be called, reset config + return IAlgebraPlugin.beforeFlash.selector; + } + + /// @dev unused + function afterFlash(address, address, uint256, uint256, uint256, uint256, bytes calldata) external override onlyPool returns (bytes4) { + _updatePluginConfigInPool(defaultPluginConfig); // should not be called, reset config + return IAlgebraPlugin.afterFlash.selector; + } + + function getCurrentFee() external view returns(uint16 fee) { + ( , , fee, ) = _getPoolState(); + } + +} diff --git a/src/plugin/contracts/plugins/SlidingFeePlugin.sol b/src/plugin/contracts/plugins/SlidingFeePlugin.sol index 5556f41fb..a57134bff 100644 --- a/src/plugin/contracts/plugins/SlidingFeePlugin.sol +++ b/src/plugin/contracts/plugins/SlidingFeePlugin.sol @@ -5,9 +5,9 @@ import {IAlgebraPool} from '@cryptoalgebra/integral-core/contracts/interfaces/IA import {Timestamp} from '@cryptoalgebra/integral-core/contracts/base/common/Timestamp.sol'; import {TickMath} from '@cryptoalgebra/integral-core/contracts/libraries/TickMath.sol'; -import 'hardhat/console.sol'; +import {BasePlugin} from '../base/BasePlugin.sol'; -abstract contract SlidingFeeModule is Timestamp { +abstract contract SlidingFeePlugin is BasePlugin { struct FeeFactors { uint128 zeroToOneFeeFactor; uint128 oneToZeroFeeFactor; @@ -31,25 +31,14 @@ abstract contract SlidingFeeModule is Timestamp { } function _getFeeAndUpdateFactors( - int24 currenTick, + bool zeroToOne, + int24 currentTick, int24 lastTick, - uint16 poolFee, - bool zeroToOne + uint16 poolFee ) internal returns (uint16) { FeeFactors memory currentFeeFactors; - // console.log('current price: ', currentPrice); - // console.log('last price: ', lastPrice); - // console.log('zero to one: ', zeroToOne); - - // ❗❗❗ - // раньше было currentPrice = 0, я так понял проверка на то, инициализирована ли была цена - // теперь currentTick = 0 - валидное значение, возможно стоит передавать доп аргумент - if (lastTick == 0) { - return poolFee; - } - - currentFeeFactors = _calculateFeeFactors(currenTick, lastTick); + currentFeeFactors = _calculateFeeFactors(currentTick, lastTick); s_feeFactors = currentFeeFactors; @@ -64,14 +53,8 @@ abstract contract SlidingFeeModule is Timestamp { int24 currentTick, int24 lastTick ) internal view returns (FeeFactors memory feeFactors) { - console.log('currentTick: '); - console.logInt(int256(currentTick)); - console.log('lastTick: '); - console.logInt(int256(lastTick)); // price change is positive after zeroToOne prevalence int256 priceChangeRatio = int256(uint256(TickMath.getSqrtRatioAtTick(currentTick - lastTick))) - int256(1 << FEE_FACTOR_SHIFT); // (currentPrice - lastPrice) / lastPrice - console.log('priceChangeRatio: '); - console.logInt(priceChangeRatio); int128 feeFactorImpact = int128(priceChangeRatio * int256(s_priceChangeFactor)); feeFactors = s_feeFactors; diff --git a/src/plugin/contracts/plugins/VolatilityOraclePlugin.sol b/src/plugin/contracts/plugins/VolatilityOraclePlugin.sol index 97ed661a4..f5bef6727 100644 --- a/src/plugin/contracts/plugins/VolatilityOraclePlugin.sol +++ b/src/plugin/contracts/plugins/VolatilityOraclePlugin.sol @@ -112,4 +112,9 @@ abstract contract VolatilityOraclePlugin is BasePlugin, IVolatilityOracle{ volatilityAverage = timepoints.getAverageVolatility(currentTimestamp, tick, lastTimepointIndex, oldestIndex); } + + function _getLastTick() internal view returns(int24 lastTick) { + VolatilityOracle.Timepoint memory lastTimepoint = timepoints[timepointIndex]; + return lastTimepoint.tick; + } } From a9171521d0ff7be384ad27f9125c70546a721aeb Mon Sep 17 00:00:00 2001 From: IliaAzhel Date: Tue, 10 Sep 2024 10:58:32 +0300 Subject: [PATCH 08/21] [Plugin] add tests base plugin v2 --- .../test/MockTimeAlgebraBasePluginV2.sol | 70 +++ .../contracts/test/MockTimeDSFactoryV2.sol | 73 +++ src/plugin/test/AlgebraBasePluginV2.spec.ts | 541 ++++++++++++++++++ .../AlgebraBasePluginV2.spec.ts.snap | 123 ++++ src/plugin/test/shared/fixtures.ts | 33 +- 5 files changed, 836 insertions(+), 4 deletions(-) create mode 100644 src/plugin/contracts/test/MockTimeAlgebraBasePluginV2.sol create mode 100644 src/plugin/contracts/test/MockTimeDSFactoryV2.sol create mode 100644 src/plugin/test/AlgebraBasePluginV2.spec.ts create mode 100644 src/plugin/test/__snapshots__/AlgebraBasePluginV2.spec.ts.snap diff --git a/src/plugin/contracts/test/MockTimeAlgebraBasePluginV2.sol b/src/plugin/contracts/test/MockTimeAlgebraBasePluginV2.sol new file mode 100644 index 000000000..62a0c2086 --- /dev/null +++ b/src/plugin/contracts/test/MockTimeAlgebraBasePluginV2.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.8.20; + +import '../AlgebraBasePluginV2.sol'; + +// used for testing time dependent behavior +contract MockTimeAlgebraBasePluginV2 is AlgebraBasePluginV2 { + using VolatilityOracle for VolatilityOracle.Timepoint[UINT16_MODULO]; + + // Monday, October 5, 2020 9:00:00 AM GMT-05:00 + uint256 public time = 1601906400; + + constructor(address _pool, address _factory, address _pluginFactory) AlgebraBasePluginV2(_pool, _factory, _pluginFactory) { + // + } + + function advanceTime(uint256 by) external { + unchecked { + time += by; + } + } + + function _blockTimestamp() internal view override returns (uint32) { + return uint32(time); + } + + struct UpdateParams { + uint32 advanceTimeBy; + int24 tick; + } + + function batchUpdate(UpdateParams[] calldata params) external { + // sload everything + uint16 _index = timepointIndex; + uint32 _time = lastTimepointTimestamp; + int24 _tick; + unchecked { + for (uint256 i; i < params.length; ++i) { + _time += params[i].advanceTimeBy; + _tick = params[i].tick; + (_index, ) = timepoints.write(_index, _time, _tick); + } + } + + // sstore everything + lastTimepointTimestamp = _time; + timepointIndex = _index; + time = _time; + } + + function checkBlockTimestamp() external view returns (bool) { + require(super._blockTimestamp() == uint32(block.timestamp)); + return true; + } + + function getTimepointsWithParams( + uint32 _time, + uint32[] memory secondsAgos, + int24 tick, + uint16 lastIndex + ) external view returns (int56[] memory tickCumulatives, uint88[] memory volatilityCumulatives) { + return timepoints.getTimepoints(_time, secondsAgos, tick, lastIndex); + } + + function getAverageVolatility(uint32 timestamp, int24 tick) public view returns (uint88 volatilityAverage) { + uint16 index = timepointIndex; + uint16 oldestIndex = timepoints.getOldestIndex(index); + return timepoints.getAverageVolatility(timestamp, tick, index, oldestIndex); + } +} diff --git a/src/plugin/contracts/test/MockTimeDSFactoryV2.sol b/src/plugin/contracts/test/MockTimeDSFactoryV2.sol new file mode 100644 index 000000000..fdc830cdd --- /dev/null +++ b/src/plugin/contracts/test/MockTimeDSFactoryV2.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity =0.8.20; + +import './MockTimeAlgebraBasePluginV2.sol'; + +import '../interfaces/IBasePluginV2Factory.sol'; + +import '@cryptoalgebra/integral-core/contracts/interfaces/plugin/IAlgebraPluginFactory.sol'; + +contract MockTimeDSFactoryV2 is IBasePluginV2Factory { + /// @inheritdoc IBasePluginV2Factory + bytes32 public constant override ALGEBRA_BASE_PLUGIN_FACTORY_ADMINISTRATOR = keccak256('ALGEBRA_BASE_PLUGIN_FACTORY_ADMINISTRATOR'); + + address public immutable override algebraFactory; + + /// @inheritdoc IBasePluginV2Factory + mapping(address => address) public override pluginByPool; + + /// @inheritdoc IBasePluginV2Factory + address public override farmingAddress; + + /// @inheritdoc IBasePluginV2Factory + uint16 public override defaultBaseFee = 500; + + constructor(address _algebraFactory) { + algebraFactory = _algebraFactory; + } + + /// @inheritdoc IAlgebraPluginFactory + function beforeCreatePoolHook(address pool, address, address, address, address, bytes calldata) external override returns (address) { + return _createPlugin(pool); + } + + /// @inheritdoc IAlgebraPluginFactory + function afterCreatePoolHook(address, address, address) external view override { + require(msg.sender == algebraFactory); + } + + function createPluginForExistingPool(address token0, address token1) external override returns (address) { + IAlgebraFactory factory = IAlgebraFactory(algebraFactory); + require(factory.hasRoleOrOwner(factory.POOLS_ADMINISTRATOR_ROLE(), msg.sender)); + + address pool = factory.poolByPair(token0, token1); + require(pool != address(0), 'Pool not exist'); + + return _createPlugin(pool); + } + + function setPluginForPool(address pool, address plugin) external { + pluginByPool[pool] = plugin; + } + + function _createPlugin(address pool) internal returns (address) { + MockTimeAlgebraBasePluginV2 plugin = new MockTimeAlgebraBasePluginV2(pool, algebraFactory, address(this)); + plugin.setBaseFee(defaultBaseFee); + pluginByPool[pool] = address(plugin); + return address(plugin); + } + + /// @inheritdoc IBasePluginV2Factory + function setDefaultBaseFee(uint16 newDefaultBaseFee) external override { + require(defaultBaseFee != newDefaultBaseFee); + defaultBaseFee = newDefaultBaseFee; + emit DefaultBaseFee(newDefaultBaseFee); + } + + /// @inheritdoc IBasePluginV2Factory + function setFarmingAddress(address newFarmingAddress) external override { + require(farmingAddress != newFarmingAddress); + farmingAddress = newFarmingAddress; + emit FarmingAddress(newFarmingAddress); + } +} diff --git a/src/plugin/test/AlgebraBasePluginV2.spec.ts b/src/plugin/test/AlgebraBasePluginV2.spec.ts new file mode 100644 index 000000000..2b30f8bbd --- /dev/null +++ b/src/plugin/test/AlgebraBasePluginV2.spec.ts @@ -0,0 +1,541 @@ +import { Wallet, ZeroAddress } from 'ethers'; +import { ethers } from 'hardhat'; +import { loadFixture } from '@nomicfoundation/hardhat-network-helpers'; +import checkTimepointEquals from './shared/checkTimepointEquals'; +import { expect } from './shared/expect'; +import { TEST_POOL_START_TIME, pluginFixtureV2 } from './shared/fixtures'; +import { PLUGIN_FLAGS, encodePriceSqrt, expandTo18Decimals, getMaxTick, getMinTick } from './shared/utilities'; + +import { MockPool, MockTimeAlgebraBasePluginV2, MockTimeDSFactoryV2, MockTimeVirtualPool } from '../typechain'; + +import snapshotGasCost from './shared/snapshotGasCost'; + +describe('AlgebraBasePluginV2', () => { + let wallet: Wallet, other: Wallet; + + let plugin: MockTimeAlgebraBasePluginV2; // modified plugin + let mockPool: MockPool; // mock of AlgebraPool + let mockPluginFactory: MockTimeDSFactoryV2; // modified plugin factory + + let minTick = getMinTick(60); + let maxTick = getMaxTick(60); + + async function initializeAtZeroTick(pool: MockPool) { + await pool.initialize(encodePriceSqrt(1, 1)); + } + + before('prepare signers', async () => { + [wallet, other] = await (ethers as any).getSigners(); + }); + + beforeEach('deploy test AlgebraBasePluginV2', async () => { + ({ plugin, mockPool, mockPluginFactory} = await loadFixture(pluginFixtureV2)); + }); + + describe('#Initialize', async () => { + it('cannot initialize twice', async () => { + await mockPool.setPlugin(plugin); + await initializeAtZeroTick(mockPool); + + await expect(plugin.initialize()).to.be.revertedWith('Already initialized'); + }); + + it('cannot initialize detached plugin', async () => { + await initializeAtZeroTick(mockPool); + await expect(plugin.initialize()).to.be.revertedWith('Plugin not attached'); + }); + + it('cannot initialize if pool not initialized', async () => { + await mockPool.setPlugin(plugin); + await expect(plugin.initialize()).to.be.revertedWith('Pool is not initialized'); + }); + + it('can initialize for existing pool', async () => { + await initializeAtZeroTick(mockPool); + await mockPool.setPlugin(plugin); + await plugin.initialize(); + + const timepoint = await plugin.timepoints(0); + expect(timepoint.initialized).to.be.true; + }); + + it('can not write to uninitialized oracle', async () => { + await initializeAtZeroTick(mockPool); + await mockPool.setPlugin(plugin); + await mockPool.setPluginConfig(1); // BEFORE_SWAP_FLAG + + await expect(mockPool.swapToTick(5)).to.be.revertedWith('Not initialized'); + }); + }); + + // plain tests for hooks functionality + describe('#Hooks', () => { + it('only pool can call hooks', async () => { + const errorMessage = 'Only pool can call this'; + await expect(plugin.beforeInitialize(wallet.address, 100)).to.be.revertedWith(errorMessage); + await expect(plugin.afterInitialize(wallet.address, 100, 100)).to.be.revertedWith(errorMessage); + await expect(plugin.beforeModifyPosition(wallet.address, wallet.address, 100, 100, 100, '0x')).to.be.revertedWith(errorMessage); + await expect(plugin.afterModifyPosition(wallet.address, wallet.address, 100, 100, 100, 100, 100, '0x')).to.be.revertedWith(errorMessage); + await expect(plugin.beforeSwap(wallet.address, wallet.address, true, 100, 100, false, '0x')).to.be.revertedWith(errorMessage); + await expect(plugin.afterSwap(wallet.address, wallet.address, true, 100, 100, 100, 100, '0x')).to.be.revertedWith(errorMessage); + await expect(plugin.beforeFlash(wallet.address, wallet.address, 100, 100, '0x')).to.be.revertedWith(errorMessage); + await expect(plugin.afterFlash(wallet.address, wallet.address, 100, 100, 100, 100, '0x')).to.be.revertedWith(errorMessage); + }); + + describe('not implemented hooks', async () => { + let defaultConfig: bigint; + + beforeEach('connect plugin to pool', async () => { + defaultConfig = await plugin.defaultPluginConfig(); + await mockPool.setPlugin(plugin); + }); + + it('resets config after beforeModifyPosition', async () => { + await mockPool.initialize(encodePriceSqrt(1, 1)); + await mockPool.setPluginConfig(PLUGIN_FLAGS.BEFORE_POSITION_MODIFY_FLAG); + expect((await mockPool.globalState()).pluginConfig).to.be.eq(PLUGIN_FLAGS.BEFORE_POSITION_MODIFY_FLAG); + await mockPool.mint(wallet.address, wallet.address, 0, 60, 100, '0x'); + expect((await mockPool.globalState()).pluginConfig).to.be.eq(defaultConfig); + }); + + it('resets config after afterModifyPosition', async () => { + await mockPool.initialize(encodePriceSqrt(1, 1)); + await mockPool.setPluginConfig(PLUGIN_FLAGS.AFTER_POSITION_MODIFY_FLAG); + expect((await mockPool.globalState()).pluginConfig).to.be.eq(PLUGIN_FLAGS.AFTER_POSITION_MODIFY_FLAG); + await mockPool.mint(wallet.address, wallet.address, 0, 60, 100, '0x'); + expect((await mockPool.globalState()).pluginConfig).to.be.eq(defaultConfig); + }); + + it('resets config after afterSwap', async () => { + await mockPool.initialize(encodePriceSqrt(1, 1)); + await mockPool.setPluginConfig(PLUGIN_FLAGS.AFTER_SWAP_FLAG); + expect((await mockPool.globalState()).pluginConfig).to.be.eq(PLUGIN_FLAGS.AFTER_SWAP_FLAG); + await mockPool.swapToTick(100); + expect((await mockPool.globalState()).pluginConfig).to.be.eq(defaultConfig); + }); + + it('resets config after beforeFlash', async () => { + await mockPool.setPluginConfig(PLUGIN_FLAGS.BEFORE_FLASH_FLAG); + expect((await mockPool.globalState()).pluginConfig).to.be.eq(PLUGIN_FLAGS.BEFORE_FLASH_FLAG); + await mockPool.flash(wallet.address, 100, 100, '0x'); + expect((await mockPool.globalState()).pluginConfig).to.be.eq(defaultConfig); + }); + + it('resets config after afterFlash', async () => { + await mockPool.setPluginConfig(PLUGIN_FLAGS.AFTER_FLASH_FLAG); + expect((await mockPool.globalState()).pluginConfig).to.be.eq(PLUGIN_FLAGS.AFTER_FLASH_FLAG); + await mockPool.flash(wallet.address, 100, 100, '0x'); + expect((await mockPool.globalState()).pluginConfig).to.be.eq(defaultConfig); + }); + }); + }); + + describe('#VolatilityVolatilityOracle', () => { + beforeEach('connect plugin to pool', async () => { + await mockPool.setPlugin(plugin); + }); + + it('initializes timepoints slot', async () => { + await initializeAtZeroTick(mockPool); + checkTimepointEquals(await plugin.timepoints(0), { + initialized: true, + blockTimestamp: BigInt(TEST_POOL_START_TIME), + tickCumulative: 0n, + }); + }); + + describe('#getTimepoints', () => { + beforeEach(async () => await initializeAtZeroTick(mockPool)); + + // zero tick + it('current tick accumulator increases by tick over time', async () => { + let { + tickCumulatives: [tickCumulative], + } = await plugin.getTimepoints([0]); + expect(tickCumulative).to.eq(0); + await plugin.advanceTime(10); + ({ + tickCumulatives: [tickCumulative], + } = await plugin.getTimepoints([0])); + expect(tickCumulative).to.eq(0); + }); + + it('current tick accumulator after single swap', async () => { + // moves to tick -1 + await mockPool.swapToTick(-1); + + await plugin.advanceTime(4); + let { + tickCumulatives: [tickCumulative], + } = await plugin.getTimepoints([0]); + expect(tickCumulative).to.eq(-4); + }); + + it('current tick accumulator after swaps', async () => { + await mockPool.swapToTick(-4463); + expect((await mockPool.globalState()).tick).to.eq(-4463); + await plugin.advanceTime(4); + await mockPool.swapToTick(-1560); + expect((await mockPool.globalState()).tick).to.eq(-1560); + let { + tickCumulatives: [tickCumulative0], + } = await plugin.getTimepoints([0]); + expect(tickCumulative0).to.eq(-17852); + await plugin.advanceTime(60 * 5); + await mockPool.swapToTick(-1561); + let { + tickCumulatives: [tickCumulative1], + } = await plugin.getTimepoints([0]); + expect(tickCumulative1).to.eq(-485852); + }); + }); + + it('writes an timepoint', async () => { + await initializeAtZeroTick(mockPool); + checkTimepointEquals(await plugin.timepoints(0), { + tickCumulative: 0n, + blockTimestamp: BigInt(TEST_POOL_START_TIME), + initialized: true, + }); + await plugin.advanceTime(1); + await mockPool.swapToTick(10); + checkTimepointEquals(await plugin.timepoints(1), { + tickCumulative: 0n, + blockTimestamp: BigInt(TEST_POOL_START_TIME + 1), + initialized: true, + }); + }); + + it('does not write an timepoint', async () => { + await initializeAtZeroTick(mockPool); + checkTimepointEquals(await plugin.timepoints(0), { + tickCumulative: 0n, + blockTimestamp: BigInt(TEST_POOL_START_TIME), + initialized: true, + }); + await plugin.advanceTime(1); + await mockPool.mint(wallet.address, wallet.address, -240, 0, 100, '0x'); + checkTimepointEquals(await plugin.timepoints(0), { + tickCumulative: 0n, + blockTimestamp: BigInt(TEST_POOL_START_TIME), + initialized: true, + }); + }); + + describe('#getSingleTimepoint', () => { + beforeEach(async () => await initializeAtZeroTick(mockPool)); + + // zero tick + it('current tick accumulator increases by tick over time', async () => { + let { tickCumulative } = await plugin.getSingleTimepoint(0); + expect(tickCumulative).to.eq(0); + await plugin.advanceTime(10); + ({ tickCumulative } = await plugin.getSingleTimepoint(0)); + expect(tickCumulative).to.eq(0); + }); + + it('current tick accumulator after single swap', async () => { + // moves to tick -1 + await mockPool.swapToTick(-1); + + await plugin.advanceTime(4); + let { tickCumulative } = await plugin.getSingleTimepoint(0); + expect(tickCumulative).to.eq(-4); + }); + + it('current tick accumulator after swaps', async () => { + await mockPool.swapToTick(-4463); + expect((await mockPool.globalState()).tick).to.eq(-4463); + await plugin.advanceTime(4); + await mockPool.swapToTick(-1560); + expect((await mockPool.globalState()).tick).to.eq(-1560); + let { tickCumulative: tickCumulative0 } = await plugin.getSingleTimepoint(0); + expect(tickCumulative0).to.eq(-17852); + await plugin.advanceTime(60 * 5); + await mockPool.swapToTick(-1561); + let { tickCumulative: tickCumulative1 } = await plugin.getSingleTimepoint(0); + expect(tickCumulative1).to.eq(-485852); + }); + }); + + describe('#prepayTimepointsStorageSlots', () => { + it('can prepay', async () => { + await plugin.prepayTimepointsStorageSlots(0, 50); + }); + + it('can prepay with space', async () => { + await plugin.prepayTimepointsStorageSlots(10, 50); + }); + + it('writes after swap, prepaid after init', async () => { + await initializeAtZeroTick(mockPool); + await plugin.prepayTimepointsStorageSlots(1, 1); + expect((await plugin.timepoints(1)).blockTimestamp).to.be.eq(1); + await mockPool.swapToTick(-4463); + expect((await mockPool.globalState()).tick).to.eq(-4463); + await plugin.advanceTime(4); + await mockPool.swapToTick(-1560); + expect((await plugin.timepoints(1)).blockTimestamp).to.be.not.eq(1); + expect((await mockPool.globalState()).tick).to.eq(-1560); + let { tickCumulative: tickCumulative0 } = await plugin.getSingleTimepoint(0); + expect(tickCumulative0).to.eq(-17852); + }); + + it('writes after swap, prepaid before init', async () => { + await plugin.prepayTimepointsStorageSlots(0, 2); + await initializeAtZeroTick(mockPool); + expect((await plugin.timepoints(1)).blockTimestamp).to.be.eq(1); + await mockPool.swapToTick(-4463); + expect((await mockPool.globalState()).tick).to.eq(-4463); + await plugin.advanceTime(4); + await mockPool.swapToTick(-1560); + expect((await plugin.timepoints(1)).blockTimestamp).to.be.not.eq(1); + expect((await mockPool.globalState()).tick).to.eq(-1560); + let { tickCumulative: tickCumulative0 } = await plugin.getSingleTimepoint(0); + expect(tickCumulative0).to.eq(-17852); + }); + + describe('failure cases', async () => { + it('cannot rewrite initialized slot', async () => { + await initializeAtZeroTick(mockPool); + await expect(plugin.prepayTimepointsStorageSlots(0, 2)).to.be.reverted; + await plugin.advanceTime(4); + await mockPool.swapToTick(-1560); + await expect(plugin.prepayTimepointsStorageSlots(1, 2)).to.be.reverted; + await expect(plugin.prepayTimepointsStorageSlots(2, 2)).to.be.not.reverted; + }); + + it('cannot prepay 0 slots', async () => { + await expect(plugin.prepayTimepointsStorageSlots(0, 0)).to.be.revertedWithoutReason; + }); + + it('cannot overflow index', async () => { + await plugin.prepayTimepointsStorageSlots(0, 10); + expect(plugin.prepayTimepointsStorageSlots(11, 2n ** 16n - 5n)).to.be.revertedWithoutReason; + expect(plugin.prepayTimepointsStorageSlots(11, 2n ** 16n)).to.be.revertedWithoutReason; + }); + }); + }); + }); + + describe('#FarmingPlugin', () => { + describe('virtual pool tests', () => { + let virtualPoolMock: MockTimeVirtualPool; + + beforeEach('deploy virtualPoolMock', async () => { + await mockPluginFactory.setFarmingAddress(wallet); + const virtualPoolMockFactory = await ethers.getContractFactory('MockTimeVirtualPool'); + virtualPoolMock = (await virtualPoolMockFactory.deploy()) as any as MockTimeVirtualPool; + }); + + it('set incentive works', async () => { + await mockPool.setPlugin(plugin); + await plugin.setIncentive(virtualPoolMock); + expect(await plugin.incentive()).to.be.eq(await virtualPoolMock.getAddress()); + }); + + it('can detach incentive', async () => { + await mockPool.setPlugin(plugin); + await plugin.setIncentive(virtualPoolMock); + await plugin.setIncentive(ZeroAddress); + expect(await plugin.incentive()).to.be.eq(ZeroAddress); + }); + + it('can detach incentive even if no more has rights to connect plugins', async () => { + await mockPool.setPlugin(plugin); + await plugin.setIncentive(virtualPoolMock); + await mockPluginFactory.setFarmingAddress(other); + await plugin.setIncentive(ZeroAddress); + expect(await plugin.incentive()).to.be.eq(ZeroAddress); + }); + + it('cannot attach incentive even if no more has rights to connect plugins', async () => { + await mockPool.setPlugin(plugin); + await plugin.setIncentive(virtualPoolMock); + await mockPluginFactory.setFarmingAddress(other); + await expect(plugin.setIncentive(other)).to.be.revertedWith('Not allowed to set incentive'); + }); + + it('new farming can detach old incentive', async () => { + await mockPool.setPlugin(plugin); + await plugin.setIncentive(virtualPoolMock); + await mockPluginFactory.setFarmingAddress(other); + await plugin.connect(other).setIncentive(ZeroAddress); + expect(await plugin.incentive()).to.be.eq(ZeroAddress); + }); + + it('cannot detach incentive if nothing connected', async () => { + await mockPool.setPlugin(plugin); + await expect(plugin.setIncentive(ZeroAddress)).to.be.revertedWith('Already active'); + expect(await plugin.incentive()).to.be.eq(ZeroAddress); + }); + + it('cannot set same incentive twice', async () => { + await mockPool.setPlugin(plugin); + await plugin.setIncentive(virtualPoolMock); + await expect(plugin.setIncentive(virtualPoolMock)).to.be.revertedWith('Already active'); + }); + + it('cannot set incentive if has active', async () => { + await mockPool.setPlugin(plugin); + await plugin.setIncentive(virtualPoolMock); + await expect(plugin.setIncentive(wallet.address)).to.be.revertedWith('Has active incentive'); + }); + + it('can detach incentive if not connected to pool', async () => { + const defaultConfig = await plugin.defaultPluginConfig(); + await mockPool.setPlugin(plugin); + await mockPool.setPluginConfig(BigInt(PLUGIN_FLAGS.AFTER_SWAP_FLAG) | defaultConfig); + await plugin.setIncentive(virtualPoolMock); + expect(await plugin.incentive()).to.be.eq(await virtualPoolMock.getAddress()); + await mockPool.setPlugin(ZeroAddress); + await plugin.setIncentive(ZeroAddress); + expect(await plugin.incentive()).to.be.eq(ZeroAddress); + }); + + it('can set incentive if afterSwap hook is active', async () => { + const defaultConfig = await plugin.defaultPluginConfig(); + await mockPool.setPlugin(plugin); + await mockPool.setPluginConfig(BigInt(PLUGIN_FLAGS.AFTER_SWAP_FLAG) | defaultConfig); + await plugin.setIncentive(virtualPoolMock); + expect(await plugin.incentive()).to.be.eq(await virtualPoolMock.getAddress()); + expect((await mockPool.globalState()).pluginConfig).to.be.eq(BigInt(PLUGIN_FLAGS.AFTER_SWAP_FLAG) | defaultConfig); + }); + + it('set incentive works only for PluginFactory.farmingAddress', async () => { + await mockPluginFactory.setFarmingAddress(ZeroAddress); + await expect(plugin.setIncentive(virtualPoolMock)).to.be.revertedWith('Not allowed to set incentive'); + }); + + it('incentive can not be attached if plugin is not attached', async () => { + await expect(plugin.setIncentive(virtualPoolMock)).to.be.revertedWith('Plugin not attached'); + }); + + it('incentive attached before initialization', async () => { + await mockPool.setPlugin(plugin); + + await plugin.setIncentive(virtualPoolMock); + await mockPool.initialize(encodePriceSqrt(1, 1)); + await mockPool.mint(wallet.address, wallet.address, -120, 120, 1, '0x'); + await mockPool.mint(wallet.address, wallet.address, minTick, maxTick, 1, '0x'); + + await mockPool.swapToTick(-130); + + expect(await plugin.incentive()).to.be.eq(await virtualPoolMock.getAddress()); + expect(await plugin.isIncentiveConnected(virtualPoolMock)).to.be.true; + + const tick = (await mockPool.globalState()).tick; + expect(await virtualPoolMock.currentTick()).to.be.eq(tick); + expect(await virtualPoolMock.timestamp()).to.be.gt(0); + }); + + it('incentive attached after initialization', async () => { + await mockPool.setPlugin(plugin); + await mockPool.initialize(encodePriceSqrt(1, 1)); + await plugin.setIncentive(virtualPoolMock); + + await mockPool.mint(wallet.address, wallet.address, -120, 120, 1, '0x'); + await mockPool.mint(wallet.address, wallet.address, minTick, maxTick, 1, '0x'); + + await mockPool.swapToTick(-130); + + expect(await plugin.incentive()).to.be.eq(await virtualPoolMock.getAddress()); + expect(await plugin.isIncentiveConnected(virtualPoolMock)).to.be.true; + + const tick = (await mockPool.globalState()).tick; + expect(await virtualPoolMock.currentTick()).to.be.eq(tick); + expect(await virtualPoolMock.timestamp()).to.be.gt(0); + }); + + it.skip('swap with finished incentive', async () => { + /*await virtualPoolMock.setIsExist(false); + await mockPool.setIncentive(virtualPoolMock.address); + await mockPool.initialize(encodePriceSqrt(1, 1)); + await mint(wallet.address, -120, 120, 1); + await mint(wallet.address, minTick, maxTick, 1); + expect(await mockPool.activeIncentive()).to.be.eq(virtualPoolMock.address); + + await swapToLowerPrice(encodePriceSqrt(1, 2), wallet.address); + + expect(await mockPool.activeIncentive()).to.be.eq(ethers.constants.AddressZero); + expect(await virtualPoolMock.currentTick()).to.be.eq(0); + expect(await virtualPoolMock.timestamp()).to.be.eq(0); + */ + }); + + it.skip('swap with not started yet incentive', async () => { + /* + await virtualPoolMock.setIsStarted(false); + await mockPool.setIncentive(virtualPoolMock.address); + await mockPool.initialize(encodePriceSqrt(1, 1)); + await mint(wallet.address, -120, 120, 1); + await mint(wallet.address, minTick, maxTick, 1); + expect(await mockPool.activeIncentive()).to.be.eq(virtualPoolMock.address); + + await swapToLowerPrice(encodePriceSqrt(1, 2), wallet.address); + + const tick = (await mockPool.globalState()).tick; + expect(await mockPool.activeIncentive()).to.be.eq(virtualPoolMock.address); + expect(await virtualPoolMock.currentTick()).to.be.eq(tick); + expect(await virtualPoolMock.timestamp()).to.be.eq(0); + */ + }); + }); + + describe('#isIncentiveConnected', () => { + let virtualPoolMock: MockTimeVirtualPool; + + beforeEach('deploy virtualPoolMock', async () => { + await mockPluginFactory.setFarmingAddress(wallet); + const virtualPoolMockFactory = await ethers.getContractFactory('MockTimeVirtualPool'); + virtualPoolMock = (await virtualPoolMockFactory.deploy()) as any as MockTimeVirtualPool; + }); + + it('true with active incentive', async () => { + await mockPool.setPlugin(plugin); + await plugin.setIncentive(virtualPoolMock); + expect(await plugin.isIncentiveConnected(virtualPoolMock)).to.be.true; + }); + + it('false with invalid address', async () => { + await mockPool.setPlugin(plugin); + await plugin.setIncentive(virtualPoolMock); + expect(await plugin.isIncentiveConnected(wallet.address)).to.be.false; + }); + + it('false if plugin detached', async () => { + await mockPool.setPlugin(plugin); + await plugin.setIncentive(virtualPoolMock); + await mockPool.setPlugin(ZeroAddress); + expect(await plugin.isIncentiveConnected(virtualPoolMock)).to.be.false; + }); + + it('false if hook deactivated', async () => { + await mockPool.setPlugin(plugin); + await plugin.setIncentive(virtualPoolMock); + await mockPool.setPluginConfig(0); + expect(await plugin.isIncentiveConnected(virtualPoolMock)).to.be.false; + }); + }); + + describe('#Incentive', () => { + it('incentive is not detached after swap', async () => { + await mockPool.setPlugin(plugin); + await initializeAtZeroTick(mockPool); + await mockPluginFactory.setFarmingAddress(wallet.address); + + const vpStubFactory = await ethers.getContractFactory('MockTimeVirtualPool'); + let vpStub = (await vpStubFactory.deploy()) as any as MockTimeVirtualPool; + + await plugin.setIncentive(vpStub); + const initLiquidityAmount = 10000000000n; + await mockPool.mint(wallet.address, wallet.address, -120, 120, initLiquidityAmount, '0x'); + await mockPool.mint(wallet.address, wallet.address, -1200, 1200, initLiquidityAmount, '0x'); + await mockPool.swapToTick(-200); + + expect(await plugin.incentive()).to.be.eq(await vpStub.getAddress()); + }); + }); + }); + +}); diff --git a/src/plugin/test/__snapshots__/AlgebraBasePluginV2.spec.ts.snap b/src/plugin/test/__snapshots__/AlgebraBasePluginV2.spec.ts.snap new file mode 100644 index 000000000..63e73906c --- /dev/null +++ b/src/plugin/test/__snapshots__/AlgebraBasePluginV2.spec.ts.snap @@ -0,0 +1,123 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AlgebraBasePluginV2 #DynamicFeeManager #adaptiveFee single huge spike after day 1`] = ` +Array [ + "Fee: 123 ", + "Fee: 123 ", + "Fee: 123 ", + "Fee: 123 ", + "Fee: 123 ", + "Fee: 123 ", + "Fee: 123 ", + "Fee: 123 ", + "Fee: 123 ", + "Fee: 123 ", + "Fee: 123 ", + "Fee: 123 ", + "Fee: 123 ", + "Fee: 123 ", + "Fee: 123 ", + "Fee: 123 ", + "Fee: 123 ", + "Fee: 123 ", + "Fee: 123 ", + "Fee: 123 ", + "Fee: 124 ", + "Fee: 124 ", + "Fee: 124 ", + "Fee: 124 ", + "Fee: 100 ", +] +`; + +exports[`AlgebraBasePluginV2 #DynamicFeeManager #adaptiveFee single huge spike after initialization 1`] = ` +Array [ + "Fee: 3714 ", + "Fee: 3000 ", + "Fee: 3000 ", + "Fee: 2965 ", + "Fee: 2602 ", + "Fee: 1733 ", + "Fee: 995 ", + "Fee: 607 ", + "Fee: 411 ", + "Fee: 309 ", + "Fee: 250 ", + "Fee: 215 ", + "Fee: 191 ", + "Fee: 174 ", + "Fee: 163 ", + "Fee: 154 ", + "Fee: 147 ", + "Fee: 142 ", + "Fee: 139 ", + "Fee: 135 ", + "Fee: 132 ", + "Fee: 130 ", + "Fee: 128 ", + "Fee: 126 ", + "Fee: 100 ", +] +`; + +exports[`AlgebraBasePluginV2 #DynamicFeeManager #adaptiveFee single huge step after day 1`] = ` +Array [ + "Fee: 3000 ", + "Fee: 3000 ", + "Fee: 3000 ", + "Fee: 3000 ", + "Fee: 3000 ", + "Fee: 3000 ", + "Fee: 3000 ", + "Fee: 3000 ", + "Fee: 3000 ", + "Fee: 3000 ", + "Fee: 3000 ", + "Fee: 3000 ", + "Fee: 3000 ", + "Fee: 3000 ", + "Fee: 3000 ", + "Fee: 3000 ", + "Fee: 3000 ", + "Fee: 3000 ", + "Fee: 3000 ", + "Fee: 3000 ", + "Fee: 3000 ", + "Fee: 3000 ", + "Fee: 3000 ", + "Fee: 3000 ", + "Fee: 100 ", +] +`; + +exports[`AlgebraBasePluginV2 #DynamicFeeManager #adaptiveFee single huge step after initialization 1`] = ` +Array [ + "Fee: 15000 ", + "Fee: 15000 ", + "Fee: 15000 ", + "Fee: 15000 ", + "Fee: 14311 ", + "Fee: 11402 ", + "Fee: 7651 ", + "Fee: 5386 ", + "Fee: 4312 ", + "Fee: 3795 ", + "Fee: 3525 ", + "Fee: 3371 ", + "Fee: 3277 ", + "Fee: 3216 ", + "Fee: 3174 ", + "Fee: 3144 ", + "Fee: 3123 ", + "Fee: 3106 ", + "Fee: 3093 ", + "Fee: 3083 ", + "Fee: 3075 ", + "Fee: 3068 ", + "Fee: 3062 ", + "Fee: 3058 ", + "Fee: 3037 ", +] +`; + +exports[`AlgebraBasePluginV2 AlgebraBasePluginV1 external methods #changeFeeConfiguration feeConfig getter gas cost [ @skip-on-coverage ] 1`] = `23708`; diff --git a/src/plugin/test/shared/fixtures.ts b/src/plugin/test/shared/fixtures.ts index a9f53da9a..b6614ef1f 100644 --- a/src/plugin/test/shared/fixtures.ts +++ b/src/plugin/test/shared/fixtures.ts @@ -1,5 +1,5 @@ import { ethers } from 'hardhat'; -import { MockFactory, MockPool, MockTimeAlgebraBasePluginV1, MockTimeDSFactory, BasePluginV1Factory } from '../../typechain'; +import { MockFactory, MockPool, MockTimeAlgebraBasePluginV1, MockTimeAlgebraBasePluginV2, MockTimeDSFactoryV2, MockTimeDSFactory, BasePluginV1Factory, BasePluginV2Factory } from '../../typechain'; type Fixture = () => Promise; interface MockFactoryFixture { @@ -15,8 +15,8 @@ async function mockFactoryFixture(): Promise { } interface PluginFixture extends MockFactoryFixture { - plugin: MockTimeAlgebraBasePluginV1; - mockPluginFactory: MockTimeDSFactory; + plugin: MockTimeAlgebraBasePluginV1 | MockTimeAlgebraBasePluginV2; + mockPluginFactory: MockTimeDSFactory | MockTimeDSFactoryV2; mockPool: MockPool; } @@ -49,7 +49,7 @@ export const pluginFixture: Fixture = async function (): Promise< }; interface PluginFactoryFixture extends MockFactoryFixture { - pluginFactory: BasePluginV1Factory; + pluginFactory: BasePluginV1Factory | BasePluginV2Factory; } export const pluginFactoryFixture: Fixture = async function (): Promise { @@ -63,3 +63,28 @@ export const pluginFactoryFixture: Fixture = async functio mockFactory, }; }; + + +export const pluginFixtureV2: Fixture = async function (): Promise { + const { mockFactory } = await mockFactoryFixture(); + //const { token0, token1, token2 } = await tokensFixture() + + const mockPluginFactoryFactory = await ethers.getContractFactory('MockTimeDSFactoryV2'); + const mockPluginFactory = (await mockPluginFactoryFactory.deploy(mockFactory)) as any as MockTimeDSFactoryV2; + + const mockPoolFactory = await ethers.getContractFactory('MockPool'); + const mockPool = (await mockPoolFactory.deploy()) as any as MockPool; + + await mockPluginFactory.beforeCreatePoolHook(mockPool, ZERO_ADDRESS, ZERO_ADDRESS, ZERO_ADDRESS, ZERO_ADDRESS, '0x'); + const pluginAddress = await mockPluginFactory.pluginByPool(mockPool); + + const mockDSOperatorFactory = await ethers.getContractFactory('MockTimeAlgebraBasePluginV2'); + const plugin = mockDSOperatorFactory.attach(pluginAddress) as any as MockTimeAlgebraBasePluginV2; + + return { + plugin, + mockPluginFactory, + mockPool, + mockFactory, + }; +}; \ No newline at end of file From 6b0f859fbe480252452a853ce1e71a34fd05e73f Mon Sep 17 00:00:00 2001 From: IliaAzhel Date: Fri, 27 Sep 2024 11:37:14 +0300 Subject: [PATCH 09/21] [Plugin] sliding fee fixes --- src/plugin/contracts/AlgebraBasePluginV2.sol | 27 +-- src/plugin/contracts/BasePluginV2Factory.sol | 79 +++++++ .../interfaces/IBasePluginV2Factory.sol | 46 ++++ .../interfaces/plugins/ISlidingFeePlugin.sol | 11 + .../contracts/plugins/SlidingFeePlugin.sol | 150 +++++++------ src/plugin/contracts/test/SlidingFeeTest.sol | 29 +++ src/plugin/test/SlidingFee.spec.ts | 211 ++++++++++++++++++ .../__snapshots__/SlidingFee.spec.ts.snap | 7 + 8 files changed, 476 insertions(+), 84 deletions(-) create mode 100644 src/plugin/contracts/BasePluginV2Factory.sol create mode 100644 src/plugin/contracts/interfaces/IBasePluginV2Factory.sol create mode 100644 src/plugin/contracts/interfaces/plugins/ISlidingFeePlugin.sol create mode 100644 src/plugin/contracts/test/SlidingFeeTest.sol create mode 100644 src/plugin/test/SlidingFee.spec.ts create mode 100644 src/plugin/test/__snapshots__/SlidingFee.spec.ts.snap diff --git a/src/plugin/contracts/AlgebraBasePluginV2.sol b/src/plugin/contracts/AlgebraBasePluginV2.sol index ae1a9bbdf..06d5c3556 100644 --- a/src/plugin/contracts/AlgebraBasePluginV2.sol +++ b/src/plugin/contracts/AlgebraBasePluginV2.sol @@ -11,15 +11,14 @@ import './plugins/VolatilityOraclePlugin.sol'; /// @title Algebra Integral 1.1 default plugin /// @notice This contract stores timepoints and calculates adaptive fee and statistical averages -contract AlgebraBasePluginV1 is SlidingFeePlugin, FarmingProxyPlugin, VolatilityOraclePlugin { +contract AlgebraBasePluginV2 is SlidingFeePlugin, FarmingProxyPlugin, VolatilityOraclePlugin { using Plugins for uint8; /// @inheritdoc IAlgebraPlugin - uint8 public constant override defaultPluginConfig = uint8(Plugins.AFTER_INIT_FLAG | Plugins.BEFORE_SWAP_FLAG | Plugins.AFTER_SWAP_FLAG | Plugins.DYNAMIC_FEE); + uint8 public constant override defaultPluginConfig = + uint8(Plugins.AFTER_INIT_FLAG | Plugins.BEFORE_SWAP_FLAG | Plugins.AFTER_SWAP_FLAG | Plugins.DYNAMIC_FEE); - constructor(address _pool, address _factory, address _pluginFactory) BasePlugin(_pool, _factory, _pluginFactory) { - - } + constructor(address _pool, address _factory, address _pluginFactory) BasePlugin(_pool, _factory, _pluginFactory) {} // ###### HOOKS ###### @@ -47,15 +46,12 @@ contract AlgebraBasePluginV1 is SlidingFeePlugin, FarmingProxyPlugin, Volatility } function beforeSwap(address, address, bool zeroToOne, int256, uint160, bool, bytes calldata) external override onlyPool returns (bytes4, uint24, uint24) { - ( , int24 currentTick, uint16 fee, ) = _getPoolState(); + ( , int24 currentTick, , ) = _getPoolState(); int24 lastTick = _getLastTick(); - uint16 newFee = _getFeeAndUpdateFactors(zeroToOne, currentTick, lastTick, fee); - if (newFee != fee) { - IAlgebraPool(pool).setFee(newFee); - } + uint16 newFee = _getFeeAndUpdateFactors(zeroToOne, currentTick, lastTick); _writeTimepoint(); - return (IAlgebraPlugin.beforeSwap.selector, 0, 0); + return (IAlgebraPlugin.beforeSwap.selector, newFee, 0); } function afterSwap(address, address, bool zeroToOne, int256, uint160, int256, int256, bytes calldata) external override onlyPool returns (bytes4) { @@ -75,8 +71,7 @@ contract AlgebraBasePluginV1 is SlidingFeePlugin, FarmingProxyPlugin, Volatility return IAlgebraPlugin.afterFlash.selector; } - function getCurrentFee() external view returns(uint16 fee) { - ( , , fee, ) = _getPoolState(); - } - -} + function getCurrentFee() external view returns (uint16 fee) { + (, , fee, ) = _getPoolState(); + } +} \ No newline at end of file diff --git a/src/plugin/contracts/BasePluginV2Factory.sol b/src/plugin/contracts/BasePluginV2Factory.sol new file mode 100644 index 000000000..adcf07819 --- /dev/null +++ b/src/plugin/contracts/BasePluginV2Factory.sol @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity =0.8.20; + +import './interfaces/IBasePluginV2Factory.sol'; +import './AlgebraBasePluginV2.sol'; +import './interfaces/plugins/ISlidingFeePlugin.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 +contract BasePluginV2Factory is IBasePluginV2Factory { + /// @inheritdoc IBasePluginV2Factory + bytes32 public constant override ALGEBRA_BASE_PLUGIN_FACTORY_ADMINISTRATOR = keccak256('ALGEBRA_BASE_PLUGIN_FACTORY_ADMINISTRATOR'); + + /// @inheritdoc IBasePluginV2Factory + address public immutable override algebraFactory; + + /// @inheritdoc IBasePluginV2Factory + address public override farmingAddress; + + /// @inheritdoc IBasePluginV2Factory + uint16 public override defaultBaseFee = 500; + + /// @inheritdoc IBasePluginV2Factory + mapping(address poolAddress => address pluginAddress) public override pluginByPool; + + modifier onlyAdministrator() { + require(IAlgebraFactory(algebraFactory).hasRoleOrOwner(ALGEBRA_BASE_PLUGIN_FACTORY_ADMINISTRATOR, msg.sender), 'Only administrator'); + _; + } + + constructor(address _algebraFactory) { + algebraFactory = _algebraFactory; + } + + /// @inheritdoc IAlgebraPluginFactory + function beforeCreatePoolHook(address pool, address, address, address, address, bytes calldata) external override returns (address) { + require(msg.sender == algebraFactory); + return _createPlugin(pool); + } + + /// @inheritdoc IAlgebraPluginFactory + function afterCreatePoolHook(address, address, address) external view override { + require(msg.sender == algebraFactory); + } + + /// @inheritdoc IBasePluginV2Factory + function createPluginForExistingPool(address token0, address token1) external override returns (address) { + IAlgebraFactory factory = IAlgebraFactory(algebraFactory); + require(factory.hasRoleOrOwner(factory.POOLS_ADMINISTRATOR_ROLE(), msg.sender)); + + address pool = factory.poolByPair(token0, token1); + require(pool != address(0), 'Pool not exist'); + + return _createPlugin(pool); + } + + function _createPlugin(address pool) internal returns (address) { + require(pluginByPool[pool] == address(0), 'Already created'); + ISlidingFeePlugin plugin = new AlgebraBasePluginV2(pool, algebraFactory, address(this)); + plugin.setBaseFee(defaultBaseFee); + pluginByPool[pool] = address(plugin); + return address(plugin); + } + + /// @inheritdoc IBasePluginV2Factory + function setFarmingAddress(address newFarmingAddress) external override onlyAdministrator { + require(farmingAddress != newFarmingAddress); + farmingAddress = newFarmingAddress; + emit FarmingAddress(newFarmingAddress); + } + + /// @inheritdoc IBasePluginV2Factory + function setDefaultBaseFee(uint16 newDefaultBaseFee) external override onlyAdministrator { + require(defaultBaseFee != newDefaultBaseFee); + defaultBaseFee = newDefaultBaseFee; + emit DefaultBaseFee(newDefaultBaseFee); + } +} \ No newline at end of file diff --git a/src/plugin/contracts/interfaces/IBasePluginV2Factory.sol b/src/plugin/contracts/interfaces/IBasePluginV2Factory.sol new file mode 100644 index 000000000..77597ffc3 --- /dev/null +++ b/src/plugin/contracts/interfaces/IBasePluginV2Factory.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; +pragma abicoder v2; + +import '@cryptoalgebra/integral-core/contracts/interfaces/plugin/IAlgebraPluginFactory.sol'; + +/// @title The interface for the BasePluginV2Factory +/// @notice This contract creates Algebra default plugins for Algebra liquidity pools +interface IBasePluginV2Factory is IAlgebraPluginFactory { + /// @notice Emitted when the farming address is changed + /// @param newFarmingAddress The farming address after the address was changed + event FarmingAddress(address newFarmingAddress); + + event DefaultBaseFee(uint16 newDefaultBaseFee); + + /// @notice The hash of 'ALGEBRA_BASE_PLUGIN_FACTORY_ADMINISTRATOR' used as role + /// @dev allows to change settings of BasePluginV2Factory + function ALGEBRA_BASE_PLUGIN_FACTORY_ADMINISTRATOR() external pure returns (bytes32); + + /// @notice Returns the address of AlgebraFactory + /// @return The AlgebraFactory contract address + function algebraFactory() external view returns (address); + + /// @notice Returns current farming address + /// @return The farming contract address + function farmingAddress() external view returns (address); + + function defaultBaseFee() external view returns (uint16); + + /// @notice Returns address of plugin created for given AlgebraPool + /// @param pool The address of AlgebraPool + /// @return The address of corresponding plugin + function pluginByPool(address pool) external view returns (address); + + /// @notice Create plugin for already existing pool + /// @param token0 The address of first token in pool + /// @param token1 The address of second token in pool + /// @return The address of created plugin + function createPluginForExistingPool(address token0, address token1) external returns (address); + + /// @dev updates farmings manager address on the factory + /// @param newFarmingAddress The new tokenomics contract address + function setFarmingAddress(address newFarmingAddress) external; + + function setDefaultBaseFee(uint16 newDefaultBaseFee) external; +} \ No newline at end of file diff --git a/src/plugin/contracts/interfaces/plugins/ISlidingFeePlugin.sol b/src/plugin/contracts/interfaces/plugins/ISlidingFeePlugin.sol new file mode 100644 index 000000000..723cc0d5b --- /dev/null +++ b/src/plugin/contracts/interfaces/plugins/ISlidingFeePlugin.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +interface ISlidingFeePlugin { + event PriceChangeFactor(uint256 priceChangeFactor); + event BaseFee(uint16 baseFee); + + function setBaseFee(uint16 newBaseFee) external; + + function setPriceChangeFactor(uint16 newPriceChangeFactor) external; +} \ No newline at end of file diff --git a/src/plugin/contracts/plugins/SlidingFeePlugin.sol b/src/plugin/contracts/plugins/SlidingFeePlugin.sol index a57134bff..9585adfd9 100644 --- a/src/plugin/contracts/plugins/SlidingFeePlugin.sol +++ b/src/plugin/contracts/plugins/SlidingFeePlugin.sol @@ -1,92 +1,106 @@ // SPDX-License-Identifier: MIT pragma solidity =0.8.20; +import {IAlgebraFactory} from '@cryptoalgebra/integral-core/contracts/interfaces/IAlgebraFactory.sol'; import {IAlgebraPool} from '@cryptoalgebra/integral-core/contracts/interfaces/IAlgebraPool.sol'; import {Timestamp} from '@cryptoalgebra/integral-core/contracts/base/common/Timestamp.sol'; import {TickMath} from '@cryptoalgebra/integral-core/contracts/libraries/TickMath.sol'; +import {FullMath} from '@cryptoalgebra/integral-core/contracts/libraries/FullMath.sol'; +import {ISlidingFeePlugin} from '../interfaces/plugins/ISlidingFeePlugin.sol'; import {BasePlugin} from '../base/BasePlugin.sol'; -abstract contract SlidingFeePlugin is BasePlugin { - struct FeeFactors { - uint128 zeroToOneFeeFactor; - uint128 oneToZeroFeeFactor; - } +abstract contract SlidingFeePlugin is BasePlugin, ISlidingFeePlugin { + struct FeeFactors { + uint128 zeroToOneFeeFactor; + uint128 oneToZeroFeeFactor; + } + + int16 internal constant FACTOR_DENOMINATOR = 1000; + uint64 internal constant FEE_FACTOR_SHIFT = 96; + + FeeFactors public s_feeFactors; + + uint16 public s_priceChangeFactor = 1000; + uint16 public s_baseFee = 500; - uint64 internal constant FEE_FACTOR_SHIFT = 96; + constructor() { + FeeFactors memory feeFactors = FeeFactors(uint128(1 << FEE_FACTOR_SHIFT), uint128(1 << FEE_FACTOR_SHIFT)); - FeeFactors public s_feeFactors; + s_feeFactors = feeFactors; + } - uint256 public s_priceChangeFactor = 1; + function _getFeeAndUpdateFactors(bool zeroToOne, int24 currenTick, int24 lastTick) internal returns (uint16) { + FeeFactors memory currentFeeFactors; - event PriceChangeFactor(uint256 priceChangeFactor); + uint16 priceChangeFactor = s_priceChangeFactor; + uint16 baseFee = s_baseFee; - constructor() { - FeeFactors memory feeFactors = FeeFactors( - uint128(1 << FEE_FACTOR_SHIFT), - uint128(1 << FEE_FACTOR_SHIFT) - ); + if (currenTick != lastTick) { + currentFeeFactors = _calculateFeeFactors(currenTick, lastTick, priceChangeFactor); - s_feeFactors = feeFactors; + s_feeFactors = currentFeeFactors; + } else { + currentFeeFactors = s_feeFactors; } - function _getFeeAndUpdateFactors( - bool zeroToOne, - int24 currentTick, - int24 lastTick, - uint16 poolFee - ) internal returns (uint16) { - FeeFactors memory currentFeeFactors; + uint256 adjustedFee = zeroToOne + ? (uint256(baseFee) * currentFeeFactors.zeroToOneFeeFactor) >> FEE_FACTOR_SHIFT + : (uint256(baseFee) * currentFeeFactors.oneToZeroFeeFactor) >> FEE_FACTOR_SHIFT; - currentFeeFactors = _calculateFeeFactors(currentTick, lastTick); + if (adjustedFee > type(uint16).max) adjustedFee = type(uint16).max; + return uint16(adjustedFee); + } - s_feeFactors = currentFeeFactors; + function setPriceChangeFactor(uint16 newPriceChangeFactor) external override { + require(IAlgebraFactory(factory).hasRoleOrOwner(ALGEBRA_BASE_PLUGIN_MANAGER, msg.sender)); - uint16 adjustedFee = zeroToOne ? - uint16((poolFee * currentFeeFactors.zeroToOneFeeFactor) >> FEE_FACTOR_SHIFT) : - uint16((poolFee * currentFeeFactors.oneToZeroFeeFactor) >> FEE_FACTOR_SHIFT); + s_priceChangeFactor = newPriceChangeFactor; - return adjustedFee; - } + emit PriceChangeFactor(newPriceChangeFactor); + } + + function setBaseFee(uint16 newBaseFee) external override { + require(msg.sender == pluginFactory || IAlgebraFactory(factory).hasRoleOrOwner(ALGEBRA_BASE_PLUGIN_MANAGER, msg.sender)); - function _calculateFeeFactors( - int24 currentTick, - int24 lastTick - ) internal view returns (FeeFactors memory feeFactors) { - // price change is positive after zeroToOne prevalence - int256 priceChangeRatio = int256(uint256(TickMath.getSqrtRatioAtTick(currentTick - lastTick))) - int256(1 << FEE_FACTOR_SHIFT); // (currentPrice - lastPrice) / lastPrice - int128 feeFactorImpact = int128(priceChangeRatio * int256(s_priceChangeFactor)); - - feeFactors = s_feeFactors; - - // if there were zeroToOne prevalence in the last price change, - // in result price has increased - // we need to increase zeroToOneFeeFactor - // and vice versa - int128 newZeroToOneFeeFactor = int128(feeFactors.zeroToOneFeeFactor) + feeFactorImpact; - - if ((int128(-2) << FEE_FACTOR_SHIFT) < newZeroToOneFeeFactor && newZeroToOneFeeFactor < int128(uint128(2) << FEE_FACTOR_SHIFT)) { - feeFactors = FeeFactors( - uint128(newZeroToOneFeeFactor), - uint128(int128(feeFactors.oneToZeroFeeFactor) - feeFactorImpact) - ); - } else if (newZeroToOneFeeFactor <= 0) { - // In this case price has decreased that much so newZeroToOneFeeFactor is less than 0 - // So we set it to the minimal value == 0 - // It means that there were too much oneToZero prevalence and we want to decrease it - // Basically price change is -100% - feeFactors = FeeFactors( - uint128(2 << FEE_FACTOR_SHIFT), - 0 - ); - } else { - // In this case priceChange is big enough that newZeroToOneFeeFactor is greater than 2 - // So we set it to the maximum value - // It means that there were too much zeroToOne prevalence and we want to decrease it - feeFactors = FeeFactors( - 0, - uint128(2 << FEE_FACTOR_SHIFT) - ); - } + s_baseFee = newBaseFee; + emit BaseFee(newBaseFee); + } + + function _calculateFeeFactors(int24 currentTick, int24 lastTick, uint16 priceChangeFactor) internal view returns (FeeFactors memory feeFactors) { + int256 tickDelta = int256(currentTick) - int256(lastTick); + if (tickDelta > TickMath.MAX_TICK) { + tickDelta = TickMath.MAX_TICK; + } else if (tickDelta < TickMath.MIN_TICK) { + tickDelta = TickMath.MIN_TICK; + } + uint256 sqrtPriceDelta = uint256(TickMath.getSqrtRatioAtTick(int24(tickDelta))); + + // price change is positive after oneToZero prevalence + int256 priceChangeRatio = int256(FullMath.mulDiv(sqrtPriceDelta, sqrtPriceDelta, 2 ** 96)) - int256(1 << FEE_FACTOR_SHIFT); // (currentPrice - lastPrice) / lastPrice + int256 feeFactorImpact = (priceChangeRatio * int256(uint256(priceChangeFactor))) / FACTOR_DENOMINATOR; + + feeFactors = s_feeFactors; + + // if there were zeroToOne prevalence in the last price change, + // in result price has increased + // we need to decrease zeroToOneFeeFactor + // and vice versa + int256 newZeroToOneFeeFactor = int128(feeFactors.zeroToOneFeeFactor) - feeFactorImpact; + + if (0 < newZeroToOneFeeFactor && newZeroToOneFeeFactor < (int128(2) << FEE_FACTOR_SHIFT)) { + feeFactors = FeeFactors(uint128(int128(newZeroToOneFeeFactor)), uint128(int128(feeFactors.oneToZeroFeeFactor) + int128(feeFactorImpact))); + } else if (newZeroToOneFeeFactor <= 0) { + // In this case price has decreased that much so newZeroToOneFeeFactor is less than 0 + // So we set it to the minimal value == 0 + // It means that there were too much oneToZero prevalence and we want to decrease it + // Basically price change is -100% + feeFactors = FeeFactors(0, uint128(2 << FEE_FACTOR_SHIFT)); + } else { + // In this case priceChange is big enough that newZeroToOneFeeFactor is greater than 2 + // So we set it to the maximum value + // It means that there were too much zeroToOne prevalence and we want to decrease it + feeFactors = FeeFactors(uint128(2 << FEE_FACTOR_SHIFT), 0); } + } } \ No newline at end of file diff --git a/src/plugin/contracts/test/SlidingFeeTest.sol b/src/plugin/contracts/test/SlidingFeeTest.sol new file mode 100644 index 000000000..9d47fa8ca --- /dev/null +++ b/src/plugin/contracts/test/SlidingFeeTest.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.8.20; + +import '../plugins/SlidingFeePlugin.sol'; + +contract SlidingFeeTest is SlidingFeePlugin { + uint8 public constant override defaultPluginConfig = 0; + constructor() BasePlugin(msg.sender, msg.sender, msg.sender) {} + + function getFeeForSwap(bool zeroToOne, int24 lastTick, int24 currentTick) external returns (uint16 fee) { + fee = _getFeeAndUpdateFactors(zeroToOne, currentTick, lastTick); + } + + function getGasCostOfGetFeeForSwap(bool zeroToOne, int24 lastTick, int24 currentTick) external returns (uint256) { + unchecked { + uint256 gasBefore = gasleft(); + _getFeeAndUpdateFactors(zeroToOne, currentTick, lastTick); + return gasBefore - gasleft(); + } + } + + function changeBaseFee(uint16 newFee) external { + s_baseFee = newFee; + } + + function changeFactor(uint16 newFactor) external { + s_priceChangeFactor = newFactor; + } +} \ No newline at end of file diff --git a/src/plugin/test/SlidingFee.spec.ts b/src/plugin/test/SlidingFee.spec.ts new file mode 100644 index 000000000..57e5ed6b2 --- /dev/null +++ b/src/plugin/test/SlidingFee.spec.ts @@ -0,0 +1,211 @@ +import { expect } from './shared/expect'; +import { ethers } from 'hardhat'; +import { SlidingFeeTest } from '../typechain'; +import { loadFixture } from '@nomicfoundation/hardhat-network-helpers'; +import snapshotGasCost from './shared/snapshotGasCost'; + +describe('SlidingFee', () => { + let slidingFeePlugin: SlidingFeeTest; + + async function slidingFeeFixture() { + const factory = await ethers.getContractFactory('SlidingFeeTest'); + return (await factory.deploy()) as any as SlidingFeeTest; + } + + beforeEach('deploy SlidingFeeTest', async () => { + slidingFeePlugin = await loadFixture(slidingFeeFixture); + }); + + it('set config', async () => { + await slidingFeePlugin.changeBaseFee(500) + await slidingFeePlugin.changeFactor(1000) + + expect(await slidingFeePlugin.s_baseFee()).to.be.eq(500) + expect(await slidingFeePlugin.s_priceChangeFactor()).to.be.eq(1000) + }); + + describe('#getSlidingFee', () => { + beforeEach('set config', async () => { + await slidingFeePlugin.changeBaseFee(500) + await slidingFeePlugin.changeFactor(1000) + }); + + for (const factor of [500, 1000, 2000]) { + it("Shifts correct with positive price change, factor is " + factor, async function () { + + await slidingFeePlugin.changeFactor(factor) + // swap, price increased x2 (otz) + let lastTick = 10000 + let currentTick = 16932 + + await slidingFeePlugin.getFeeForSwap(false, lastTick, currentTick); + + if (factor == 500) { + expect((await slidingFeePlugin.s_feeFactors()).oneToZeroFeeFactor).to.be.approximately((3n << 96n) / 2n, 1n << 81n); // 1.5 + expect((await slidingFeePlugin.s_feeFactors()).zeroToOneFeeFactor).to.be.approximately(1n << 95n, 1n << 81n); // 0.5 + } + + if (factor == 1000) { + expect((await slidingFeePlugin.s_feeFactors()).oneToZeroFeeFactor).to.be.approximately(2n << 96n, 1n << 81n); // 2 + expect((await slidingFeePlugin.s_feeFactors()).zeroToOneFeeFactor).to.be.approximately(0n << 96n, 1n << 81n); // 0 + } + + if (factor == 2000) { + expect((await slidingFeePlugin.s_feeFactors()).oneToZeroFeeFactor).to.be.eq(2n << 96n); // 2 + expect((await slidingFeePlugin.s_feeFactors()).zeroToOneFeeFactor).to.be.eq(0n << 96n); // 0 + } + }); + + it("Shifts correct with negative price change, factor is " + factor, async function () { + await slidingFeePlugin.changeFactor(factor) + + // swap, price decreased x0.25 (zto) + let lastTick = 16932 + let currentTick = 10000 + + await slidingFeePlugin.getFeeForSwap(false, lastTick, currentTick); + + if (factor == 500) { + expect((await slidingFeePlugin.s_feeFactors()).oneToZeroFeeFactor).to.be.approximately((3n << 96n )/ 4n, 1n << 81n); // 0.75 + expect((await slidingFeePlugin.s_feeFactors()).zeroToOneFeeFactor).to.be.approximately((5n << 96n) / 4n, 1n << 81n); // 1.25 + } + + if (factor == 1000) { + expect((await slidingFeePlugin.s_feeFactors()).oneToZeroFeeFactor).to.be.approximately(1n << 95n, 1n << 81n); // 0 + expect((await slidingFeePlugin.s_feeFactors()).zeroToOneFeeFactor).to.be.approximately((3n << 96n) / 2n, 1n << 81n); // 2 + } + + if (factor == 2000) { + expect((await slidingFeePlugin.s_feeFactors()).oneToZeroFeeFactor).to.be.eq(0n << 96n); // 0 + expect((await slidingFeePlugin.s_feeFactors()).zeroToOneFeeFactor).to.be.eq(2n << 96n); // 2 + } + }); + } + + + + it("Factors should be reset", async function () { + + // swap, price increased x1.5 (otz) + let lastTick = 10000 + let currentTick = 14055 + await slidingFeePlugin.getFeeForSwap(false, lastTick, currentTick); // 1.5, 0.5 + + // swap, price decreased x0.5 (zto) + lastTick = 14055 + currentTick = 7123 + await slidingFeePlugin.getFeeForSwap(true, lastTick, currentTick); // 1, 1 + + expect((await slidingFeePlugin.s_feeFactors()).oneToZeroFeeFactor).to.be.approximately(1n << 96n, 1n << 81n); // 1 + expect((await slidingFeePlugin.s_feeFactors()).zeroToOneFeeFactor).to.be.approximately(1n << 96n, 1n << 81n); // 1 + }); + + it("Huge swap otz", async function () { + + // swap, price changed from min to max + let lastTick = -887272 + let currentTick = 887272 + + await slidingFeePlugin.getFeeForSwap(true, lastTick, currentTick); + + expect((await slidingFeePlugin.s_feeFactors()).oneToZeroFeeFactor).to.be.eq(2n << 96n); // 2 + expect((await slidingFeePlugin.s_feeFactors()).zeroToOneFeeFactor).to.be.eq(0n << 96n); // 0 + }); + + it("Huge swap zto", async function () { + + // swap, price changed from min to max + let lastTick = 887272 + let currentTick = -887272 + + await slidingFeePlugin.getFeeForSwap(true, lastTick, currentTick); + + expect((await slidingFeePlugin.s_feeFactors()).oneToZeroFeeFactor).to.be.eq(0n << 96n); // 0 + expect((await slidingFeePlugin.s_feeFactors()).zeroToOneFeeFactor).to.be.eq(2n << 96n); // 2 + }); + + it("Shift correct after two oneToZero movements", async function () { + await slidingFeePlugin.changeFactor(500) + // swap, price increased x2 (otz) + let lastTick = 10000 + let currentTick = 16932 + await slidingFeePlugin.getFeeForSwap(true, lastTick, currentTick); + + // swap, price increased x1.5 (otz) + lastTick = 16932 + currentTick = 20987 + await slidingFeePlugin.getFeeForSwap(true, lastTick, currentTick); + + + expect((await slidingFeePlugin.s_feeFactors()).oneToZeroFeeFactor).to.be.approximately((7n << 96n) / 4n, 1n << 81n); // 1.75 + expect((await slidingFeePlugin.s_feeFactors()).zeroToOneFeeFactor).to.be.approximately((1n << 96n) / 4n, 1n << 81n); // 0.25 + }); + + it("Shift correct after two zeroToOne movements", async function () { + await slidingFeePlugin.changeFactor(500) + // swap, price decreased x0.5 (zt0) + let lastTick = 20987 + let currentTick = 14055 + await slidingFeePlugin.getFeeForSwap(true, lastTick, currentTick); + + + // swap, price decreased x0.5 (zt0) + lastTick = 14055 + currentTick = 7123 + await slidingFeePlugin.getFeeForSwap(true, lastTick, currentTick); + + expect((await slidingFeePlugin.s_feeFactors()).oneToZeroFeeFactor).to.be.approximately(1n << 95n , 1n << 81n); // 0.5 + expect((await slidingFeePlugin.s_feeFactors()).zeroToOneFeeFactor).to.be.approximately((3n << 96n) / 2n, 1n << 81n); // 1.5 + }); + + it("Shift correct after two oneToZero movements(negative ticks)", async function () { + await slidingFeePlugin.changeFactor(500) + // swap, price increased x2 (otz) + let lastTick = -20987 + let currentTick = -14055 + await slidingFeePlugin.getFeeForSwap(true, lastTick, currentTick); + + + // swap, price increased x1.5(otz) + lastTick = -14055 + currentTick = -10000 + await slidingFeePlugin.getFeeForSwap(true, lastTick, currentTick); + + expect((await slidingFeePlugin.s_feeFactors()).oneToZeroFeeFactor).to.be.approximately((7n << 96n) / 4n, 1n << 81n); // 1.75 + expect((await slidingFeePlugin.s_feeFactors()).zeroToOneFeeFactor).to.be.approximately((1n << 96n) / 4n, 1n << 81n); // 0.25 + + }); + + it("Shift correct after two zeroToOne movements(negative ticks)", async function () { + await slidingFeePlugin.changeFactor(500) + // swap, price decreased x0.5 (zto) + let lastTick = -10000 + let currentTick = -16932 + await slidingFeePlugin.getFeeForSwap(true, lastTick, currentTick); + + // swap, price decreased x0.5 (zto) + lastTick = -16932 + currentTick = -23864 + await slidingFeePlugin.getFeeForSwap(true, lastTick, currentTick); + + expect((await slidingFeePlugin.s_feeFactors()).oneToZeroFeeFactor).to.be.approximately(1n << 95n, 1n << 81n); // 0.5 + expect((await slidingFeePlugin.s_feeFactors()).zeroToOneFeeFactor).to.be.approximately((3n << 96n) / 2n, 1n << 81n); // 1.5 + }); + + }); + + describe('#getFee gas cost [ @skip-on-coverage ]', () => { + it('gas cost of same tick', async () => { + await snapshotGasCost(slidingFeePlugin.getGasCostOfGetFeeForSwap(true, 100, 100)); + }); + + it('gas cost of tick increase', async () => { + await snapshotGasCost(slidingFeePlugin.getGasCostOfGetFeeForSwap(true, 10000, 40000)); + }); + + it('gas cost of tick decrease', async () => { + await snapshotGasCost(slidingFeePlugin.getGasCostOfGetFeeForSwap(false, 40000, 10000)); + }); + }); + +}); \ No newline at end of file diff --git a/src/plugin/test/__snapshots__/SlidingFee.spec.ts.snap b/src/plugin/test/__snapshots__/SlidingFee.spec.ts.snap new file mode 100644 index 000000000..6efd0b687 --- /dev/null +++ b/src/plugin/test/__snapshots__/SlidingFee.spec.ts.snap @@ -0,0 +1,7 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SlidingFee #getFee gas cost [ @skip-on-coverage ] gas cost of same tick 1`] = `26753`; + +exports[`SlidingFee #getFee gas cost [ @skip-on-coverage ] gas cost of tick decrease 1`] = `31852`; + +exports[`SlidingFee #getFee gas cost [ @skip-on-coverage ] gas cost of tick increase 1`] = `31738`; From cefcb293feec15e958d537a720189996baeb2fbc Mon Sep 17 00:00:00 2001 From: IliaAzhel Date: Fri, 27 Sep 2024 12:40:43 +0300 Subject: [PATCH 10/21] [Common] some fixes --- src/farming/contracts/FarmingCenter.sol | 2 +- src/plugin/contracts/base/BasePlugin.sol | 37 +++++++++++++------ .../contracts/interfaces/IBasePlugin.sol | 4 -- .../interfaces/plugins/IFarmingPlugin.sol | 4 ++ .../contracts/plugins/FarmingProxyPlugin.sol | 4 +- 5 files changed, 34 insertions(+), 17 deletions(-) diff --git a/src/farming/contracts/FarmingCenter.sol b/src/farming/contracts/FarmingCenter.sol index b704a964e..e5c5d9ea3 100644 --- a/src/farming/contracts/FarmingCenter.sol +++ b/src/farming/contracts/FarmingCenter.sol @@ -139,7 +139,7 @@ contract FarmingCenter is IFarmingCenter, IPositionFollower, Multicall { function _checkParamsForVirtualPoolToggle(address virtualPool, IFarmingPlugin plugin) internal view returns (IAlgebraPool pool) { require(msg.sender == address(eternalFarming), 'Only farming can call this'); require(virtualPool != address(0), 'Zero address as virtual pool'); - pool = IAlgebraPool(plugin.pool()); + pool = IAlgebraPool(plugin.getPool()); require( address(pool) == PoolAddress.computeAddress(algebraPoolDeployer, PoolAddress.PoolKey(address(0), pool.token0(), pool.token1())), 'Invalid pool' diff --git a/src/plugin/contracts/base/BasePlugin.sol b/src/plugin/contracts/base/BasePlugin.sol index 93850a00c..3713cf7b1 100644 --- a/src/plugin/contracts/base/BasePlugin.sol +++ b/src/plugin/contracts/base/BasePlugin.sol @@ -21,7 +21,7 @@ abstract contract BasePlugin is IBasePlugin, Timestamp { uint8 private constant defaultPluginConfig = 0; - address public immutable override pool; + address public immutable pool; address internal immutable factory; address internal immutable pluginFactory; @@ -59,40 +59,56 @@ abstract contract BasePlugin is IBasePlugin, Timestamp { // ###### HOOKS ###### - function beforeInitialize(address, uint160) external override virtual onlyPool returns (bytes4) { + function beforeInitialize(address, uint160) external virtual override onlyPool returns (bytes4) { return IAlgebraPlugin.beforeInitialize.selector; } - function afterInitialize(address, uint160, int24) external override virtual onlyPool returns (bytes4) { + function afterInitialize(address, uint160, int24) external virtual override onlyPool returns (bytes4) { return IAlgebraPlugin.afterInitialize.selector; } - function beforeModifyPosition(address, address, int24, int24, int128, bytes calldata) external override virtual onlyPool returns (bytes4, uint24) { + function beforeModifyPosition(address, address, int24, int24, int128, bytes calldata) external virtual override onlyPool returns (bytes4, uint24) { return (IAlgebraPlugin.beforeModifyPosition.selector, 0); } - function afterModifyPosition(address, address, int24, int24, int128, uint256, uint256, bytes calldata) external override virtual onlyPool returns (bytes4) { + function afterModifyPosition( + address, + address, + int24, + int24, + int128, + uint256, + uint256, + bytes calldata + ) external virtual override onlyPool returns (bytes4) { return IAlgebraPlugin.afterModifyPosition.selector; } - function beforeSwap(address, address, bool, int256, uint160, bool, bytes calldata) external override virtual onlyPool returns (bytes4, uint24, uint24) { + function beforeSwap( + address, + address, + bool, + int256, + uint160, + bool, + bytes calldata + ) external virtual override onlyPool returns (bytes4, uint24, uint24) { return (IAlgebraPlugin.beforeSwap.selector, 0, 0); } - function afterSwap(address, address, bool, int256, uint160, int256, int256, bytes calldata) external override virtual onlyPool returns (bytes4) { + function afterSwap(address, address, bool, int256, uint160, int256, int256, bytes calldata) external virtual override onlyPool returns (bytes4) { return IAlgebraPlugin.afterSwap.selector; } - function beforeFlash(address, address, uint256, uint256, bytes calldata) external override virtual onlyPool returns (bytes4) { + function beforeFlash(address, address, uint256, uint256, bytes calldata) external virtual override onlyPool returns (bytes4) { return IAlgebraPlugin.beforeFlash.selector; } - function afterFlash(address, address, uint256, uint256, uint256, uint256, bytes calldata) external override virtual onlyPool returns (bytes4) { + function afterFlash(address, address, uint256, uint256, uint256, uint256, bytes calldata) external virtual override onlyPool returns (bytes4) { return IAlgebraPlugin.afterFlash.selector; } function _updatePluginConfigInPool(uint8 newPluginConfig) internal { - (, , , uint8 currentPluginConfig) = _getPoolState(); if (currentPluginConfig != newPluginConfig) { IAlgebraPool(pool).setPluginConfig(newPluginConfig); @@ -114,5 +130,4 @@ abstract contract BasePlugin is IBasePlugin, Timestamp { IAlgebraPool(pool).setPluginConfig(newPluginConfig); } } - } diff --git a/src/plugin/contracts/interfaces/IBasePlugin.sol b/src/plugin/contracts/interfaces/IBasePlugin.sol index 4730809d0..c31d3fecd 100644 --- a/src/plugin/contracts/interfaces/IBasePlugin.sol +++ b/src/plugin/contracts/interfaces/IBasePlugin.sol @@ -11,8 +11,4 @@ interface IBasePlugin is IAlgebraPlugin { /// @param amount Amount of tokens /// @param recipient Recipient address function collectPluginFee(address token, uint256 amount, address recipient) external; - - /// @notice Returns the address of the pool the plugin is created for - /// @return address of the pool - function pool() external view returns (address); } diff --git a/src/plugin/contracts/interfaces/plugins/IFarmingPlugin.sol b/src/plugin/contracts/interfaces/plugins/IFarmingPlugin.sol index a5e43882f..b914a99c5 100644 --- a/src/plugin/contracts/interfaces/plugins/IFarmingPlugin.sol +++ b/src/plugin/contracts/interfaces/plugins/IFarmingPlugin.sol @@ -25,4 +25,8 @@ interface IFarmingPlugin { /// @dev if there is no active incentive at the moment, incentiveAddress would be equal to address(0) /// @return The address associated with the current active incentive function incentive() external view returns (address); + + /// @notice Returns the address of the pool the plugin is created for + /// @return address of the pool + function getPool() external view returns (address); } diff --git a/src/plugin/contracts/plugins/FarmingProxyPlugin.sol b/src/plugin/contracts/plugins/FarmingProxyPlugin.sol index 4e54da36f..39bfb6d39 100644 --- a/src/plugin/contracts/plugins/FarmingProxyPlugin.sol +++ b/src/plugin/contracts/plugins/FarmingProxyPlugin.sol @@ -77,7 +77,9 @@ abstract contract FarmingProxyPlugin is BasePlugin, IFarmingPlugin { } else { _disablePluginFlags(defaultPluginConfig); // should not be called, reset config } - } + function getPool() external view override returns (address) { + return pool; + } } From 7c1024af68e962c2e1b0249c45f5a24dae3e105a Mon Sep 17 00:00:00 2001 From: IliaAzhel Date: Fri, 27 Sep 2024 12:42:17 +0300 Subject: [PATCH 11/21] [Common] change config and deployment script --- hardhat.base.config.ts | 5 +++++ src/farming/scripts/deploy.js | 4 ++-- src/periphery/scripts/deploy.js | 2 +- src/plugin/scripts/deploy.js | 6 +++--- src/plugin/scripts/verify.js | 4 ++-- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/hardhat.base.config.ts b/hardhat.base.config.ts index 6e0ec93fa..babb1010b 100644 --- a/hardhat.base.config.ts +++ b/hardhat.base.config.ts @@ -72,6 +72,11 @@ export default { chainId: 34443, accounts: [`0x${MNEMONIC || '1000000000000000000000000000000000000000000000000000000000000000'}`], }, + holesky: { + url: `https://ethereum-holesky-rpc.publicnode.com`, + chainId: 17000, + accounts: [`0x${MNEMONIC || '1000000000000000000000000000000000000000000000000000000000000000'}`], + }, blastTestnet: { url: `https://blast-sepolia.blockpi.network/v1/rpc/public`, chainId: 168587773, diff --git a/src/farming/scripts/deploy.js b/src/farming/scripts/deploy.js index 3c897b6c8..e0b2623b6 100644 --- a/src/farming/scripts/deploy.js +++ b/src/farming/scripts/deploy.js @@ -1,7 +1,7 @@ const hre = require('hardhat') const fs = require('fs') const path = require('path') -const BasePluginV1FactoryComplied = require('@cryptoalgebra/integral-base-plugin/artifacts/contracts/BasePluginV1Factory.sol/BasePluginV1Factory.json'); +const BasePluginV2FactoryComplied = require('@cryptoalgebra/integral-base-plugin/artifacts/contracts/BasePluginV2Factory.sol/BasePluginV2Factory.json'); async function main() { const deployDataPath = path.resolve(__dirname, '../../../deploys.json') @@ -26,7 +26,7 @@ async function main() { await (await AlgebraEternalFarming.setFarmingCenterAddress(FarmingCenter.target)).wait() console.log('Updated farming center address in eternal(incentive) farming') - const pluginFactory = await hre.ethers.getContractAt(BasePluginV1FactoryComplied.abi, deploysData.BasePluginV1Factory) + const pluginFactory = await hre.ethers.getContractAt(BasePluginV2FactoryComplied.abi, deploysData.BasePluginV2Factory) await (await pluginFactory.setFarmingAddress(FarmingCenter.target)).wait() console.log('Updated farming center address in plugin factory') diff --git a/src/periphery/scripts/deploy.js b/src/periphery/scripts/deploy.js index 8560c63a4..02e3041fe 100644 --- a/src/periphery/scripts/deploy.js +++ b/src/periphery/scripts/deploy.js @@ -9,7 +9,7 @@ async function main() { let deploysData = JSON.parse(fs.readFileSync(deployDataPath, 'utf8')); // WNativeTokenAddress - const WNativeTokenAddress = '0x6E2542aFC68a1697FeB2810437DF9409D3b93493'; + const WNativeTokenAddress = '0x94373a4919b3240d86ea41593d5eba789fef3848'; const signers = await hre.ethers.getSigners(); const ProxyAdmin = signers[0].address; diff --git a/src/plugin/scripts/deploy.js b/src/plugin/scripts/deploy.js index fa1ac5a91..b64ac9840 100644 --- a/src/plugin/scripts/deploy.js +++ b/src/plugin/scripts/deploy.js @@ -7,8 +7,8 @@ async function main() { const deployDataPath = path.resolve(__dirname, '../../../deploys.json') const deploysData = JSON.parse(fs.readFileSync(deployDataPath, 'utf8')) - const BasePluginV1Factory = await hre.ethers.getContractFactory("BasePluginV1Factory"); - const dsFactory = await BasePluginV1Factory.deploy(deploysData.factory); + const BasePluginV2Factory = await hre.ethers.getContractFactory("BasePluginV2Factory"); + const dsFactory = await BasePluginV2Factory.deploy(deploysData.factory); await dsFactory.waitForDeployment() @@ -19,7 +19,7 @@ async function main() { await factory.setDefaultPluginFactory(dsFactory.target) console.log('Updated plugin factory address in factory') - deploysData.BasePluginV1Factory = dsFactory.target; + deploysData.BasePluginV2Factory = dsFactory.target; fs.writeFileSync(deployDataPath, JSON.stringify(deploysData), 'utf-8'); } diff --git a/src/plugin/scripts/verify.js b/src/plugin/scripts/verify.js index 3af76260e..c9e38a23c 100644 --- a/src/plugin/scripts/verify.js +++ b/src/plugin/scripts/verify.js @@ -7,10 +7,10 @@ async function main() { const deployDataPath = path.resolve(__dirname, '../../../deploys.json'); let deploysData = JSON.parse(fs.readFileSync(deployDataPath, 'utf8')); - const BasePluginV1Factory = deploysData.BasePluginV1Factory; + const BasePluginV2Factory = deploysData.BasePluginV2Factory; await hre.run("verify:verify", { - address: BasePluginV1Factory, + address: BasePluginV2Factory, constructorArguments: [ deploysData.factory ], From 5fa6215092293a2183e4ae366ba1602b997bcc00 Mon Sep 17 00:00:00 2001 From: IliaAzhel Date: Mon, 7 Oct 2024 12:25:23 +0300 Subject: [PATCH 12/21] [Core] tests for plugin caller --- src/core/contracts/test/MockPoolPlugin.sol | 26 ++++++++++++++++++++++ src/core/test/AlgebraPool.spec.ts | 20 +++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/src/core/contracts/test/MockPoolPlugin.sol b/src/core/contracts/test/MockPoolPlugin.sol index b3675f9aa..cf3b1052d 100644 --- a/src/core/contracts/test/MockPoolPlugin.sol +++ b/src/core/contracts/test/MockPoolPlugin.sol @@ -6,6 +6,7 @@ import '../interfaces/plugin/IAlgebraPlugin.sol'; import '../interfaces/plugin/IAlgebraDynamicFeePlugin.sol'; import '../interfaces/IAlgebraPool.sol'; import '../libraries/Plugins.sol'; +import './TestERC20.sol'; contract MockPoolPlugin is IAlgebraPlugin, IAlgebraDynamicFeePlugin { address public pool; @@ -197,4 +198,29 @@ contract MockPoolPlugin is IAlgebraPlugin, IAlgebraDynamicFeePlugin { if (!Plugins.hasFlag(selectorsDisableConfig, Plugins.AFTER_FLASH_FLAG)) return IAlgebraPlugin.afterFlash.selector; return IAlgebraPlugin.defaultPluginConfig.selector; } + + function swap() external { + IAlgebraPool(pool).swap(address(this), true, 10000, 4295128740, ''); + } + + function algebraSwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata ) external { + require(amount0Delta > 0 || amount1Delta > 0, 'Zero liquidity swap'); // swaps entirely within 0-liquidity regions are not supported + + (address token, uint256 amountToPay) = amount0Delta > 0 + ? (IAlgebraPool(pool).token0(), uint256(amount0Delta)) + : (IAlgebraPool(pool).token1(), uint256(amount1Delta)); + + TestERC20(token).transfer(pool, amountToPay); + } + + + function mint() external { + IAlgebraPool(pool).mint(address(this), address(this), -60, 60, 1000, ''); + } + + function algebraMintCallback(uint256 amount0Owed, uint256 amount1Owed, bytes calldata ) external { + + if (amount0Owed > 0) TestERC20(IAlgebraPool(pool).token0()).transfer(pool, amount0Owed); + if (amount1Owed > 0) TestERC20(IAlgebraPool(pool).token1()).transfer(pool, amount1Owed); + } } diff --git a/src/core/test/AlgebraPool.spec.ts b/src/core/test/AlgebraPool.spec.ts index f5a1b2cd5..5aa088a54 100644 --- a/src/core/test/AlgebraPool.spec.ts +++ b/src/core/test/AlgebraPool.spec.ts @@ -37,6 +37,7 @@ import { PriceMovementMathTest, IERC20Minimal, } from '../typechain'; +import { plugin } from '../typechain/contracts/interfaces'; type ThenArg = T extends PromiseLike ? U : T; @@ -2898,6 +2899,25 @@ describe('AlgebraPool', () => { await pool.setPluginConfig(223); await expect(flash(100, 200, other.address)).not.to.be.emit(poolPlugin, 'AfterFlash'); }); + + it('before/after swap hook is not called if caller is a plugin,', async () => { + await pool.initialize(encodePriceSqrt(1, 1)); + await mint(wallet.address, minTick, maxTick, expandTo18Decimals(1)); + await token0.transfer(poolPlugin, expandTo18Decimals(1)) + await token1.transfer(poolPlugin, expandTo18Decimals(1)) + await expect(poolPlugin.swap()).not.to.be.emit(poolPlugin, 'BeforeSwap'); + await expect(poolPlugin.swap()).not.to.be.emit(poolPlugin, 'AfterSwap'); + }); + + it('before/after modify hook is not called if caller is a plugin,', async () => { + await pool.initialize(encodePriceSqrt(1, 1)); + await mint(wallet.address, minTick, maxTick, expandTo18Decimals(1)); + await token0.transfer(poolPlugin, expandTo18Decimals(1)) + await token1.transfer(poolPlugin, expandTo18Decimals(1)) + await expect(poolPlugin.mint()).not.to.be.emit(poolPlugin, 'BeforeModifyPosition'); + await expect(poolPlugin.mint()).not.to.be.emit(poolPlugin, 'AfterModifyPosition'); + }); + }); describe('#setPlugin', () => { From 4d943d4c00e8c40367b806e92092bfa234d52e04 Mon Sep 17 00:00:00 2001 From: IliaAzhel Date: Mon, 7 Oct 2024 12:27:00 +0300 Subject: [PATCH 13/21] [Core] custom data on pool creation test --- src/core/contracts/test/MockDefaultPluginFactory.sol | 5 ++++- src/core/test/AlgebraFactory.spec.ts | 7 +++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/core/contracts/test/MockDefaultPluginFactory.sol b/src/core/contracts/test/MockDefaultPluginFactory.sol index 25dc1407a..7d5590103 100644 --- a/src/core/contracts/test/MockDefaultPluginFactory.sol +++ b/src/core/contracts/test/MockDefaultPluginFactory.sol @@ -9,10 +9,13 @@ import './MockPoolPlugin.sol'; contract MockDefaultPluginFactory is IAlgebraPluginFactory { mapping(address => address) public pluginsForPools; + event DataOnPoolCreation(bytes data); + function afterCreatePoolHook(address plugin, address pool, address deployer) external override {} - function beforeCreatePoolHook(address pool, address, address, address, address, bytes calldata) external override returns (address plugin) { + function beforeCreatePoolHook(address pool, address, address, address, address, bytes calldata data) external override returns (address plugin) { plugin = address(new MockPoolPlugin(pool)); pluginsForPools[pool] = plugin; + emit DataOnPoolCreation(data); } } diff --git a/src/core/test/AlgebraFactory.spec.ts b/src/core/test/AlgebraFactory.spec.ts index 7843aafcf..1d59fe0ac 100644 --- a/src/core/test/AlgebraFactory.spec.ts +++ b/src/core/test/AlgebraFactory.spec.ts @@ -170,6 +170,13 @@ describe('AlgebraFactory', () => { expect(await pool.plugin()).to.be.eq(pluginAddress); }); + it('data passed to defaultPluginFactory', async () => { + await factory.setDefaultPluginFactory(defaultPluginFactory); + const create = factory.createPool(TEST_ADDRESSES[0], TEST_ADDRESSES[1], '0x0200'); + + await expect(create).to.emit(defaultPluginFactory, 'DataOnPoolCreation').withArgs('0x0200'); + }); + it('sets vault in pool', async () => { await createAndCheckPool([TEST_ADDRESSES[0], TEST_ADDRESSES[1]]); From bf756cdf25aec83fbc7d5cd8dfec2374f2825d06 Mon Sep 17 00:00:00 2001 From: IliaAzhel Date: Mon, 7 Oct 2024 13:10:38 +0300 Subject: [PATCH 14/21] [Core] plugin and override fee in Swap/Burn events test --- src/core/test/AlgebraPool.spec.ts | 36 +++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/core/test/AlgebraPool.spec.ts b/src/core/test/AlgebraPool.spec.ts index 5aa088a54..790901256 100644 --- a/src/core/test/AlgebraPool.spec.ts +++ b/src/core/test/AlgebraPool.spec.ts @@ -2506,6 +2506,18 @@ describe('AlgebraPool', () => { expect(pluginBalance1After - pluginBalance1Before).to.be.eq(6n * 10n**15n-1n); }) + it('works correct on burn single-sided position', async () => { + await poolPlugin.setPluginFees(0, 6000); + await mint(wallet.address, -120, -60, expandTo18Decimals(1)); + await mint(wallet.address, 60, 120, expandTo18Decimals(1)); + await pool.burn(minTick, maxTick, expandTo18Decimals(1), '0x') + await pool.burn(-120, -60, expandTo18Decimals(1), '0x') + await pool.burn(60, 120, expandTo18Decimals(1), '0x') + let res = await pool.getPluginFeePending() + expect(res[0]).to.be.eq(17918296827593n); + expect(res[1]).to.be.eq(17918296827593n); + }) + it('fees transfered to plugin', async () => { await poolPlugin.setPluginFees(5000, 4000); const pluginBalance0Before = await token0.balanceOf(poolPlugin); @@ -2529,6 +2541,30 @@ describe('AlgebraPool', () => { expect(pluginFees[0]).to.be.eq(4n * 10n**15n); }) + it('emits an event with plugin fee and override fee on swap', async () => { + await poolPlugin.setPluginFees(4000, 6000); + await expect(swapExact0For1(expandTo18Decimals(1), wallet.address)).to.be.emit(pool, 'Swap').withArgs( + await swapTarget.getAddress(), + wallet.address, + 10n**18n, + -497487437185929648n, + 39813146992092631956554748913n, + 1000000000000000000n, + -13764, + 4000, + 6000 + ) + }) + + it('emits an event with plugin fee and override fee on burn', async () => { + await poolPlugin.setPluginFees(4000, 6000); + await mint(wallet.address, 60, 120, expandTo18Decimals(1)); + await expect(pool.burn(60, 120, expandTo18Decimals(1), '0x')) + .to.emit(pool, 'Burn') + .withArgs(wallet.address, 60, 120, expandTo18Decimals(1), '2968464507771288', 0, 6000) + + }) + }) describe('PermissionedActions', async () => { From 6ab190d652cd35f33058e2ab05cc14e227070fcd Mon Sep 17 00:00:00 2001 From: IliaAzhel Date: Mon, 7 Oct 2024 17:26:20 +0300 Subject: [PATCH 15/21] [Core] update snapshots & add plugin fee transfer test --- src/core/test/AlgebraPool.spec.ts | 47 ++++++++++++++++++- .../__snapshots__/AlgebraFactory.spec.ts.snap | 10 ++-- .../AlgebraPool.gas.spec.ts.snap | 38 +++++++-------- 3 files changed, 70 insertions(+), 25 deletions(-) diff --git a/src/core/test/AlgebraPool.spec.ts b/src/core/test/AlgebraPool.spec.ts index 790901256..0a7b4dc7b 100644 --- a/src/core/test/AlgebraPool.spec.ts +++ b/src/core/test/AlgebraPool.spec.ts @@ -1186,7 +1186,7 @@ describe('AlgebraPool', () => { await swapExact0For1(expandTo18Decimals(2), other.address); await expect(pool.burn(-120, 0, expandTo18Decimals(1), '0x')) .to.emit(pool, 'Burn') - .withArgs(wallet.address, -120, 0, expandTo18Decimals(1), '6017734268818165', 0) + .withArgs(wallet.address, -120, 0, expandTo18Decimals(1),'6017734268818165', 0, 0) .to.not.emit(token0, 'Transfer') .to.not.emit(token1, 'Transfer'); await expect(pool.collect(wallet.address, -120, 0, MaxUint128, MaxUint128)) @@ -2529,6 +2529,51 @@ describe('AlgebraPool', () => { expect(pluginBalance1After - pluginBalance1Before).to.be.eq(0); }) + it('fees transfered to plugin, if comm fee is zero', async () => { + await poolPlugin.setPluginFees(5000, 4000); + await swapExact1For0(expandTo18Decimals(1), wallet.address) + const pluginBalance0Before = await token0.balanceOf(poolPlugin); + const pluginBalance1Before = await token1.balanceOf(poolPlugin); + await pool.advanceTime(86400); + await swapExact0For1(expandTo18Decimals(1), wallet.address) + const pluginBalance0After = await token0.balanceOf(poolPlugin); + const pluginBalance1After = await token1.balanceOf(poolPlugin); + expect(pluginBalance0After - pluginBalance0Before).to.be.eq(4n * 10n**15n); + expect(pluginBalance1After - pluginBalance1Before).to.be.eq(0); + }) + + it('fees transfered to plugin, after disable plugin fee and enable comm fee', async () => { + await poolPlugin.setPluginFees(5000, 4000); + await swapExact1For0(expandTo18Decimals(1), wallet.address) + await swapExact0For1(expandTo18Decimals(1), wallet.address) + const pluginBalance0Before = await token0.balanceOf(poolPlugin); + const pluginBalance1Before = await token1.balanceOf(poolPlugin); + await pool.advanceTime(86400); + await poolPlugin.setPluginFees(5000, 0); + await pool.setCommunityFee(100); + await swapExact0For1(expandTo18Decimals(1), wallet.address) + const pluginBalance0After = await token0.balanceOf(poolPlugin); + const pluginBalance1After = await token1.balanceOf(poolPlugin); + expect(pluginBalance0After - pluginBalance0Before).to.be.eq(4n * 10n**15n); + expect(pluginBalance1After - pluginBalance1Before).to.be.eq(0); + }) + + it('fees transfered to vault, after disable comm fee and enable plugin fee', async () => { + await pool.setCommunityFee(100); + await swapExact1For0(expandTo18Decimals(1), wallet.address) + await swapExact0For1(expandTo18Decimals(1), wallet.address) + const vaultBalance0Before = await token0.balanceOf(vaultAddress); + const vaultBalance1Before = await token1.balanceOf(vaultAddress); + await pool.advanceTime(86400); + await poolPlugin.setPluginFees(5000, 4000); + await pool.setCommunityFee(0); + await swapExact0For1(expandTo18Decimals(1), wallet.address) + const vaultBalance0After = await token0.balanceOf(vaultAddress); + const vaultBalance1After = await token1.balanceOf(vaultAddress); + expect(vaultBalance0After - vaultBalance0Before).to.be.eq(5n * 10n**13n); + expect(vaultBalance1After - vaultBalance1Before).to.be.eq(0); + }) + it('works correct with communityFee', async () => { await poolPlugin.setPluginFees(1000, 4000); await pool.setCommunityFee(500); diff --git a/src/core/test/__snapshots__/AlgebraFactory.spec.ts.snap b/src/core/test/__snapshots__/AlgebraFactory.spec.ts.snap index 977a472ff..5ba6df9f9 100644 --- a/src/core/test/__snapshots__/AlgebraFactory.spec.ts.snap +++ b/src/core/test/__snapshots__/AlgebraFactory.spec.ts.snap @@ -1,13 +1,13 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`AlgebraFactory #createCustomPool gas [ @skip-on-coverage ] 1`] = `4756512`; +exports[`AlgebraFactory #createCustomPool gas [ @skip-on-coverage ] 1`] = `4752295`; -exports[`AlgebraFactory #createCustomPool gas for second pool [ @skip-on-coverage ] 1`] = `4756512`; +exports[`AlgebraFactory #createCustomPool gas for second pool [ @skip-on-coverage ] 1`] = `4752295`; -exports[`AlgebraFactory #createPool gas [ @skip-on-coverage ] 1`] = `4744315`; +exports[`AlgebraFactory #createPool gas [ @skip-on-coverage ] 1`] = `4740098`; -exports[`AlgebraFactory #createPool gas for second pool [ @skip-on-coverage ] 1`] = `4744315`; +exports[`AlgebraFactory #createPool gas for second pool [ @skip-on-coverage ] 1`] = `4740098`; exports[`AlgebraFactory factory bytecode size [ @skip-on-coverage ] 1`] = `10699`; -exports[`AlgebraFactory pool bytecode size [ @skip-on-coverage ] 1`] = `22456`; +exports[`AlgebraFactory pool bytecode size [ @skip-on-coverage ] 1`] = `22435`; diff --git a/src/core/test/__snapshots__/AlgebraPool.gas.spec.ts.snap b/src/core/test/__snapshots__/AlgebraPool.gas.spec.ts.snap index 52a5071fd..c7f4f1b7e 100644 --- a/src/core/test/__snapshots__/AlgebraPool.gas.spec.ts.snap +++ b/src/core/test/__snapshots__/AlgebraPool.gas.spec.ts.snap @@ -92,11 +92,11 @@ exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swapExact1For0 exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swapExact1For0 second swap in block with no tick movement 1`] = `103936`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swapExact1For0 with plugin fee on first swap in block moves tick, no initialized crossings, with transfer 1`] = `157999`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swapExact1For0 with plugin fee on first swap in block moves tick, no initialized crossings, with transfer 1`] = `158006`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swapExact1For0 with plugin fee on first swap in block with no tick movement, with transfer 1`] = `157947`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swapExact1For0 with plugin fee on first swap in block with no tick movement, with transfer 1`] = `157954`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swapExact1For0 with plugin fee on first swap in block with no tick movement, without transfer 1`] = `132509`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swapExact1For0 with plugin fee on first swap in block with no tick movement, without transfer 1`] = `132494`; exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #burn above current price burn entire position after some time passes 1`] = `117308`; @@ -152,42 +152,42 @@ exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #mint below curre exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #poke best case 1`] = `63735`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swapExact0For1 first swap in block moves tick, no initialized crossings 1`] = `112770`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swapExact0For1 first swap in block moves tick, no initialized crossings 1`] = `112710`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swapExact0For1 first swap in block with no tick movement 1`] = `112739`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swapExact0For1 first swap in block with no tick movement 1`] = `112679`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swapExact0For1 first swap in block, large swap crossing a single initialized tick 1`] = `128752`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swapExact0For1 first swap in block, large swap crossing a single initialized tick 1`] = `128692`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swapExact0For1 first swap in block, large swap crossing several initialized ticks 1`] = `162977`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swapExact0For1 first swap in block, large swap crossing several initialized ticks 1`] = `162917`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swapExact0For1 first swap in block, large swap, no initialized crossings 1`] = `112908`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swapExact0For1 first swap in block, large swap, no initialized crossings 1`] = `112848`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swapExact0For1 large swap crossing several initialized ticks after some time passes 1`] = `162977`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swapExact0For1 large swap crossing several initialized ticks after some time passes 1`] = `162917`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swapExact0For1 large swap crossing several initialized ticks second time after some time passes 1`] = `182177`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swapExact0For1 large swap crossing several initialized ticks second time after some time passes 1`] = `182117`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swapExact0For1 second swap in block moves tick, no initialized crossings 1`] = `112770`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swapExact0For1 second swap in block moves tick, no initialized crossings 1`] = `112710`; exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swapExact0For1 second swap in block with no tick movement 1`] = `104112`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swapExact0For1 second swap in block, large swap crossing a single initialized tick 1`] = `129576`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swapExact0For1 second swap in block, large swap crossing a single initialized tick 1`] = `129516`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swapExact0For1 second swap in block, large swap crossing several initialized ticks 1`] = `163828`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swapExact0For1 second swap in block, large swap crossing several initialized ticks 1`] = `163768`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swapExact0For1 several large swaps with pauses 1`] = `182177`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swapExact0For1 several large swaps with pauses 1`] = `182117`; exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swapExact0For1 small swap after several large swaps with pauses 1`] = `103986`; exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swapExact0For1 small swap with filled dataStorage 1`] = `103982`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swapExact1For0 first swap in block moves tick, no initialized crossings 1`] = `112842`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swapExact1For0 first swap in block moves tick, no initialized crossings 1`] = `112782`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swapExact1For0 first swap in block with no tick movement 1`] = `112790`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swapExact1For0 first swap in block with no tick movement 1`] = `112730`; exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swapExact1For0 second swap in block with no tick movement 1`] = `104173`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swapExact1For0 with plugin fee on first swap in block moves tick, no initialized crossings, with transfer 1`] = `169095`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swapExact1For0 with plugin fee on first swap in block moves tick, no initialized crossings, with transfer 1`] = `169321`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swapExact1For0 with plugin fee on first swap in block with no tick movement, with transfer 1`] = `169043`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swapExact1For0 with plugin fee on first swap in block with no tick movement, with transfer 1`] = `169269`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swapExact1For0 with plugin fee on first swap in block with no tick movement, without transfer 1`] = `136208`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swapExact1For0 with plugin fee on first swap in block with no tick movement, without transfer 1`] = `136199`; From f1d78cb4e1e742051e7dfef87289d23980d27cc0 Mon Sep 17 00:00:00 2001 From: IliaAzhel Date: Mon, 7 Oct 2024 18:26:05 +0300 Subject: [PATCH 16/21] [Plugin] MIN sliding fee value --- src/plugin/contracts/plugins/SlidingFeePlugin.sol | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/plugin/contracts/plugins/SlidingFeePlugin.sol b/src/plugin/contracts/plugins/SlidingFeePlugin.sol index 9585adfd9..a86a56a3a 100644 --- a/src/plugin/contracts/plugins/SlidingFeePlugin.sol +++ b/src/plugin/contracts/plugins/SlidingFeePlugin.sol @@ -48,7 +48,11 @@ abstract contract SlidingFeePlugin is BasePlugin, ISlidingFeePlugin { ? (uint256(baseFee) * currentFeeFactors.zeroToOneFeeFactor) >> FEE_FACTOR_SHIFT : (uint256(baseFee) * currentFeeFactors.oneToZeroFeeFactor) >> FEE_FACTOR_SHIFT; - if (adjustedFee > type(uint16).max) adjustedFee = type(uint16).max; + if (adjustedFee > type(uint16).max) { + adjustedFee = type(uint16).max; + } else if (adjustedFee == 0) { + adjustedFee = 1; + } return uint16(adjustedFee); } @@ -103,4 +107,4 @@ abstract contract SlidingFeePlugin is BasePlugin, ISlidingFeePlugin { feeFactors = FeeFactors(uint128(2 << FEE_FACTOR_SHIFT), 0); } } -} \ No newline at end of file +} From ee41e68da7438f0078dae088ae707cf3d9e3541f Mon Sep 17 00:00:00 2001 From: IliaAzhel Date: Mon, 7 Oct 2024 18:52:00 +0300 Subject: [PATCH 17/21] [Periphery] update snapshots --- .../NonfungibleTokenPositionDescriptor.spec.ts | 4 ++-- src/periphery/test/PositionValue.spec.ts | 2 +- src/periphery/test/SwapRouter.spec.ts | 2 +- src/periphery/test/TickLens.spec.ts | 2 +- src/periphery/test/V3Migrator.spec.ts | 14 +++++++------- .../NonfungiblePositionManager.spec.ts.snap | 18 +++++++++--------- .../__snapshots__/PoolAddress.spec.ts.snap | 2 +- .../__snapshots__/PositionValue.spec.ts.snap | 8 ++++---- .../test/__snapshots__/TickLens.spec.ts.snap | 12 ++++++------ .../test/__snapshots__/V3Migrator.spec.ts.snap | 2 +- src/periphery/test/shared/quoter.ts | 6 +++--- 11 files changed, 36 insertions(+), 36 deletions(-) diff --git a/src/periphery/test/NonfungibleTokenPositionDescriptor.spec.ts b/src/periphery/test/NonfungibleTokenPositionDescriptor.spec.ts index 53a7b3093..87e854715 100644 --- a/src/periphery/test/NonfungibleTokenPositionDescriptor.spec.ts +++ b/src/periphery/test/NonfungibleTokenPositionDescriptor.spec.ts @@ -115,7 +115,7 @@ describe('NonfungibleTokenPositionDescriptor', () => { describe('#tokenURI', () => { it('displays Native as token symbol for WNativeToken token', async () => { const [token0, token1] = await sortedTokens(wnative, tokens[1]); - await nft.createAndInitializePoolIfNecessary(token0, token1, ZERO_ADDRESS, encodePriceSqrt(1, 1)); + await nft.createAndInitializePoolIfNecessary(token0, token1, ZERO_ADDRESS, encodePriceSqrt(1, 1), '0x'); await wnative.approve(nft, 100); await tokens[1].approve(nft, 100); await nft.mint({ @@ -140,7 +140,7 @@ describe('NonfungibleTokenPositionDescriptor', () => { it('displays returned token symbols when neither token is WNativeToken ', async () => { const [token0, token1] = await sortedTokens(tokens[2], tokens[1]); - await nft.createAndInitializePoolIfNecessary(token0, token1, ZERO_ADDRESS, encodePriceSqrt(1, 1)); + await nft.createAndInitializePoolIfNecessary(token0, token1, ZERO_ADDRESS, encodePriceSqrt(1, 1), '0x'); await tokens[1].approve(nft, 100); await tokens[2].approve(nft, 100); await nft.mint({ diff --git a/src/periphery/test/PositionValue.spec.ts b/src/periphery/test/PositionValue.spec.ts index b7a01f6fe..415a3aabb 100644 --- a/src/periphery/test/PositionValue.spec.ts +++ b/src/periphery/test/PositionValue.spec.ts @@ -40,7 +40,7 @@ describe('PositionValue', async () => { await token.transfer(wallets[0].address, expandTo18Decimals(1_000_000)); } - await nft.createAndInitializePoolIfNecessary(tokens[0], tokens[1], ZERO_ADDRESS, encodePriceSqrt(1, 1)); + await nft.createAndInitializePoolIfNecessary(tokens[0], tokens[1], ZERO_ADDRESS, encodePriceSqrt(1, 1), '0x'); return { positionValue, diff --git a/src/periphery/test/SwapRouter.spec.ts b/src/periphery/test/SwapRouter.spec.ts index 2e358112e..1285ad447 100644 --- a/src/periphery/test/SwapRouter.spec.ts +++ b/src/periphery/test/SwapRouter.spec.ts @@ -45,7 +45,7 @@ describe('SwapRouter', function () { if (tokenAddressA.toLowerCase() > tokenAddressB.toLowerCase()) [tokenAddressA, tokenAddressB] = [tokenAddressB, tokenAddressA]; - await _nft.createAndInitializePoolIfNecessary(tokenAddressA, tokenAddressB, deployer, encodePriceSqrt(1, 1)); + await _nft.createAndInitializePoolIfNecessary(tokenAddressA, tokenAddressB, deployer, encodePriceSqrt(1, 1), '0x'); const liquidityParams = { token0: tokenAddressA, diff --git a/src/periphery/test/TickLens.spec.ts b/src/periphery/test/TickLens.spec.ts index d9a62ca9a..06ba8ec13 100644 --- a/src/periphery/test/TickLens.spec.ts +++ b/src/periphery/test/TickLens.spec.ts @@ -78,7 +78,7 @@ describe('TickLens', () => { if (BigInt(tokenAddressA) > BigInt(tokenAddressB)) [tokenAddressA, tokenAddressB] = [tokenAddressB, tokenAddressA]; - const tx = await _nft.createAndInitializePoolIfNecessary(tokenAddressA, tokenAddressB, ZERO_ADDRESS, encodePriceSqrt(1, 1)); + const tx = await _nft.createAndInitializePoolIfNecessary(tokenAddressA, tokenAddressB, ZERO_ADDRESS, encodePriceSqrt(1, 1), '0x'); await tx.wait(); const liquidityParams = { diff --git a/src/periphery/test/V3Migrator.spec.ts b/src/periphery/test/V3Migrator.spec.ts index ea60a44bf..be596283e 100644 --- a/src/periphery/test/V3Migrator.spec.ts +++ b/src/periphery/test/V3Migrator.spec.ts @@ -143,7 +143,7 @@ describe('V3Migrator', () => { it('works once v3 pool is initialized', async () => { const [token0, token1] = await sortedTokens(wnative, token); - await migrator.createAndInitializePoolIfNecessary(token0, token1, ZERO_ADDRESS, encodePriceSqrt(1, 1)); + await migrator.createAndInitializePoolIfNecessary(token0, token1, ZERO_ADDRESS, encodePriceSqrt(1, 1), '0x'); await pair.approve(migrator, expectedLiquidity); await migrator.migrate({ @@ -172,7 +172,7 @@ describe('V3Migrator', () => { it('works for partial', async () => { const [token0, token1] = await sortedTokens(wnative, token); - await migrator.createAndInitializePoolIfNecessary(token0, token1, ZERO_ADDRESS, encodePriceSqrt(1, 1)); + await migrator.createAndInitializePoolIfNecessary(token0, token1, ZERO_ADDRESS, encodePriceSqrt(1, 1), '0x'); const tokenBalanceBefore = await token.balanceOf(wallet.address); const wnativeBalanceBefore = await wnative.balanceOf(wallet.address); @@ -210,7 +210,7 @@ describe('V3Migrator', () => { it('double the price', async () => { const [token0, token1] = await sortedTokens(wnative, token); - await migrator.createAndInitializePoolIfNecessary(token0, token1, ZERO_ADDRESS, encodePriceSqrt(2, 1)); + await migrator.createAndInitializePoolIfNecessary(token0, token1, ZERO_ADDRESS, encodePriceSqrt(2, 1), '0x'); const tokenBalanceBefore = await token.balanceOf(wallet.address); const wnativeBalanceBefore = await wnative.balanceOf(wallet.address); @@ -254,7 +254,7 @@ describe('V3Migrator', () => { it('half the price', async () => { const [token0, token1] = await sortedTokens(wnative, token); - await migrator.createAndInitializePoolIfNecessary(token0, token1, ZERO_ADDRESS, encodePriceSqrt(1, 2)); + await migrator.createAndInitializePoolIfNecessary(token0, token1, ZERO_ADDRESS, encodePriceSqrt(1, 2), '0x'); const tokenBalanceBefore = await token.balanceOf(wallet.address); const wnativeBalanceBefore = await wnative.balanceOf(wallet.address); @@ -298,7 +298,7 @@ describe('V3Migrator', () => { it('double the price - as Native', async () => { const [token0, token1] = await sortedTokens(wnative, token); - await migrator.createAndInitializePoolIfNecessary(token0, token1, ZERO_ADDRESS, encodePriceSqrt(2, 1)); + await migrator.createAndInitializePoolIfNecessary(token0, token1, ZERO_ADDRESS, encodePriceSqrt(2, 1), '0x'); const tokenBalanceBefore = await token.balanceOf(wallet.address); @@ -342,7 +342,7 @@ describe('V3Migrator', () => { it('half the price - as Native', async () => { const [token0, token1] = await sortedTokens(wnative, token); - await migrator.createAndInitializePoolIfNecessary(token0, token1, ZERO_ADDRESS, encodePriceSqrt(1, 2)); + await migrator.createAndInitializePoolIfNecessary(token0, token1, ZERO_ADDRESS, encodePriceSqrt(1, 2), '0x'); const tokenBalanceBefore = await token.balanceOf(wallet.address); @@ -386,7 +386,7 @@ describe('V3Migrator', () => { it('gas [ @skip-on-coverage ]', async () => { const [token0, token1] = await sortedTokens(wnative, token); - await migrator.createAndInitializePoolIfNecessary(token0, token1, ZERO_ADDRESS, encodePriceSqrt(1, 1)); + await migrator.createAndInitializePoolIfNecessary(token0, token1, ZERO_ADDRESS, encodePriceSqrt(1, 1), '0x'); await pair.approve(migrator, expectedLiquidity); await snapshotGasCost( diff --git a/src/periphery/test/__snapshots__/NonfungiblePositionManager.spec.ts.snap b/src/periphery/test/__snapshots__/NonfungiblePositionManager.spec.ts.snap index 447467e09..07ca780c7 100644 --- a/src/periphery/test/__snapshots__/NonfungiblePositionManager.spec.ts.snap +++ b/src/periphery/test/__snapshots__/NonfungiblePositionManager.spec.ts.snap @@ -8,23 +8,23 @@ exports[`NonfungiblePositionManager #collect gas transfers token0 only [ @skip-o exports[`NonfungiblePositionManager #collect gas transfers token1 only [ @skip-on-coverage ] 1`] = `122936`; -exports[`NonfungiblePositionManager #createAndInitializePoolIfNecessary gas [ @skip-on-coverage ] 1`] = `5231043`; +exports[`NonfungiblePositionManager #createAndInitializePoolIfNecessary gas [ @skip-on-coverage ] 1`] = `5234040`; -exports[`NonfungiblePositionManager #decreaseLiquidity gas complete decrease [ @skip-on-coverage ] 1`] = `171665`; +exports[`NonfungiblePositionManager #decreaseLiquidity gas complete decrease [ @skip-on-coverage ] 1`] = `171889`; -exports[`NonfungiblePositionManager #decreaseLiquidity gas partial decrease [ @skip-on-coverage ] 1`] = `176589`; +exports[`NonfungiblePositionManager #decreaseLiquidity gas partial decrease [ @skip-on-coverage ] 1`] = `176869`; exports[`NonfungiblePositionManager #increaseLiquidity gas [ @skip-on-coverage ] 1`] = `184149`; -exports[`NonfungiblePositionManager #mint gas first mint for pool [ @skip-on-coverage ] 1`] = `636773`; +exports[`NonfungiblePositionManager #mint gas first mint for pool [ @skip-on-coverage ] 1`] = `636761`; -exports[`NonfungiblePositionManager #mint gas first mint for pool using eth with non-zero refund [ @skip-on-coverage ] 1`] = `651121`; +exports[`NonfungiblePositionManager #mint gas first mint for pool using eth with non-zero refund [ @skip-on-coverage ] 1`] = `651124`; -exports[`NonfungiblePositionManager #mint gas first mint for pool using eth with zero refund [ @skip-on-coverage ] 1`] = `643954`; +exports[`NonfungiblePositionManager #mint gas first mint for pool using eth with zero refund [ @skip-on-coverage ] 1`] = `643957`; -exports[`NonfungiblePositionManager #mint gas mint for same pool, different ticks [ @skip-on-coverage ] 1`] = `442385`; +exports[`NonfungiblePositionManager #mint gas mint for same pool, different ticks [ @skip-on-coverage ] 1`] = `442373`; -exports[`NonfungiblePositionManager #mint gas mint on same ticks [ @skip-on-coverage ] 1`] = `332156`; +exports[`NonfungiblePositionManager #mint gas mint on same ticks [ @skip-on-coverage ] 1`] = `332144`; exports[`NonfungiblePositionManager #permit owned by eoa gas [ @skip-on-coverage ] 1`] = `60003`; @@ -36,4 +36,4 @@ exports[`NonfungiblePositionManager #transferFrom gas comes from approved [ @ski exports[`NonfungiblePositionManager bytecode size [ @skip-on-coverage ] 1`] = `22015`; -exports[`NonfungiblePositionManager multicall exit gas [ @skip-on-coverage ] 1`] = `247432`; +exports[`NonfungiblePositionManager multicall exit gas [ @skip-on-coverage ] 1`] = `247656`; diff --git a/src/periphery/test/__snapshots__/PoolAddress.spec.ts.snap b/src/periphery/test/__snapshots__/PoolAddress.spec.ts.snap index 52345af82..53704799a 100644 --- a/src/periphery/test/__snapshots__/PoolAddress.spec.ts.snap +++ b/src/periphery/test/__snapshots__/PoolAddress.spec.ts.snap @@ -2,4 +2,4 @@ exports[`PoolAddress #computeAddress gas cost [ @skip-on-coverage ] 1`] = `673`; -exports[`PoolAddress #computeAddress matches example from core repo 1`] = `"0xe5Ddfe98A0958dF35aAEac697d7E589baF0c5C79"`; +exports[`PoolAddress #computeAddress matches example from core repo 1`] = `"0xC8dF379114c2BeEdd8cBFaE53fF162cD2027ab5e"`; diff --git a/src/periphery/test/__snapshots__/PositionValue.spec.ts.snap b/src/periphery/test/__snapshots__/PositionValue.spec.ts.snap index 3fc64e25c..4b6cbc34b 100644 --- a/src/periphery/test/__snapshots__/PositionValue.spec.ts.snap +++ b/src/periphery/test/__snapshots__/PositionValue.spec.ts.snap @@ -1,11 +1,11 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`PositionValue #fees when price is above the position range gas 1`] = `51867`; +exports[`PositionValue #fees when price is above the position range gas 1`] = `53868`; -exports[`PositionValue #fees when price is below the position range gas 1`] = `51933`; +exports[`PositionValue #fees when price is below the position range gas 1`] = `53934`; -exports[`PositionValue #fees when price is within the position range gas 1`] = `57458`; +exports[`PositionValue #fees when price is within the position range gas 1`] = `60204`; exports[`PositionValue #principal gas 1`] = `26316`; -exports[`PositionValue #total gas 1`] = `60549`; +exports[`PositionValue #total gas 1`] = `63295`; diff --git a/src/periphery/test/__snapshots__/TickLens.spec.ts.snap b/src/periphery/test/__snapshots__/TickLens.spec.ts.snap index 722c60622..e691dc2a9 100644 --- a/src/periphery/test/__snapshots__/TickLens.spec.ts.snap +++ b/src/periphery/test/__snapshots__/TickLens.spec.ts.snap @@ -1,13 +1,13 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`TickLens #getClosestActiveTicks gas for almost all tick space [ @skip-on-coverage ] 1`] = `31483`; +exports[`TickLens #getClosestActiveTicks gas for almost all tick space [ @skip-on-coverage ] 1`] = `34119`; -exports[`TickLens #getNextActiveTicks gas for single populated tick [ @skip-on-coverage ] 1`] = `22186`; +exports[`TickLens #getNextActiveTicks gas for single populated tick [ @skip-on-coverage ] 1`] = `23594`; -exports[`TickLens #getPopulatedTicksInWord gas for single populated tick [ @skip-on-coverage ] 1`] = `53689`; +exports[`TickLens #getPopulatedTicksInWord gas for single populated tick [ @skip-on-coverage ] 1`] = `54876`; -exports[`TickLens fully populated word getNextActiveTicks 255 ticks [ @skip-on-coverage ] 1`] = `2465710`; +exports[`TickLens fully populated word getNextActiveTicks 255 ticks [ @skip-on-coverage ] 1`] = `2645934`; -exports[`TickLens fully populated word getNextActiveTicks 512 ticks [ @skip-on-coverage ] 1`] = `2507102`; +exports[`TickLens fully populated word getNextActiveTicks 512 ticks [ @skip-on-coverage ] 1`] = `2690142`; -exports[`TickLens fully populated word getPopulatedTicksInWord [ @skip-on-coverage ] 1`] = `92600`; +exports[`TickLens fully populated word getPopulatedTicksInWord [ @skip-on-coverage ] 1`] = `96603`; diff --git a/src/periphery/test/__snapshots__/V3Migrator.spec.ts.snap b/src/periphery/test/__snapshots__/V3Migrator.spec.ts.snap index a93877c95..50541e227 100644 --- a/src/periphery/test/__snapshots__/V3Migrator.spec.ts.snap +++ b/src/periphery/test/__snapshots__/V3Migrator.spec.ts.snap @@ -1,3 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`V3Migrator #migrate gas [ @skip-on-coverage ] 1`] = `754282`; +exports[`V3Migrator #migrate gas [ @skip-on-coverage ] 1`] = `755875`; diff --git a/src/periphery/test/shared/quoter.ts b/src/periphery/test/shared/quoter.ts index 267284406..f94e69a01 100644 --- a/src/periphery/test/shared/quoter.ts +++ b/src/periphery/test/shared/quoter.ts @@ -15,7 +15,7 @@ export async function createPool( if (tokenAddressA.toLowerCase() > tokenAddressB.toLowerCase()) [tokenAddressA, tokenAddressB] = [tokenAddressB, tokenAddressA]; - await nft.createAndInitializePoolIfNecessary(tokenAddressA, tokenAddressB, deployer, encodePriceSqrt(1, 1)); + await nft.createAndInitializePoolIfNecessary(tokenAddressA, tokenAddressB, deployer, encodePriceSqrt(1, 1), '0x'); const liquidityParams = { token0: tokenAddressA, @@ -43,7 +43,7 @@ export async function createPoolWithMultiplePositions( if (tokenAddressA.toLowerCase() > tokenAddressB.toLowerCase()) [tokenAddressA, tokenAddressB] = [tokenAddressB, tokenAddressA]; - await nft.createAndInitializePoolIfNecessary(tokenAddressA, tokenAddressB, ZERO_ADDRESS, encodePriceSqrt(1, 1)); + await nft.createAndInitializePoolIfNecessary(tokenAddressA, tokenAddressB, ZERO_ADDRESS, encodePriceSqrt(1, 1), '0x'); const liquidityParams = { token0: tokenAddressA, @@ -103,7 +103,7 @@ export async function createPoolWithZeroTickInitialized( if (tokenAddressA.toLowerCase() > tokenAddressB.toLowerCase()) [tokenAddressA, tokenAddressB] = [tokenAddressB, tokenAddressA]; - await nft.createAndInitializePoolIfNecessary(tokenAddressA, tokenAddressB, ZERO_ADDRESS, encodePriceSqrt(1, 1)); + await nft.createAndInitializePoolIfNecessary(tokenAddressA, tokenAddressB, ZERO_ADDRESS, encodePriceSqrt(1, 1), '0x'); const liquidityParams = { token0: tokenAddressA, From 41ab42f26ea24bdc42618a9415cd36788f3ced45 Mon Sep 17 00:00:00 2001 From: IliaAzhel Date: Mon, 7 Oct 2024 18:49:48 +0300 Subject: [PATCH 18/21] [Farming] fix tests & update snapshots --- src/farming/.solcover.js | 1 + src/farming/test/AlgebraFarming.spec.ts | 8 ++++---- src/farming/test/helpers/index.ts | 4 ++-- src/farming/test/shared/fixtures.ts | 4 ++-- src/farming/test/unit/EternalFarms.spec.ts | 8 ++++---- src/farming/test/unit/FarmingCenter.spec.ts | 4 ++-- src/farming/test/unit/Multicall.spec.ts | 4 +--- .../test/unit/__snapshots__/EternalFarms.spec.ts.snap | 4 ++-- 8 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/farming/.solcover.js b/src/farming/.solcover.js index 53c8df822..7c8f6101a 100644 --- a/src/farming/.solcover.js +++ b/src/farming/.solcover.js @@ -5,6 +5,7 @@ const skipFiles = testContracts.map((x) => "test/" + x) module.exports = { skipFiles: skipFiles, + configureYulOptimizer: true, mocha: { grep: '@skip-on-coverage', // Find everything with this tag invert: true, // Run the grep's inverse set. diff --git a/src/farming/test/AlgebraFarming.spec.ts b/src/farming/test/AlgebraFarming.spec.ts index 81d937764..7c54bfc2b 100644 --- a/src/farming/test/AlgebraFarming.spec.ts +++ b/src/farming/test/AlgebraFarming.spec.ts @@ -271,7 +271,7 @@ describe('AlgebraFarming', () => { }) ) ); - await time.setNextBlockTimestamp(startTime + 1); + await time.setNextBlockTimestamp(startTime + 100); const trader = actors.traderUser0(); await helpers.makeTickGoFlow({ trader, @@ -300,7 +300,7 @@ describe('AlgebraFarming', () => { const { helpers, createIncentiveResult } = subject; - await time.increaseTo(endTime + 1); + await time.increaseTo(endTime + 100); const trader = actors.traderUser0(); await helpers.makeTickGoFlow({ @@ -341,7 +341,7 @@ describe('AlgebraFarming', () => { const startTime = epoch + 1_000; const endTime = startTime + duration; - await time.increaseTo(endTime + 1); + await time.increaseTo(endTime + 100); const trader = actors.traderUser0(); await helpers.makeTickGoFlow({ @@ -407,7 +407,7 @@ describe('AlgebraFarming', () => { deadline: (await blockTimestamp()) + 1000, }); - await time.setNextBlockTimestamp(endTime + 1); + await time.setNextBlockTimestamp(endTime + 100); const trader = actors.traderUser0(); await helpers.makeTickGoFlow({ diff --git a/src/farming/test/helpers/index.ts b/src/farming/test/helpers/index.ts index 4e2cb9c6b..f4e7a09b4 100644 --- a/src/farming/test/helpers/index.ts +++ b/src/farming/test/helpers/index.ts @@ -125,9 +125,9 @@ export class HelperCommands { }, pluginAddres ); - // @ts-ignore - virtualPoolAddress = (await txResult.wait(1)).logs[4].args['virtualPool']; + // @ts-ignore + virtualPoolAddress = (await txResult.wait()).logs[3].args['virtualPool']; return { ..._.pick(params, ['poolAddress', 'totalReward', 'bonusReward', 'rewardToken', 'bonusRewardToken']), nonce, diff --git a/src/farming/test/shared/fixtures.ts b/src/farming/test/shared/fixtures.ts index 008bd967a..3fdc27682 100644 --- a/src/farming/test/shared/fixtures.ts +++ b/src/farming/test/shared/fixtures.ts @@ -273,9 +273,9 @@ export const algebraFixture: () => Promise = async () => { const fee = FeeAmount.MEDIUM; - await nft.createAndInitializePoolIfNecessary(tokens[0], tokens[1], ZERO_ADDRESS, encodePriceSqrt(1, 1)); + await nft.createAndInitializePoolIfNecessary(tokens[0], tokens[1], ZERO_ADDRESS, encodePriceSqrt(1, 1),'0x'); - await nft.createAndInitializePoolIfNecessary(tokens[1], tokens[2], ZERO_ADDRESS, encodePriceSqrt(1, 1)); + await nft.createAndInitializePoolIfNecessary(tokens[1], tokens[2], ZERO_ADDRESS, encodePriceSqrt(1, 1),'0x'); const pool01 = await factory.poolByPair(tokens[0], tokens[1]); diff --git a/src/farming/test/unit/EternalFarms.spec.ts b/src/farming/test/unit/EternalFarms.spec.ts index 6f337cdf6..780b77717 100644 --- a/src/farming/test/unit/EternalFarms.spec.ts +++ b/src/farming/test/unit/EternalFarms.spec.ts @@ -1270,7 +1270,7 @@ describe('unit/EternalFarms', () => { bonusRewardRate: 50n, }); - await Time.setAndMine(timestamps.startTime + 1); + await Time.setAndMine(timestamps.startTime + 100); const mintResult = await helpers.mintDepositFarmFlow({ lp: lpUser0, @@ -1412,7 +1412,7 @@ describe('unit/EternalFarms', () => { bonusRewardRate: 50n, }); - await Time.setAndMine(timestamps.startTime + 1); + await Time.setAndMine(timestamps.startTime + 100); const mintResult = await helpers.mintDepositFarmFlow({ lp: lpUser0, @@ -1594,7 +1594,7 @@ describe('unit/EternalFarms', () => { deadline: (await blockTimestamp()) + 10000, }); - await Time.setAndMine(timestamps.startTime + 1); + await Time.setAndMine(timestamps.startTime + 100); await context.nft.connect(lpUser0).approveForFarming(tokenId, true, context.farmingCenter); await context.nft.connect(lpUser0).approveForFarming(tokenIdOut, true, context.farmingCenter); @@ -1898,7 +1898,7 @@ describe('unit/EternalFarms', () => { await erc20Helper.ensureBalancesAndApprovals(lpUser0, [token0, token1], amountDesired, await context.nft.getAddress()); - await context.nft.createAndInitializePoolIfNecessary(token0, token1, ZERO_ADDRESS, encodePriceSqrt(1, 1)); + await context.nft.createAndInitializePoolIfNecessary(token0, token1, ZERO_ADDRESS, encodePriceSqrt(1, 1), '0x'); const poolAddress = await context.factory.poolByPair(token0, token1); diff --git a/src/farming/test/unit/FarmingCenter.spec.ts b/src/farming/test/unit/FarmingCenter.spec.ts index 9b77a2c9e..58f91bb65 100644 --- a/src/farming/test/unit/FarmingCenter.spec.ts +++ b/src/farming/test/unit/FarmingCenter.spec.ts @@ -190,7 +190,7 @@ describe('unit/FarmingCenter', () => { bonusRewardRate: 50n, }); - await Time.setAndMine(timestamps.startTime + 1); + await Time.setAndMine(timestamps.startTime + 100); const mintResultEternal = await helpers.mintDepositFarmFlow({ lp: lpUser0, @@ -489,7 +489,7 @@ describe('unit/FarmingCenter', () => { bonusRewardRate: 50n, }); - await Time.setAndMine(timestamps.startTime + 1); + await Time.setAndMine(timestamps.startTime + 100); const mintResultEternal = await helpers.mintDepositFarmFlow({ lp: lpUser0, diff --git a/src/farming/test/unit/Multicall.spec.ts b/src/farming/test/unit/Multicall.spec.ts index 3f9f4f8b8..a18652981 100644 --- a/src/farming/test/unit/Multicall.spec.ts +++ b/src/farming/test/unit/Multicall.spec.ts @@ -42,8 +42,8 @@ describe('unit/Multicall', () => { multicallFixture = async () => { const context = await algebraFixture(); - const helpers = HelperCommands.fromTestContext(context, actors, provider); + const helpers = HelperCommands.fromTestContext(context, actors, provider); await erc20Helper.ensureBalancesAndApprovals(multicaller, [context.token0, context.token1], amountDesired, await context.nft.getAddress()); const mintResult = await helpers.mintFlow({ @@ -66,7 +66,6 @@ describe('unit/Multicall', () => { totalReward, await context.eternalFarming.getAddress() ); - await helpers.createIncentiveFlow({ rewardToken: context.rewardToken, bonusRewardToken: context.bonusRewardToken, @@ -77,7 +76,6 @@ describe('unit/Multicall', () => { rewardRate: 10n, bonusRewardRate: 50n, }); - await context.nft.connect(multicaller).approveForFarming(tokenId, true, context.farmingCenter); await context.farmingCenter.connect(multicaller).enterFarming(farmIncentiveKey, tokenId); diff --git a/src/farming/test/unit/__snapshots__/EternalFarms.spec.ts.snap b/src/farming/test/unit/__snapshots__/EternalFarms.spec.ts.snap index 7eee69436..2616188c3 100644 --- a/src/farming/test/unit/__snapshots__/EternalFarms.spec.ts.snap +++ b/src/farming/test/unit/__snapshots__/EternalFarms.spec.ts.snap @@ -2,6 +2,6 @@ exports[`unit/EternalFarms #claimReward when requesting the full amount has gas cost [ @skip-on-coverage ] 1`] = `60772`; -exports[`unit/EternalFarms #enterFarming works and has gas cost [ @skip-on-coverage ] 1`] = `498913`; +exports[`unit/EternalFarms #enterFarming works and has gas cost [ @skip-on-coverage ] 1`] = `506125`; -exports[`unit/EternalFarms #exitFarming after end time works and has gas cost [ @skip-on-coverage ] 1`] = `171767`; +exports[`unit/EternalFarms #exitFarming after end time works and has gas cost [ @skip-on-coverage ] 1`] = `175750`; From 8e5967b90dfc219947f8fda021a6eb5c54817317 Mon Sep 17 00:00:00 2001 From: IliaAzhel Date: Mon, 7 Oct 2024 21:31:25 +0300 Subject: [PATCH 19/21] [Plugin] add some tests for plugin fee && update snapshots --- src/plugin/contracts/AlgebraBasePluginV2.sol | 18 ++- src/plugin/contracts/test/SlidingFeeTest.sol | 4 +- src/plugin/test/AlgebraBasePluginV1.spec.ts | 8 -- src/plugin/test/AlgebraBasePluginV2.spec.ts | 52 ++++++-- src/plugin/test/BasePluginV2Factory.spec.ts | 125 ++++++++++++++++++ src/plugin/test/SlidingFee.spec.ts | 44 +++++- .../AlgebraBasePluginV1.spec.ts.snap | 2 +- .../AlgebraBasePluginV2.spec.ts.snap | 123 ----------------- .../AlgebraPool.gas.spec.ts.snap | 118 ++++++++--------- .../__snapshots__/SlidingFee.spec.ts.snap | 6 +- src/plugin/test/shared/fixtures.ts | 12 ++ 11 files changed, 301 insertions(+), 211 deletions(-) create mode 100644 src/plugin/test/BasePluginV2Factory.spec.ts delete mode 100644 src/plugin/test/__snapshots__/AlgebraBasePluginV2.spec.ts.snap diff --git a/src/plugin/contracts/AlgebraBasePluginV2.sol b/src/plugin/contracts/AlgebraBasePluginV2.sol index 06d5c3556..ca4ae7726 100644 --- a/src/plugin/contracts/AlgebraBasePluginV2.sol +++ b/src/plugin/contracts/AlgebraBasePluginV2.sol @@ -45,8 +45,16 @@ contract AlgebraBasePluginV2 is SlidingFeePlugin, FarmingProxyPlugin, Volatility return IAlgebraPlugin.afterModifyPosition.selector; } - function beforeSwap(address, address, bool zeroToOne, int256, uint160, bool, bytes calldata) external override onlyPool returns (bytes4, uint24, uint24) { - ( , int24 currentTick, , ) = _getPoolState(); + function beforeSwap( + address, + address, + bool zeroToOne, + int256, + uint160, + bool, + bytes calldata + ) external override onlyPool returns (bytes4, uint24, uint24) { + (, int24 currentTick, , ) = _getPoolState(); int24 lastTick = _getLastTick(); uint16 newFee = _getFeeAndUpdateFactors(zeroToOne, currentTick, lastTick); @@ -70,8 +78,4 @@ contract AlgebraBasePluginV2 is SlidingFeePlugin, FarmingProxyPlugin, Volatility _updatePluginConfigInPool(defaultPluginConfig); // should not be called, reset config return IAlgebraPlugin.afterFlash.selector; } - - function getCurrentFee() external view returns (uint16 fee) { - (, , fee, ) = _getPoolState(); - } -} \ No newline at end of file +} diff --git a/src/plugin/contracts/test/SlidingFeeTest.sol b/src/plugin/contracts/test/SlidingFeeTest.sol index 9d47fa8ca..e2d9cc886 100644 --- a/src/plugin/contracts/test/SlidingFeeTest.sol +++ b/src/plugin/contracts/test/SlidingFeeTest.sol @@ -4,11 +4,13 @@ pragma solidity =0.8.20; import '../plugins/SlidingFeePlugin.sol'; contract SlidingFeeTest is SlidingFeePlugin { + event Fee(uint16 fee); uint8 public constant override defaultPluginConfig = 0; constructor() BasePlugin(msg.sender, msg.sender, msg.sender) {} function getFeeForSwap(bool zeroToOne, int24 lastTick, int24 currentTick) external returns (uint16 fee) { fee = _getFeeAndUpdateFactors(zeroToOne, currentTick, lastTick); + emit Fee(fee); } function getGasCostOfGetFeeForSwap(bool zeroToOne, int24 lastTick, int24 currentTick) external returns (uint256) { @@ -26,4 +28,4 @@ contract SlidingFeeTest is SlidingFeePlugin { function changeFactor(uint16 newFactor) external { s_priceChangeFactor = newFactor; } -} \ No newline at end of file +} diff --git a/src/plugin/test/AlgebraBasePluginV1.spec.ts b/src/plugin/test/AlgebraBasePluginV1.spec.ts index a82a3cd57..b4637fc78 100644 --- a/src/plugin/test/AlgebraBasePluginV1.spec.ts +++ b/src/plugin/test/AlgebraBasePluginV1.spec.ts @@ -106,14 +106,6 @@ describe('AlgebraBasePluginV1', () => { expect((await mockPool.globalState()).pluginConfig).to.be.eq(defaultConfig); }); - it('resets config after afterSwap', async () => { - await mockPool.initialize(encodePriceSqrt(1, 1)); - await mockPool.setPluginConfig(PLUGIN_FLAGS.AFTER_SWAP_FLAG); - expect((await mockPool.globalState()).pluginConfig).to.be.eq(PLUGIN_FLAGS.AFTER_SWAP_FLAG); - await mockPool.swapToTick(100); - expect((await mockPool.globalState()).pluginConfig).to.be.eq(defaultConfig); - }); - it('resets config after beforeFlash', async () => { await mockPool.setPluginConfig(PLUGIN_FLAGS.BEFORE_FLASH_FLAG); expect((await mockPool.globalState()).pluginConfig).to.be.eq(PLUGIN_FLAGS.BEFORE_FLASH_FLAG); diff --git a/src/plugin/test/AlgebraBasePluginV2.spec.ts b/src/plugin/test/AlgebraBasePluginV2.spec.ts index 2b30f8bbd..6c732703d 100644 --- a/src/plugin/test/AlgebraBasePluginV2.spec.ts +++ b/src/plugin/test/AlgebraBasePluginV2.spec.ts @@ -106,14 +106,6 @@ describe('AlgebraBasePluginV2', () => { expect((await mockPool.globalState()).pluginConfig).to.be.eq(defaultConfig); }); - it('resets config after afterSwap', async () => { - await mockPool.initialize(encodePriceSqrt(1, 1)); - await mockPool.setPluginConfig(PLUGIN_FLAGS.AFTER_SWAP_FLAG); - expect((await mockPool.globalState()).pluginConfig).to.be.eq(PLUGIN_FLAGS.AFTER_SWAP_FLAG); - await mockPool.swapToTick(100); - expect((await mockPool.globalState()).pluginConfig).to.be.eq(defaultConfig); - }); - it('resets config after beforeFlash', async () => { await mockPool.setPluginConfig(PLUGIN_FLAGS.BEFORE_FLASH_FLAG); expect((await mockPool.globalState()).pluginConfig).to.be.eq(PLUGIN_FLAGS.BEFORE_FLASH_FLAG); @@ -318,6 +310,46 @@ describe('AlgebraBasePluginV2', () => { }); }); + describe('#SlidingFee', () => { + + beforeEach('initialize pool', async () => { + await mockPool.setPlugin(plugin); + await initializeAtZeroTick(mockPool); + }); + + describe('#setPriceChangeFactor', () => { + it('works correct', async () => { + await plugin.setPriceChangeFactor(1500) + let factor = await plugin.s_priceChangeFactor() + expect(factor).to.be.equal(1500); + }); + + it('emit event', async () => { + await expect(plugin.setPriceChangeFactor(1500)).to.emit(plugin, "PriceChangeFactor"); + }); + + it('fails if caller is not owner or manager', async () => { + await expect(plugin.connect(other).setPriceChangeFactor(1500)).to.be.reverted; + }); + }) + + describe('#setBaseFee', () => { + it('works correct', async () => { + await plugin.setBaseFee(1500) + let baseFee = await plugin.s_baseFee() + expect(baseFee).to.be.equal(1500); + }); + + it('emit event', async () => { + await expect(plugin.setBaseFee(1500)).to.emit(plugin, "BaseFee"); + }); + + it('fails if caller is not owner or manager', async () => { + await expect(plugin.connect(other).setBaseFee(1500)).to.be.reverted; + }); + }) + }) + describe('#FarmingPlugin', () => { describe('virtual pool tests', () => { let virtualPoolMock: MockTimeVirtualPool; @@ -328,6 +360,10 @@ describe('AlgebraBasePluginV2', () => { virtualPoolMock = (await virtualPoolMockFactory.deploy()) as any as MockTimeVirtualPool; }); + it('returns pool address', async () => { + expect(await plugin.getPool()).to.be.eq(mockPool); + }); + it('set incentive works', async () => { await mockPool.setPlugin(plugin); await plugin.setIncentive(virtualPoolMock); diff --git a/src/plugin/test/BasePluginV2Factory.spec.ts b/src/plugin/test/BasePluginV2Factory.spec.ts new file mode 100644 index 000000000..787c6f5df --- /dev/null +++ b/src/plugin/test/BasePluginV2Factory.spec.ts @@ -0,0 +1,125 @@ +import { Wallet } from 'ethers'; +import { ethers } from 'hardhat'; +import { loadFixture } from '@nomicfoundation/hardhat-network-helpers'; +import { expect } from './shared/expect'; +import { ZERO_ADDRESS, pluginFactoryFixtureV2 } from './shared/fixtures'; + +import { BasePluginV2Factory, AlgebraBasePluginV2, MockFactory } from '../typechain'; + +describe('BasePluginV2Factory', () => { + let wallet: Wallet, other: Wallet; + + let pluginFactory: BasePluginV2Factory; + let mockAlgebraFactory: MockFactory; + + before('prepare signers', async () => { + [wallet, other] = await (ethers as any).getSigners(); + }); + + beforeEach('deploy test volatilityOracle', async () => { + ({ pluginFactory, mockFactory: mockAlgebraFactory } = await loadFixture(pluginFactoryFixtureV2)); + }); + + describe('#Create plugin', () => { + it('only factory', async () => { + expect(pluginFactory.beforeCreatePoolHook(wallet.address, ZERO_ADDRESS, ZERO_ADDRESS, ZERO_ADDRESS, ZERO_ADDRESS, '0x')).to.be + .revertedWithoutReason; + }); + + it('factory can create plugin', async () => { + const pluginFactoryFactory = await ethers.getContractFactory('BasePluginV2Factory'); + const pluginFactoryMock = (await pluginFactoryFactory.deploy(wallet.address)) as any as BasePluginV2Factory; + + const pluginAddress = await pluginFactoryMock.beforeCreatePoolHook.staticCall( + wallet.address, + ZERO_ADDRESS, + ZERO_ADDRESS, + ZERO_ADDRESS, + ZERO_ADDRESS, + '0x' + ); + await pluginFactoryMock.beforeCreatePoolHook(wallet.address, ZERO_ADDRESS, ZERO_ADDRESS, ZERO_ADDRESS, ZERO_ADDRESS, '0x'); + + const pluginMock = (await ethers.getContractFactory('AlgebraBasePluginV2')).attach(pluginAddress) as any as AlgebraBasePluginV2; + const baseFee = await pluginMock.s_baseFee(); + expect(baseFee).to.be.not.eq(0); + }); + }); + + describe('#CreatePluginForExistingPool', () => { + it('only if has role', async () => { + expect(pluginFactory.connect(other).createPluginForExistingPool(wallet.address, other.address)).to.be.revertedWithoutReason; + }); + + it('cannot create for nonexistent pool', async () => { + await expect(pluginFactory.createPluginForExistingPool(wallet.address, other.address)).to.be.revertedWith('Pool not exist'); + }); + + it('can create for existing pool', async () => { + await mockAlgebraFactory.stubPool(wallet.address, other.address, other.address); + + await pluginFactory.createPluginForExistingPool(wallet.address, other.address); + const pluginAddress = await pluginFactory.pluginByPool(other.address); + expect(pluginAddress).to.not.be.eq(ZERO_ADDRESS); + const pluginMock = (await ethers.getContractFactory('AlgebraBasePluginV2')).attach(pluginAddress) as any as AlgebraBasePluginV2; + const baseFee = await pluginMock.s_baseFee(); + expect(baseFee).to.be.not.eq(0); + }); + + it('cannot create twice for existing pool', async () => { + await mockAlgebraFactory.stubPool(wallet.address, other.address, other.address); + + await pluginFactory.createPluginForExistingPool(wallet.address, other.address); + + await expect(pluginFactory.createPluginForExistingPool(wallet.address, other.address)).to.be.revertedWith('Already created'); + }); + }); + + describe('#Default base fee ', () => { + describe('#setDefaultBaseFee', () => { + + it('fails if caller is not owner', async () => { + await expect(pluginFactory.connect(other).setDefaultBaseFee(1000)).to.be.revertedWith('Only administrator'); + }); + + it('fails if try to set same value', async () => { + await expect(pluginFactory.connect(other).setDefaultBaseFee(500)).to.be.reverted; + }); + + it('updates defaultFeeConfiguration', async () => { + await pluginFactory.setDefaultBaseFee(1000); + + const newFee = await pluginFactory.defaultBaseFee(); + + expect(newFee).to.eq(1000); + }); + + it('emits event', async () => { + await expect(pluginFactory.setDefaultBaseFee(1000)) + .to.emit(pluginFactory, 'DefaultBaseFee') + .withArgs(1000); + }); + + }); + }); + + describe('#setFarmingAddress', () => { + it('fails if caller is not owner', async () => { + await expect(pluginFactory.connect(other).setFarmingAddress(wallet.address)).to.be.revertedWith('Only administrator'); + }); + + it('updates farmingAddress', async () => { + await pluginFactory.setFarmingAddress(other.address); + expect(await pluginFactory.farmingAddress()).to.eq(other.address); + }); + + it('emits event', async () => { + await expect(pluginFactory.setFarmingAddress(other.address)).to.emit(pluginFactory, 'FarmingAddress').withArgs(other.address); + }); + + it('cannot set current address', async () => { + await pluginFactory.setFarmingAddress(other.address); + await expect(pluginFactory.setFarmingAddress(other.address)).to.be.reverted; + }); + }); +}); diff --git a/src/plugin/test/SlidingFee.spec.ts b/src/plugin/test/SlidingFee.spec.ts index 57e5ed6b2..73cc8d4f9 100644 --- a/src/plugin/test/SlidingFee.spec.ts +++ b/src/plugin/test/SlidingFee.spec.ts @@ -24,7 +24,7 @@ describe('SlidingFee', () => { expect(await slidingFeePlugin.s_priceChangeFactor()).to.be.eq(1000) }); - describe('#getSlidingFee', () => { + describe('#FeeFactors', () => { beforeEach('set config', async () => { await slidingFeePlugin.changeBaseFee(500) await slidingFeePlugin.changeFactor(1000) @@ -194,6 +194,48 @@ describe('SlidingFee', () => { }); + describe('#getSlidingFee', () => { + + async function getFee(zto: boolean, lastTick: number, currentTick: number) : Promise{ + let tx = await slidingFeePlugin.getFeeForSwap(zto, lastTick, currentTick); + return (await tx.wait()).logs[0].args['fee'] + } + + beforeEach('set config', async () => { + await slidingFeePlugin.changeBaseFee(500) + await slidingFeePlugin.changeFactor(1000) + }); + + it("returns base fee value", async function () { + let fee = await getFee(false, 10000, 10000) + expect(fee).to.be.eq(500) + }); + + it("one to zero fee should be increased x1.5", async function () { + let feeOtZ = await getFee(false, 10000, 14055) + expect(feeOtZ).to.be.eq(750) + }); + + it("zero to one fee should be decreased x1.5", async function () { + let feeZtO = await getFee(true, 10000, 14054) + expect(feeZtO).to.be.eq(250) + }); + + it("handle overflow", async function () { + await slidingFeePlugin.changeBaseFee(50000) + let feeOtZ = await getFee(false, 10000,100000) + expect(feeOtZ).to.be.eq(65535) + }); + + it("MIN fee is 1 (0.0001%)", async function () { + await slidingFeePlugin.changeBaseFee(50000) + let feeOtZ = await getFee(true, 10000,100000) + expect(feeOtZ).to.be.eq(1) + }); + + }) + + describe('#getFee gas cost [ @skip-on-coverage ]', () => { it('gas cost of same tick', async () => { await snapshotGasCost(slidingFeePlugin.getGasCostOfGetFeeForSwap(true, 100, 100)); diff --git a/src/plugin/test/__snapshots__/AlgebraBasePluginV1.spec.ts.snap b/src/plugin/test/__snapshots__/AlgebraBasePluginV1.spec.ts.snap index 369d2135e..f1a709f59 100644 --- a/src/plugin/test/__snapshots__/AlgebraBasePluginV1.spec.ts.snap +++ b/src/plugin/test/__snapshots__/AlgebraBasePluginV1.spec.ts.snap @@ -120,4 +120,4 @@ Array [ ] `; -exports[`AlgebraBasePluginV1 AlgebraBasePluginV1 external methods #changeFeeConfiguration feeConfig getter gas cost [ @skip-on-coverage ] 1`] = `23774`; +exports[`AlgebraBasePluginV1 AlgebraBasePluginV1 external methods #changeFeeConfiguration feeConfig getter gas cost [ @skip-on-coverage ] 1`] = `23708`; diff --git a/src/plugin/test/__snapshots__/AlgebraBasePluginV2.spec.ts.snap b/src/plugin/test/__snapshots__/AlgebraBasePluginV2.spec.ts.snap deleted file mode 100644 index 63e73906c..000000000 --- a/src/plugin/test/__snapshots__/AlgebraBasePluginV2.spec.ts.snap +++ /dev/null @@ -1,123 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`AlgebraBasePluginV2 #DynamicFeeManager #adaptiveFee single huge spike after day 1`] = ` -Array [ - "Fee: 123 ", - "Fee: 123 ", - "Fee: 123 ", - "Fee: 123 ", - "Fee: 123 ", - "Fee: 123 ", - "Fee: 123 ", - "Fee: 123 ", - "Fee: 123 ", - "Fee: 123 ", - "Fee: 123 ", - "Fee: 123 ", - "Fee: 123 ", - "Fee: 123 ", - "Fee: 123 ", - "Fee: 123 ", - "Fee: 123 ", - "Fee: 123 ", - "Fee: 123 ", - "Fee: 123 ", - "Fee: 124 ", - "Fee: 124 ", - "Fee: 124 ", - "Fee: 124 ", - "Fee: 100 ", -] -`; - -exports[`AlgebraBasePluginV2 #DynamicFeeManager #adaptiveFee single huge spike after initialization 1`] = ` -Array [ - "Fee: 3714 ", - "Fee: 3000 ", - "Fee: 3000 ", - "Fee: 2965 ", - "Fee: 2602 ", - "Fee: 1733 ", - "Fee: 995 ", - "Fee: 607 ", - "Fee: 411 ", - "Fee: 309 ", - "Fee: 250 ", - "Fee: 215 ", - "Fee: 191 ", - "Fee: 174 ", - "Fee: 163 ", - "Fee: 154 ", - "Fee: 147 ", - "Fee: 142 ", - "Fee: 139 ", - "Fee: 135 ", - "Fee: 132 ", - "Fee: 130 ", - "Fee: 128 ", - "Fee: 126 ", - "Fee: 100 ", -] -`; - -exports[`AlgebraBasePluginV2 #DynamicFeeManager #adaptiveFee single huge step after day 1`] = ` -Array [ - "Fee: 3000 ", - "Fee: 3000 ", - "Fee: 3000 ", - "Fee: 3000 ", - "Fee: 3000 ", - "Fee: 3000 ", - "Fee: 3000 ", - "Fee: 3000 ", - "Fee: 3000 ", - "Fee: 3000 ", - "Fee: 3000 ", - "Fee: 3000 ", - "Fee: 3000 ", - "Fee: 3000 ", - "Fee: 3000 ", - "Fee: 3000 ", - "Fee: 3000 ", - "Fee: 3000 ", - "Fee: 3000 ", - "Fee: 3000 ", - "Fee: 3000 ", - "Fee: 3000 ", - "Fee: 3000 ", - "Fee: 3000 ", - "Fee: 100 ", -] -`; - -exports[`AlgebraBasePluginV2 #DynamicFeeManager #adaptiveFee single huge step after initialization 1`] = ` -Array [ - "Fee: 15000 ", - "Fee: 15000 ", - "Fee: 15000 ", - "Fee: 15000 ", - "Fee: 14311 ", - "Fee: 11402 ", - "Fee: 7651 ", - "Fee: 5386 ", - "Fee: 4312 ", - "Fee: 3795 ", - "Fee: 3525 ", - "Fee: 3371 ", - "Fee: 3277 ", - "Fee: 3216 ", - "Fee: 3174 ", - "Fee: 3144 ", - "Fee: 3123 ", - "Fee: 3106 ", - "Fee: 3093 ", - "Fee: 3083 ", - "Fee: 3075 ", - "Fee: 3068 ", - "Fee: 3062 ", - "Fee: 3058 ", - "Fee: 3037 ", -] -`; - -exports[`AlgebraBasePluginV2 AlgebraBasePluginV1 external methods #changeFeeConfiguration feeConfig getter gas cost [ @skip-on-coverage ] 1`] = `23708`; diff --git a/src/plugin/test/__snapshots__/AlgebraPool.gas.spec.ts.snap b/src/plugin/test/__snapshots__/AlgebraPool.gas.spec.ts.snap index ef3c1b4a2..7371eda9f 100644 --- a/src/plugin/test/__snapshots__/AlgebraPool.gas.spec.ts.snap +++ b/src/plugin/test/__snapshots__/AlgebraPool.gas.spec.ts.snap @@ -1,42 +1,42 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps dynamic fee large swap crossing several initialized ticks 1`] = `216020`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps dynamic fee large swap crossing several initialized ticks 1`] = `216571`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps dynamic fee small swap with filled volatilityOracle 1`] = `168035`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps dynamic fee small swap with filled volatilityOracle 1`] = `168586`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps dynamic fee small swap with filled volatilityOracle after 4h 1`] = `203473`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps dynamic fee small swap with filled volatilityOracle after 4h 1`] = `204024`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps dynamic fee small swap with filled volatilityOracle after 8h 1`] = `196259`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps dynamic fee small swap with filled volatilityOracle after 8h 1`] = `196810`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps dynamic fee small swap with filled volatilityOracle after 24h 1`] = `164527`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps dynamic fee small swap with filled volatilityOracle after 24h 1`] = `165078`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps static fee large swap crossing several initialized ticks 1`] = `214790`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps static fee large swap crossing several initialized ticks 1`] = `215341`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps static fee small swap with filled volatilityOracle 1`] = `164537`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps static fee small swap with filled volatilityOracle 1`] = `165088`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps static fee small swap with filled volatilityOracle after 4h 1`] = `198002`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps static fee small swap with filled volatilityOracle after 4h 1`] = `198553`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps static fee small swap with filled volatilityOracle after 8h 1`] = `208936`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps static fee small swap with filled volatilityOracle after 8h 1`] = `209487`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps static fee small swap with filled volatilityOracle after 24h 1`] = `164009`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] Filled VolatilityOracle swaps static fee small swap with filled volatilityOracle after 24h 1`] = `164560`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] Positions #burn above current price burn when only position using ticks 1`] = `117065`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] Positions #burn above current price burn when only position using ticks 1`] = `117289`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] Positions #burn above current price entire position burn but other positions are using the ticks 1`] = `110857`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] Positions #burn above current price entire position burn but other positions are using the ticks 1`] = `111137`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] Positions #burn above current price partial position burn 1`] = `115657`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] Positions #burn above current price partial position burn 1`] = `115937`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] Positions #burn around current price burn when only position using ticks 1`] = `126944`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] Positions #burn around current price burn when only position using ticks 1`] = `127168`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] Positions #burn around current price entire position burn but other positions are using the ticks 1`] = `115265`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] Positions #burn around current price entire position burn but other positions are using the ticks 1`] = `115545`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] Positions #burn around current price partial position burn 1`] = `120065`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] Positions #burn around current price partial position burn 1`] = `120345`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] Positions #burn below current price burn when only position using ticks 1`] = `126501`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] Positions #burn below current price burn when only position using ticks 1`] = `126725`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] Positions #burn below current price entire position burn but other positions are using the ticks 1`] = `111518`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] Positions #burn below current price entire position burn but other positions are using the ticks 1`] = `111798`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] Positions #burn below current price partial position burn 1`] = `116318`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] Positions #burn below current price partial position burn 1`] = `116598`; exports[`AlgebraPool gas tests [ @skip-on-coverage ] Positions #collect close to worst case 1`] = `52593`; @@ -62,82 +62,82 @@ exports[`AlgebraPool gas tests [ @skip-on-coverage ] Positions #mint below curre exports[`AlgebraPool gas tests [ @skip-on-coverage ] Positions #poke best case 1`] = `63711`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 first swap in block moves tick, no initialized crossings 1`] = `157448`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 first swap in block moves tick, no initialized crossings 1`] = `157999`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 first swap in block with no tick movement 1`] = `157417`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 first swap in block with no tick movement 1`] = `157968`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 first swap in block with no tick movement, static fee 1`] = `159202`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 first swap in block with no tick movement, static fee 1`] = `159753`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 first swap in block, large swap crossing a single initialized tick 1`] = `174008`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 first swap in block, large swap crossing a single initialized tick 1`] = `174559`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 first swap in block, large swap crossing several initialized ticks 1`] = `207558`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 first swap in block, large swap crossing several initialized ticks 1`] = `208109`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 first swap in block, large swap, no initialized crossings 1`] = `157515`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 first swap in block, large swap, no initialized crossings 1`] = `158066`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 large swap crossing several initialized ticks after some time passes 1`] = `207558`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 large swap crossing several initialized ticks after some time passes 1`] = `208109`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 large swap crossing several initialized ticks second time after some time passes 1`] = `226758`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 large swap crossing several initialized ticks second time after some time passes 1`] = `227309`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 second swap in block moves tick, no initialized crossings 1`] = `127681`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 second swap in block moves tick, no initialized crossings 1`] = `128232`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 second swap in block with no tick movement 1`] = `127645`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 second swap in block with no tick movement 1`] = `128196`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 second swap in block, large swap crossing a single initialized tick 1`] = `143426`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 second swap in block, large swap crossing a single initialized tick 1`] = `143977`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 second swap in block, large swap crossing several initialized ticks 1`] = `177797`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 second swap in block, large swap crossing several initialized ticks 1`] = `178348`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 several large swaps with pauses 1`] = `234520`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 several large swaps with pauses 1`] = `235071`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 small swap after several large swaps with pauses 1`] = `165048`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact0For1 small swap after several large swaps with pauses 1`] = `165599`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact1For0 first swap in block moves tick, no initialized crossings 1`] = `157509`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact1For0 first swap in block moves tick, no initialized crossings 1`] = `158060`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact1For0 first swap in block with no tick movement 1`] = `157457`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact1For0 first swap in block with no tick movement 1`] = `158008`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact1For0 second swap in block with no tick movement 1`] = `127706`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap #swapExact1For0 second swap in block with no tick movement 1`] = `128257`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap farming connected first swap in block moves tick, no initialized crossings 1`] = `188859`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap farming connected first swap in block moves tick, no initialized crossings 1`] = `189407`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap farming connected first swap in block with no tick movement 1`] = `188807`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap farming connected first swap in block with no tick movement 1`] = `189355`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap farming connected second swap in block with no tick movement 1`] = `141956`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is off #swap farming connected second swap in block with no tick movement 1`] = `142504`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 first swap in block moves tick, no initialized crossings 1`] = `167935`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 first swap in block moves tick, no initialized crossings 1`] = `166798`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 first swap in block with no tick movement 1`] = `167904`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 first swap in block with no tick movement 1`] = `166767`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 first swap in block with no tick movement, static fee 1`] = `159427`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 first swap in block with no tick movement, static fee 1`] = `159990`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 first swap in block, large swap crossing a single initialized tick 1`] = `184732`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 first swap in block, large swap crossing a single initialized tick 1`] = `183595`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 first swap in block, large swap crossing several initialized ticks 1`] = `218993`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 first swap in block, large swap crossing several initialized ticks 1`] = `217856`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 first swap in block, large swap, no initialized crossings 1`] = `168002`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 first swap in block, large swap, no initialized crossings 1`] = `166865`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 large swap crossing several initialized ticks after some time passes 1`] = `218993`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 large swap crossing several initialized ticks after some time passes 1`] = `217856`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 large swap crossing several initialized ticks second time after some time passes 1`] = `238193`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 large swap crossing several initialized ticks second time after some time passes 1`] = `237056`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 second swap in block moves tick, no initialized crossings 1`] = `138168`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 second swap in block moves tick, no initialized crossings 1`] = `137031`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 second swap in block with no tick movement 1`] = `138132`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 second swap in block with no tick movement 1`] = `136995`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 second swap in block, large swap crossing a single initialized tick 1`] = `154150`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 second swap in block, large swap crossing a single initialized tick 1`] = `153013`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 second swap in block, large swap crossing several initialized ticks 1`] = `189232`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 second swap in block, large swap crossing several initialized ticks 1`] = `188095`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 several large swaps with pauses 1`] = `245955`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 several large swaps with pauses 1`] = `244818`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 small swap after several large swaps with pauses 1`] = `165273`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact0For1 small swap after several large swaps with pauses 1`] = `165836`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact1For0 first swap in block moves tick, no initialized crossings 1`] = `168007`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact1For0 first swap in block moves tick, no initialized crossings 1`] = `166870`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact1For0 first swap in block with no tick movement 1`] = `167955`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact1For0 first swap in block with no tick movement 1`] = `166818`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact1For0 second swap in block with no tick movement 1`] = `138204`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap #swapExact1For0 second swap in block with no tick movement 1`] = `137067`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap farming connected first swap in block moves tick, no initialized crossings 1`] = `199357`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap farming connected first swap in block moves tick, no initialized crossings 1`] = `198217`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap farming connected first swap in block with no tick movement 1`] = `199305`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap farming connected first swap in block with no tick movement 1`] = `198165`; -exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap farming connected second swap in block with no tick movement 1`] = `152454`; +exports[`AlgebraPool gas tests [ @skip-on-coverage ] fee is on #swap farming connected second swap in block with no tick movement 1`] = `151314`; diff --git a/src/plugin/test/__snapshots__/SlidingFee.spec.ts.snap b/src/plugin/test/__snapshots__/SlidingFee.spec.ts.snap index 6efd0b687..08bddc64a 100644 --- a/src/plugin/test/__snapshots__/SlidingFee.spec.ts.snap +++ b/src/plugin/test/__snapshots__/SlidingFee.spec.ts.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`SlidingFee #getFee gas cost [ @skip-on-coverage ] gas cost of same tick 1`] = `26753`; +exports[`SlidingFee #getFee gas cost [ @skip-on-coverage ] gas cost of same tick 1`] = `26776`; -exports[`SlidingFee #getFee gas cost [ @skip-on-coverage ] gas cost of tick decrease 1`] = `31852`; +exports[`SlidingFee #getFee gas cost [ @skip-on-coverage ] gas cost of tick decrease 1`] = `31875`; -exports[`SlidingFee #getFee gas cost [ @skip-on-coverage ] gas cost of tick increase 1`] = `31738`; +exports[`SlidingFee #getFee gas cost [ @skip-on-coverage ] gas cost of tick increase 1`] = `31766`; diff --git a/src/plugin/test/shared/fixtures.ts b/src/plugin/test/shared/fixtures.ts index b6614ef1f..2e464009b 100644 --- a/src/plugin/test/shared/fixtures.ts +++ b/src/plugin/test/shared/fixtures.ts @@ -64,6 +64,18 @@ export const pluginFactoryFixture: Fixture = async functio }; }; +export const pluginFactoryFixtureV2: Fixture = async function (): Promise { + const { mockFactory } = await mockFactoryFixture(); + + const pluginFactoryFactory = await ethers.getContractFactory('BasePluginV2Factory'); + const pluginFactory = (await pluginFactoryFactory.deploy(mockFactory)) as any as BasePluginV2Factory; + + return { + pluginFactory, + mockFactory, + }; +}; + export const pluginFixtureV2: Fixture = async function (): Promise { const { mockFactory } = await mockFactoryFixture(); From 00791ade67bc5b83f6d605a5a6beec01e2fb1095 Mon Sep 17 00:00:00 2001 From: IliaAzhel Date: Tue, 8 Oct 2024 17:08:23 +0300 Subject: [PATCH 20/21] [Common] update natspecs & packages --- src/core/contracts/AlgebraCommunityVault.sol | 2 +- src/core/contracts/AlgebraFactory.sol | 2 +- src/core/contracts/AlgebraPool.sol | 2 +- src/core/contracts/AlgebraPoolDeployer.sol | 2 +- src/core/package.json | 2 +- src/farming/contracts/FarmingCenter.sol | 2 +- .../farmings/AlgebraEternalFarming.sol | 2 +- .../contracts/farmings/EternalVirtualPool.sol | 2 +- src/farming/package.json | 8 ++++---- .../contracts/AlgebraCustomPoolEntryPoint.sol | 2 +- .../contracts/NonfungiblePositionManager.sol | 2 +- src/periphery/contracts/SwapRouter.sol | 2 +- src/periphery/contracts/lens/Quoter.sol | 2 +- src/periphery/contracts/lens/QuoterV2.sol | 2 +- src/periphery/contracts/lens/TickLens.sol | 2 +- src/periphery/package.json | 4 ++-- src/plugin/contracts/AlgebraBasePluginV1.sol | 17 +++++++---------- src/plugin/contracts/AlgebraBasePluginV2.sol | 3 +-- src/plugin/contracts/BasePluginV1Factory.sol | 4 ++-- src/plugin/contracts/BasePluginV2Factory.sol | 6 +++--- src/plugin/contracts/base/BasePlugin.sol | 2 +- .../contracts/lens/AlgebraOracleV1TWAP.sol | 2 +- .../contracts/libraries/VolatilityOracle.sol | 1 - .../contracts/plugins/DynamicFeePlugin.sol | 2 +- .../contracts/plugins/FarmingProxyPlugin.sol | 2 +- .../plugins/VolatilityOraclePlugin.sol | 9 +++------ src/plugin/package.json | 6 +++--- 27 files changed, 43 insertions(+), 51 deletions(-) diff --git a/src/core/contracts/AlgebraCommunityVault.sol b/src/core/contracts/AlgebraCommunityVault.sol index a2914a682..1f1f0f8c5 100644 --- a/src/core/contracts/AlgebraCommunityVault.sol +++ b/src/core/contracts/AlgebraCommunityVault.sol @@ -10,7 +10,7 @@ import './interfaces/vault/IAlgebraCommunityVault.sol'; /// @title Algebra community fee vault /// @notice Community fee from pools is sent here, if it is enabled /// @dev Role system is used to withdraw tokens -/// @dev Version: Algebra Integral 1.1 +/// @dev Version: Algebra Integral 1.2 contract AlgebraCommunityVault is IAlgebraCommunityVault { /// @dev The role can be granted in AlgebraFactory bytes32 public constant COMMUNITY_FEE_WITHDRAWER_ROLE = keccak256('COMMUNITY_FEE_WITHDRAWER'); diff --git a/src/core/contracts/AlgebraFactory.sol b/src/core/contracts/AlgebraFactory.sol index b54650515..f01b0e605 100644 --- a/src/core/contracts/AlgebraFactory.sol +++ b/src/core/contracts/AlgebraFactory.sol @@ -17,7 +17,7 @@ import '@openzeppelin/contracts/security/ReentrancyGuard.sol'; /// @title Algebra factory /// @notice Is used to deploy pools and its plugins -/// @dev Version: Algebra Integral 1.1 +/// @dev Version: Algebra Integral 1.2 contract AlgebraFactory is IAlgebraFactory, Ownable2Step, AccessControlEnumerable, ReentrancyGuard { /// @inheritdoc IAlgebraFactory bytes32 public constant override POOLS_ADMINISTRATOR_ROLE = keccak256('POOLS_ADMINISTRATOR'); // it`s here for the public visibility of the value diff --git a/src/core/contracts/AlgebraPool.sol b/src/core/contracts/AlgebraPool.sol index a64fe7625..734ae51ae 100644 --- a/src/core/contracts/AlgebraPool.sol +++ b/src/core/contracts/AlgebraPool.sol @@ -21,7 +21,7 @@ import './interfaces/IAlgebraFactory.sol'; /// @title Algebra concentrated liquidity pool /// @notice This contract is responsible for liquidity positions, swaps and flashloans -/// @dev Version: Algebra Integral 1.1 +/// @dev Version: Algebra Integral 1.2 contract AlgebraPool is AlgebraPoolBase, TickStructure, ReentrancyGuard, Positions, SwapCalculation, ReservesManager { using SafeCast for uint256; using SafeCast for uint128; diff --git a/src/core/contracts/AlgebraPoolDeployer.sol b/src/core/contracts/AlgebraPoolDeployer.sol index 25a6ed710..449514f00 100644 --- a/src/core/contracts/AlgebraPoolDeployer.sol +++ b/src/core/contracts/AlgebraPoolDeployer.sol @@ -8,7 +8,7 @@ import './AlgebraPool.sol'; /// @title Algebra pool deployer /// @notice Is used by AlgebraFactory to deploy pools -/// @dev Version: Algebra Integral 1.1 +/// @dev Version: Algebra Integral 1.2 contract AlgebraPoolDeployer is IAlgebraPoolDeployer { /// @dev two storage slots for dense cache packing bytes32 private cache0; diff --git a/src/core/package.json b/src/core/package.json index 2a4cead3a..dcddfd481 100644 --- a/src/core/package.json +++ b/src/core/package.json @@ -5,7 +5,7 @@ "publishConfig": { "access": "public" }, - "version": "1.1.0", + "version": "1.2.0", "keywords": [ "algebra" ], diff --git a/src/farming/contracts/FarmingCenter.sol b/src/farming/contracts/FarmingCenter.sol index e5c5d9ea3..4c8634b7f 100644 --- a/src/farming/contracts/FarmingCenter.sol +++ b/src/farming/contracts/FarmingCenter.sol @@ -12,7 +12,7 @@ import '@cryptoalgebra/integral-base-plugin/contracts/interfaces/plugins/IFarmin import './interfaces/IFarmingCenter.sol'; import './libraries/IncentiveId.sol'; -/// @title Algebra Integral 1.1 main farming contract +/// @title Algebra Integral 1.2 main farming contract /// @dev Manages farmings and performs entry, exit and other actions. contract FarmingCenter is IFarmingCenter, IPositionFollower, Multicall { /// @inheritdoc IFarmingCenter diff --git a/src/farming/contracts/farmings/AlgebraEternalFarming.sol b/src/farming/contracts/farmings/AlgebraEternalFarming.sol index 0a098fe6d..284d730e7 100644 --- a/src/farming/contracts/farmings/AlgebraEternalFarming.sol +++ b/src/farming/contracts/farmings/AlgebraEternalFarming.sol @@ -23,7 +23,7 @@ import '../libraries/NFTPositionInfo.sol'; import './EternalVirtualPool.sol'; -/// @title Algebra Integral 1.1 eternal (v2-like) farming +/// @title Algebra Integral 1.2 eternal (v2-like) farming /// @notice Manages rewards and virtual pools contract AlgebraEternalFarming is IAlgebraEternalFarming { using SafeCast for int256; diff --git a/src/farming/contracts/farmings/EternalVirtualPool.sol b/src/farming/contracts/farmings/EternalVirtualPool.sol index 0b0efdf7c..9e5c7b561 100644 --- a/src/farming/contracts/farmings/EternalVirtualPool.sol +++ b/src/farming/contracts/farmings/EternalVirtualPool.sol @@ -12,7 +12,7 @@ import '@cryptoalgebra/integral-core/contracts/interfaces/pool/IAlgebraPoolError import '../base/VirtualTickStructure.sol'; -/// @title Algebra Integral 1.1 eternal virtual pool +/// @title Algebra Integral 1.2 eternal virtual pool /// @notice used to track active liquidity in farming and distribute rewards contract EternalVirtualPool is Timestamp, VirtualTickStructure { using TickManagement for mapping(int24 => TickManagement.Tick); diff --git a/src/farming/package.json b/src/farming/package.json index 149189a0d..a111a7427 100644 --- a/src/farming/package.json +++ b/src/farming/package.json @@ -2,7 +2,7 @@ "name": "@cryptoalgebra/integral-farming", "description": "Liquidity mining contracts for Algebra Integral protocol", "license": "GPL-3.0-or-later", - "version": "1.1.0", + "version": "1.2.0", "publishConfig": { "access": "public" }, @@ -24,9 +24,9 @@ ], "dependencies": { "@openzeppelin/contracts": "4.9.3", - "@cryptoalgebra/integral-core": "1.1.0", - "@cryptoalgebra/integral-periphery": "1.1.0", - "@cryptoalgebra/integral-base-plugin": "1.1.0" + "@cryptoalgebra/integral-core": "1.2.0", + "@cryptoalgebra/integral-periphery": "1.2.0", + "@cryptoalgebra/integral-base-plugin": "1.2.0" }, "devDependencies": { "@types/lodash": "^4.14.170", diff --git a/src/periphery/contracts/AlgebraCustomPoolEntryPoint.sol b/src/periphery/contracts/AlgebraCustomPoolEntryPoint.sol index b7a4c4073..d595e8a8e 100644 --- a/src/periphery/contracts/AlgebraCustomPoolEntryPoint.sol +++ b/src/periphery/contracts/AlgebraCustomPoolEntryPoint.sol @@ -7,7 +7,7 @@ import {IAlgebraFactory} from '@cryptoalgebra/integral-core/contracts/interfaces /// @title Algebra custom pool entry point /// @notice Is used to create custom pools -/// @dev Version: Algebra Integral 2.0 +/// @dev Version: Algebra Integral 1.2 contract AlgebraCustomPoolEntryPoint is IAlgebraCustomPoolEntryPoint { /// @inheritdoc IAlgebraCustomPoolEntryPoint address public immutable override factory; diff --git a/src/periphery/contracts/NonfungiblePositionManager.sol b/src/periphery/contracts/NonfungiblePositionManager.sol index 7e1de9349..407a1278a 100644 --- a/src/periphery/contracts/NonfungiblePositionManager.sol +++ b/src/periphery/contracts/NonfungiblePositionManager.sol @@ -19,7 +19,7 @@ import './base/PeripheryValidation.sol'; import './base/SelfPermit.sol'; import './base/PoolInitializer.sol'; -/// @title Algebra Integral 1.1 NFT positions +/// @title Algebra Integral 1.2 NFT positions /// @notice Wraps Algebra positions in the ERC721 non-fungible token interface /// @dev Credit to Uniswap Labs under GPL-2.0-or-later license: /// https://github.com/Uniswap/v3-periphery diff --git a/src/periphery/contracts/SwapRouter.sol b/src/periphery/contracts/SwapRouter.sol index 5bba6bdab..a6599a9be 100644 --- a/src/periphery/contracts/SwapRouter.sol +++ b/src/periphery/contracts/SwapRouter.sol @@ -15,7 +15,7 @@ import './libraries/Path.sol'; import './libraries/PoolAddress.sol'; import './libraries/CallbackValidation.sol'; -/// @title Algebra Integral 1.1 Swap Router +/// @title Algebra Integral 1.2 Swap Router /// @notice Router for stateless execution of swaps against Algebra /// @dev Credit to Uniswap Labs under GPL-2.0-or-later license: /// https://github.com/Uniswap/v3-periphery diff --git a/src/periphery/contracts/lens/Quoter.sol b/src/periphery/contracts/lens/Quoter.sol index ade42f985..0526b2aa7 100644 --- a/src/periphery/contracts/lens/Quoter.sol +++ b/src/periphery/contracts/lens/Quoter.sol @@ -14,7 +14,7 @@ import '../libraries/Path.sol'; import '../libraries/PoolAddress.sol'; import '../libraries/CallbackValidation.sol'; -/// @title Algebra Integral 1.1 Quoter +/// @title Algebra Integral 1.2 Quoter /// @notice Allows getting the expected amount out or amount in for a given swap without executing the swap /// @dev These functions are not gas efficient and should _not_ be called on chain. Instead, optimistically execute /// the swap and check the amounts in the callback. diff --git a/src/periphery/contracts/lens/QuoterV2.sol b/src/periphery/contracts/lens/QuoterV2.sol index 492c02de2..741739119 100644 --- a/src/periphery/contracts/lens/QuoterV2.sol +++ b/src/periphery/contracts/lens/QuoterV2.sol @@ -13,7 +13,7 @@ import '../libraries/PoolAddress.sol'; import '../libraries/CallbackValidation.sol'; import '../libraries/PoolTicksCounter.sol'; -/// @title Algebra Integral 1.1 QuoterV2 +/// @title Algebra Integral 1.2 QuoterV2 /// @notice Allows getting the expected amount out or amount in for a given swap without executing the swap /// @dev These functions are not gas efficient and should _not_ be called on chain. Instead, optimistically execute /// the swap and check the amounts in the callback. diff --git a/src/periphery/contracts/lens/TickLens.sol b/src/periphery/contracts/lens/TickLens.sol index e048d928f..95fe98585 100644 --- a/src/periphery/contracts/lens/TickLens.sol +++ b/src/periphery/contracts/lens/TickLens.sol @@ -6,7 +6,7 @@ import '@cryptoalgebra/integral-core/contracts/libraries/TickTree.sol'; import '../interfaces/ITickLens.sol'; -/// @title Algebra Integral 1.1 Tick Lens contract +/// @title Algebra Integral 1.2 Tick Lens contract /// @dev Credit to Uniswap Labs under GPL-2.0-or-later license: /// https://github.com/Uniswap/v3-periphery contract TickLens is ITickLens { diff --git a/src/periphery/package.json b/src/periphery/package.json index 4881786f9..781e1e7c9 100644 --- a/src/periphery/package.json +++ b/src/periphery/package.json @@ -5,7 +5,7 @@ "publishConfig": { "access": "public" }, - "version": "1.1.0", + "version": "1.2.0", "keywords": [ "algebra", "periphery" @@ -27,7 +27,7 @@ "dependencies": { "@openzeppelin/contracts": "4.9.3", "@uniswap/v2-core": "1.0.1", - "@cryptoalgebra/integral-core": "1.1.0" + "@cryptoalgebra/integral-core": "1.2.0" }, "devDependencies": { "is-svg": "^4.3.1" diff --git a/src/plugin/contracts/AlgebraBasePluginV1.sol b/src/plugin/contracts/AlgebraBasePluginV1.sol index 53d71da42..36fe6a873 100644 --- a/src/plugin/contracts/AlgebraBasePluginV1.sol +++ b/src/plugin/contracts/AlgebraBasePluginV1.sol @@ -10,17 +10,15 @@ import './plugins/FarmingProxyPlugin.sol'; import './plugins/SlidingFeePlugin.sol'; import './plugins/VolatilityOraclePlugin.sol'; -/// @title Algebra Integral 1.1 default plugin -/// @notice This contract stores timepoints and calculates adaptive fee and statistical averages +/// @title Algebra Integral 1.2 adaptive fee plugin contract AlgebraBasePluginV1 is DynamicFeePlugin, FarmingProxyPlugin, VolatilityOraclePlugin { using Plugins for uint8; /// @inheritdoc IAlgebraPlugin - uint8 public constant override defaultPluginConfig = uint8(Plugins.AFTER_INIT_FLAG | Plugins.BEFORE_SWAP_FLAG | Plugins.AFTER_SWAP_FLAG | Plugins.DYNAMIC_FEE); + uint8 public constant override defaultPluginConfig = + uint8(Plugins.AFTER_INIT_FLAG | Plugins.BEFORE_SWAP_FLAG | Plugins.AFTER_SWAP_FLAG | Plugins.DYNAMIC_FEE); - constructor(address _pool, address _factory, address _pluginFactory) BasePlugin(_pool, _factory, _pluginFactory) { - - } + constructor(address _pool, address _factory, address _pluginFactory) BasePlugin(_pool, _factory, _pluginFactory) {} // ###### HOOKS ###### @@ -72,9 +70,8 @@ contract AlgebraBasePluginV1 is DynamicFeePlugin, FarmingProxyPlugin, Volatility return IAlgebraPlugin.afterFlash.selector; } - function getCurrentFee() external view override returns(uint16 fee) { + function getCurrentFee() external view override returns (uint16 fee) { uint88 volatilityAverage = _getAverageVolatilityLast(); - fee =_getCurrentFee(volatilityAverage); - } - + fee = _getCurrentFee(volatilityAverage); + } } diff --git a/src/plugin/contracts/AlgebraBasePluginV2.sol b/src/plugin/contracts/AlgebraBasePluginV2.sol index ca4ae7726..16040b4db 100644 --- a/src/plugin/contracts/AlgebraBasePluginV2.sol +++ b/src/plugin/contracts/AlgebraBasePluginV2.sol @@ -9,8 +9,7 @@ import './plugins/FarmingProxyPlugin.sol'; import './plugins/SlidingFeePlugin.sol'; import './plugins/VolatilityOraclePlugin.sol'; -/// @title Algebra Integral 1.1 default plugin -/// @notice This contract stores timepoints and calculates adaptive fee and statistical averages +/// @title Algebra Integral 1.2 sliding fee plugin contract AlgebraBasePluginV2 is SlidingFeePlugin, FarmingProxyPlugin, VolatilityOraclePlugin { using Plugins for uint8; diff --git a/src/plugin/contracts/BasePluginV1Factory.sol b/src/plugin/contracts/BasePluginV1Factory.sol index d68331dec..7192a2e1f 100644 --- a/src/plugin/contracts/BasePluginV1Factory.sol +++ b/src/plugin/contracts/BasePluginV1Factory.sol @@ -5,8 +5,8 @@ import './interfaces/IBasePluginV1Factory.sol'; import './libraries/AdaptiveFee.sol'; import './AlgebraBasePluginV1.sol'; -/// @title Algebra Integral 1.1 default plugin factory -/// @notice This contract creates Algebra default plugins for Algebra liquidity pools +/// @title Algebra Integral 1.2 default plugin factory +/// @notice This contract creates Algebra adaptive fee plugins for Algebra liquidity pools /// @dev This plugin factory can only be used for Algebra base pools contract BasePluginV1Factory is IBasePluginV1Factory { /// @inheritdoc IBasePluginV1Factory diff --git a/src/plugin/contracts/BasePluginV2Factory.sol b/src/plugin/contracts/BasePluginV2Factory.sol index adcf07819..512d34c79 100644 --- a/src/plugin/contracts/BasePluginV2Factory.sol +++ b/src/plugin/contracts/BasePluginV2Factory.sol @@ -5,8 +5,8 @@ import './interfaces/IBasePluginV2Factory.sol'; import './AlgebraBasePluginV2.sol'; import './interfaces/plugins/ISlidingFeePlugin.sol'; -/// @title Algebra Integral 1.1 default plugin factory -/// @notice This contract creates Algebra default plugins for Algebra liquidity pools +/// @title Algebra Integral 1.2 default plugin factory +/// @notice This contract creates Algebra sliding fee plugins for Algebra liquidity pools /// @dev This plugin factory can only be used for Algebra base pools contract BasePluginV2Factory is IBasePluginV2Factory { /// @inheritdoc IBasePluginV2Factory @@ -76,4 +76,4 @@ contract BasePluginV2Factory is IBasePluginV2Factory { defaultBaseFee = newDefaultBaseFee; emit DefaultBaseFee(newDefaultBaseFee); } -} \ No newline at end of file +} diff --git a/src/plugin/contracts/base/BasePlugin.sol b/src/plugin/contracts/base/BasePlugin.sol index 3713cf7b1..1e0d63a05 100644 --- a/src/plugin/contracts/base/BasePlugin.sol +++ b/src/plugin/contracts/base/BasePlugin.sol @@ -11,7 +11,7 @@ import '@cryptoalgebra/integral-core/contracts/interfaces/IAlgebraPool.sol'; import '../interfaces/IBasePlugin.sol'; -/// @title Algebra Integral 1.1 default plugin +/// @title Algebra Integral 1.2 default plugin /// @notice This contract stores timepoints and calculates adaptive fee and statistical averages abstract contract BasePlugin is IBasePlugin, Timestamp { using Plugins for uint8; diff --git a/src/plugin/contracts/lens/AlgebraOracleV1TWAP.sol b/src/plugin/contracts/lens/AlgebraOracleV1TWAP.sol index cded7f0ef..34ee2c5c1 100644 --- a/src/plugin/contracts/lens/AlgebraOracleV1TWAP.sol +++ b/src/plugin/contracts/lens/AlgebraOracleV1TWAP.sol @@ -8,7 +8,7 @@ import './IAlgebraOracleV1TWAP.sol'; import '../libraries/integration/OracleLibrary.sol'; -/// @title Algebra Integral 1.1 base plugin V1 oracle frontend +/// @title Algebra Integral 1.2 base plugin V1 oracle frontend /// @notice Provides data from oracle corresponding pool /// @dev These functions are not very gas efficient and it is better not to use them on-chain contract AlgebraOracleV1TWAP is IAlgebraOracleV1TWAP { diff --git a/src/plugin/contracts/libraries/VolatilityOracle.sol b/src/plugin/contracts/libraries/VolatilityOracle.sol index 9abd9d6b5..d65dafeb3 100644 --- a/src/plugin/contracts/libraries/VolatilityOracle.sol +++ b/src/plugin/contracts/libraries/VolatilityOracle.sol @@ -6,7 +6,6 @@ pragma solidity =0.8.20; /// @dev Instances of stored oracle data, "timepoints", are collected in the oracle array /// Timepoints are overwritten when the full length of the timepoints array is populated. /// The most recent timepoint is available by passing 0 to getSingleTimepoint(). -/// Version for AlgebraBasePluginV1 library VolatilityOracle { /// @notice `target` timestamp is older than oldest timepoint error targetIsTooOld(); diff --git a/src/plugin/contracts/plugins/DynamicFeePlugin.sol b/src/plugin/contracts/plugins/DynamicFeePlugin.sol index 77b6ea4b9..ed8dcf679 100644 --- a/src/plugin/contracts/plugins/DynamicFeePlugin.sol +++ b/src/plugin/contracts/plugins/DynamicFeePlugin.sol @@ -13,7 +13,7 @@ import '../libraries/AdaptiveFee.sol'; import '../types/AlgebraFeeConfigurationU144.sol'; import '../base/BasePlugin.sol'; -/// @title Algebra Integral 1.1 default plugin +/// @title Algebra Integral 1.2 default plugin /// @notice This contract stores timepoints and calculates adaptive fee and statistical averages abstract contract DynamicFeePlugin is BasePlugin, IDynamicFeeManager { using Plugins for uint8; diff --git a/src/plugin/contracts/plugins/FarmingProxyPlugin.sol b/src/plugin/contracts/plugins/FarmingProxyPlugin.sol index 39bfb6d39..cab8d57f5 100644 --- a/src/plugin/contracts/plugins/FarmingProxyPlugin.sol +++ b/src/plugin/contracts/plugins/FarmingProxyPlugin.sol @@ -11,7 +11,7 @@ import '../interfaces/plugins/IFarmingPlugin.sol'; import '../base/BasePlugin.sol'; -/// @title Algebra Integral 1.1 default plugin +/// @title Algebra Integral 1.2 default plugin /// @notice This contract stores timepoints and calculates adaptive fee and statistical averages abstract contract FarmingProxyPlugin is BasePlugin, IFarmingPlugin { using Plugins for uint8; diff --git a/src/plugin/contracts/plugins/VolatilityOraclePlugin.sol b/src/plugin/contracts/plugins/VolatilityOraclePlugin.sol index f5bef6727..5afe8bc78 100644 --- a/src/plugin/contracts/plugins/VolatilityOraclePlugin.sol +++ b/src/plugin/contracts/plugins/VolatilityOraclePlugin.sol @@ -10,9 +10,9 @@ import '../interfaces/plugins/IVolatilityOracle.sol'; import '../libraries/VolatilityOracle.sol'; import '../base/BasePlugin.sol'; -/// @title Algebra Integral 1.1 default plugin +/// @title Algebra Integral 1.2 VolatilityOraclePlugin plugin /// @notice This contract stores timepoints and calculates adaptive fee and statistical averages -abstract contract VolatilityOraclePlugin is BasePlugin, IVolatilityOracle{ +abstract contract VolatilityOraclePlugin is BasePlugin, IVolatilityOracle { using Plugins for uint8; uint256 internal constant UINT16_MODULO = 65536; @@ -39,11 +39,9 @@ abstract contract VolatilityOraclePlugin is BasePlugin, IVolatilityOracle{ (uint160 price, int24 tick, , ) = _getPoolState(); require(price != 0, 'Pool is not initialized'); _initialize_TWAP(tick); - } function _initialize_TWAP(int24 tick) internal { - uint32 time = _blockTimestamp(); timepoints.initialize(time, tick); lastTimepointTimestamp = time; @@ -103,7 +101,6 @@ abstract contract VolatilityOraclePlugin is BasePlugin, IVolatilityOracle{ } function _getAverageVolatilityLast() internal view returns (uint88 volatilityAverage) { - uint32 currentTimestamp = _blockTimestamp(); (, int24 tick, , ) = _getPoolState(); @@ -113,7 +110,7 @@ abstract contract VolatilityOraclePlugin is BasePlugin, IVolatilityOracle{ volatilityAverage = timepoints.getAverageVolatility(currentTimestamp, tick, lastTimepointIndex, oldestIndex); } - function _getLastTick() internal view returns(int24 lastTick) { + function _getLastTick() internal view returns (int24 lastTick) { VolatilityOracle.Timepoint memory lastTimepoint = timepoints[timepointIndex]; return lastTimepoint.tick; } diff --git a/src/plugin/package.json b/src/plugin/package.json index 967f64871..78dce7ee8 100644 --- a/src/plugin/package.json +++ b/src/plugin/package.json @@ -5,7 +5,7 @@ "publishConfig": { "access": "public" }, - "version": "1.1.0", + "version": "1.2.0", "keywords": [ "algebra" ], @@ -14,8 +14,8 @@ "url": "https://github.com/cryptoalgebra/Algebra/" }, "dependencies": { - "@cryptoalgebra/integral-core": "1.1.0", - "@cryptoalgebra/integral-periphery": "1.1.0" + "@cryptoalgebra/integral-core": "1.2.0", + "@cryptoalgebra/integral-periphery": "1.2.0" }, "scripts": { "precommit": "pretty-quick --staged --pattern **/*.sol && hardhat compile", From 66396ab58d5ac80255a1602e0a44077676f171d1 Mon Sep 17 00:00:00 2001 From: IliaAzhel Date: Wed, 9 Oct 2024 17:25:26 +0300 Subject: [PATCH 21/21] [Common] update package-lock --- src/core/package-lock.json | 4 +- src/farming/package-lock.json | 34 ++++----- src/periphery/package-lock.json | 17 ++++- src/plugin/package-lock.json | 124 ++++++++++++++++++++++++++++++++ 4 files changed, 158 insertions(+), 21 deletions(-) create mode 100644 src/plugin/package-lock.json diff --git a/src/core/package-lock.json b/src/core/package-lock.json index 54bd07b6d..52be15bb8 100644 --- a/src/core/package-lock.json +++ b/src/core/package-lock.json @@ -1,12 +1,12 @@ { "name": "@cryptoalgebra/integral-core", - "version": "1.1.0", + "version": "1.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@cryptoalgebra/integral-core", - "version": "1.1.0", + "version": "1.2.0", "license": "GPL-2.0-or-later", "dependencies": { "@openzeppelin/contracts": "4.9.3" diff --git a/src/farming/package-lock.json b/src/farming/package-lock.json index 70589eed9..5199c8703 100644 --- a/src/farming/package-lock.json +++ b/src/farming/package-lock.json @@ -1,17 +1,17 @@ { "name": "@cryptoalgebra/integral-farming", - "version": "1.1.0", + "version": "1.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@cryptoalgebra/integral-farming", - "version": "1.1.0", + "version": "1.2.0", "license": "GPL-3.0-or-later", "dependencies": { - "@cryptoalgebra/integral-base-plugin": "1.1.0", - "@cryptoalgebra/integral-core": "1.1.0", - "@cryptoalgebra/integral-periphery": "1.1.0", + "@cryptoalgebra/integral-base-plugin": "1.2.0", + "@cryptoalgebra/integral-core": "1.2.0", + "@cryptoalgebra/integral-periphery": "1.2.0", "@openzeppelin/contracts": "4.9.3" }, "devDependencies": { @@ -24,12 +24,12 @@ } }, "node_modules/@cryptoalgebra/integral-base-plugin": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@cryptoalgebra/integral-base-plugin/-/integral-base-plugin-1.1.0.tgz", - "integrity": "sha512-fWpdTjIf1VFLB+qQSU9TP7BdgWrRjlxIBVYEN4I8BtfIIwr8Ay3QrYYsUvI2FFdN2I5HroZLanmgYS0+IjXLDg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@cryptoalgebra/integral-base-plugin/-/integral-base-plugin-1.2.0.tgz", + "integrity": "sha512-sgP6POAGgQj3AWLdun4VsoOKVx4S9Zr4dfEOkLZYeqKzqI/KRhMdodt+wqd+mwcocXXuIh9e3JmF0rm1db7mqA==", "dependencies": { - "@cryptoalgebra/integral-core": "1.1.0", - "@cryptoalgebra/integral-periphery": "1.1.0" + "@cryptoalgebra/integral-core": "1.2.0", + "@cryptoalgebra/integral-periphery": "1.2.0" }, "engines": { "node": ">=16.0.0", @@ -37,9 +37,9 @@ } }, "node_modules/@cryptoalgebra/integral-core": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@cryptoalgebra/integral-core/-/integral-core-1.1.0.tgz", - "integrity": "sha512-SESM8dIrNd2vkLsiYDBZxewmsz4ZyhfuVIe2SNTCCayl14W2dUbZ1y4qWRqSZ6JPKIFOHdrdLoNk+fFIC7Ukvg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@cryptoalgebra/integral-core/-/integral-core-1.2.0.tgz", + "integrity": "sha512-BKi/iOj3QflrBOeV5slpqXszpkNEirEd1qXwJxx4bNuTu9lih3aSorvhoNdgg6Jr5wlDuZrw/JrGvOIj4OqhbQ==", "dependencies": { "@openzeppelin/contracts": "4.9.3" }, @@ -49,11 +49,11 @@ } }, "node_modules/@cryptoalgebra/integral-periphery": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@cryptoalgebra/integral-periphery/-/integral-periphery-1.1.0.tgz", - "integrity": "sha512-Z+/FVtucH2GeoNuMcf+3Z859fNIOBNJTHSynu+UVV9NtMia9c7olEzcaPpyFkE7kynQgUcc+KffgE7KFDS0H+Q==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@cryptoalgebra/integral-periphery/-/integral-periphery-1.2.0.tgz", + "integrity": "sha512-gH3qLqoDHeq3qf0Pe277v7ei1712wGsYvW73KplNOtXtsPBmh4T3L6ZvL0DokZNvf676xdhyqo2zFp8Ip6ojNg==", "dependencies": { - "@cryptoalgebra/integral-core": "1.1.0", + "@cryptoalgebra/integral-core": "1.2.0", "@openzeppelin/contracts": "4.9.3", "@uniswap/v2-core": "1.0.1" }, diff --git a/src/periphery/package-lock.json b/src/periphery/package-lock.json index 0e3b96d55..4cdedd32b 100644 --- a/src/periphery/package-lock.json +++ b/src/periphery/package-lock.json @@ -1,14 +1,15 @@ { "name": "@cryptoalgebra/integral-periphery", - "version": "1.1.0", + "version": "1.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@cryptoalgebra/integral-periphery", - "version": "1.1.0", + "version": "1.2.0", "license": "GPL-2.0-or-later", "dependencies": { + "@cryptoalgebra/integral-core": "1.2.0", "@openzeppelin/contracts": "4.9.3", "@uniswap/v2-core": "1.0.1" }, @@ -20,6 +21,18 @@ "npm": ">=8.0.0" } }, + "node_modules/@cryptoalgebra/integral-core": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@cryptoalgebra/integral-core/-/integral-core-1.2.0.tgz", + "integrity": "sha512-BKi/iOj3QflrBOeV5slpqXszpkNEirEd1qXwJxx4bNuTu9lih3aSorvhoNdgg6Jr5wlDuZrw/JrGvOIj4OqhbQ==", + "dependencies": { + "@openzeppelin/contracts": "4.9.3" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + } + }, "node_modules/@openzeppelin/contracts": { "version": "4.9.3", "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.9.3.tgz", diff --git a/src/plugin/package-lock.json b/src/plugin/package-lock.json new file mode 100644 index 000000000..02015ec7c --- /dev/null +++ b/src/plugin/package-lock.json @@ -0,0 +1,124 @@ +{ + "name": "@cryptoalgebra/integral-base-plugin", + "version": "1.2.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@cryptoalgebra/integral-base-plugin", + "version": "1.2.0", + "license": "GPL-2.0-or-later", + "dependencies": { + "@cryptoalgebra/integral-core": "1.2.0", + "@cryptoalgebra/integral-periphery": "1.2.0" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + } + }, + "../core": { + "name": "@cryptoalgebra/integral-core", + "version": "1.2.0", + "license": "GPL-2.0-or-later", + "dependencies": { + "@openzeppelin/contracts": "4.9.3" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + } + }, + "../core/node_modules/@openzeppelin/contracts": { + "version": "4.9.3", + "license": "MIT" + }, + "../periphery": { + "name": "@cryptoalgebra/integral-periphery", + "version": "1.2.0", + "license": "GPL-2.0-or-later", + "dependencies": { + "@cryptoalgebra/integral-core": "1.2.0", + "@openzeppelin/contracts": "4.9.3", + "@uniswap/v2-core": "1.0.1" + }, + "devDependencies": { + "is-svg": "^4.3.1" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + } + }, + "../periphery/node_modules/@cryptoalgebra/integral-core": { + "version": "1.2.0", + "license": "GPL-2.0-or-later", + "dependencies": { + "@openzeppelin/contracts": "4.9.3" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + } + }, + "../periphery/node_modules/@openzeppelin/contracts": { + "version": "4.9.3", + "license": "MIT" + }, + "../periphery/node_modules/@uniswap/v2-core": { + "version": "1.0.1", + "license": "GPL-3.0-or-later", + "engines": { + "node": ">=10" + } + }, + "../periphery/node_modules/fast-xml-parser": { + "version": "4.3.4", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "../periphery/node_modules/is-svg": { + "version": "4.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-xml-parser": "^4.1.3" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "../periphery/node_modules/strnum": { + "version": "1.0.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@cryptoalgebra/integral-core": { + "resolved": "../core", + "link": true + }, + "node_modules/@cryptoalgebra/integral-periphery": { + "resolved": "../periphery", + "link": true + } + } +}