Skip to content

Commit

Permalink
BasePluginV1Factory
Browse files Browse the repository at this point in the history
  • Loading branch information
fourlen committed May 20, 2024
1 parent f2a0b78 commit 1234198
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ interface IAlgebraPluginFactory {
/// @param token0 First token of the pool
/// @param token1 Second token of the pool
/// @return New plugin address
function createPlugin(address pool, address token0, address token1) external returns (address, address[] memory);
function createPlugin(address pool, address token0, address token1) external returns (address);
}
13 changes: 6 additions & 7 deletions src/plugin/contracts/BasePluginV1Factory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,13 @@ contract BasePluginV1Factory is IBasePluginV1Factory {
}

/// @inheritdoc IAlgebraPluginFactory
function createPlugin(address pool, address, address) external override returns (address, address[] memory) {
function createPlugin(address pool, address, address) external override returns (address) {
require(msg.sender == algebraFactory);
return _createPlugin(pool);
}

/// @inheritdoc IBasePluginV1Factory
function createPluginForExistingPool(address token0, address token1) external override returns (address, address[] memory) {
function createPluginForExistingPool(address token0, address token1) external override returns (address) {
IAlgebraFactory factory = IAlgebraFactory(algebraFactory);
require(factory.hasRoleOrOwner(factory.POOLS_ADMINISTRATOR_ROLE(), msg.sender));

Expand All @@ -72,7 +72,7 @@ contract BasePluginV1Factory is IBasePluginV1Factory {
return _createPlugin(pool);
}

function _createPlugin(address pool) internal returns (address, address[] memory) {
function _createPlugin(address pool) internal returns (address) {
require(pluginByPool[pool] == address(0), 'Already created');
// IAlgebraBasePluginV1 volatilityOracle = new AlgebraBasePluginV1(pool, algebraFactory, address(this));
// volatilityOracle.changeFeeConfiguration(defaultFeeConfiguration);
Expand All @@ -82,22 +82,21 @@ contract BasePluginV1Factory is IBasePluginV1Factory {
IAlgebraPool(pool).setPluginConfig(uint8(Plugins.DYNAMIC_FEE));

AlgebraModularHub modularHub = new AlgebraModularHub(pool, algebraFactory);
address[] memory modulesAddresses = new address[](factoriesCounter);

IAlgebraPool(pool).setPlugin(address(modularHub));

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

modulesAddresses[i] = moduleAddress;

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

modularHub.insertModulesToHookLists(insertModuleParams);
}

pluginByPool[pool] = address(modularHub);
return (address(modularHub), modulesAddresses);
return address(modularHub);
}

/// @inheritdoc IBasePluginV1Factory
Expand Down
2 changes: 1 addition & 1 deletion src/plugin/contracts/interfaces/IBasePluginV1Factory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ interface IBasePluginV1Factory is IAlgebraPluginFactory {
/// @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, address[] memory);
function createPluginForExistingPool(address token0, address token1) external returns (address);

/// @notice Changes initial fee configuration for new pools
/// @dev changes coefficients for sigmoids: α / (1 + e^( (β-x) / γ))
Expand Down
15 changes: 15 additions & 0 deletions src/plugin/contracts/test/MockFactory.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.20;

import '../BasePluginV1Factory.sol';
import './MockPool.sol';

/// @title Mock of Algebra factory for plugins testing
contract MockFactory {
bytes32 public constant POOLS_ADMINISTRATOR_ROLE = keccak256('POOLS_ADMINISTRATOR');
Expand All @@ -27,6 +30,18 @@ contract MockFactory {
hasRole[account][role] = false;
}

function createPlugin(address pluginFactory, address pool) external returns (address) {
return BasePluginV1Factory(pluginFactory).createPlugin(pool, address(0), address(0));
}

function createPool(address tokenA, address tokenB) external returns (address) {
(address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);

MockPool pool = new MockPool{salt: keccak256(abi.encode(token0, token1))}();
pool.setOwner(msg.sender);
return address(pool);
}

function stubPool(address token0, address token1, address pool) public {
poolByPair[token0][token1] = pool;
poolByPair[token1][token0] = pool;
Expand Down
4 changes: 4 additions & 0 deletions src/plugin/contracts/test/MockPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,10 @@ contract MockPool is IAlgebraPoolActions, IAlgebraPoolPermissionedActions, IAlge
tickSpacing = newTickSpacing;
}

function setOwner(address newOwner) external {
owner = newOwner;
}

function setPluginFactory(address newPluginFactory) external {
require(msg.sender == owner);
pluginFactory = newPluginFactory;
Expand Down
12 changes: 4 additions & 8 deletions src/plugin/contracts/test/MockTimeDSFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ contract MockTimeDSFactory is IBasePluginV1Factory {
}

/// @inheritdoc IAlgebraPluginFactory
function createPlugin(address pool, address, address) external override returns (address, address[] memory) {
function createPlugin(address pool, address, address) external override returns (address) {
return _createPlugin(pool);
}

function createPluginForExistingPool(address token0, address token1) external override returns (address, address[] memory) {
function createPluginForExistingPool(address token0, address token1) external override returns (address) {
IAlgebraFactory factory = IAlgebraFactory(algebraFactory);
require(factory.hasRoleOrOwner(factory.POOLS_ADMINISTRATOR_ROLE(), msg.sender));

Expand All @@ -58,30 +58,26 @@ contract MockTimeDSFactory is IBasePluginV1Factory {
pluginByPool[pool] = plugin;
}

function _createPlugin(address pool) internal returns (address, address[] memory) {
function _createPlugin(address pool) internal returns (address) {
require(pluginByPool[pool] == address(0), 'Already created');
AlgebraModularHub modularHub = new AlgebraModularHub(pool, algebraFactory);
// console.log("modular hub: ", address(modularHub));

MockPool(pool).setPlugin(address(modularHub));
MockPool(pool).setPluginConfig(uint8(Plugins.DYNAMIC_FEE));

address[] memory modulesAddresses = new address[](factoriesCounter);

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

modulesAddresses[i] = moduleAddress;

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

modularHub.insertModulesToHookLists(insertModuleParams);
}

pluginByPool[pool] = address(modularHub);
return (address(modularHub), modulesAddresses);
return address(modularHub);
}

/// @inheritdoc IBasePluginV1Factory
Expand Down
94 changes: 73 additions & 21 deletions src/plugin/test/BasePluginV1Factory.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,25 @@ import { loadFixture } from '@nomicfoundation/hardhat-network-helpers';
import { expect } from './shared/expect';
import { ZERO_ADDRESS, pluginFactoryFixture } from './shared/fixtures';

import { BasePluginV1Factory, AlgebraBasePluginV1, MockFactory } from '../typechain';
import { BasePluginV1Factory, MockFactory, FarmingModuleFactory, MockTimeDynamicFeeModuleFactory, MockTimeOracleModuleFactory, MockPool, AlgebraModularHub, DynamicFeeModule } from '../typechain';

describe('BasePluginV1Factory', () => {
let wallet: Wallet, other: Wallet;

let pluginFactory: BasePluginV1Factory;
// let mockPool: MockPool;
let mockOracleModuleFactory: MockTimeOracleModuleFactory;
let mockDynamicFeeModuleFactory: MockTimeDynamicFeeModuleFactory;
let farmingModuleFactory: FarmingModuleFactory;
let mockAlgebraFactory: MockFactory;


before('prepare signers', async () => {
[wallet, other] = await (ethers as any).getSigners();
});

beforeEach('deploy test volatilityOracle', async () => {
({ pluginFactory, mockFactory: mockAlgebraFactory } = await loadFixture(pluginFactoryFixture));
({ pluginFactory, mockOracleModuleFactory, mockDynamicFeeModuleFactory, farmingModuleFactory, mockFactory: mockAlgebraFactory } = await loadFixture(pluginFactoryFixture));
});

describe('#Create plugin', () => {
Expand All @@ -27,13 +32,26 @@ describe('BasePluginV1Factory', () => {

it('factory can create plugin', async () => {
const pluginFactoryFactory = await ethers.getContractFactory('BasePluginV1Factory');
const pluginFactoryMock = (await pluginFactoryFactory.deploy(wallet.address)) as any as BasePluginV1Factory;
const pluginFactoryMock = (await pluginFactoryFactory.deploy(mockAlgebraFactory, mockDynamicFeeModuleFactory, farmingModuleFactory, mockOracleModuleFactory)) as any as BasePluginV1Factory;

const mockPoolFactory = await ethers.getContractFactory('MockPool');
const mockPool = (await mockPoolFactory.deploy()) as any as MockPool;

await mockPool.setPluginFactory(pluginFactoryMock);

await mockAlgebraFactory.grantRole(ethers.keccak256(ethers.toUtf8Bytes("POOLS_ADMINISTRATOR")), pluginFactoryMock);
const pluginAddress = await mockAlgebraFactory.createPlugin.staticCall(pluginFactoryMock, mockPool);
await mockAlgebraFactory.createPlugin(pluginFactoryMock, mockPool);

const pluginMock = (await ethers.getContractFactory('AlgebraModularHub')).attach(pluginAddress) as any as AlgebraModularHub;

const pluginAddress = await pluginFactoryMock.createPlugin.staticCall(wallet.address, ZERO_ADDRESS, ZERO_ADDRESS);
await pluginFactoryMock.createPlugin(wallet.address, ZERO_ADDRESS, ZERO_ADDRESS);
// plugin indexing inside modular hub starts from 1
const dynamicFeeModuleAddress = await pluginMock.modules(2);

const DynamicFeeModule_ethers = await ethers.getContractFactory('DynamicFeeModule');
const dynamicFeeModule = DynamicFeeModule_ethers.attach(dynamicFeeModuleAddress) as any as DynamicFeeModule;

const pluginMock = (await ethers.getContractFactory('AlgebraBasePluginV1')).attach(pluginAddress) as any as AlgebraBasePluginV1;
const feeConfig = await pluginMock.feeConfig();
const feeConfig = await dynamicFeeModule.feeConfig();
expect(feeConfig.baseFee).to.be.not.eq(0);
});
});
Expand All @@ -48,22 +66,56 @@ describe('BasePluginV1Factory', () => {
});

it('can create for existing pool', async () => {
await mockAlgebraFactory.stubPool(wallet.address, other.address, other.address);
const tokenFactory = await ethers.getContractFactory('TestERC20');
const token0 = await tokenFactory.deploy(1337);
const token1 = await tokenFactory.deploy(1337);

const poolAddress = await mockAlgebraFactory.createPool.staticCall(token0, token1);
await mockAlgebraFactory.createPool(token0, token1)

const mockPoolFactory = await ethers.getContractFactory('MockPool');
const mockPool = (await mockPoolFactory.attach(poolAddress)) as any as MockPool;

await mockPool.setPluginFactory(pluginFactory);

await mockAlgebraFactory.stubPool(token0, token1, mockPool);

await pluginFactory.createPluginForExistingPool(wallet.address, other.address);
const pluginAddress = await pluginFactory.pluginByPool(other.address);
await mockAlgebraFactory.grantRole(ethers.keccak256(ethers.toUtf8Bytes("POOLS_ADMINISTRATOR")), pluginFactory);

await pluginFactory.createPluginForExistingPool(token0, token1);
const pluginAddress = await pluginFactory.pluginByPool(mockPool);
expect(pluginAddress).to.not.be.eq(ZERO_ADDRESS);
const pluginMock = (await ethers.getContractFactory('AlgebraBasePluginV1')).attach(pluginAddress) as any as AlgebraBasePluginV1;
const feeConfig = await pluginMock.feeConfig();
const pluginMock = (await ethers.getContractFactory('AlgebraModularHub')).attach(pluginAddress) as any as AlgebraModularHub;

// plugin indexing inside modular hub starts from 1
const dynamicFeeModuleAddress = await pluginMock.modules(2);

const DynamicFeeModule_ethers = await ethers.getContractFactory('DynamicFeeModule');
const dynamicFeeModule = DynamicFeeModule_ethers.attach(dynamicFeeModuleAddress) as any as DynamicFeeModule;

const feeConfig = await dynamicFeeModule.feeConfig();
expect(feeConfig.baseFee).to.be.not.eq(0);
});

it('cannot create twice for existing pool', async () => {
await mockAlgebraFactory.stubPool(wallet.address, other.address, other.address);
const tokenFactory = await ethers.getContractFactory('TestERC20');
const token0 = await tokenFactory.deploy(1337);
const token1 = await tokenFactory.deploy(1337);

const poolAddress = await mockAlgebraFactory.createPool.staticCall(token0, token1);
await mockAlgebraFactory.createPool(token0, token1)

const mockPoolFactory = await ethers.getContractFactory('MockPool');
const mockPool = (await mockPoolFactory.attach(poolAddress)) as any as MockPool;
mockPool.setPluginFactory(pluginFactory);

await mockAlgebraFactory.stubPool(token0, token1, mockPool);

await mockAlgebraFactory.grantRole(ethers.keccak256(ethers.toUtf8Bytes("POOLS_ADMINISTRATOR")), pluginFactory);

await pluginFactory.createPluginForExistingPool(wallet.address, other.address);
await pluginFactory.createPluginForExistingPool(token0, token1);

await expect(pluginFactory.createPluginForExistingPool(wallet.address, other.address)).to.be.revertedWith('Already created');
await expect(pluginFactory.createPluginForExistingPool(token0, token1)).to.be.revertedWith('Already created');
});
});

Expand Down Expand Up @@ -137,21 +189,21 @@ describe('BasePluginV1Factory', () => {

describe('#setFarmingAddress', () => {
it('fails if caller is not owner', async () => {
await expect(pluginFactory.connect(other).setFarmingAddress(wallet.address)).to.be.revertedWith('Only administrator');
await expect(farmingModuleFactory.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);
await farmingModuleFactory.setFarmingAddress(other.address);
expect(await farmingModuleFactory.farmingAddress()).to.eq(other.address);
});

it('emits event', async () => {
await expect(pluginFactory.setFarmingAddress(other.address)).to.emit(pluginFactory, 'FarmingAddress').withArgs(other.address);
await expect(farmingModuleFactory.setFarmingAddress(other.address)).to.emit(farmingModuleFactory, 'FarmingAddress').withArgs(other.address);
});

it('cannot set current address', async () => {
await pluginFactory.setFarmingAddress(other.address);
await expect(pluginFactory.setFarmingAddress(other.address)).to.be.reverted;
await farmingModuleFactory.setFarmingAddress(other.address);
await expect(farmingModuleFactory.setFarmingAddress(other.address)).to.be.reverted;
});
});
});
26 changes: 19 additions & 7 deletions src/plugin/test/shared/fixtures.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ethers } from 'hardhat';
import { AlgebraModularHub, DynamicFeeModule, FarmingModule, MockPool, MockTimeOracleModule, MockTimeDynamicFeeModule, MockTimeDSFactory, BasePluginV1Factory, MockFactory, FarmingModuleFactory } from '../../typechain';
import { AlgebraModularHub, DynamicFeeModule, FarmingModule, MockPool, MockTimeOracleModule, MockTimeDynamicFeeModule, MockTimeDSFactory, BasePluginV1Factory, MockFactory, FarmingModuleFactory, MockTimeOracleModuleFactory, MockTimeDynamicFeeModuleFactory } from '../../typechain';

type Fixture<T> = () => Promise<T>;
interface MockFactoryFixture {
Expand Down Expand Up @@ -99,26 +99,38 @@ export const pluginFixture: Fixture<PluginFixture> = async function (): Promise<

interface PluginFactoryFixture extends MockFactoryFixture {
pluginFactory: BasePluginV1Factory;
mockPool: MockPool;
mockOracleModuleFactory: MockTimeOracleModuleFactory;
mockDynamicFeeModuleFactory: MockTimeDynamicFeeModuleFactory;
farmingModuleFactory: FarmingModuleFactory;
}

export const pluginFactoryFixture: Fixture<PluginFactoryFixture> = async function (): Promise<PluginFactoryFixture> {
const { mockFactory } = await mockFactoryFixture();

const dynamicFeeModuleFactoryFactory = await ethers.getContractFactory('DynamicFeeModuleFactory');
const dynamicFeeModuleFactory = await dynamicFeeModuleFactoryFactory.deploy(mockFactory);
const mockDynamicFeeModuleFactoryFactory = await ethers.getContractFactory('MockTimeDynamicFeeModuleFactory');
const mockDynamicFeeModuleFactory = await mockDynamicFeeModuleFactoryFactory.deploy(mockFactory) as any as MockTimeDynamicFeeModuleFactory;

const farmingModuleFactoryFactory = await ethers.getContractFactory('FarmingModuleFactory');
const farmingModuleFactory = await farmingModuleFactoryFactory.deploy(mockFactory) as any as FarmingModuleFactory;

const oracleModuleFactoryFactory = await ethers.getContractFactory('OracleModuleFactory');
const oracleModuleFactory = await oracleModuleFactoryFactory.deploy(mockFactory);

const mockOracleModuleFactoryFactory = await ethers.getContractFactory('MockTimeOracleModuleFactory');
const mockOracleModuleFactory = await mockOracleModuleFactoryFactory.deploy(mockFactory) as any as MockTimeOracleModuleFactory;

const pluginFactoryFactory = await ethers.getContractFactory('BasePluginV1Factory');
const pluginFactory = (await pluginFactoryFactory.deploy(mockFactory, dynamicFeeModuleFactory, farmingModuleFactory, oracleModuleFactory)) as any as BasePluginV1Factory;
const pluginFactory = (await pluginFactoryFactory.deploy(mockFactory, mockDynamicFeeModuleFactory, farmingModuleFactory, mockOracleModuleFactory)) as any as BasePluginV1Factory;

const mockPoolFactory = await ethers.getContractFactory('MockPool');
const mockPool = (await mockPoolFactory.deploy()) as any as MockPool;

await mockPool.setPluginFactory(pluginFactory);

return {
pluginFactory,
mockPool,
mockOracleModuleFactory,
mockDynamicFeeModuleFactory,
farmingModuleFactory,
mockFactory,
};
};

0 comments on commit 1234198

Please sign in to comment.