Skip to content

Commit 5d645a3

Browse files
authored
Merge pull request #2 from sturgeon-protocol/dev
Dev
2 parents c896844 + 5c260cd commit 5d645a3

23 files changed

+802
-267
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ out/
55
# Ignores development broadcast logs
66
!/broadcast
77
/broadcast/*/31337/
8+
/broadcast/*/5/
89
/broadcast/**/dry-run/
910

1011
# Docs

README.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
## Sturgeon contracts
1+
# Sturgeon contracts
22

33
**Under development**
44

@@ -11,3 +11,16 @@ forge test -vv
1111
forge coverage
1212
forge fmt
1313
```
14+
15+
## Deployments
16+
17+
### Goerli testnet
18+
19+
* Controller 0xE3f1d1B8ea9721FF0399cF6c2990A4bE5e4fc023
20+
* IFO 0xE0D142466d1BF88FE23D5D265d76068077E4D6F0
21+
* STGN 0x95b012C1D02c859dab6b302F4b72941Ba4E3C3C3
22+
* veSTGN proxy 0x33222Ee7eAb1aBE6fC1724eAce207fA3Fa62C7C3
23+
* Multigauge 0xee7751bF946Da4cbb39A76fd8dD99a8872871a7F
24+
* IFO Harvester 0x4e02AbD5Aa2731bdD1655D3a0a936912Ea5f0857
25+
* Harvester 0x9FeCf0827e7253F4dd3e9bF92cDd7A0ebC547D16
26+
* CompunderVault 0x0A45e97ACEBa96650F47DA979bde3A8642f26739

chains/PolygonLib.sol

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,25 @@ library PolygonLib {
1818
vestingAmount[1] = 375_000e18;
1919
vestingAmount[1] = 250_000e18;
2020

21-
Controller _c = Controller(DeployLib.deployPlatform(DeployLib.DeployParams({
22-
governance: governance,
23-
ifoRate: 12e17,
24-
vestingClaimant: vestingClaimant,
25-
vestingAmount: vestingAmount,
26-
vestingPeriod: 365 days,
27-
vestingCliff: 180 days,
28-
rewardToken: TOKEN_PEARL
29-
})));
21+
Controller _c = Controller(
22+
DeployLib.deployPlatform(
23+
DeployLib.DeployParams({
24+
governance: governance,
25+
ifoRate: 12e17,
26+
vestingClaimant: vestingClaimant,
27+
vestingAmount: vestingAmount,
28+
vestingPeriod: 365 days,
29+
vestingCliff: 180 days,
30+
rewardToken: TOKEN_PEARL,
31+
liquidator: address(0)
32+
})
33+
)
34+
);
3035

3136
if (showLog) {
32-
console.log('Deployed. Controller:', address(_c));
37+
console.log("Deployed. Controller:", address(_c));
3338
}
3439
}
40+
41+
function testA() public {}
3542
}

chains/TestnetLib.sol

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity ^0.8.21;
3+
4+
import {console} from "forge-std/Test.sol";
5+
import "../script/lib/DeployLib.sol";
6+
import "../test/mock/MockTetuLiquidator.sol";
7+
8+
library TestnetLib {
9+
address public constant TOKEN_WETH = 0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9;
10+
11+
function runDeploy(bool showLog) internal returns(address) {
12+
address governance = 0x3d0c177E035C30bb8681e5859EB98d114b48b935; // test deployer
13+
address[] memory vestingClaimant = new address[](3);
14+
uint[] memory vestingAmount = new uint[](3);
15+
vestingClaimant[0] = 0x520Ab98a23100369E5280d214799b1E1c0123045; // Claw
16+
vestingClaimant[1] = 0xe25e4df0432Ea55Fd76816fD8d4A21226dEE4bFF; // Minion
17+
vestingClaimant[2] = 0xcc16d636dD05b52FF1D8B9CE09B09BC62b11412B; // Tetu
18+
vestingAmount[0] = 375_000e18;
19+
vestingAmount[1] = 375_000e18;
20+
vestingAmount[1] = 250_000e18;
21+
22+
MockTetuLiquidator l = new MockTetuLiquidator();
23+
24+
Controller _c = Controller(
25+
DeployLib.deployPlatform(
26+
DeployLib.DeployParams({
27+
governance: governance,
28+
ifoRate: 12e17,
29+
vestingClaimant: vestingClaimant,
30+
vestingAmount: vestingAmount,
31+
vestingPeriod: 365 days,
32+
vestingCliff: 180 days,
33+
rewardToken: TOKEN_WETH,
34+
liquidator: address(l)
35+
})
36+
)
37+
);
38+
39+
if (showLog) {
40+
console.log("Deployed. Controller:", address(_c));
41+
}
42+
43+
return address(_c);
44+
}
45+
46+
function testA() public {}
47+
}

script/Deploy.Testnet.s.sol

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity ^0.8.21;
3+
4+
import "forge-std/Script.sol";
5+
import "../chains/TestnetLib.sol";
6+
import "../src/HarvesterVault.sol";
7+
import "../src/PearlStrategy.sol";
8+
import "../src/CompounderVault.sol";
9+
import "../test/mock/MockERC20.sol";
10+
import "../test/mock/MockPearlGaugeV2.sol";
11+
12+
contract DeployTestnet is Script {
13+
function run() external {
14+
uint deployerPrivateKey = vm.envUint("PRIVATE_KEY");
15+
vm.startBroadcast(deployerPrivateKey);
16+
console.log("deployer", vm.addr(deployerPrivateKey));
17+
IController controller = IController(TestnetLib.runDeploy(true));
18+
19+
// deploy mocks
20+
address tokenA = address(new MockERC20("Mock Token A", "MOCK_A", 18));
21+
address tokenC = address(new MockERC20("Mock Token C", "MOCK_C", 18));
22+
address tokenD = address(new MockERC20("Mock Token D", "MOCK_D", 18));
23+
IPearlGaugeV2 pearlGauge = IPearlGaugeV2(address(new MockPearlGaugeV2(tokenA, tokenC)));
24+
25+
// deploy IFO harvester
26+
HarvesterVault vault =
27+
new HarvesterVault(address(controller), IERC20(tokenA), "IFO Harvester MOCK_A", "xTokenA", 4_000);
28+
PearlStrategy strategy = new PearlStrategy(address(vault), address(pearlGauge), true, address(0));
29+
vault.setStrategy(address(strategy));
30+
IGauge(controller.multigauge()).addStakingToken(address(vault));
31+
32+
// deploy compounder + harvester
33+
vault = new HarvesterVault(address(controller), IERC20(tokenA), "Harvester MOCK_A", "xTokenA", 4_000);
34+
35+
CompounderVault compounderVault =
36+
new CompounderVault(IERC20(tokenD), "Compounder vault for xTokenA", "xxTokenA");
37+
38+
strategy = new PearlStrategy(address(vault), address(pearlGauge), false, address(compounderVault));
39+
vault.setStrategy(address(strategy));
40+
41+
IGauge(controller.multigauge()).addStakingToken(address(vault));
42+
IMultiPool(controller.multigauge()).registerRewardToken(address(vault), address(compounderVault));
43+
44+
vm.stopBroadcast();
45+
}
46+
47+
function testDeployPolygon() external {}
48+
}

script/lib/DeployLib.sol

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,43 +19,53 @@ library DeployLib {
1919
uint vestingPeriod;
2020
uint vestingCliff;
2121
address rewardToken; // PEARL
22+
address liquidator;
23+
}
24+
25+
struct DeployPlatformVars {
26+
Controller c;
27+
IFO ifo;
28+
uint len;
29+
address[] vesting;
30+
STGN stgn;
2231
}
2332

2433
function deployPlatform(DeployParams memory params) internal returns (address controller) {
25-
Controller _c = new Controller(params.governance);
26-
IFO ifo = new IFO(params.ifoRate); // 12e17
34+
DeployPlatformVars memory v;
35+
v.c = new Controller(params.governance);
36+
v.ifo = new IFO(params.ifoRate); // 12e17
2737

28-
uint len = params.vestingClaimant.length;
29-
address[] memory vesting = new address[](len);
30-
for (uint i; i < len; ++i) {
31-
vesting[i] = address(new Vesting());
38+
v.len = params.vestingClaimant.length;
39+
v.vesting = new address[](v.len);
40+
for (uint i; i < v.len; ++i) {
41+
v.vesting[i] = address(new Vesting());
3242
}
3343

34-
STGN stgn = new STGN(params.governance, address(ifo), 1e26, 5e25, vesting, params.vestingAmount);
35-
ifo.setup(address(_c), address(stgn), params.rewardToken);
44+
v.stgn = new STGN(params.governance, address(v.ifo), 1e26, 5e25, v.vesting, params.vestingAmount);
45+
v.ifo.setup(address(v.c), address(v.stgn), params.rewardToken);
3646

37-
for (uint i; i < len; ++i) {
38-
Vesting(vesting[i]).setup(
39-
address(stgn), params.vestingPeriod, params.vestingCliff, params.vestingClaimant[i]
47+
for (uint i; i < v.len; ++i) {
48+
Vesting(v.vesting[i]).setup(
49+
address(v.stgn), params.vestingPeriod, params.vestingCliff, params.vestingClaimant[i]
4050
);
4151
}
4252

4353
ControllableProxy proxy = new ControllableProxy();
4454
address impl = address(new VeSTGN());
4555
proxy.initProxy(impl);
4656
VeSTGN ve = VeSTGN(address(proxy));
47-
ve.init(address(stgn), 1e18, address(_c));
48-
// assertEq(IProxyControlled(proxy).implementation(), impl);
57+
ve.init(address(v.stgn), 1e18, address(v.c));
58+
// assertEq(IProxyControlled(proxy).implementation(), impl);
4959

5060
proxy = new ControllableProxy();
5161
impl = address(new MultiGauge());
5262
proxy.initProxy(impl);
5363
MultiGauge multigauge = MultiGauge(address(proxy));
54-
multigauge.init(address(_c), address(ve), address(stgn));
64+
multigauge.init(address(v.c), /* address(ve),*/ address(v.stgn));
5565

56-
_c.setup(address(ifo), address(ve), address(stgn), address(multigauge));
66+
v.c.setup(address(v.ifo), address(ve), address(v.stgn), address(multigauge), params.liquidator);
5767

58-
return address(_c);
68+
return address(v.c);
5969
}
6070

6171
function testDeployLib() external {}

src/Compounder.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,11 +139,11 @@ contract Compounder is Controllable {
139139

140140
IController _controller = IController(controller());
141141
uint _delay = delay;
142-
uint vaultsLength = _controller.vaultsListLength();
142+
uint vaultsLength = _controller.harvesterVaultsListLength();
143143
address[] memory _vaults = new address[](vaultsLength);
144144
uint counter;
145145
for (uint i; i < vaultsLength; ++i) {
146-
address vault = _controller.vaults(i);
146+
address vault = _controller.harvesterVaults(i);
147147
if (!excludedVaults[vault]) {
148148
bool strategyNeedHardwork;
149149
IStrategyStrict strategy = IVault(vault).strategy();

src/CompounderVault.sol

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity ^0.8.21;
3+
4+
import "openzeppelin-contracts/contracts/token/ERC20/extensions/ERC4626.sol";
5+
import "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
6+
import "openzeppelin-contracts/contracts/utils/math/Math.sol";
7+
import "openzeppelin-contracts/contracts/utils/ReentrancyGuard.sol";
8+
import "./interfaces/IStrategyStrict.sol";
9+
import "./interfaces/IController.sol";
10+
import "./interfaces/IGauge.sol";
11+
12+
/// @title Compounder ERC4626 tokenized vault implementation
13+
/// @author a17
14+
contract CompounderVault is ERC4626, ReentrancyGuard {
15+
using SafeERC20 for IERC20;
16+
using Math for uint;
17+
18+
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
19+
/* INITIALIZATION */
20+
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
21+
22+
constructor(IERC20 asset_, string memory name_, string memory symbol_) ERC20(name_, symbol_) ERC4626(asset_) {}
23+
24+
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
25+
/* USER ACTIONS */
26+
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
27+
28+
/// @inheritdoc IERC4626
29+
function deposit(uint assets, address receiver) public override nonReentrant returns (uint) {
30+
uint shares = super.deposit(assets, receiver);
31+
_afterDeposit(assets, shares, receiver);
32+
return shares;
33+
}
34+
35+
/// @inheritdoc IERC4626
36+
function mint(uint shares, address receiver) public override nonReentrant returns (uint) {
37+
uint assets = super.mint(shares, receiver);
38+
_afterDeposit(assets, shares, receiver);
39+
return assets;
40+
}
41+
42+
/// @inheritdoc IERC4626
43+
function withdraw(uint assets, address receiver, address owner) public override nonReentrant returns (uint) {
44+
uint maxAssets = maxWithdraw(owner);
45+
if (assets > maxAssets) {
46+
revert ERC4626ExceededMaxWithdraw(owner, assets, maxAssets);
47+
}
48+
49+
uint shares = previewWithdraw(assets);
50+
51+
_beforeWithdraw(assets, shares);
52+
53+
_withdraw(_msgSender(), receiver, owner, assets, shares);
54+
55+
return shares;
56+
}
57+
58+
/// @inheritdoc IERC4626
59+
function redeem(uint shares, address receiver, address owner) public override nonReentrant returns (uint) {
60+
uint maxShares = maxRedeem(owner);
61+
if (shares > maxShares) {
62+
revert ERC4626ExceededMaxRedeem(owner, shares, maxShares);
63+
}
64+
65+
uint assets = previewRedeem(shares);
66+
67+
require(assets != 0, "ZERO_ASSETS");
68+
_beforeWithdraw(assets, shares);
69+
70+
_withdraw(_msgSender(), receiver, owner, assets, shares);
71+
72+
return assets;
73+
}
74+
75+
/// @dev Withdraw all available shares for tx sender.
76+
/// The revert is expected if the balance is higher than `maxRedeem`
77+
/// It suppose to be used only on UI - for on-chain interactions withdraw concrete amount with properly checks.
78+
function withdrawAll() external {
79+
redeem(balanceOf(msg.sender), msg.sender, msg.sender);
80+
}
81+
82+
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
83+
/* VIEW FUNCTIONS */
84+
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
85+
86+
/// @dev Total amount of the underlying asset that is “managed” by Vault
87+
function totalAssets() public view override returns (uint) {
88+
return IERC20(asset()).balanceOf(address(this));
89+
}
90+
91+
/// @dev Price of 1 full share
92+
function sharePrice() external view returns (uint) {
93+
uint units = 10 ** uint(decimals());
94+
uint totalSupply_ = totalSupply();
95+
return totalSupply_ == 0 ? units : units * totalAssets() / totalSupply_;
96+
}
97+
98+
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
99+
/* INTERNAL LOGIC */
100+
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
101+
102+
/// @dev Calculate available to invest amount and send this amount to strategy
103+
function _afterDeposit(uint, /*assets*/ uint, /*shares*/ address receiver) internal {}
104+
105+
/// @dev Internal hook for getting necessary assets from strategy.
106+
function _beforeWithdraw(uint, /*assets*/ uint /*shares*/ ) internal {}
107+
}

0 commit comments

Comments
 (0)