Skip to content

Commit

Permalink
feat: introduce VaultFactory
Browse files Browse the repository at this point in the history
This commit introduces a first version of a `VaultFactory` that later
will be extended to be capable of instantiating reward vaults and
possible keep track of vault instances per owner.

As a first step, this implementation comes with a `createVault()`
function which takes care of creating vaults.

Because `VaultFactory` also knows about `StakeManager` it can derive the
manager's address and stake token from it when creating vaults, allowing
the API to be without arguments.

Partially addresses #37
  • Loading branch information
0x-r4bbit committed Oct 12, 2023
1 parent 296cd0a commit 93dbb1c
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 6 deletions.
36 changes: 36 additions & 0 deletions contracts/VaultFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.18;

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

contract VaultFactory is Ownable2Step {
error VaultFactory__InvalidStakeManagerAddress();

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

StakeManager public stakeManager;

constructor(address _stakeManager) {
if (_stakeManager == address(0)) {
revert VaultFactory__InvalidStakeManagerAddress();
}
stakeManager = StakeManager(_stakeManager);
}

function setStakeManager(address _stakeManager) external onlyOwner {
if (_stakeManager == address(0) || _stakeManager == address(stakeManager)) {
revert VaultFactory__InvalidStakeManagerAddress();
}
stakeManager = StakeManager(_stakeManager);
}

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
7 changes: 5 additions & 2 deletions test/StakeVault.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@ 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 +27,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));
}
}

0 comments on commit 93dbb1c

Please sign in to comment.