Skip to content

Commit

Permalink
fix strategy, update unreal addresses, IFO event, scripts
Browse files Browse the repository at this point in the history
  • Loading branch information
a17 committed Feb 11, 2024
1 parent 95330cb commit 3e0a32f
Show file tree
Hide file tree
Showing 10 changed files with 266 additions and 14 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ cast call --rpc-url https://rpc.unreal.gelato.digital 0x35bf701C24357FD0C7F60376

### Unreal testnet


#### Deployed contracts

* Controller 0x4F69329E8dE13aA7EAc664368C5858AF6371FA4c [Blockscout](https://unreal.blockscout.com/address/0x4F69329E8dE13aA7EAc664368C5858AF6371FA4c?tab=contract)
* IFO 0x3222eb4824cEb0E9CcfE11018C83429105dFE00F [Blockscout](https://unreal.blockscout.com/address/0x3222eb4824cEb0E9CcfE11018C83429105dFE00F?tab=contract)
* STGN 0x609e0d74fAB81085283df92B563750624054F8bE [Blockscout](https://unreal.blockscout.com/address/0x609e0d74fAB81085283df92B563750624054F8bE?tab=contract)
Expand All @@ -41,6 +44,16 @@ cast call --rpc-url https://rpc.unreal.gelato.digital 0x35bf701C24357FD0C7F60376
* DepositHelper 0x7c8d0C7B63249A314df84707F8690F62CF625820 [Blockscout](https://unreal.blockscout.com/address/0x7c8d0C7B63249A314df84707F8690F62CF625820?tab=contract)
* Compounder proxy 0x89c06219C24ab4aBd762A49cdE97ce69B05f3EAF [Blockscout](https://unreal.blockscout.com/address/0x89c06219C24ab4aBd762A49cdE97ce69B05f3EAF?tab=contract)

#### Addresses

* PEARL 0xCE1581d7b4bA40176f0e219b2CaC30088Ad50C7A
* CVR 0xC0Fd0e8d5c3Bdc2C06b2Ee9FfE81ceCbE1B59364
* pool PEARL-CVR 0x725565FF3d821D655A475819031e168ffb63901C
* LiquidBoxManager 0xCc2b204Fb8BdE255B5820049108c53137F7DdeD4
* DAI-USDC LiquidBox ALM 0xAB5a4189e947E0e3EbEc25637f73deb55f6CEEA9
* DAI-USDC GaugeV2ALM 0x54cbd289b263cd14e6707ecbda01161c4385dfe3


### Goerli testnet

* Controller 0x8216C9afFC982428aF33D1D9F165bAf9D75AebBa
Expand Down
10 changes: 5 additions & 5 deletions chains/UnrealLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,19 @@ library UnrealLib {
// Unreal tokens
address public constant TOKEN_USDC = 0xabAa4C39cf3dF55480292BBDd471E88de8Cc3C97;
address public constant TOKEN_DAI = 0x665D4921fe931C0eA1390Ca4e0C422ba34d26169;
address public constant TOKEN_PEARL = 0x1ef116600bBb2e99Ce6CE96B7E66A0df71AF5980;
address public constant TOKEN_CVR = 0xC716C749106B21aa4e8231b7ec891bdEab9bFB30;
address public constant TOKEN_PEARL = 0xCE1581d7b4bA40176f0e219b2CaC30088Ad50C7A;
address public constant TOKEN_CVR = 0xC0Fd0e8d5c3Bdc2C06b2Ee9FfE81ceCbE1B59364;

// Pearl DeX
address public constant POOL_PEARL_CVR_3000 = 0x6592E84E1903B990C5015F1Ff1A6cc27405EABfB;
address public constant POOL_DAI_USDC_1000 = 0x1933cB66cB5A2b47A93753773C556ab6CA825831;
address public constant POOL_DAI_PEARL_3000 = 0xeC491B6bC5554f76348FB40eEfbf0Ed60cd22Bd2;
address public constant POOL_PEARL_USDC_3000 = 0x76994b9683e17B0A9Ed344fCD432A8E3BF7E22C1;
address public constant POOL_PEARL_USDC_3000 = 0xd7e172f7e2F60B6438ffC2e52434150878644469;
// Trident TDT-DAI-USDC
address public constant LIQUID_BOX_DAI_USDC = 0x8B9184243B8a787eaff8C304b17ED23fFD6F8c23;
address public constant LIQUID_BOX_DAI_USDC = 0xAB5a4189e947E0e3EbEc25637f73deb55f6CEEA9;

// IGaugeV2ALM
address public constant ALM_GAUGE_DAI_USDC = 0x659f401aE8194e00673fe58367Ed77137542faA3;
address public constant ALM_GAUGE_DAI_USDC = 0x54CbD289B263CD14E6707EcbDa01161c4385DFe3;

// Sturgeon infrastructure
address public constant LIQUIDATOR = 0xE0D142466d1BF88FE23D5D265d76068077E4D6F0;
Expand Down
46 changes: 46 additions & 0 deletions script/DeployCustomIfoVaultAndStrategy.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.21;

import "forge-std/Script.sol";
import "../src/interfaces/IVault.sol";
import "../src/HarvesterVault.sol";
import "../src/interfaces/IMultiPool.sol";
import "../src/IFO.sol";
import "../src/PearlStrategyCustomIFO.sol";


contract DeployCustomIfoVaultAndStrategy is Script {
address public constant CONTROLLER = 0x4F69329E8dE13aA7EAc664368C5858AF6371FA4c;
address public constant STGN = 0x609e0d74fAB81085283df92B563750624054F8bE;
address public constant PEARL_TOKEN = 0xCE1581d7b4bA40176f0e219b2CaC30088Ad50C7A;

function run() external {
address underlying = 0xAB5a4189e947E0e3EbEc25637f73deb55f6CEEA9;
address pearlGauge = 0x54CbD289B263CD14E6707EcbDa01161c4385DFe3;

uint deployerPrivateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(deployerPrivateKey);

IFO ifo = new IFO(12e17);
ifo.setup(CONTROLLER, STGN, PEARL_TOKEN);

address vault = address(
new HarvesterVault(
CONTROLLER,
IERC20(underlying),
"IFO Harvester DAI-USDC custom",
"ifocTDT-DAI-USDC",
4_000
)
);
address strategy = address(new PearlStrategyCustomIFO(address(ifo), vault, pearlGauge, true, address(0)));
IVault(vault).setStrategy(strategy);
address multigauge = IController(CONTROLLER).multigauge();
IGauge(multigauge).addStakingToken(vault);
IController(CONTROLLER).registerVault(vault, true);

vm.stopBroadcast();
}

function testDeploy_() external {}
}
42 changes: 42 additions & 0 deletions script/DeployVaultAndStrategy.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.21;

import "forge-std/Script.sol";
import "../src/interfaces/IVault.sol";
import "../src/HarvesterVault.sol";
import "../src/PearlStrategy.sol";
import "../src/interfaces/IMultiPool.sol";


contract DeployVaultAndStrategy is Script {
address public constant CONTROLLER = 0x4F69329E8dE13aA7EAc664368C5858AF6371FA4c;

function run() external {
address underlying = 0xAB5a4189e947E0e3EbEc25637f73deb55f6CEEA9;
address pearlGauge = 0x54CbD289B263CD14E6707EcbDa01161c4385DFe3;
address compounderVault = 0xE983c2da7Ef6bFFF9b0b10ADdBF2f0E1ed9b5043;

uint deployerPrivateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(deployerPrivateKey);

address vault = address(
new HarvesterVault(
CONTROLLER,
IERC20(underlying),
"Harvester DAI-USDC (New)",
"xTDT-DAI-USDC",
4_000
)
);
address 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);
IController(CONTROLLER).registerVault(vault, true);

vm.stopBroadcast();
}

function testDeploy_() external {}
}
16 changes: 16 additions & 0 deletions script/PrepareFactoryUpgrade.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.21;

import "forge-std/Script.sol";
import "../src/Factory.sol";

contract PrepareFactoryUpgrade is Script {
function run() external {
uint deployerPrivateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(deployerPrivateKey);
new Factory();
vm.stopBroadcast();
}

function testDeploy_() external {}
}
2 changes: 2 additions & 0 deletions src/Factory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import "./interfaces/IMultiPool.sol";
import "./lib/DeployerLib.sol";

contract Factory is Controllable {
string public constant VERSION = '1.0.0';

function init(address controller_) external initializer {
__Controllable_init(controller_);
}
Expand Down
10 changes: 5 additions & 5 deletions src/IFO.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import "./interfaces/IController.sol";
import "./interfaces/IIFO.sol";
import "./interfaces/IStrategyStrict.sol";

/// @title Initial Farm Offering
/// @notice Contract contain all preminted STGN and allowing to change them to LP rewards until tokens exists on the balance.
/// The exchange processed by a fixed immutable rate.
/// Rewards goes directly to governance.
contract IFO is IIFO {
using SafeERC20 for IERC20;

// This contract should contain all preminted STGN and allowing to change them to LP rewards until tokens exists on the balance.
// The exchange will be done by a fixed rate that setup on deploy. Will be not changed later.
// Rewards will be sent directly to governance.

address public stgn;
address public rewardToken;
address public controller;
Expand All @@ -28,7 +28,7 @@ contract IFO is IIFO {
stgn = stgn_;
rewardToken = rewardToken_;
controller = controller_;
// add event
emit Setup(controller_, stgn_, rewardToken_, rate);
}

function exchange(uint amount) external returns (bool, uint) {
Expand Down
8 changes: 4 additions & 4 deletions src/PearlStrategy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import "./interfaces/IVeDistributor.sol";
contract PearlStrategy is StrategyStrictBase {
using SafeERC20 for IERC20;

uint internal constant LIQUIDATOR_PRICE_IMPACT_TOLERANCE = 20_000;

uint public lastHardWork;

address public gauge;
Expand Down Expand Up @@ -80,15 +82,13 @@ contract PearlStrategy is StrategyStrictBase {
(bool exchanged, uint got) = _ifo.exchange(rtReward);
if (exchanged && got > 0) {
multigauge.notifyRewardAmount(vault, controller.stgn(), got);
} /* else {
multigauge.notifyRewardAmount(vault, IGaugeV2ALM(gauge).rewardToken(), rtReward);
}*/
}
} else {
address _compounder = compounder;
ITetuLiquidator l = ITetuLiquidator(controller.liquidator());
address asset = IERC4626(_compounder).asset();
uint b = IERC20(asset).balanceOf(address(this));
l.liquidate(gaugeRewardToken, asset, rtReward, 0);
l.liquidate(gaugeRewardToken, asset, rtReward, LIQUIDATOR_PRICE_IMPACT_TOLERANCE);
uint got = IERC20(asset).balanceOf(address(this)) - b;
if (got > 0) {
uint shares = IERC4626(_compounder).deposit(got, address(this));
Expand Down
131 changes: 131 additions & 0 deletions src/PearlStrategyCustomIFO.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.21;

import "./base/StrategyStrictBase.sol";
import "./interfaces/IVault.sol";
import "./interfaces/IGaugeV2ALM.sol";
import "./interfaces/IController.sol";
import "./interfaces/IIFO.sol";
import "./interfaces/IGauge.sol";
import "./interfaces/ITetuLiquidator.sol";
import "./interfaces/IVeDistributor.sol";

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.°:°•.°+.*•´.*:*.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*/
/* Pearl strategy */
/* Contract for taking care of all necessary actions for the underlying token such us: */
/* Rewards utilization via Liquidator */
/* Compounding - creating more underlying */
/* Sending profit to different destinations */
/* Have rewards/compounding logic, depending on setup case */
/* Strategy should send gas compensation on every compounding */
/* Compensation will be taken from user part of profit but no more than 10% */
/* Compounding should be called no more frequently than 1 per 12h */
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.°:°•.°+.*•´.*:*.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*/

contract PearlStrategyCustomIFO is StrategyStrictBase {
using SafeERC20 for IERC20;

uint internal constant LIQUIDATOR_PRICE_IMPACT_TOLERANCE = 20_000;

uint public lastHardWork;

address public gauge;

address public gaugeRewardToken;

address public ifoAddress;

bool public ifo;

address public compounder;

constructor(address ifoAddress_, address vault_, address gauge_, bool ifo_, address compounder_) StrategyStrictBase(vault_) {
gauge = gauge_;
ifo = ifo_;
ifoAddress = ifoAddress_;
compounder = compounder_;
IERC20(asset).approve(gauge_, type(uint).max);
IController controller = IController(IVault(vault).controller());
address _gaugeRewardToken = IGaugeV2ALM(gauge_).rewardToken();
gaugeRewardToken = _gaugeRewardToken;
if (ifo) {
IERC20(_gaugeRewardToken).approve(ifoAddress_, type(uint).max);
IERC20(controller.stgn()).approve(controller.multigauge(), type(uint).max);
} else {
IERC20(_gaugeRewardToken).approve(controller.liquidator(), type(uint).max);
address compounderAsset = IERC4626(compounder_).asset();
IERC20(compounderAsset).approve(compounder_, type(uint).max);
IERC20(compounder_).approve(controller.multigauge(), type(uint).max);
}
}

function isReadyToHardWork() external view returns (bool) {
return IGaugeV2ALM(gauge).earnedReward(address(this)) > 0;
}

function doHardWork() external /* returns (uint earned, uint lost)*/ {
// claim fees if available
// 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) {
IIFO _ifo = IIFO(ifoAddress);
(bool exchanged, uint got) = _ifo.exchange(rtReward);
if (exchanged && got > 0) {
multigauge.notifyRewardAmount(vault, controller.stgn(), got);
}
} else {
address _compounder = compounder;
ITetuLiquidator l = ITetuLiquidator(controller.liquidator());
address asset = IERC4626(_compounder).asset();
uint b = IERC20(asset).balanceOf(address(this));
l.liquidate(gaugeRewardToken, asset, rtReward, LIQUIDATOR_PRICE_IMPACT_TOLERANCE);
uint got = IERC20(asset).balanceOf(address(this)) - b;
if (got > 0) {
uint shares = IERC4626(_compounder).deposit(got, address(this));
multigauge.notifyRewardAmount(vault, _compounder, shares);
}
}
}

function investedAssets() public view override returns (uint) {
return IGaugeV2ALM(gauge).balanceOf(address(this));
}

function _claim() internal override returns (uint rtReward) {
IERC20 rt = IERC20(gaugeRewardToken);
uint oldBal = rt.balanceOf(address(this));
IGaugeV2ALM(gauge).collectReward();
rtReward = rt.balanceOf(address(this)) - oldBal;
}

function _depositToPool(uint amount) internal override {
IGaugeV2ALM(gauge).deposit(amount);
}

function _emergencyExitFromPool() internal override {
_withdrawAllFromPool();
IERC20(asset).safeTransfer(vault, IERC20(asset).balanceOf(address(this)));
}

function _withdrawFromPool(uint amount) internal override {
IGaugeV2ALM(gauge).withdraw(amount);
IERC20(asset).safeTransfer(vault, amount);
}

function _withdrawAllFromPool() internal override {
_withdrawFromPool(IGaugeV2ALM(gauge).balanceOf(address(this)));
}
}
2 changes: 2 additions & 0 deletions src/interfaces/IIFO.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@
pragma solidity 0.8.21;

interface IIFO {
event Setup(address controller, address stgn, address rewardToken, uint rate);

function exchange(uint amount) external returns (bool, uint);
}

0 comments on commit 3e0a32f

Please sign in to comment.