Skip to content

Commit

Permalink
Merge branch 'custom-pools'
Browse files Browse the repository at this point in the history
  • Loading branch information
IliaAzhel committed May 22, 2024
2 parents 80234bb + b842a81 commit ed8ed95
Show file tree
Hide file tree
Showing 64 changed files with 1,353 additions and 862 deletions.
726 changes: 166 additions & 560 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"eslint": "^8.48.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-unused-imports": "^3.0.0",
"hardhat": "2.19.5",
"hardhat": "^2.22.3",
"hardhat-contract-sizer": "^2.8.0",
"hardhat-dependency-compiler": "1.1.3",
"hardhat-output-validator": "^0.1.19",
Expand Down Expand Up @@ -47,6 +47,6 @@
},
"engines": {
"npm": ">=8.0.0",
"node": ">=14.0.0"
"node": ">=16.0.0"
}
}
2 changes: 1 addition & 1 deletion src/core/contracts/AlgebraCommunityVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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.0
/// @dev Version: Algebra Integral 1.1
contract AlgebraCommunityVault is IAlgebraCommunityVault {
/// @dev The role can be granted in AlgebraFactory
bytes32 public constant COMMUNITY_FEE_WITHDRAWER_ROLE = keccak256('COMMUNITY_FEE_WITHDRAWER');
Expand Down
85 changes: 65 additions & 20 deletions src/core/contracts/AlgebraFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity =0.8.20;
import './libraries/Constants.sol';

import './interfaces/IAlgebraFactory.sol';
import './interfaces/IAlgebraPool.sol';
import './interfaces/IAlgebraPoolDeployer.sol';
import './interfaces/vault/IAlgebraVaultFactory.sol';
import './interfaces/plugin/IAlgebraPluginFactory.sol';
Expand All @@ -12,14 +13,18 @@ import './AlgebraCommunityVault.sol';

import '@openzeppelin/contracts/access/Ownable2Step.sol';
import '@openzeppelin/contracts/access/AccessControlEnumerable.sol';
import '@openzeppelin/contracts/security/ReentrancyGuard.sol';

/// @title Algebra factory
/// @notice Is used to deploy pools and its plugins
/// @dev Version: Algebra Integral 1.0
contract AlgebraFactory is IAlgebraFactory, Ownable2Step, AccessControlEnumerable {
/// @dev Version: Algebra Integral 1.1
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

/// @inheritdoc IAlgebraFactory
bytes32 public constant override CUSTOM_POOL_DEPLOYER = keccak256('CUSTOM_POOL_DEPLOYER');

/// @inheritdoc IAlgebraFactory
address public immutable override poolDeployer;

Expand Down Expand Up @@ -47,9 +52,12 @@ contract AlgebraFactory is IAlgebraFactory, Ownable2Step, AccessControlEnumerabl
/// @inheritdoc IAlgebraFactory
mapping(address => mapping(address => address)) public override poolByPair;

/// @inheritdoc IAlgebraFactory
mapping(address => mapping(address => mapping(address => address))) public override customPoolByPair;

/// @inheritdoc IAlgebraFactory
/// @dev keccak256 of AlgebraPool init bytecode. Used to compute pool address deterministically
bytes32 public constant POOL_INIT_CODE_HASH = 0xf96d2474815c32e070cd63233f06af5413efc5dcb430aee4ff18cc29007c562d;
bytes32 public constant POOL_INIT_CODE_HASH = 0x4b9e4a8044ce5695e06fce9421a63b6f5c3db8a561eebb30ea4c775469e36eaf;

constructor(address _poolDeployer) {
require(_poolDeployer != address(0));
Expand All @@ -72,13 +80,8 @@ contract AlgebraFactory is IAlgebraFactory, Ownable2Step, AccessControlEnumerabl
}

/// @inheritdoc IAlgebraFactory
function defaultConfigurationForPool(
address pool
) external view override returns (uint16 communityFee, int24 tickSpacing, uint16 fee, address communityVault) {
if (address(vaultFactory) != address(0)) {
communityVault = vaultFactory.getVaultForPool(pool);
}
return (defaultCommunityFee, defaultTickspacing, defaultFee, communityVault);
function defaultConfigurationForPool() external view override returns (uint16 communityFee, int24 tickSpacing, uint16 fee) {
return (defaultCommunityFee, defaultTickspacing, defaultFee);
}

/// @inheritdoc IAlgebraFactory
Expand All @@ -87,25 +90,67 @@ contract AlgebraFactory is IAlgebraFactory, Ownable2Step, AccessControlEnumerabl
}

/// @inheritdoc IAlgebraFactory
function createPool(address tokenA, address tokenB) external override returns (address pool) {
function computeCustomPoolAddress(address deployer, address token0, address token1) public view override returns (address customPool) {
customPool = address(
uint160(uint256(keccak256(abi.encodePacked(hex'ff', poolDeployer, keccak256(abi.encode(deployer, token0, token1)), POOL_INIT_CODE_HASH))))
);
}

/// @inheritdoc IAlgebraFactory
function createPool(address tokenA, address tokenB) external override nonReentrant returns (address pool) {
return _createPool(address(0), msg.sender, tokenA, tokenB, '');
}

/// @inheritdoc IAlgebraFactory
function createCustomPool(
address deployer,
address creator,
address tokenA,
address tokenB,
bytes calldata data
) external override nonReentrant returns (address customPool) {
require(hasRole(CUSTOM_POOL_DEPLOYER, msg.sender), 'Can`t create custom pools');
return _createPool(deployer, creator, tokenA, tokenB, data);
}

function _createPool(address deployer, address creator, address tokenA, address tokenB, bytes memory data) private returns (address pool) {
require(tokenA != tokenB);
(address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
require(token0 != address(0));
require(poolByPair[token0][token1] == address(0));

address defaultPlugin;
if (address(defaultPluginFactory) != address(0)) {
defaultPlugin = defaultPluginFactory.createPlugin(computePoolAddress(token0, token1), token0, token1);
mapping(address => mapping(address => address)) storage _poolByPair = deployer == address(0) ? poolByPair : customPoolByPair[deployer];
require(_poolByPair[token0][token1] == address(0));

address plugin;
if (deployer == address(0)) {
if (address(defaultPluginFactory) != address(0)) {
plugin = defaultPluginFactory.beforeCreatePoolHook(computePoolAddress(token0, token1), creator, address(0), token0, token1, '');
}
} else {
plugin = IAlgebraPluginFactory(msg.sender).beforeCreatePoolHook(
computeCustomPoolAddress(deployer, token0, token1),
creator,
deployer,
token0,
token1,
data
);
}

pool = IAlgebraPoolDeployer(poolDeployer).deploy(defaultPlugin, token0, token1);
pool = IAlgebraPoolDeployer(poolDeployer).deploy(plugin, token0, token1, deployer);

poolByPair[token0][token1] = pool; // to avoid future addresses comparison we are populating the mapping twice
poolByPair[token1][token0] = pool;
emit Pool(token0, token1, pool);
_poolByPair[token0][token1] = pool;
_poolByPair[token1][token0] = pool;

if (deployer == address(0)) {
emit Pool(token0, token1, pool);
} else {
emit CustomPool(deployer, token0, token1, pool);
}

if (address(vaultFactory) != address(0)) {
vaultFactory.createVaultForPool(pool);
address vault = vaultFactory.createVaultForPool(pool, creator, deployer, token0, token1);
IAlgebraPool(pool).setCommunityVault(vault);
}
}

Expand Down
52 changes: 37 additions & 15 deletions src/core/contracts/AlgebraPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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.0
/// @dev Version: Algebra Integral 1.1
contract AlgebraPool is AlgebraPoolBase, TickStructure, ReentrancyGuard, Positions, SwapCalculation, ReservesManager {
using SafeCast for uint256;
using SafeCast for uint128;
Expand All @@ -33,26 +33,21 @@ contract AlgebraPool is AlgebraPoolBase, TickStructure, ReentrancyGuard, Positio
int24 tick = TickMath.getTickAtSqrtRatio(initialPrice); // getTickAtSqrtRatio checks validity of initialPrice inside
if (globalState.price != 0) revert alreadyInitialized(); // after initialization, the price can never become zero
globalState.price = initialPrice;
globalState.tick = tick;
emit Initialize(initialPrice, tick);

if (plugin != address(0)) {
IAlgebraPlugin(plugin).beforeInitialize(msg.sender, initialPrice).shouldReturn(IAlgebraPlugin.beforeInitialize.selector);
}

(uint16 _communityFee, int24 _tickSpacing, uint16 _fee, address _communityVault) = _getDefaultConfiguration();

uint8 pluginConfig = globalState.pluginConfig;
globalState.tick = tick;
globalState.communityFee = _communityFee;

emit Initialize(initialPrice, tick);
(uint16 _communityFee, int24 _tickSpacing, uint16 _fee) = _getDefaultConfiguration();

_setFee(_fee);
_setTickSpacing(_tickSpacing);
_setCommunityFeeVault(_communityVault);
if (_communityFee != 0 && _communityVault == address(0)) revert invalidNewCommunityFee(); // the pool should not accumulate a community fee without a vault
if (_communityFee != 0 && communityVault == address(0)) revert invalidNewCommunityFee(); // the pool should not accumulate a community fee without a vault
_setCommunityFee(_communityFee);

if (pluginConfig.hasFlag(Plugins.AFTER_INIT_FLAG)) {
if (globalState.pluginConfig.hasFlag(Plugins.AFTER_INIT_FLAG)) {
IAlgebraPlugin(plugin).afterInitialize(msg.sender, initialPrice, tick).shouldReturn(IAlgebraPlugin.afterInitialize.selector);
}
}
Expand Down Expand Up @@ -242,7 +237,7 @@ contract AlgebraPool is AlgebraPoolBase, TickStructure, ReentrancyGuard, Positio
_changeReserves(amount0, amount1, 0, communityFee); // reflect reserve change and pay communityFee
}

emit Swap(msg.sender, recipient, amount0, amount1, currentPrice, currentLiquidity, currentTick);
_emitSwapEvent(recipient, amount0, amount1, currentPrice, currentLiquidity, currentTick);
}

_unlock();
Expand Down Expand Up @@ -310,12 +305,17 @@ contract AlgebraPool is AlgebraPoolBase, TickStructure, ReentrancyGuard, Positio
}
}

emit Swap(msg.sender, recipient, amount0, amount1, currentPrice, currentLiquidity, currentTick);
_emitSwapEvent(recipient, amount0, amount1, currentPrice, currentLiquidity, currentTick);

_unlock();
_afterSwap(recipient, zeroToOne, amountToSell, limitSqrtPrice, amount0, amount1, data);
}

/// @dev internal function to reduce bytecode size
function _emitSwapEvent(address recipient, int256 amount0, int256 amount1, uint160 newPrice, uint128 newLiquidity, int24 newTick) private {
emit Swap(msg.sender, recipient, amount0, amount1, newPrice, newLiquidity, newTick);
}

function _beforeSwap(address recipient, bool zto, int256 amount, uint160 limitPrice, bool payInAdvance, bytes calldata data) internal {
if (globalState.pluginConfig.hasFlag(Plugins.BEFORE_SWAP_FLAG)) {
IAlgebraPlugin(plugin).beforeSwap(msg.sender, recipient, zto, amount, limitPrice, payInAdvance, data).shouldReturn(
Expand Down Expand Up @@ -428,15 +428,16 @@ contract AlgebraPool is AlgebraPoolBase, TickStructure, ReentrancyGuard, Positio

/// @inheritdoc IAlgebraPoolPermissionedActions
function setCommunityVault(address newCommunityVault) external override onlyUnlocked {
_checkIfAdministrator();
// factory is allowed to set initial vault
if (msg.sender != factory) _checkIfAdministrator();
if (newCommunityVault == address(0) && globalState.communityFee != 0) _setCommunityFee(0); // the pool should not accumulate a community fee without a vault
_setCommunityFeeVault(newCommunityVault); // accumulated but not yet sent to the vault community fees once will be sent to the `newCommunityVault` address
}

/// @inheritdoc IAlgebraPoolPermissionedActions
function setFee(uint16 newFee) external override {
bool isDynamicFeeEnabled = globalState.pluginConfig.hasFlag(Plugins.DYNAMIC_FEE);
if (!globalState.unlocked) revert IAlgebraPoolErrors.locked(); // cheaper to check lock here
if (!globalState.unlocked) revert locked(); // cheaper to check lock here

if (msg.sender == plugin) {
if (!isDynamicFeeEnabled) revert dynamicFeeDisabled();
Expand All @@ -446,4 +447,25 @@ contract AlgebraPool is AlgebraPoolBase, TickStructure, ReentrancyGuard, Positio
}
_setFee(newFee);
}

/// @dev using function to save bytecode
function _checkIfPlugin() private view {
if (msg.sender != plugin) revert notAllowed();
}

/// @inheritdoc IAlgebraPoolPermissionedActions
function sync() external override {
_checkIfPlugin();
_lock();
_updateReserves();
_unlock();
}

/// @inheritdoc IAlgebraPoolPermissionedActions
function skim() external override {
_checkIfPlugin();
_lock();
_skimReserves(msg.sender);
_unlock();
}
}
12 changes: 9 additions & 3 deletions src/core/contracts/AlgebraPoolDeployer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import './AlgebraPool.sol';

/// @title Algebra pool deployer
/// @notice Is used by AlgebraFactory to deploy pools
/// @dev Version: Algebra Integral 1.0
/// @dev Version: Algebra Integral 1.1
contract AlgebraPoolDeployer is IAlgebraPoolDeployer {
/// @dev two storage slots for dense cache packing
bytes32 private cache0;
Expand All @@ -28,11 +28,17 @@ contract AlgebraPoolDeployer is IAlgebraPoolDeployer {
}

/// @inheritdoc IAlgebraPoolDeployer
function deploy(address plugin, address token0, address token1) external override returns (address pool) {
function deploy(address plugin, address token0, address token1, address deployer) external override returns (address pool) {
require(msg.sender == factory);

_writeToCache(plugin, token0, token1);
pool = address(new AlgebraPool{salt: keccak256(abi.encode(token0, token1))}());
bytes memory _encodedParams;
if (deployer == address(0)) {
_encodedParams = abi.encode(token0, token1);
} else {
_encodedParams = abi.encode(deployer, token0, token1);
}
pool = address(new AlgebraPool{salt: keccak256(_encodedParams)}());
(cache0, cache1) = (bytes32(0), bytes32(0));
}

Expand Down
2 changes: 1 addition & 1 deletion src/core/contracts/AlgebraVaultFactoryStub.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ contract AlgebraVaultFactoryStub is IAlgebraVaultFactory {
}

/// @inheritdoc IAlgebraVaultFactory
function createVaultForPool(address) external view override returns (address) {
function createVaultForPool(address, address, address, address, address) external view override returns (address) {
return defaultAlgebraCommunityVault;
}
}
4 changes: 2 additions & 2 deletions src/core/contracts/base/AlgebraPoolBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@ abstract contract AlgebraPoolBase is IAlgebraPool, Timestamp {
}

/// @dev Gets the default settings for pool initialization. Can be overridden in tests
function _getDefaultConfiguration() internal virtual returns (uint16, int24, uint16, address) {
return IAlgebraFactory(factory).defaultConfigurationForPool(address(this));
function _getDefaultConfiguration() internal virtual returns (uint16, int24, uint16) {
return IAlgebraFactory(factory).defaultConfigurationForPool();
}

// The main external calls that are used by the pool. Can be overridden in tests
Expand Down
12 changes: 12 additions & 0 deletions src/core/contracts/base/ReservesManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,23 @@ abstract contract ReservesManager is AlgebraPoolBase {
unchecked {
if (hasExcessToken0) totalFeeGrowth0Token += FullMath.mulDiv(balance0 - _reserve0, Constants.Q128, _liquidity);
if (hasExcessToken1) totalFeeGrowth1Token += FullMath.mulDiv(balance1 - _reserve1, Constants.Q128, _liquidity);
emit ExcessTokens(balance0 - _reserve0, balance1 - _reserve1);
(reserve0, reserve1) = (uint128(balance0), uint128(balance1));
}
}
}

/// @notice Forces reserves to match balances. Excess of tokens will be sent to `receiver`
function _skimReserves(address receiver) internal {
(uint256 balance0, uint256 balance1) = (_balanceToken0(), _balanceToken1());
(uint128 _reserve0, uint128 _reserve1) = (reserve0, reserve1);
if (balance0 > _reserve0 || balance1 > _reserve1) {
if (balance0 > _reserve0) _transfer(token0, receiver, balance0 - _reserve0);
if (balance1 > _reserve1) _transfer(token1, receiver, balance1 - _reserve1);
emit Skim(receiver, balance0 - _reserve0, balance1 - _reserve1);
}
}

/// @notice Applies deltas to reserves and pays communityFees
/// @dev Community fee is sent to the vault at a specified frequency or when variables communityFeePending{0,1} overflow
/// @param deltaR0 Amount of token0 to add/subtract to/from reserve0, must not exceed uint128
Expand Down
Loading

0 comments on commit ed8ed95

Please sign in to comment.