Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use minimal proxy for new pool deployments #3

Merged
merged 4 commits into from
Jan 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions script/CreateAssetPool.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
pragma solidity ^0.8.20;

import "forge-std/Script.sol";
import "../src/interfaces/IAssetPool.sol";
import "../src/protocol/AssetPoolFactory.sol";
import "../src/protocol/LPRegistry.sol";
import "../src/protocol/AssetPool.sol";

contract CreatePoolScript is Script {
// Pool configuration'
Expand Down Expand Up @@ -78,8 +78,8 @@ contract CreatePoolScript is Script {
console.log("----------------------------------------------------");

// Verify the pool was created correctly
AssetPool pool = AssetPool(poolAddress);
(uint256 supply, AssetPool.CycleState state, uint256 cycle,,, uint256 price) = pool.getGeneralInfo();
IAssetPool pool = IAssetPool(poolAddress);
(uint256 supply, IAssetPool.CycleState state, uint256 cycle,,, uint256 price) = pool.getGeneralInfo();
console.log("Pool Initial State:");
console.log("Supply:", supply);
console.log("State:", uint256(state));
Expand Down
5 changes: 3 additions & 2 deletions script/DeployPoolFactory.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import "forge-std/Script.sol";
import "../src/protocol/AssetPoolFactory.sol";

contract AssetPoolDeployScript is Script {
address constant lpRegistry = 0x82d533e4a2973D5c1E29eB207af0B6f387E395C9;
address constant lpRegistry = 0x82d533e4a2973D5c1E29eB207af0B6f387E395C9; // Replace with actual LPRegistry address
address constant assetPoolImplementation = 0x82d533e4a2973D5c1E29eB207af0B6f387E395C9; // Replace with actual implementation address

function setUp() public {}

Expand All @@ -17,7 +18,7 @@ contract AssetPoolDeployScript is Script {
vm.startBroadcast(deployerPrivateKey);

// Deploy AssetPoolFactory with the LPRegistry address
AssetPoolFactory poolFactory = new AssetPoolFactory(lpRegistry);
AssetPoolFactory poolFactory = new AssetPoolFactory(lpRegistry, assetPoolImplementation);
console.log("AssetPoolFactory deployed at:", address(poolFactory));

// Stop broadcasting transactions
Expand Down
5 changes: 5 additions & 0 deletions src/interfaces/IAssetPoolFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ interface IAssetPoolFactory {
*/
function lpRegistry() external view returns (ILPRegistry);

/**
* @return address The address of the asset pool implementation contract.
*/
function assetPoolImplementation() external view returns (address);

/**
* @dev Creates a new asset pool with the specified parameters.
* @param depositToken Address of the token used for deposits.
Expand Down
18 changes: 13 additions & 5 deletions src/protocol/AssetPoolFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
pragma solidity ^0.8.20;

import 'openzeppelin-contracts/contracts/access/Ownable.sol';
import 'openzeppelin-contracts/contracts/proxy/Clones.sol';
import {ILPRegistry} from '../interfaces/ILPRegistry.sol';
import {IAssetPoolFactory} from '../interfaces/IAssetPoolFactory.sol';
import {AssetPool} from './AssetPool.sol';
import {AssetPoolImplementation} from "../protocol/AssetPoolImplementation.sol";


/**
* @title PoolFactory
Expand All @@ -16,15 +18,18 @@ import {AssetPool} from './AssetPool.sol';
contract AssetPoolFactory is IAssetPoolFactory, Ownable {
/// @notice Reference to the LP Registry contract.
ILPRegistry public lpRegistry;
/// @notice Address of the asset pool implementation contract.
address public immutable assetPoolImplementation;

/**
* @dev Constructor to initialize the PoolFactory contract.
* @param _lpRegistry Address of the LP Registry contract.
* Reverts if the address is zero.
*/
constructor(address _lpRegistry) Ownable(msg.sender) {
constructor(address _lpRegistry, address _assetPoolImplementation) Ownable(msg.sender) {
if (_lpRegistry == address(0)) revert ZeroAddress();
lpRegistry = ILPRegistry(_lpRegistry);
assetPoolImplementation = _assetPoolImplementation;
}

/**
Expand Down Expand Up @@ -58,16 +63,19 @@ contract AssetPoolFactory is IAssetPoolFactory, Ownable {
rebalancingPeriod >= cyclePeriod
) revert InvalidParams();

// Deploy a new AssetPool contract instance.
AssetPool pool = new AssetPool(
address owner = owner();

// Clones a new AssetPool contract instance.
address pool = Clones.clone(assetPoolImplementation);
AssetPoolImplementation(pool).initialize(
depositToken,
assetName,
assetSymbol,
oracle,
address(lpRegistry),
cyclePeriod,
rebalancingPeriod,
msg.sender
owner
);

// Emit the AssetPoolCreated event to notify listeners.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pragma solidity ^0.8.20;
import "openzeppelin-contracts/contracts/access/Ownable.sol";
import "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import "openzeppelin-contracts/contracts/utils/Pausable.sol";
import "openzeppelin-contracts/contracts/proxy/utils/Initializable.sol";
import "openzeppelin-contracts/contracts/utils/math/Math.sol";
import {IAssetPool} from "../interfaces/IAssetPool.sol";
import {IXToken} from "../interfaces/IXToken.sol";
Expand All @@ -14,35 +15,35 @@ import {IAssetOracle} from "../interfaces/IAssetOracle.sol";
import {xToken} from "./xToken.sol";

/**
* @title AssetPool
* @title AssetPoolImplementation
* @notice Manages the lifecycle of assets and reserves in a decentralized pool.
* Facilitates deposits, minting, redemptions, and rebalancing of assets.
* Includes governance controls for updating operational parameters.
*/
contract AssetPool is IAssetPool, Ownable, Pausable {
contract AssetPoolImplementation is IAssetPool, Ownable, Pausable, Initializable {
// --------------------------------------------------------------------------------
// STATE VARIABLES
// --------------------------------------------------------------------------------

/**
* @notice Address of the reserve token (e.g., USDC).
*/
IERC20 public immutable reserveToken;
IERC20 public reserveToken;

/**
* @notice Address of the xToken contract used for asset representation.
*/
IXToken public immutable assetToken;
IXToken public assetToken;

/**
* @notice Address of the LP Registry contract for managing LPs.
*/
ILPRegistry public immutable lpRegistry;
ILPRegistry public lpRegistry;

/**
* @notice Address of the Asset Oracle contract for fetching asset prices.
*/
IAssetOracle public immutable assetOracle;
IAssetOracle public assetOracle;

/**
* @notice Index of the current operational cycle.
Expand Down Expand Up @@ -144,8 +145,13 @@ contract AssetPool is IAssetPool, Ownable, Pausable {
*/
uint256 private constant PRECISION = 1e18;

constructor() Ownable(msg.sender) {
// Disable the implementation contract
_disableInitializers();
}

// --------------------------------------------------------------------------------
// CONSTRUCTOR
// INITIALIZER
// --------------------------------------------------------------------------------

/**
Expand All @@ -157,9 +163,8 @@ contract AssetPool is IAssetPool, Ownable, Pausable {
* @param _lpRegistry Address of the LP registry contract.
* @param _cyclePeriod Duration of each operational cycle.
* @param _rebalancingPeriod Duration of the rebalance period.
* @param _owner Address of the contract owner.
*/
constructor(
function initialize (
address _reserveToken,
string memory _xTokenName,
string memory _xTokenSymbol,
Expand All @@ -168,10 +173,12 @@ contract AssetPool is IAssetPool, Ownable, Pausable {
uint256 _cyclePeriod,
uint256 _rebalancingPeriod,
address _owner
) Ownable(_owner) {
) external initializer {
if (_reserveToken == address(0) || _assetOracle == address(0) || _lpRegistry == address(0))
revert ZeroAddress();

_transferOwnership(_owner);

reserveToken = IERC20(_reserveToken);
assetToken = new xToken(_xTokenName, _xTokenSymbol);
lpRegistry = ILPRegistry(_lpRegistry);
Expand Down
79 changes: 54 additions & 25 deletions test/AssetPool.t.sol → test/AssetPoolFactory.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,20 @@
pragma solidity ^0.8.20;

import "forge-std/Test.sol";
import "../src/protocol/AssetPool.sol";

import "../src/protocol/AssetPoolFactory.sol";
import "../src/protocol/AssetPoolImplementation.sol";
import "../src/protocol/xToken.sol";
import "../src/protocol/LPRegistry.sol";
import "../src/interfaces/IAssetPool.sol";
import "../src/interfaces/IAssetOracle.sol";
import "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";

contract AssetPoolTest is Test {
contract AssetPoolFactoryTest is Test {
// Test contracts
AssetPool public pool;
AssetPoolFactory public factory;
AssetPoolImplementation public implementation;
IAssetPool public pool;
IERC20 public reserveToken;
xToken public assetToken;
LPRegistry public lpRegistry;
Expand Down Expand Up @@ -40,20 +44,21 @@ contract AssetPoolTest is Test {
// Deploy core contracts
lpRegistry = new LPRegistry();
assetOracle = new MockAssetOracle();
implementation = new AssetPoolImplementation();
factory = new AssetPoolFactory(address(lpRegistry), address(implementation));

assetOracle.setAssetPrice(1e18); // Set default price to 1.0

pool = new AssetPool(
address poolAddress = factory.createPool(
address(reserveToken),
"Tesla Stock Token",
"xTSLA",
address(assetOracle),
address(lpRegistry),
CYCLE_PERIOD,
REBALANCE_PERIOD,
owner
REBALANCE_PERIOD
);

pool = IAssetPool(poolAddress);
assetToken = xToken(address(pool.assetToken()));

// Setup initial states
Expand All @@ -72,35 +77,61 @@ contract AssetPoolTest is Test {
}

// --------------------------------------------------------------------------------
// DEPLOYMENT TESTS
// FACTORY TESTS
// --------------------------------------------------------------------------------

function testConstructor() public view {
assertEq(address(pool.reserveToken()), address(reserveToken));
assertEq(address(pool.lpRegistry()), address(lpRegistry));
assertEq(address(pool.assetOracle()), address(assetOracle));
assertEq(pool.cycleTime(), CYCLE_PERIOD);
assertEq(pool.rebalanceTime(), REBALANCE_PERIOD);
function testFactoryDeployment() public view {
assertEq(address(factory.lpRegistry()), address(lpRegistry));
assertEq(factory.assetPoolImplementation(), address(implementation));
}

function testConstructorReverts() public {
vm.expectRevert(IAssetPool.ZeroAddress.selector);
new AssetPool(
function testCreatePool() public {
vm.startPrank(owner);
address newPool = factory.createPool(
address(reserveToken),
"Apple Stock Token",
"xAAPL",
address(assetOracle),
CYCLE_PERIOD,
REBALANCE_PERIOD
);
vm.stopPrank();

assertTrue(newPool != address(0));
IAssetPool poolInstance = IAssetPool(newPool);
assertEq(address(poolInstance.reserveToken()), address(reserveToken));
assertEq(poolInstance.cycleTime(), CYCLE_PERIOD);
assertEq(poolInstance.rebalanceTime(), REBALANCE_PERIOD);
}

function testCreatePoolReverts() public {
vm.startPrank(owner);
vm.expectRevert(IAssetPoolFactory.InvalidParams.selector);
factory.createPool(
address(0),
"Tesla Stock Token",
"xTSLA",
"Apple Stock Token",
"xAAPL",
address(assetOracle),
address(lpRegistry),
CYCLE_PERIOD,
REBALANCE_PERIOD,
owner
REBALANCE_PERIOD
);
vm.stopPrank();
}

function testUpdateLPRegistry() public {
address newRegistry = address(new LPRegistry());

vm.prank(owner);
factory.updateLPRegistry(newRegistry);

assertEq(address(factory.lpRegistry()), newRegistry);
}

// --------------------------------------------------------------------------------
// DEPOSIT TESTS
// POOL IMPLEMENTATION TESTS
// --------------------------------------------------------------------------------


function testDepositReserve() public {
uint256 depositAmount = 100e18;

Expand Down Expand Up @@ -306,8 +337,6 @@ contract AssetPoolTest is Test {
vm.prank(owner);
pool.pausePool();

assertTrue(pool.paused());

// Test that operations revert when paused
bytes memory error = abi.encodeWithSignature("EnforcedPause()");
vm.expectRevert(error);
Expand Down
Loading