Skip to content

Commit

Permalink
Merge pull request #3 from sturgeon-protocol/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
a17 authored Dec 17, 2023
2 parents 5d645a3 + 266d251 commit e03ade1
Show file tree
Hide file tree
Showing 16 changed files with 467 additions and 14 deletions.
1 change: 1 addition & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[profile.default]
fs_permissions = [{ access = "read-write", path = "./"}]
src = "src"
out = "out"
libs = ["lib"]
Expand Down
33 changes: 31 additions & 2 deletions script/lib/DeployLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import "../../src/STGN.sol";
import "../../src/ControllableProxy.sol";
import "../../src/VeSTGN.sol";
import "../../src/MultiGauge.sol";
import "../../src/Factory.sol";
import "../../src/PerfFeeTreasury.sol";
import "../../src/VeDistributor.sol";

library DeployLib {
struct DeployParams {
Expand All @@ -28,6 +31,9 @@ library DeployLib {
uint len;
address[] vesting;
STGN stgn;
Factory factory;
address perfFeeTreasury;
VeDistributor veDist;
}

function deployPlatform(DeployParams memory params) internal returns (address controller) {
Expand All @@ -54,7 +60,7 @@ library DeployLib {
address impl = address(new VeSTGN());
proxy.initProxy(impl);
VeSTGN ve = VeSTGN(address(proxy));
ve.init(address(v.stgn), 1e18, address(v.c));
ve.init(address(v.stgn), 100e18, address(v.c));
// assertEq(IProxyControlled(proxy).implementation(), impl);

proxy = new ControllableProxy();
Expand All @@ -63,7 +69,30 @@ library DeployLib {
MultiGauge multigauge = MultiGauge(address(proxy));
multigauge.init(address(v.c), /* address(ve),*/ address(v.stgn));

v.c.setup(address(v.ifo), address(ve), address(v.stgn), address(multigauge), params.liquidator);
proxy = new ControllableProxy();
impl = address(new Factory());
proxy.initProxy(impl);
v.factory = Factory(address(proxy));
v.factory.init(address(v.c));

v.perfFeeTreasury = address(new PerfFeeTreasury(params.governance));

proxy = new ControllableProxy();
impl = address(new VeDistributor());
proxy.initProxy(impl);
v.veDist = VeDistributor(address(proxy));
v.veDist.init(address(v.c), address(ve), params.rewardToken);

v.c.setup(
v.perfFeeTreasury,
address(v.veDist),
address(v.factory),
address(v.ifo),
address(ve),
address(v.stgn),
address(multigauge),
params.liquidator
);

return address(v.c);
}
Expand Down
21 changes: 19 additions & 2 deletions src/Controller.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ contract Controller is IController {
/// @inheritdoc IController
address public governance;

address public perfFeeTreasury;

address public factory;

address public stgn;

/// @dev External solution for sell any tokens with minimal gas usage.
Expand Down Expand Up @@ -59,16 +63,29 @@ contract Controller is IController {
_operators.add(governance_);
}

function setup(address ifo_, address ve_, address stgn_, address multigauge_, address liquidator_) external {
function setup(
address perfFeeTreasury_,
address veDistributor_,
address factory_,
address ifo_,
address ve_,
address stgn_,
address multigauge_,
address liquidator_
) external {
require(
ifo_ != address(0) && stgn_ != address(0) && multigauge_ != address(0) && ve_ != address(0), "WRONG_INPUT"
);
require(ifo == address(0), "ALREADY");
perfFeeTreasury = perfFeeTreasury_;
veDistributor = veDistributor_;
factory = factory_;
ifo = ifo_;
ve = ve_;
stgn = stgn_;
multigauge = multigauge_;
liquidator = liquidator_; // todo event
liquidator = liquidator_;
_operators.add(factory_); // todo event
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
Expand Down
61 changes: 61 additions & 0 deletions src/Factory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.21;

import "./base/Controllable.sol";
import "./CompounderVault.sol";
import "./PearlStrategy.sol";
import "./interfaces/IMultiPool.sol";
import "./lib/DeployerLib.sol";

contract Factory is Controllable {
function init(address controller_) external initializer {
__Controllable_init(controller_);
}

modifier onlyGovernance() {
_requireGovernance();
_;
}

function deployIfoHarvester(
address underlying,
address pearlGauge,
string calldata vaultName,
string calldata vaultSymbol
) external onlyGovernance returns (address vault, address strategy) {
address _controller = controller();
vault = DeployerLib.deployHarvesterVault(_controller, underlying, vaultName, vaultSymbol);
strategy = address(new PearlStrategy(vault, pearlGauge, true, address(0)));
IVault(vault).setStrategy(strategy);
IGauge(IController(_controller).multigauge()).addStakingToken(vault);
IController(_controller).registerVault(vault, true);
}

function deployCompounder(
address underlying,
string calldata vaultName,
string calldata vaultSymbol
) external onlyGovernance returns (address compounder) {
compounder = address(new CompounderVault(IERC20(underlying), vaultName, vaultSymbol));
}

function deployHarvester(
address underlying,
address pearlGauge,
string calldata vaultName,
string calldata vaultSymbol,
address compounderVault
) external onlyGovernance returns (address vault, address strategy) {
address _controller = controller();
vault = DeployerLib.deployHarvesterVault(_controller, underlying, vaultName, vaultSymbol);
strategy = address(new PearlStrategy(vault, pearlGauge, false, compounderVault));
IVault(vault).setStrategy(strategy);
address multigauge = IController(_controller).multigauge();
IGauge(multigauge).addStakingToken(vault);
IMultiPool(multigauge).registerRewardToken(vault, compounderVault);
}

function _requireGovernance() internal view {
require(isGovernance(msg.sender), "Denied");
}
}
5 changes: 3 additions & 2 deletions src/IFO.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,12 @@ contract IFO is IIFO {

function exchange(uint amount) external returns (bool, uint) {
address vault = IStrategyStrict(msg.sender).vault();
require(IController(controller).isValidVault(vault), "Not valid vault");
IController _controller = IController(controller);
require(_controller.isValidVault(vault), "Not valid vault");
uint stgnBal = IERC20(stgn).balanceOf(address(this));
uint stgnOut = amount * rate / 1e18;
if (stgnOut <= stgnBal) {
IERC20(rewardToken).safeTransferFrom(msg.sender, address(this), amount);
IERC20(rewardToken).safeTransferFrom(msg.sender, _controller.perfFeeTreasury(), amount);
IERC20(stgn).safeTransfer(msg.sender, stgnOut);
return (true, stgnOut);
}
Expand Down
9 changes: 9 additions & 0 deletions src/PearlStrategy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import "./interfaces/IController.sol";
import "./interfaces/IIFO.sol";
import "./interfaces/IGauge.sol";
import "./interfaces/ITetuLiquidator.sol";
import "./interfaces/IVeDistributor.sol";

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.°:°•.°+.*•´.*:*.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*/
/* Pearl strategy */
Expand Down Expand Up @@ -62,8 +63,16 @@ contract PearlStrategy is StrategyStrictBase {
// liquidate fee if available

uint rtReward = _claim();
uint perfFee = rtReward / 10;
uint veDistFee = perfFee / 2;
rtReward -= perfFee;
IERC20 rt = IERC20(gaugeRewardToken);

IController controller = IController(IVault(vault).controller());
IVeDistributor veDist = IVeDistributor(controller.veDistributor());
rt.safeTransfer(address(veDist), veDistFee);
veDist.checkpoint();
rt.safeTransfer(controller.perfFeeTreasury(), perfFee - veDistFee);

IGauge multigauge = IGauge(controller.multigauge());
if (ifo) {
Expand Down
10 changes: 7 additions & 3 deletions src/base/StakelessMultiPoolBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ abstract contract StakelessMultiPoolBase is ReentrancyGuard, IMultiPool, Control
// *************************************************************

modifier onlyAllowedContracts() {
_requireGovOrIfo();
_requireGovOrIfoOrFactory();
_;
}

Expand Down Expand Up @@ -339,8 +339,12 @@ abstract contract StakelessMultiPoolBase is ReentrancyGuard, IMultiPool, Control
emit NotifyReward(msg.sender, stakingToken, rewardToken, amount);
}

function _requireGovOrIfo() internal view {
function _requireGovOrIfoOrFactory() internal view {
IController _controller = IController(controller());
require(msg.sender == _controller.governance() || msg.sender == _controller.ifo(), "Not allowed");
require(
msg.sender == _controller.governance() || msg.sender == _controller.ifo()
|| msg.sender == _controller.factory(),
"Not allowed"
);
}
}
6 changes: 5 additions & 1 deletion src/interfaces/IController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ interface IController {
/// @notice Gnosis safe multi signature wallet with maximum power under the platform.
function governance() external view returns (address);

function perfFeeTreasury() external view returns (address);

function factory() external view returns (address);

function stgn() external view returns (address);

/// @notice A dedicated solution for swap tokens via different chains.
Expand Down Expand Up @@ -69,7 +73,7 @@ interface IController {
/// @return True if the vault valid
function isValidVault(address vault) external view returns (bool);

// --- restrictions
function registerVault(address vault, bool isHarvester) external;

function isOperator(address _adr) external view returns (bool);
}
2 changes: 2 additions & 0 deletions src/interfaces/IVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@ interface IVault is IERC4626 {
function strategy() external view returns (IStrategyStrict);

function controller() external view returns (address);

function setStrategy(address strategy) external;
}
15 changes: 15 additions & 0 deletions src/lib/DeployerLib.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.21;

import "../HarvesterVault.sol";

library DeployerLib {
function deployHarvesterVault(
address controller,
address underlying,
string calldata vaultName,
string calldata vaultSymbol
) external returns (address) {
return address(new HarvesterVault(controller, IERC20(underlying), vaultName, vaultSymbol, 4_000));
}
}
4 changes: 2 additions & 2 deletions src/lib/VeSTGNLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -413,9 +413,9 @@ library VeSTGNLib {
bytes(
string(
abi.encodePacked(
'{"name": "veTETU #',
'{"name": "veSTGN #',
Strings.toString(_tokenId),
'", "description": "Locked TETU tokens", "image": "data:image/svg+xml;base64,',
'", "description": "Locked STGN tokens", "image": "data:image/svg+xml;base64,',
Base64.encode(bytes(output)),
'"}'
)
Expand Down
4 changes: 2 additions & 2 deletions test/Controller.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ contract ControllerTest is Test, MockSetup {
controller.registerOperator(address(2));
controller.registerOperator(address(2));
assertEq(controller.isOperator(address(2)), true);
assertEq(controller.operatorsList().length, 2);
assertEq(controller.operatorsList().length, 3);
controller.removeOperator(address(2));
assertEq(controller.operatorsList().length, 1);
assertEq(controller.operatorsList().length, 2);

controller.registerVault(address(3), true);
assertEq(controller.harvesterVaultsListLength(), 1);
Expand Down
58 changes: 58 additions & 0 deletions test/PearlStrategy.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,31 @@ contract PearlStrategyTest is MockSetup {
assertGt(_getRewardFromIfoGauge(address(vault), controller.stgn()), 0);
}

function test_factory_ifo() public {
(address v, address s) =
factory.deployIfoHarvester(tokenA, address(pearlGauge), "IFO Harvester MOCK_A", "xTokenA");
IVault vault = IVault(v);
IStrategyStrict strategy = IStrategyStrict(s);

deal(tokenA, address(this), 1e20);
IERC20(tokenA).approve(address(vault), 1e20);
vault.mint(1e18, address(this));
assertEq(vault.balanceOf(address(this)), 1e18);
assertEq(IMultiPool(controller.multigauge()).balanceOf(address(vault), address(this)), 1e18);
vault.redeem(1e18, address(this), address(this));
assertEq(vault.balanceOf(address(this)), 0);
vault.deposit(1e18, address(this));
assertEq(vault.balanceOf(address(this)), 1e18);
skip(3600);
strategy.doHardWork();
deal(tokenC, address(pearlGauge), 1e18);
skip(3600);
strategy.doHardWork();
assertEq(_getRewardFromIfoGauge(address(vault), controller.stgn()), 0);
skip(360000);
assertGt(_getRewardFromIfoGauge(address(vault), controller.stgn()), 0);
}

function test_compounder() public {
HarvesterVault vault =
new HarvesterVault(address(controller), IERC20(tokenA), "Harvester MOCK_A", "xTokenA", 4_000);
Expand Down Expand Up @@ -83,6 +108,39 @@ contract PearlStrategyTest is MockSetup {
compounderVault.withdrawAll();
}

function test_factory_compounder() public {
CompounderVault compounderVault =
CompounderVault(factory.deployCompounder(tokenD, "Compounder vault for xTokenA", "xxTokenA"));
(address v, address s) = factory.deployHarvester(
tokenA, address(pearlGauge), "Harvester MOCK_A", "xTokenA", address(compounderVault)
);
IVault vault = IVault(v);
IStrategyStrict strategy = IStrategyStrict(s);

deal(tokenA, address(this), 1e20);
IERC20(tokenA).approve(address(vault), 1e20);
vault.mint(1e18, address(this));
assertEq(vault.balanceOf(address(this)), 1e18);

skip(3600);

deal(tokenC, address(pearlGauge), 1e18);
deal(tokenD, address(controller.liquidator()), 1e20);
strategy.doHardWork();

assertEq(compounderVault.sharePrice(), 1e18);

assertEq(_getRewardFromIfoGauge(address(vault), address(compounderVault)), 0);

skip(360000);

assertGt(_getRewardFromIfoGauge(address(vault), address(compounderVault)), 0);

assertGt(compounderVault.balanceOf(address(this)), 0);

compounderVault.withdrawAll();
}

function _getRewardFromIfoGauge(address vault, address rewardTokenToCheck) internal returns (uint claimedRt) {
uint b = IERC20(rewardTokenToCheck).balanceOf(address(this));
IGauge(controller.multigauge()).getAllRewards(vault, address(this));
Expand Down
Loading

0 comments on commit e03ade1

Please sign in to comment.