编译器
0.8.21+commit.d9974bed
文件 1 的 16:AssetsVault.sol
pragma solidity 0.8.21;
import {TransferHelper} from "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
contract AssetsVault {
address public stoneVault;
address public strategyController;
modifier onlyPermit() {
require(
stoneVault == msg.sender || strategyController == msg.sender,
"not permit"
);
_;
}
constructor(address _stoneVault, address _strategyController) {
require(
_stoneVault != address(0) && _strategyController != address(0),
"ZERO ADDRESS"
);
stoneVault = _stoneVault;
strategyController = _strategyController;
}
function deposit() external payable {
require(msg.value != 0, "too small");
}
function withdraw(address _to, uint256 _amount) external onlyPermit {
TransferHelper.safeTransferETH(_to, _amount);
}
function getBalance() external view returns (uint256 amount) {
amount = address(this).balance;
}
receive() external payable {}
}
文件 2 的 16:Context.sol
pragma solidity ^0.8.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
文件 3 的 16:EnumerableSet.sol
pragma solidity ^0.8.0;
library EnumerableSet {
struct Set {
bytes32[] _values;
mapping(bytes32 => uint256) _indexes;
}
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
function _remove(Set storage set, bytes32 value) private returns (bool) {
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
if (lastIndex != toDeleteIndex) {
bytes32 lastValue = set._values[lastIndex];
set._values[toDeleteIndex] = lastValue;
set._indexes[lastValue] = valueIndex;
}
set._values.pop();
delete set._indexes[value];
return true;
} else {
return false;
}
}
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
struct Bytes32Set {
Set _inner;
}
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
assembly {
result := store
}
return result;
}
struct AddressSet {
Set _inner;
}
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
assembly {
result := store
}
return result;
}
struct UintSet {
Set _inner;
}
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
assembly {
result := store
}
return result;
}
}
文件 4 的 16:ICollateral.sol
pragma solidity 0.8.21;
interface ICollateral {
function deposit(
address recipient,
uint256 amount
) external returns (uint256);
function withdraw(address recipient, uint256 amount) external;
function limit() external view returns (uint256);
function totalSupply() external view returns (uint256);
}
文件 5 的 16:IERC20.sol
pragma solidity ^0.8.0;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
文件 6 的 16:ILido.sol
pragma solidity 0.8.21;
interface ILido {
function submit(address _referral) external payable returns (uint256);
function balanceOf(address account) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
}
文件 7 的 16:ILidoWithdrawalQueue.sol
pragma solidity 0.8.21;
interface ILidoWithdrawalQueue {
struct WithdrawalRequestStatus {
uint256 amountOfStETH;
uint256 amountOfShares;
address owner;
uint256 timestamp;
bool isFinalized;
bool isClaimed;
}
function MAX_STETH_WITHDRAWAL_AMOUNT() external view returns (uint256);
function MIN_STETH_WITHDRAWAL_AMOUNT() external view returns (uint256);
function getWithdrawalRequests(
address _owner
) external view returns (uint256[] memory requestsIds);
function getWithdrawalStatus(
uint256[] calldata _requestIds
) external view returns (WithdrawalRequestStatus[] memory statuses);
function requestWithdrawals(
uint256[] calldata _amounts,
address _owner
) external returns (uint256[] memory requestIds);
function claimWithdrawals(
uint256[] calldata _requestIds,
uint256[] calldata _hints
) external;
function claimWithdrawalsTo(
uint256[] calldata _requestIds,
uint256[] calldata _hints,
address _recipient
) external;
function claimWithdrawal(uint256 _requestId) external;
}
文件 8 的 16:IMellowVault.sol
pragma solidity ^0.8.17;
interface IMellowVault {
struct WithdrawalRequest {
address to;
uint256 lpAmount;
bytes32 tokensHash;
uint256[] minAmounts;
uint256 deadline;
uint256 timestamp;
}
struct ProcessWithdrawalsStack {
address[] tokens;
uint128[] ratiosX96;
uint256[] erc20Balances;
uint256 totalSupply;
uint256 totalValue;
uint256 ratiosX96Value;
uint256 timestamp;
uint256 feeD9;
bytes32 tokensHash;
}
function underlyingTokens() external view returns (address[] memory);
function calculateStack()
external
view
returns (ProcessWithdrawalsStack memory s);
function withdrawalRequest(
address user
) external view returns (WithdrawalRequest memory);
function deposit(
address to,
uint256[] memory amounts,
uint256 minLpAmount,
uint256 deadline
) external returns (uint256[] memory actualAmounts, uint256 lpAmount);
function emergencyWithdraw(
uint256[] memory minAmounts,
uint256 deadline
) external returns (uint256[] memory actualAmounts);
function cancelWithdrawalRequest() external;
function registerWithdrawal(
address to,
uint256 lpAmount,
uint256[] memory minAmounts,
uint256 deadline,
uint256 requestDeadline,
bool closePrevious
) external;
function processWithdrawals(
address[] memory users
) external returns (bool[] memory statuses);
}
文件 9 的 16:IWstETH.sol
pragma solidity 0.8.21;
interface IWstETH {
function stETH() external view returns (address);
function getWstETHByStETH(
uint256 _stETHAmount
) external view returns (uint256);
function getStETHByWstETH(
uint256 _wstETHAmount
) external view returns (uint256);
function wrap(uint256 _stETHAmount) external returns (uint256);
function unwrap(uint256 _wstETHAmount) external returns (uint256);
function balanceOf(address account) external view returns (uint256);
}
文件 10 的 16:MellowDepositWstETHStrategy.sol
pragma solidity 0.8.21;
import {TransferHelper} from "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {StrategyV2} from "../strategies/StrategyV2.sol";
import {StrategyController} from "../strategies/StrategyController.sol";
import {ILido} from "../interfaces/ILido.sol";
import {ILidoWithdrawalQueue} from "../interfaces/ILidoWithdrawalQueue.sol";
import {IWstETH} from "../interfaces/IWstETH.sol";
import {IMellowVault} from "../interfaces/IMellowVault.sol";
import {ICollateral} from "../interfaces/ICollateral.sol";
contract MellowDepositWstETHStrategy is StrategyV2 {
address public immutable stETHAddr;
address public immutable wstETHAddr;
address public immutable lidoWithdrawalQueue;
address public immutable mellowVaultAddr;
address public immutable collateralAddr;
uint256 internal immutable MULTIPLIER = 1e18;
event MintToWstETH(
uint256 etherAmount,
uint256 stETHAmount,
uint256 wstETHAmount
);
event WrapToWstETH(uint256 stETHAmount, uint256 wstETHAmount);
event UnwrapToStETH(uint256 wstETHAmount, uint256 stETHAmount);
event DepositIntoMellow(
address indexed vault,
address indexed recipient,
uint256[] amounts,
uint256 share
);
event WithdrawFromMellow(
address indexed vault,
address indexed recipient,
uint256 share
);
event WithdrawFromSymbiotic(
address indexed collateral,
address indexed recipient,
uint256 share,
uint256 amount
);
constructor(
address payable _controller,
address _wstETHAddr,
address _lidoWithdrawalQueue,
address _mellowVaultAddr,
address _collateralAddr,
string memory _name
) StrategyV2(_controller, _name) {
wstETHAddr = _wstETHAddr;
lidoWithdrawalQueue = _lidoWithdrawalQueue;
mellowVaultAddr = _mellowVaultAddr;
collateralAddr = _collateralAddr;
stETHAddr = IWstETH(wstETHAddr).stETH();
}
function deposit() public payable override onlyController {
require(msg.value != 0, "zero value");
latestUpdateTime = block.timestamp;
}
function withdraw(
uint256 _amount
) public override onlyController returns (uint256 actualAmount) {
actualAmount = _withdraw(_amount);
}
function instantWithdraw(
uint256 _amount
) public override onlyController returns (uint256 actualAmount) {
actualAmount = _withdraw(_amount);
}
function clear() public override onlyController returns (uint256 amount) {
uint256 balance = address(this).balance;
if (balance != 0) {
TransferHelper.safeTransferETH(controller, balance);
amount = balance;
}
}
function _withdraw(
uint256 _amount
) internal returns (uint256 actualAmount) {
require(_amount != 0, "zero value");
actualAmount = _amount;
TransferHelper.safeTransferETH(controller, actualAmount);
latestUpdateTime = block.timestamp;
}
function getAllValue() public override returns (uint256 value) {
value = getInvestedValue();
}
function getInvestedValue() public override returns (uint256 value) {
uint256 etherValue = address(this).balance;
uint256 stETHValue = IERC20(stETHAddr).balanceOf(address(this));
(, uint256 claimableValue, uint256 pendingValue) = checkPendingAssets();
uint256 mellowPending = getPendingValueFromMellow();
value =
etherValue +
stETHValue +
claimableValue +
pendingValue +
getWstETHValue() +
getDepositedValue() +
mellowPending;
}
function getWstETHValue() public view returns (uint256 value) {
uint256 wstBalance = IERC20(wstETHAddr).balanceOf(address(this));
uint256 collateralValue = IERC20(collateralAddr).balanceOf(
address(this)
);
value = IWstETH(wstETHAddr).getStETHByWstETH(
wstBalance + collateralValue
);
}
function getDepositedValue() public view returns (uint256 value) {
uint256 lpAmount = IERC20(mellowVaultAddr).balanceOf(address(this));
value = (getLpRate() * lpAmount) / MULTIPLIER;
}
function mintToWstETH(
uint256 _ethAmount,
address _referral
) external onlyOwner returns (uint256 amount) {
require(_ethAmount != 0, "zero");
require(_ethAmount <= address(this).balance, "exceed balance");
ILido lido = ILido(stETHAddr);
uint256 balanceBefore = lido.balanceOf(address(this));
ILido(stETHAddr).submit{value: _ethAmount}(_referral);
uint256 stETHAmount = lido.balanceOf(address(this)) - balanceBefore;
TransferHelper.safeApprove(stETHAddr, wstETHAddr, stETHAmount);
amount = IWstETH(wstETHAddr).wrap(stETHAmount);
emit MintToWstETH(_ethAmount, stETHAmount, amount);
}
function wrapToWstETH(
uint256 _stethAmount
) external onlyOwner returns (uint256 amount) {
require(_stethAmount != 0, "zero");
TransferHelper.safeApprove(stETHAddr, wstETHAddr, _stethAmount);
amount = IWstETH(wstETHAddr).wrap(_stethAmount);
emit WrapToWstETH(_stethAmount, amount);
}
function unwrapToStETH(
uint256 _wstETHAmount
) external onlyOwner returns (uint256 amount) {
require(_wstETHAmount != 0, "zero");
amount = IWstETH(wstETHAddr).unwrap(_wstETHAmount);
emit UnwrapToStETH(_wstETHAmount, amount);
}
function depositIntoMellow(
uint256[] memory _amounts,
uint256 _minLpAmount
) external onlyOwner returns (uint256 lpAmount) {
require(_minLpAmount != 0, "zero lp");
IMellowVault mellowVault = IMellowVault(mellowVaultAddr);
address[] memory underlyingTokens = mellowVault.underlyingTokens();
require(_amounts.length == underlyingTokens.length, "invalid length");
uint256 i;
uint256 total;
for (i; i < _amounts.length; i++) {
if (_amounts[i] > 0) {
TransferHelper.safeApprove(
underlyingTokens[i],
mellowVaultAddr,
_amounts[i]
);
total += _amounts[i];
}
}
require(total != 0, "zero amount");
(, lpAmount) = mellowVault.deposit(
address(this),
_amounts,
_minLpAmount,
block.timestamp
);
emit DepositIntoMellow(
mellowVaultAddr,
address(this),
_amounts,
lpAmount
);
}
function requestWithdrawFromMellow(
uint256 _share,
uint256[] memory _minAmounts
) external onlyOwner {
require(_share != 0, "zero share");
IMellowVault mellowVault = IMellowVault(mellowVaultAddr);
address[] memory underlyingTokens = mellowVault.underlyingTokens();
require(
_minAmounts.length == underlyingTokens.length,
"invalid length"
);
mellowVault.registerWithdrawal(
address(this),
_share,
_minAmounts,
block.timestamp,
type(uint256).max,
true
);
emit WithdrawFromMellow(mellowVaultAddr, address(this), _share);
}
function withdrawFromSymbiotic(
uint256 _share
) external onlyOwner returns (uint256 wstETHAmount) {
require(_share != 0, "zero");
wstETHAmount = IWstETH(wstETHAddr).balanceOf(address(this));
ICollateral(collateralAddr).withdraw(address(this), _share);
wstETHAmount =
IWstETH(wstETHAddr).balanceOf(address(this)) -
wstETHAmount;
emit WithdrawFromSymbiotic(
collateralAddr,
address(this),
_share,
wstETHAmount
);
}
function emergencyWithdrawFromMellow(
uint256[] memory _minAmounts,
uint256 _deadline
) external onlyOwner returns (uint256 wstETHAmount) {
IMellowVault(mellowVaultAddr).emergencyWithdraw(_minAmounts, _deadline);
}
function cancelWithdrawFromMellow() external onlyOwner {
IMellowVault(mellowVaultAddr).cancelWithdrawalRequest();
}
function getPendingValueFromMellow() public view returns (uint256 value) {
IMellowVault.WithdrawalRequest memory withdrawalRequest = IMellowVault(
mellowVaultAddr
).withdrawalRequest(address(this));
value = (getLpRate() * withdrawalRequest.lpAmount) / MULTIPLIER;
}
function requestToEther(
uint256 _amount
) external onlyOwner returns (uint256 etherAmount) {
IERC20 token = IERC20(stETHAddr);
require(_amount != 0, "zero");
require(_amount <= token.balanceOf(address(this)), "exceed balance");
token.approve(lidoWithdrawalQueue, _amount);
ILidoWithdrawalQueue withdrawalQueue = ILidoWithdrawalQueue(
lidoWithdrawalQueue
);
uint256 maxAmountPerRequest = withdrawalQueue
.MAX_STETH_WITHDRAWAL_AMOUNT();
uint256 minAmountPerRequest = withdrawalQueue
.MIN_STETH_WITHDRAWAL_AMOUNT();
uint256[] memory amounts;
if (_amount <= maxAmountPerRequest) {
amounts = new uint256[](1);
amounts[0] = _amount;
} else {
uint256 length = _amount / maxAmountPerRequest + 1;
uint256 remainder = _amount % maxAmountPerRequest;
if (remainder >= minAmountPerRequest) {
amounts = new uint256[](length);
amounts[length - 1] = remainder;
} else {
amounts = new uint256[](length - 1);
}
uint256 i;
for (i; i < length - 1; i++) {
amounts[i] = maxAmountPerRequest;
}
}
uint256[] memory ids = withdrawalQueue.requestWithdrawals(
amounts,
address(this)
);
require(ids.length != 0, "Lido request withdrawal error");
etherAmount = _amount;
}
function claimPendingAssets(uint256[] memory _ids) external onlyOwner {
uint256 length = _ids.length;
require(length != 0, "invalid length");
for (uint256 i; i < length; i++) {
if (_ids[i] == 0) continue;
ILidoWithdrawalQueue(lidoWithdrawalQueue).claimWithdrawal(_ids[i]);
}
}
function claimAllPendingAssets() external onlyOwner {
(uint256[] memory ids, , ) = checkPendingAssets();
uint256 length = ids.length;
for (uint256 i; i < length; i++) {
if (ids[i] == 0) continue;
ILidoWithdrawalQueue(lidoWithdrawalQueue).claimWithdrawal(ids[i]);
}
}
function checkPendingAssets()
public
returns (
uint256[] memory ids,
uint256 totalClaimable,
uint256 totalPending
)
{
ILidoWithdrawalQueue queue = ILidoWithdrawalQueue(lidoWithdrawalQueue);
uint256[] memory allIds = queue.getWithdrawalRequests(address(this));
if (allIds.length == 0) {
return (new uint256[](0), 0, 0);
}
ids = new uint256[](allIds.length);
ILidoWithdrawalQueue.WithdrawalRequestStatus[] memory statuses = queue
.getWithdrawalStatus(allIds);
uint256 j;
uint256 length = statuses.length;
for (uint256 i; i < length; i++) {
ILidoWithdrawalQueue.WithdrawalRequestStatus
memory status = statuses[i];
if (status.isClaimed) {
continue;
}
if (status.isFinalized) {
ids[j++] = allIds[i];
totalClaimable = totalClaimable + status.amountOfStETH;
} else {
totalPending = totalPending + status.amountOfStETH;
}
}
assembly {
mstore(ids, j)
}
}
function getLpRate() public view returns (uint256 rate) {
IMellowVault.ProcessWithdrawalsStack memory stack = IMellowVault(
mellowVaultAddr
).calculateStack();
rate = (stack.totalValue * MULTIPLIER) / stack.totalSupply;
}
receive() external payable {}
}
文件 11 的 16:Ownable.sol
pragma solidity ^0.8.0;
import "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor() {
_transferOwnership(_msgSender());
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 12 的 16:Ownable2Step.sol
pragma solidity ^0.8.0;
import "./Ownable.sol";
abstract contract Ownable2Step is Ownable {
address private _pendingOwner;
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
function pendingOwner() public view virtual returns (address) {
return _pendingOwner;
}
function transferOwnership(address newOwner) public virtual override onlyOwner {
_pendingOwner = newOwner;
emit OwnershipTransferStarted(owner(), newOwner);
}
function _transferOwnership(address newOwner) internal virtual override {
delete _pendingOwner;
super._transferOwnership(newOwner);
}
function acceptOwnership() public virtual {
address sender = _msgSender();
require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner");
_transferOwnership(sender);
}
}
文件 13 的 16:Strategy.sol
pragma solidity 0.8.21;
import {StrategyController} from "../strategies/StrategyController.sol";
abstract contract Strategy {
address payable public immutable controller;
address public governance;
uint256 public latestUpdateTime;
uint256 public bufferTime = 12;
string public name;
modifier onlyGovernance() {
require(governance == msg.sender, "not governace");
_;
}
modifier notAtSameBlock() {
require(
latestUpdateTime + bufferTime <= block.timestamp,
"at the same block"
);
_;
}
event TransferGovernance(address oldOwner, address newOwner);
constructor(address payable _controller, string memory _name) {
require(_controller != address(0), "ZERO ADDRESS");
governance = msg.sender;
controller = _controller;
name = _name;
}
modifier onlyController() {
require(controller == msg.sender, "not controller");
_;
}
function deposit() public payable virtual onlyController notAtSameBlock {}
function withdraw(
uint256 _amount
)
public
virtual
onlyController
notAtSameBlock
returns (uint256 actualAmount)
{}
function instantWithdraw(
uint256 _amount
)
public
virtual
onlyController
notAtSameBlock
returns (uint256 actualAmount)
{}
function clear() public virtual onlyController returns (uint256 amount) {}
function execPendingRequest(
uint256 _amount
) public virtual returns (uint256 amount) {}
function getAllValue() public virtual returns (uint256 value) {}
function getPendingValue() public virtual returns (uint256 value) {}
function getInvestedValue() public virtual returns (uint256 value) {}
function checkPendingStatus()
public
virtual
returns (uint256 pending, uint256 executable)
{}
function setGovernance(address governance_) external onlyGovernance {
emit TransferGovernance(governance, governance_);
governance = governance_;
}
function setBufferTime(uint256 _time) external onlyGovernance {
bufferTime = _time;
}
}
文件 14 的 16:StrategyController.sol
pragma solidity 0.8.21;
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {TransferHelper} from "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
import {Strategy} from "./Strategy.sol";
import {AssetsVault} from "../AssetsVault.sol";
contract StrategyController {
using EnumerableSet for EnumerableSet.AddressSet;
uint256 internal constant ONE_HUNDRED_PERCENT = 1e6;
address public stoneVault;
address payable public immutable assetsVault;
EnumerableSet.AddressSet private strategies;
mapping(address => uint256) public ratios;
struct StrategyDiff {
address strategy;
bool isDeposit;
uint256 amount;
}
modifier onlyVault() {
require(stoneVault == msg.sender, "not vault");
_;
}
constructor(
address payable _assetsVault,
address[] memory _strategies,
uint256[] memory _ratios
) {
require(_assetsVault != address(0), "ZERO ADDRESS");
uint256 length = _strategies.length;
for (uint256 i; i < length; i++) {
require(_strategies[i] != address(0), "ZERO ADDRESS");
}
stoneVault = msg.sender;
assetsVault = _assetsVault;
_initStrategies(_strategies, _ratios);
}
function forceWithdraw(
uint256 _amount
) external onlyVault returns (uint256 actualAmount) {
uint256 balanceBeforeRepay = address(this).balance;
if (balanceBeforeRepay >= _amount) {
_repayToVault();
actualAmount = _amount;
} else {
actualAmount =
_forceWithdraw(_amount - balanceBeforeRepay) +
balanceBeforeRepay;
}
}
function setStrategies(
address[] memory _strategies,
uint256[] memory _ratios
) external onlyVault {
_setStrategies(_strategies, _ratios);
}
function addStrategy(address _strategy) external onlyVault {
require(!strategies.contains(_strategy), "already exist");
strategies.add(_strategy);
}
function rebaseStrategies(
uint256 _in,
uint256 _out
) external payable onlyVault {
_rebase(_in, _out);
}
function destroyStrategy(address _strategy) external onlyVault {
_destoryStrategy(_strategy);
}
function _rebase(uint256 _in, uint256 _out) internal {
require(_in == 0 || _out == 0, "only deposit or withdraw");
if (_in != 0) {
AssetsVault(assetsVault).withdraw(address(this), _in);
}
uint256 total = getAllStrategyValidValue();
if (total < _out) {
total = 0;
} else {
total = total + _in - _out;
}
uint256 length = strategies.length();
StrategyDiff[] memory diffs = new StrategyDiff[](length);
uint256 head;
uint256 tail = length - 1;
for (uint i; i < length; i++) {
address strategy = strategies.at(i);
if (ratios[strategy] == 0) {
_clearStrategy(strategy, true);
continue;
}
uint256 newPosition = (total * ratios[strategy]) /
ONE_HUNDRED_PERCENT;
uint256 position = getStrategyValidValue(strategy);
if (newPosition < position) {
diffs[head] = StrategyDiff(
strategy,
false,
position - newPosition
);
head++;
} else if (newPosition > position) {
diffs[tail] = StrategyDiff(
strategy,
true,
newPosition - position
);
if (tail != 0) {
tail--;
}
}
}
length = diffs.length;
for (uint256 i; i < length; i++) {
StrategyDiff memory diff = diffs[i];
if (diff.amount == 0) {
continue;
}
if (diff.isDeposit) {
if (address(this).balance < diff.amount) {
diff.amount = address(this).balance;
}
_depositToStrategy(diff.strategy, diff.amount);
} else {
_withdrawFromStrategy(diff.strategy, diff.amount);
}
}
_repayToVault();
}
function _repayToVault() internal {
if (address(this).balance != 0) {
TransferHelper.safeTransferETH(assetsVault, address(this).balance);
}
}
function _depositToStrategy(address _strategy, uint256 _amount) internal {
Strategy(_strategy).deposit{value: _amount}();
}
function _withdrawFromStrategy(
address _strategy,
uint256 _amount
) internal {
Strategy(_strategy).withdraw(_amount);
}
function _forceWithdraw(
uint256 _amount
) internal returns (uint256 actualAmount) {
uint256 length = strategies.length();
uint256 allRatios;
for (uint i; i < length; i++) {
address strategy = strategies.at(i);
allRatios += ratios[strategy];
}
for (uint i; i < length; i++) {
address strategy = strategies.at(i);
uint256 withAmount = (_amount * ratios[strategy]) / allRatios;
if (withAmount != 0) {
actualAmount =
Strategy(strategy).instantWithdraw(withAmount) +
actualAmount;
}
}
_repayToVault();
}
function getStrategyValue(
address _strategy
) public returns (uint256 _value) {
return Strategy(_strategy).getAllValue();
}
function getStrategyValidValue(
address _strategy
) public returns (uint256 _value) {
return Strategy(_strategy).getInvestedValue();
}
function getStrategyPendingValue(
address _strategy
) public returns (uint256 _value) {
return Strategy(_strategy).getPendingValue();
}
function getAllStrategiesValue() public returns (uint256 _value) {
uint256 length = strategies.length();
for (uint i; i < length; i++) {
_value = _value + getStrategyValue(strategies.at(i));
}
}
function getAllStrategyValidValue() public returns (uint256 _value) {
uint256 length = strategies.length();
for (uint i; i < length; i++) {
_value = _value + getStrategyValidValue(strategies.at(i));
}
}
function getAllStrategyPendingValue() public returns (uint256 _value) {
uint256 length = strategies.length();
for (uint i; i < length; i++) {
_value = _value + getStrategyPendingValue(strategies.at(i));
}
}
function getStrategies()
public
view
returns (address[] memory addrs, uint256[] memory portions)
{
uint256 length = strategies.length();
addrs = new address[](length);
portions = new uint256[](length);
for (uint256 i; i < length; i++) {
address addr = strategies.at(i);
addrs[i] = addr;
portions[i] = ratios[addr];
}
}
function _initStrategies(
address[] memory _strategies,
uint256[] memory _ratios
) internal {
require(_strategies.length == _ratios.length, "invalid length");
uint256 totalRatio;
uint256 length = _strategies.length;
for (uint i; i < length; i++) {
strategies.add(_strategies[i]);
ratios[_strategies[i]] = _ratios[i];
totalRatio = totalRatio + _ratios[i];
}
require(totalRatio <= ONE_HUNDRED_PERCENT, "exceed 100%");
}
function _setStrategies(
address[] memory _strategies,
uint256[] memory _ratios
) internal {
uint256 length = _strategies.length;
require(length == _ratios.length, "invalid length");
uint256 oldLength = strategies.length();
for (uint i; i < oldLength; i++) {
ratios[strategies.at(i)] = 0;
}
uint256 totalRatio;
for (uint i; i < length; i++) {
require(
Strategy(_strategies[i]).controller() == address(this),
"controller mismatch"
);
strategies.add(_strategies[i]);
ratios[_strategies[i]] = _ratios[i];
totalRatio = totalRatio + _ratios[i];
}
require(totalRatio <= ONE_HUNDRED_PERCENT, "exceed 100%");
}
function clearStrategy(address _strategy) public onlyVault {
_clearStrategy(_strategy, false);
}
function _clearStrategy(address _strategy, bool _isRebase) internal {
Strategy(_strategy).clear();
if (!_isRebase) {
_repayToVault();
}
}
function _destoryStrategy(address _strategy) internal {
require(_couldDestroyStrategy(_strategy), "still active");
strategies.remove(_strategy);
_repayToVault();
}
function _couldDestroyStrategy(
address _strategy
) internal returns (bool status) {
return
ratios[_strategy] == 0 && Strategy(_strategy).getAllValue() < 1e4;
}
receive() external payable {}
}
文件 15 的 16:StrategyV2.sol
pragma solidity 0.8.21;
import {StrategyController} from "../strategies/StrategyController.sol";
import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol";
abstract contract StrategyV2 is Ownable2Step {
address payable public immutable controller;
uint256 public latestUpdateTime;
uint256 public bufferTime;
string public name;
modifier notAtSameBlock() {
require(
latestUpdateTime + bufferTime <= block.timestamp,
"at the same block"
);
_;
}
constructor(address payable _controller, string memory _name) {
require(_controller != address(0), "ZERO ADDRESS");
controller = _controller;
name = _name;
}
modifier onlyController() {
require(controller == msg.sender, "not controller");
_;
}
function deposit() public payable virtual onlyController notAtSameBlock {}
function withdraw(
uint256 _amount
)
public
virtual
onlyController
notAtSameBlock
returns (uint256 actualAmount)
{}
function instantWithdraw(
uint256 _amount
)
public
virtual
onlyController
notAtSameBlock
returns (uint256 actualAmount)
{}
function clear() public virtual onlyController returns (uint256 amount) {}
function getAllValue() public virtual returns (uint256 value) {}
function getPendingValue() public virtual returns (uint256 value) {}
function getInvestedValue() public virtual returns (uint256 value) {}
function setBufferTime(uint256 _time) external onlyOwner {
bufferTime = _time;
}
}
文件 16 的 16:TransferHelper.sol
pragma solidity >=0.6.0;
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
library TransferHelper {
function safeTransferFrom(
address token,
address from,
address to,
uint256 value
) internal {
(bool success, bytes memory data) =
token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'STF');
}
function safeTransfer(
address token,
address to,
uint256 value
) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'ST');
}
function safeApprove(
address token,
address to,
uint256 value
) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.approve.selector, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'SA');
}
function safeTransferETH(address to, uint256 value) internal {
(bool success, ) = to.call{value: value}(new bytes(0));
require(success, 'STE');
}
}
{
"compilationTarget": {
"project:/contracts/strategies/MellowDepositWstETHStrategy.sol": "MellowDepositWstETHStrategy"
},
"evmVersion": "shanghai",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 10
},
"remappings": []
}
[{"inputs":[{"internalType":"address payable","name":"_controller","type":"address"},{"internalType":"address","name":"_wstETHAddr","type":"address"},{"internalType":"address","name":"_lidoWithdrawalQueue","type":"address"},{"internalType":"address","name":"_mellowVaultAddr","type":"address"},{"internalType":"address","name":"_collateralAddr","type":"address"},{"internalType":"string","name":"_name","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"vault","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"share","type":"uint256"}],"name":"DepositIntoMellow","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"etherAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"stETHAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"wstETHAmount","type":"uint256"}],"name":"MintToWstETH","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"wstETHAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"stETHAmount","type":"uint256"}],"name":"UnwrapToStETH","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"vault","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"share","type":"uint256"}],"name":"WithdrawFromMellow","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"collateral","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"share","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"WithdrawFromSymbiotic","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"stETHAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"wstETHAmount","type":"uint256"}],"name":"WrapToWstETH","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"bufferTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelWithdrawFromMellow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"checkPendingAssets","outputs":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256","name":"totalClaimable","type":"uint256"},{"internalType":"uint256","name":"totalPending","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimAllPendingAssets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_ids","type":"uint256[]"}],"name":"claimPendingAssets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"clear","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"collateralAddr","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"controller","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"deposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"},{"internalType":"uint256","name":"_minLpAmount","type":"uint256"}],"name":"depositIntoMellow","outputs":[{"internalType":"uint256","name":"lpAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_minAmounts","type":"uint256[]"},{"internalType":"uint256","name":"_deadline","type":"uint256"}],"name":"emergencyWithdrawFromMellow","outputs":[{"internalType":"uint256","name":"wstETHAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getAllValue","outputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getDepositedValue","outputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getInvestedValue","outputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getLpRate","outputs":[{"internalType":"uint256","name":"rate","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPendingValue","outputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getPendingValueFromMellow","outputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getWstETHValue","outputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"instantWithdraw","outputs":[{"internalType":"uint256","name":"actualAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"latestUpdateTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lidoWithdrawalQueue","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mellowVaultAddr","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_ethAmount","type":"uint256"},{"internalType":"address","name":"_referral","type":"address"}],"name":"mintToWstETH","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"requestToEther","outputs":[{"internalType":"uint256","name":"etherAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_share","type":"uint256"},{"internalType":"uint256[]","name":"_minAmounts","type":"uint256[]"}],"name":"requestWithdrawFromMellow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_time","type":"uint256"}],"name":"setBufferTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stETHAddr","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_wstETHAmount","type":"uint256"}],"name":"unwrapToStETH","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"actualAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_share","type":"uint256"}],"name":"withdrawFromSymbiotic","outputs":[{"internalType":"uint256","name":"wstETHAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_stethAmount","type":"uint256"}],"name":"wrapToWstETH","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"wstETHAddr","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]