Skip to content

Commit 574f1bd

Browse files
committed
Merge branch 'testnet-3' of https://github.com/lidofinance/vaults-wrapper into feature/fix-solidity-version
2 parents 6461c76 + 25ecb4a commit 574f1bd

File tree

16 files changed

+663
-403
lines changed

16 files changed

+663
-403
lines changed

script/DeployFactory.s.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ contract DeployFactory is Script {
2121
f.stvStETHPoolFactory = address(new StvStETHPoolFactory());
2222
f.withdrawalQueueFactory = address(new WithdrawalQueueFactory());
2323
f.distributorFactory = address(new DistributorFactory());
24-
f.ggvStrategyFactory = address(new GGVStrategyFactory(_ggvTeller, _ggvBoringQueue, _steth, _wsteth));
24+
f.ggvStrategyFactory = address(new GGVStrategyFactory(_ggvTeller, _ggvBoringQueue));
2525
f.timelockFactory = address(new TimelockFactory());
2626
}
2727

src/Factory.sol

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {StvPoolFactory} from "./factories/StvPoolFactory.sol";
1010
import {StvStETHPoolFactory} from "./factories/StvStETHPoolFactory.sol";
1111
import {TimelockFactory} from "./factories/TimelockFactory.sol";
1212
import {WithdrawalQueueFactory} from "./factories/WithdrawalQueueFactory.sol";
13+
import {IStrategy} from "./interfaces/IStrategy.sol";
1314
import {IStrategyFactory} from "./interfaces/IStrategyFactory.sol";
1415
import {ILidoLocator} from "./interfaces/core/ILidoLocator.sol";
1516
import {IVaultHub} from "./interfaces/core/IVaultHub.sol";
@@ -491,11 +492,15 @@ contract Factory {
491492
dashboard.grantRole(dashboard.BURN_ROLE(), address(pool));
492493
}
493494

494-
address strategy = address(0);
495+
address strategyProxy = address(0);
495496
if (_intermediate.strategyFactory != address(0)) {
496-
strategy = IStrategyFactory(_intermediate.strategyFactory)
497+
address strategyImpl = IStrategyFactory(_intermediate.strategyFactory)
497498
.deploy(address(pool), _intermediate.strategyDeployBytes);
498-
pool.addToAllowList(strategy);
499+
500+
strategyProxy =
501+
address(new OssifiableProxy(strategyImpl, timelock, abi.encodeCall(IStrategy.initialize, (timelock))));
502+
503+
pool.addToAllowList(strategyProxy);
499504
}
500505

501506
pool.grantRole(DEFAULT_ADMIN_ROLE, timelock);
@@ -512,7 +517,7 @@ contract Factory {
512517
withdrawalQueue: address(withdrawalQueue),
513518
distributor: address(pool.DISTRIBUTOR()),
514519
timelock: _intermediate.timelock,
515-
strategy: strategy
520+
strategy: strategyProxy
516521
});
517522

