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

feat: introduce VaultFactory #38

Merged
merged 1 commit into from
Nov 7, 2023
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
18 changes: 12 additions & 6 deletions .gas-snapshot
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
CreateVaultTest:testDeployment() (gas: 9774)
CreateVaultTest:test_createVault() (gas: 650992)
ExecuteAccountTest:testDeployment() (gas: 26400)
ExecuteAccountTest:test_RevertWhen_InvalidLimitEpoch() (gas: 982104)
ExecuteAccountTest:test_RevertWhen_InvalidLimitEpoch() (gas: 991602)
LeaveTest:testDeployment() (gas: 26172)
LeaveTest:test_RevertWhen_NoPendingMigration() (gas: 670554)
LeaveTest:test_RevertWhen_NoPendingMigration() (gas: 678051)
LeaveTest:test_RevertWhen_SenderIsNotVault() (gas: 10562)
LockTest:testDeployment() (gas: 26400)
LockTest:test_RevertWhen_DecreasingLockTime() (gas: 985034)
LockTest:test_RevertWhen_DecreasingLockTime() (gas: 994528)
LockTest:test_RevertWhen_SenderIsNotVault() (gas: 10607)
MigrateTest:testDeployment() (gas: 26172)
MigrateTest:test_RevertWhen_NoPendingMigration() (gas: 670393)
MigrateTest:test_RevertWhen_NoPendingMigration() (gas: 677890)
MigrateTest:test_RevertWhen_SenderIsNotVault() (gas: 10629)
SetStakeManagerTest:testDeployment() (gas: 9774)
SetStakeManagerTest:test_RevertWhen_InvalidStakeManagerAddress() (gas: 20481)
SetStakeManagerTest:test_SetStakeManager() (gas: 19869)
StakeManagerTest:testDeployment() (gas: 26172)
StakeTest:testDeployment() (gas: 26172)
StakeTest:test_RevertWhen_SenderIsNotVault() (gas: 10638)
StakedTokenTest:testStakeToken() (gas: 7638)
UnstakeTest:testDeployment() (gas: 26355)
UnstakeTest:test_RevertWhen_FundsLocked() (gas: 981497)
UnstakeTest:test_RevertWhen_SenderIsNotVault() (gas: 10609)
UnstakeTest:test_RevertWhen_FundsLocked() (gas: 990991)
UnstakeTest:test_RevertWhen_SenderIsNotVault() (gas: 10609)
VaultFactoryTest:testDeployment() (gas: 9774)
64 changes: 64 additions & 0 deletions contracts/VaultFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.18;

import { Ownable2Step } from "@openzeppelin/contracts/access/Ownable2Step.sol";
import { StakeManager } from "./StakeManager.sol";
import { StakeVault } from "./StakeVault.sol";

/**
* @title VaultFactory
* @author 0x-r4bbit
*
* This contract is reponsible for creating staking vaults for users.
* A user of the staking protocol is able to create multiple vaults to facilitate
* different strategies. For example, a user may want to create a vault for
* a long-term lock period, while also creating a vault that has no lock period
* at all.
*
* @notice This contract is used by users to create staking vaults.
* @dev This contract will be deployed by Status, making Status the owner of the contract.
* @dev A contract address for a `StakeManager` has to be provided to create this contract.
* @dev Reverts with {VaultFactory__InvalidStakeManagerAddress} if the provided
* `StakeManager` address is zero.
* @dev The `StakeManager` contract address can be changed by the owner.
*/
contract VaultFactory is Ownable2Step {
error VaultFactory__InvalidStakeManagerAddress();

event VaultCreated(address indexed vault, address indexed owner);
event StakeManagerAddressChanged(address indexed newStakeManagerAddress);

/// @dev Address of the `StakeManager` contract instance.
StakeManager public stakeManager;

/// @param _stakeManager Address of the `StakeManager` contract instance.
constructor(address _stakeManager) {
if (_stakeManager == address(0)) {
revert VaultFactory__InvalidStakeManagerAddress();
}
stakeManager = StakeManager(_stakeManager);
}

/// @notice Sets the `StakeManager` contract address.
/// @dev Only the owner can call this function.
/// @dev Reverts if the provided `StakeManager` address is zero.
/// @dev Emits a {StakeManagerAddressChanged} event.
/// @param _stakeManager Address of the `StakeManager` contract instance.
function setStakeManager(address _stakeManager) external onlyOwner {
if (_stakeManager == address(0) || _stakeManager == address(stakeManager)) {
revert VaultFactory__InvalidStakeManagerAddress();
}
stakeManager = StakeManager(_stakeManager);
emit StakeManagerAddressChanged(_stakeManager);
}

/// @notice Creates an instance of a `StakeVault` contract.
/// @dev Anyone can call this function.
/// @dev Emits a {VaultCreated} event.
function createVault() external returns (StakeVault) {
StakeVault vault = new StakeVault(msg.sender, stakeManager.stakedToken(), stakeManager);
emit VaultCreated(address(vault), msg.sender);
return vault;
}
}
6 changes: 4 additions & 2 deletions script/Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@ pragma solidity >=0.8.19 <=0.9.0;
import { BaseScript } from "./Base.s.sol";
import { DeploymentConfig } from "./DeploymentConfig.s.sol";
import { StakeManager } from "../contracts/StakeManager.sol";
import { VaultFactory } from "../contracts/VaultFactory.sol";