518523
emit PoolCreated(

src/factories/GGVStrategyFactory.sol

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,21 @@ import {GGVStrategy} from "src/strategy/GGVStrategy.sol";
66
import {StrategyCallForwarder} from "src/strategy/StrategyCallForwarder.sol";
77

88
contract GGVStrategyFactory is IStrategyFactory {
9+
bytes32 public immutable STRATEGY_ID = keccak256("strategy.ggv.v1");
910
address public immutable TELLER;
1011
address public immutable BORING_QUEUE;
11-
address public immutable STETH;
12-
address public immutable WSTETH;
1312

14-
constructor(address _teller, address _boringQueue, address _steth, address _wsteth) {
13+
constructor(address _teller, address _boringQueue) {
1514
require(_teller.code.length > 0, "TELLER: not a contract");
1615
require(_boringQueue.code.length > 0, "BORING_QUEUE: not a contract");
17-
require(_steth.code.length > 0, "STETH: not a contract");
18-
require(_wsteth.code.length > 0, "WSTETH: not a contract");
1916
TELLER = _teller;
2017
BORING_QUEUE = _boringQueue;
21-
STETH = _steth;
22-
WSTETH = _wsteth;
2318
}
2419

2520
function deploy(address _pool, bytes calldata _deployBytes) external returns (address impl) {
2621
// _deployBytes is unused for GGVStrategy, but required by IStrategyFactory interface
2722
_deployBytes;
2823
address strategyCallForwarderImpl = address(new StrategyCallForwarder());
29-
impl = address(new GGVStrategy(strategyCallForwarderImpl, _pool, STETH, WSTETH, TELLER, BORING_QUEUE));
24+
impl = address(new GGVStrategy(STRATEGY_ID, strategyCallForwarderImpl, _pool, TELLER, BORING_QUEUE));
3025
}
3126
}

src/interfaces/IStrategy.sol

Lines changed: 84 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,96 @@
22
pragma solidity 0.8.30;
33

44
interface IStrategy {
5-
event StrategySupplied(address indexed user, uint256 stv, uint256 stethShares, uint256 stethAmount, bytes data);
6-
event StrategyExitRequested(address indexed user, bytes32 requestId, uint256 stethSharesToBurn, bytes data);
7-
event StrategyExitFinalized(address indexed user, bytes32 requestId, uint256 stethShares);
5+
event StrategySupplied(
6+
address indexed user, address indexed referral, uint256 ethAmount, uint256 stv, uint256 wstethToMint, bytes data
7+
);
8+
event StrategyExitRequested(address indexed user, bytes32 requestId, uint256 wsteth, bytes data);
9+
event StrategyExitFinalized(address indexed user, bytes32 requestId, uint256 wsteth);
810

11+
/**
12+
* @notice Initializes the strategy
13+
* @param _admin The admin address
14+
*/
15+
function initialize(address _admin) external;
16+
17+
/**
18+
* @notice Returns the address of the pool
19+
* @return The address of the pool
20+
*/
921
function POOL() external view returns (address);
1022

11-
/// @notice Supplies stETH to the strategy
12-
function supply(address _referral, bytes calldata _params) external payable;
23+
/**
24+
* @notice Supplies wstETH to the strategy
25+
* @param _referral The referral address
26+
* @param _wstethToMint The amount of wstETH to mint
27+
* @param _params The parameters for the supply
28+
* @return stv The minted amount of stv
29+
*/
30+
function supply(address _referral, uint256 _wstethToMint, bytes calldata _params)
31+
external
32+
payable
33+
returns (uint256 stv);
34+
35+
/**
36+
* @notice Returns the remaining minting capacity shares of a user
37+
* @param _user The user to get the remaining minting capacity shares for
38+
* @param _ethToFund The amount of ETH to fund
39+
* @return stethShares The remaining minting capacity shares
40+
*/
41+
function remainingMintingCapacitySharesOf(address _user, uint256 _ethToFund)
42+
external
43+
view
44+
returns (uint256 stethShares);
1345

14-
/// @notice Requests a withdrawal from the Withdrawal Queue
15-
function requestWithdrawal(
16-
uint256 _stvToWithdraw,
17-
uint256 _stethSharesToBurn,
18-
uint256 _stethSharesToRebalance,
19-
address _receiver
20-
) external returns (uint256 requestId);
46+
/**
47+
* @notice Requests exit from the strategy
48+
* @param _wsteth The amount of wstETH to request exit for
49+
* @param _params The parameters for the exit
50+
* @return requestId The Strategy request id
51+
*/
52+
function requestExitByWsteth(uint256 _wsteth, bytes calldata _params) external returns (bytes32 requestId);
2153

22-
/// @notice Requests a withdrawal from the strategy
23-
function requestExitByStethShares(uint256 stethSharesToBurn, bytes calldata params)
54+
/**
55+
* @notice Finalizes exit from the strategy
56+
* @param requestId The Strategy request id
57+
*/
58+
function finalizeRequestExit(bytes32 requestId) external;
59+
60+
/**
61+
* @notice Burns wstETH to reduce the user's minted stETH shares obligation
62+
* @param _wstethToBurn The amount of wstETH to burn
63+
*/
64+
function burnWsteth(uint256 _wstethToBurn) external;
65+
66+
/**
67+
* @notice Requests a withdrawal from the Withdrawal Queue
68+
* @param _recipient The address to receive the withdrawal
69+
* @param _stvToWithdraw The amount of stv to withdraw
70+
* @param _stethSharesToRebalance The amount of stETH shares to rebalance
71+
* @return requestId The Withdrawal Queue request ID
72+
*/
73+
function requestWithdrawalFromPool(address _recipient, uint256 _stvToWithdraw, uint256 _stethSharesToRebalance)
2474
external
25-
returns (bytes32 requestId);
75+
returns (uint256 requestId);
76+
77+
/**
78+
* @notice Returns the amount of wstETH of a user
79+
* @param _user The user to get the wstETH for
80+
* @return wsteth The amount of wstETH
81+
*/
82+
function wstethOf(address _user) external view returns (uint256);
2683

27-
/// @notice Finalizes a withdrawal from the strategy
28-
function finalizeRequestExit(address receiver, bytes32 requestId) external;
84+
/**
85+
* @notice Returns the amount of stv of a user
86+
* @param _user The user to get the stv for
87+
* @return stv The amount of stv
88+
*/
89+
function stvOf(address _user) external view returns (uint256);
2990

30-
/// @notice Recovers ERC20 tokens from the strategy
31-
function recoverERC20(address _token, address _recipient, uint256 _amount) external;
91+
/**
92+
* @notice Returns the amount of minted stETH shares of a user
93+
* @param _user The user to get the minted stETH shares for
94+
* @return mintedStethShares The amount of minted stETH shares
95+
*/
96+
function mintedStethSharesOf(address _user) external view returns (uint256 mintedStethShares);
3297
}

src/interfaces/IStrategyCallForwarder.sol

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,7 @@ pragma solidity 0.8.30;
33

44
interface IStrategyCallForwarder {
55
function initialize(address _owner) external;
6-
function call(address _target, bytes calldata _data) external payable returns (bytes memory);
7-
function callWithValue(address _target, bytes calldata _data, uint256 _value)
8-
external
9-
payable
10-
returns (bytes memory);
11-
function sendValue(address payable _recipient, uint256 _amount) external payable;
6+
function doCall(address _target, bytes calldata _data) external returns (bytes memory);
7+
function doCallWithValue(address _target, bytes calldata _data, uint256 _value) external returns (bytes memory);
8+
function sendValue(address payable _recipient, uint256 _amount) external;
129
}

src/mock/ggv/GGVMockTeller.sol

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
55

66
import {GGVVaultMock} from "./GGVVaultMock.sol";
77
import {IStETH} from "src/interfaces/core/IStETH.sol";
8+
import {IWstETH} from "src/interfaces/core/IWstETH.sol";
89
import {ITellerWithMultiAssetSupport} from "src/interfaces/ggv/ITellerWithMultiAssetSupport.sol";
910

1011
contract GGVMockTeller is ITellerWithMultiAssetSupport {
@@ -18,6 +19,7 @@ contract GGVMockTeller is ITellerWithMultiAssetSupport {
1819
GGVVaultMock public immutable _vault;
1920
uint256 internal immutable ONE_SHARE;
2021
IStETH public immutable steth;
22+
IWstETH public immutable wsteth;
2123

2224
mapping(ERC20 asset => Asset) public assets;
2325

@@ -27,12 +29,13 @@ contract GGVMockTeller is ITellerWithMultiAssetSupport {
2729
owner = _owner;
2830
_vault = GGVVaultMock(__vault);
2931
steth = IStETH(_steth);
32+
wsteth = IWstETH(_wsteth);
3033

3134
// eq to 10 ** vault.decimals()
3235
ONE_SHARE = 10 ** 18;
3336

3437
_updateAssetData(ERC20(_steth), true, false, 0);
35-
_updateAssetData(ERC20(_wsteth), false, true, 0);
38+
_updateAssetData(ERC20(_wsteth), true, true, 0);
3639
}
3740

3841
function deposit(ERC20 depositAsset, uint256 depositAmount, uint256 minimumMint, address referralAddress)
@@ -47,7 +50,14 @@ contract GGVMockTeller is ITellerWithMultiAssetSupport {
4750
revert("Deposit amount must be greater than 0");
4851
}
4952

50-
uint256 stethShares = steth.getSharesByPooledEth(depositAmount);
53+
uint256 stethShares;
54+
if (address(depositAsset) == address(steth)) {
55+
stethShares = steth.getSharesByPooledEth(depositAmount);
56+
} else if (address(depositAsset) == address(wsteth)) {
57+
stethShares = depositAmount;
58+
} else {
59+
revert("Unsupported asset");
60+
}
5161

5262
// hardcode share calculation for only steth
5363
shares = _vault.getSharesByAssets(stethShares);

src/mock/ggv/GGVVaultMock.sol

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {BorrowedMath} from "./BorrowedMath.sol";
77
import {GGVMockTeller} from "./GGVMockTeller.sol";
88
import {GGVQueueMock} from "./GGVQueueMock.sol";
99
import {IStETH} from "src/interfaces/core/IStETH.sol";
10+
import {IWstETH} from "src/interfaces/core/IWstETH.sol";
1011
import {IBoringOnChainQueue} from "src/interfaces/ggv/IBoringOnChainQueue.sol";
1112
import {ITellerWithMultiAssetSupport} from "src/interfaces/ggv/ITellerWithMultiAssetSupport.sol";
1213

@@ -15,34 +16,64 @@ contract GGVVaultMock is ERC20 {
1516
ITellerWithMultiAssetSupport public immutable TELLER;
1617
GGVQueueMock public immutable BORING_QUEUE;
1718
IStETH public immutable steth;
19+
IWstETH public immutable wsteth;
1820

1921
// steth shares as base vault asset
2022
// real ggv uses weth but it should be okay to peg it to steth shares for mock
2123
uint256 public _totalAssets;
2224

25+
error OnlyOwner();
26+
error OnlyTeller();
27+
error OnlyQueue();
28+
2329
constructor(address _owner, address _steth, address _wsteth) ERC20("GGVVaultMock", "tGGV") {
2430
owner = _owner;
2531
TELLER = ITellerWithMultiAssetSupport(address(new GGVMockTeller(_owner, address(this), _steth, _wsteth)));
2632
BORING_QUEUE = new GGVQueueMock(address(this), _steth, _wsteth, _owner);
2733
steth = IStETH(_steth);
34+
wsteth = IWstETH(_wsteth);
2835

2936
// Mint some initial tokens to the dead address to avoid zero totalSupply issues
3037
_mint(address(0xdead), 1e18);
3138
_totalAssets = 1e18;
3239
}
3340

34-
function rebase(uint256 stethSharesToRebaseWith) external {
35-
require(msg.sender == owner, "Only owner can rebase");
36-
steth.transferSharesFrom(msg.sender, address(this), stethSharesToRebaseWith);
37-
_totalAssets += stethSharesToRebaseWith;
41+
function _onlyOwner() internal view {
42+
if (msg.sender != owner) revert OnlyOwner();
43+
}
44+
45+
function _onlyTeller() internal view {
46+
if (msg.sender != address(TELLER)) revert OnlyTeller();
3847
}
3948

40-
function negativeRebase(uint256 stethSharesToRebaseWith) external {
41-
require(msg.sender == owner, "Only owner can rebase");
49+
function _onlyQueue() internal view {
50+
if (msg.sender != address(BORING_QUEUE)) revert OnlyQueue();
51+
}
52+
53+
function rebaseSteth(uint256 _stethShares) external {
54+
_onlyOwner();
55+
steth.transferSharesFrom(msg.sender, address(this), _stethShares);
56+
_totalAssets += _stethShares;
57+
}
58+
59+
function negativeRebaseSteth(uint256 stethSharesToRebaseWith) external {
60+
_onlyOwner();
4261
steth.transferShares(msg.sender, stethSharesToRebaseWith);
4362
_totalAssets -= stethSharesToRebaseWith;
4463
}
4564

65+
function rebaseWsteth(uint256 wstethAmount) external {
66+
_onlyOwner();
67+
wsteth.transferFrom(msg.sender, address(this), wstethAmount);
68+
_totalAssets += wstethAmount;
69+
}
70+
71+
function negativeRebaseWsteth(uint256 wstethAmount) external {
72+
_onlyOwner();
73+
wsteth.transfer(msg.sender, wstethAmount);
74+
_totalAssets -= wstethAmount;
75+
}
76+
4677
function getSharesByAssets(uint256 assets) public view returns (uint256) {
4778
uint256 supply = totalSupply();
4879
uint256 totalAssets_ = totalAssets();
@@ -59,17 +90,22 @@ contract GGVVaultMock is ERC20 {
5990
}
6091

6192
function depositByTeller(address asset, uint256 shares, uint256 assets, address user) external {
62-
require(msg.sender == address(TELLER), "Only teller can call depositByTeller");
63-
require(asset == address(steth), "Only steth asset supported");
93+
_onlyTeller();
6494

65-
steth.transferSharesFrom(user, address(this), assets);
95+
if (asset == address(steth)) {
96+
steth.transferSharesFrom(user, address(this), assets);
97+
} else if (asset == address(wsteth)) {
98+
wsteth.transferFrom(user, address(this), assets);
99+
} else {
100+
revert("Unsupported asset");
101+
}
66102

67103
_mint(user, shares);
68104
_totalAssets += assets;
69105
}
70106

71107
function burnSharesReturnAssets(ERC20 assetOut, uint256 shares, uint256 assets, address user) external {
72-
require(msg.sender == address(BORING_QUEUE), "Only queue can call burnShares");
108+
_onlyQueue();
73109
_burn(address(BORING_QUEUE), shares);
74110
_totalAssets -= assets;
75111
assetOut.transfer(user, assets);

0 commit comments

Comments
 (0)