contract Deploy is BaseScript {
function run() public returns (StakeManager, DeploymentConfig) {
function run() public returns (VaultFactory, StakeManager, DeploymentConfig) {
DeploymentConfig deploymentConfig = new DeploymentConfig(broadcaster);
(, address token) = deploymentConfig.activeNetworkConfig();

vm.startBroadcast(broadcaster);
StakeManager stakeManager = new StakeManager(token, address(0));
VaultFactory vaultFactory = new VaultFactory(address(stakeManager));
vm.stopBroadcast();

return (stakeManager, deploymentConfig);
return (vaultFactory, stakeManager, deploymentConfig);
}
}
6 changes: 4 additions & 2 deletions test/StakeManager.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,20 @@ import { Deploy } from "../script/Deploy.s.sol";
import { DeploymentConfig } from "../script/DeploymentConfig.s.sol";
import { StakeManager } from "../contracts/StakeManager.sol";
import { StakeVault } from "../contracts/StakeVault.sol";
import { VaultFactory } from "../contracts/VaultFactory.sol";

contract StakeManagerTest is Test {
DeploymentConfig internal deploymentConfig;
StakeManager internal stakeManager;
VaultFactory internal vaultFactory;

address internal stakeToken;
address internal deployer;
address internal testUser = makeAddr("testUser");

function setUp() public virtual {
Deploy deployment = new Deploy();
(stakeManager, deploymentConfig) = deployment.run();
(vaultFactory, stakeManager, deploymentConfig) = deployment.run();
(deployer, stakeToken) = deploymentConfig.activeNetworkConfig();
}

Expand All @@ -36,7 +38,7 @@ contract StakeManagerTest is Test {

function _createTestVault(address owner) internal returns (StakeVault vault) {
vm.prank(owner);
vault = new StakeVault(owner, ERC20(stakeToken), stakeManager);
vault = vaultFactory.createVault();

vm.prank(deployer);
stakeManager.setVault(address(vault).codehash);
Expand Down
9 changes: 5 additions & 4 deletions test/StakeVault.t.sol
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;

import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";

import { Test } from "forge-std/Test.sol";
import { Deploy } from "../script/Deploy.s.sol";
import { DeploymentConfig } from "../script/DeploymentConfig.s.sol";
import { StakeManager } from "../contracts/StakeManager.sol";
import { StakeVault } from "../contracts/StakeVault.sol";
import { VaultFactory } from "../contracts/VaultFactory.sol";

contract StakeVaultTest is Test {
StakeManager internal stakeManager;

DeploymentConfig internal deploymentConfig;

VaultFactory internal vaultFactory;

StakeVault internal stakeVault;

address internal deployer;
Expand All @@ -24,11 +25,11 @@ contract StakeVaultTest is Test {

function setUp() public virtual {
Deploy deployment = new Deploy();
(stakeManager, deploymentConfig) = deployment.run();
(vaultFactory, stakeManager, deploymentConfig) = deployment.run();
(deployer, stakeToken) = deploymentConfig.activeNetworkConfig();

vm.prank(testUser);
stakeVault = new StakeVault(testUser, ERC20(stakeToken), stakeManager);
stakeVault = vaultFactory.createVault();
}
}

Expand Down
74 changes: 74 additions & 0 deletions test/VaultFactory.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;

import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";

Check warning on line 4 in test/VaultFactory.t.sol

View workflow job for this annotation

GitHub Actions / lint

imported name ERC20 is not used

import { Test } from "forge-std/Test.sol";
import { Deploy } from "../script/Deploy.s.sol";
import { DeploymentConfig } from "../script/DeploymentConfig.s.sol";

import { StakeManager } from "../contracts/StakeManager.sol";
import { StakeVault } from "../contracts/StakeVault.sol";
import { VaultFactory } from "../contracts/VaultFactory.sol";

contract VaultFactoryTest is Test {
DeploymentConfig internal deploymentConfig;

StakeManager internal stakeManager;

VaultFactory internal vaultFactory;

address internal deployer;

address internal stakedToken;

address internal testUser = makeAddr("testUser");

function setUp() public virtual {
Deploy deployment = new Deploy();
(vaultFactory, stakeManager, deploymentConfig) = deployment.run();
(deployer, stakedToken) = deploymentConfig.activeNetworkConfig();
}

function testDeployment() public {
assertEq(address(vaultFactory.stakeManager()), address(stakeManager));
}
}

contract SetStakeManagerTest is VaultFactoryTest {
function setUp() public override {
VaultFactoryTest.setUp();
}

function test_RevertWhen_InvalidStakeManagerAddress() public {
vm.startPrank(deployer);
vm.expectRevert(VaultFactory.VaultFactory__InvalidStakeManagerAddress.selector);
vaultFactory.setStakeManager(address(0));

vm.expectRevert(VaultFactory.VaultFactory__InvalidStakeManagerAddress.selector);
vaultFactory.setStakeManager(address(stakeManager));
}

function test_SetStakeManager() public {
vm.prank(deployer);
vaultFactory.setStakeManager(address(this));
assertEq(address(vaultFactory.stakeManager()), address(this));
}
}

contract CreateVaultTest is VaultFactoryTest {
event VaultCreated(address indexed vault, address indexed owner);

function setUp() public override {
VaultFactoryTest.setUp();
}

function test_createVault() public {
vm.prank(testUser);
vm.expectEmit(false, false, false, false);
emit VaultCreated(makeAddr("some address"), testUser);
StakeVault vault = vaultFactory.createVault();
assertEq(vault.owner(), testUser);
assertEq(address(vault.stakedToken()), address(stakedToken));
}
}