编译器
0.8.18+commit.87f61d96
文件 1 的 9:Address.sol
pragma solidity >=0.6.11 <0.9.0;
library Address {
function isContract(address account) internal view returns (bool) {
uint256 size;
assembly { size := extcodesize(account) }
return size > 0;
}
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{ value: amount }("");
require(success, "Address: unable to send value, recipient may have reverted");
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{ value: value }(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
if (success) {
return returndata;
} else {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
文件 2 的 9:Context.sol
pragma solidity >=0.6.11;
abstract contract Context {
function _msgSender() internal view virtual returns (address payable) {
return payable(msg.sender);
}
function _msgData() internal view virtual returns (bytes memory) {
this;
return msg.data;
}
}
文件 3 的 9:IERC20.sol
pragma solidity >=0.6.11;
import "./Context.sol";
import "./SafeMath.sol";
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, 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 sender, address recipient, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
文件 4 的 9:LSDVault.sol
pragma solidity ^0.8.11;
import "communal/ReentrancyGuard.sol";
import "communal/Owned.sol";
import "communal/SafeERC20.sol";
import "communal/TransferHelper.sol";
interface IunshETH {
function minter_mint(address m_address, uint256 m_amount) external;
function minter_burn_from(address b_address, uint256 b_amount) external;
function timelock_address() external returns (address);
function addMinter(address minter_address) external;
function setTimelock(address _timelock_address) external;
function removeMinter(address minter_address) external;
}
interface ILSDVault {
function balanceInUnderlying() external view returns (uint256);
function exit(uint256 amount) external;
function shanghaiTime() external returns(uint256);
}
interface IDarknet {
function checkPrice(address lsd) external view returns (uint256);
}
contract LSDVault is Owned, ReentrancyGuard {
using SafeERC20 for IERC20;
uint256 public shanghaiTime = 1682007600;
address public constant v1VaultAddress = address(0xE76Ffee8722c21b390eebe71b67D95602f58237F);
address public unshETHAddress;
address public unshethZapAddress;
address public swapperAddress;
address public admin;
address public darknetAddress;
address[] public supportedLSDs;
mapping(address => uint256) public lsdIndex;
struct LSDConfig {
uint256 targetWeightBps;
uint256 weightCapBps;
uint256 absoluteCapEth;
}
mapping(address => LSDConfig) public lsdConfigs;
bool public useWeightCaps;
bool public useAbsoluteCaps;
bool public includeV1VaultAssets;
mapping(address => bool) public isEnabled;
uint256 public constant _TIMELOCK = 3 days;
enum TimelockFunctions { MIGRATE, AMM, DARKNET, ZAP }
struct TimelockProposal {
address proposedAddress;
uint256 unlockTime;
}
mapping(TimelockFunctions => TimelockProposal) public timelock;
uint256 public redeemFee = 0;
uint256 public constant maxRedeemFee = 200;
bool public depositsPaused;
bool public migrated = false;
bool public ammEnabled = false;
bool public withdrawalsPaused = false;
uint256 public withdrawalUnpauseTime;
event DepositPauseToggled(bool paused);
event ShanghaiTimeUpdated(uint256 newTime);
event UnshethAddressSet(address unshethAddress);
event UnshethZapAddressSet(address unshethZapAddress);
event AdminSet(address admin);
event LSDAdded(address lsd);
event LSDConfigSet(address lsd, LSDConfig config);
event LSDDisabled(address lsd);
event LSDEnabled(address lsd);
event AbsoluteCapsToggled(bool useAbsoluteCaps);
event WeightCapsToggled(bool useWeightCaps);
event IncludeV1VaultAssetsToggled(bool includeV1Assets);
event RedeemFeeUpdated(uint256 redeemFee);
event TimelockUpdateProposed(TimelockFunctions _fn, address _newAddress, uint256 _unlockTime);
event TimelockUpdateCanceled(TimelockFunctions _fn);
event TimelockUpdateCompleted(TimelockFunctions _fn);
event VdAmmDisabled(address swapper);
event WithdrawalsPaused(uint256 withdrawalUnpauseTime);
event WithdrawalsUnpaused();
constructor(address _owner, address _darknetAddress, address _unshethAddress, address[] memory _lsds) Owned(_owner){
darknetAddress = _darknetAddress;
unshETHAddress = _unshethAddress;
depositsPaused = true;
for(uint256 i=0; i < _lsds.length; i = unchkIncr(i)) {
addLSD(_lsds[i]);
setLSDConfigs(_lsds[i], 2500, 5000, 2500e18);
}
useWeightCaps = false;
useAbsoluteCaps = false;
includeV1VaultAssets = false;
}
modifier onlyZap {
require(msg.sender == unshethZapAddress, "Only the unsheth Zap contract may perform this action");
_;
}
modifier onlyOwnerOrAdmin {
require(msg.sender == owner || msg.sender == admin, "Only the owner or admin may perform this action");
_;
}
modifier postShanghai {
require(block.timestamp >= shanghaiTime + _TIMELOCK, "ShanghaiTime + Timelock has not passed" );
_;
}
modifier onlyWhenPaused {
require(depositsPaused, "Deposits must be paused before performing this action" );
_;
}
modifier timelockUnlocked(TimelockFunctions _fn) {
require(timelock[_fn].unlockTime != 0 && timelock[_fn].unlockTime <= block.timestamp, "Function is timelocked");
require(timelock[_fn].proposedAddress != address(0), "Cannot set zero address");
_;
}
function unchkIncr(uint256 i) private pure returns(uint256) {
unchecked { return i+1; }
}
function setUnshethZap(address _unshethZapAddress) external onlyOwner {
require(unshethZapAddress == address(0), "UnshETH zap address already set" );
unshethZapAddress = _unshethZapAddress;
emit UnshethZapAddressSet(unshethZapAddress);
}
function setAdmin(address _admin) external onlyOwner {
admin = _admin;
emit AdminSet(admin);
}
function addLSD(address _lsd) public onlyOwner onlyWhenPaused {
require(lsdIndex[_lsd] == 0, "Lsd has already been added");
supportedLSDs.push(_lsd);
lsdIndex[_lsd] = supportedLSDs.length-1;
isEnabled[_lsd] = false;
lsdConfigs[_lsd] = LSDConfig(0, 0, 0);
emit LSDAdded(_lsd);
}
function setLSDConfigs(address _lsd, uint256 _targetWeightBps, uint256 _maxWeightBps, uint256 _maxEthCap) public onlyOwner onlyWhenPaused {
require(_targetWeightBps <= _maxWeightBps, "Cannot set target above max weight");
require(_targetWeightBps <= 10000 && _maxWeightBps <= 10000, "Cannot set weight above 1");
lsdConfigs[_lsd] = LSDConfig(_targetWeightBps, _maxWeightBps, _maxEthCap);
emit LSDConfigSet(_lsd, lsdConfigs[_lsd]);
}
function enableLSD(address _lsd) public onlyOwner onlyWhenPaused {
require(IDarknet(darknetAddress).checkPrice(_lsd) > 0, "Configure lsd in darknet before enabling");
require(lsdConfigs[_lsd].targetWeightBps > 0 && lsdConfigs[_lsd].weightCapBps > 0 && lsdConfigs[_lsd].absoluteCapEth > 0, "Set weights before enabling");
isEnabled[_lsd] = true;
emit LSDEnabled(_lsd);
}
function enableAllLSDs() external onlyOwner onlyWhenPaused {
for(uint256 i=0; i<supportedLSDs.length; i=unchkIncr(i)) {
enableLSD(supportedLSDs[i]);
}
}
function disableLSD(address _lsd) external onlyOwner onlyWhenPaused {
lsdConfigs[_lsd] = LSDConfig(0, 0, 0);
isEnabled[_lsd] = false;
emit LSDDisabled(_lsd);
}
function toggleWeightCaps() external onlyOwner {
useWeightCaps = !useWeightCaps;
emit WeightCapsToggled(useWeightCaps);
}
function toggleAbsoluteCaps() external onlyOwner {
useAbsoluteCaps = !useAbsoluteCaps;
emit AbsoluteCapsToggled(useAbsoluteCaps);
}
function toggleV1VaultAssetsForCaps() external onlyOwner {
includeV1VaultAssets = !includeV1VaultAssets;
emit IncludeV1VaultAssetsToggled(includeV1VaultAssets);
}
function unpauseDeposits() external onlyOwner onlyWhenPaused {
uint256 totalTargetWeightBps = 0;
for(uint256 i=0; i < supportedLSDs.length; i = unchkIncr(i)) {
uint256 targetWeightBps = lsdConfigs[supportedLSDs[i]].targetWeightBps;
if(targetWeightBps > 0) {
require(isEnabled[supportedLSDs[i]], "Need to enable LSD with non-zero target weight");
}
totalTargetWeightBps += targetWeightBps;
}
require(totalTargetWeightBps == 10000, "Total target weight should equal 1");
depositsPaused = false;
emit DepositPauseToggled(depositsPaused);
}
function isLsdEnabled(address lsd) public view returns(bool) {
return isEnabled[lsd];
}
function getLsdIndex(address lsd) public view returns(uint256) {
return lsdIndex[lsd];
}
function deposit(address lsd, uint256 amount) external onlyZap nonReentrant {
_deposit(lsd, amount, true);
}
function depositNoCapCheck(address lsd, uint256 amount) external onlyZap nonReentrant {
_deposit(lsd, amount, false);
}
function _deposit(address lsd, uint256 amount, bool checkAgainstCaps) private {
require(depositsPaused == false, "Deposits are paused");
require(migrated == false, "Already migrated, deposit to new vault");
require(isEnabled[lsd], "LSD is disabled");
if(checkAgainstCaps) {
uint256 balance = getCombinedVaultBalance(lsd);
if(useAbsoluteCaps) {
require(balance + amount <= getAbsoluteCap(lsd), "Deposit exceeds absolute cap");
}
if(useWeightCaps) {
require(balance + amount <= getWeightCap(lsd, amount), "Deposit exceeds weight based cap");
}
}
uint256 price = getPrice(lsd);
TransferHelper.safeTransferFrom(lsd, msg.sender, address(this), amount);
IunshETH(unshETHAddress).minter_mint(msg.sender, price*amount/1e18);
}
function getEthConversionRate(address lsd) public view returns(uint256) {
return IDarknet(darknetAddress).checkPrice(lsd);
}
function getPrice(address lsd) public view returns(uint256) {
uint256 rate = getEthConversionRate(lsd);
if(IERC20(unshETHAddress).totalSupply() == 0){
return rate;
}
else {
return 1e18* rate /stakedETHperunshETH();
}
}
function stakedETHperunshETH() public view returns (uint256) {
return 1e18*balanceInUnderlying()/IERC20(unshETHAddress).totalSupply();
}
function balanceInUnderlying() public view returns (uint256) {
uint256 underlyingBalance = 0;
for (uint256 i = 0; i < supportedLSDs.length; i = unchkIncr(i)) {
uint256 rate = getEthConversionRate(supportedLSDs[i]);
underlyingBalance += rate *IERC20(supportedLSDs[i]).balanceOf(address(this))/1e18;
}
return underlyingBalance;
}
function getAbsoluteCap(address lsd) public view returns(uint256) {
if(!useAbsoluteCaps) {
return type(uint256).max;
}
uint256 absoluteCap = 1e18*lsdConfigs[lsd].absoluteCapEth/getEthConversionRate(lsd);
return absoluteCap;
}
function getWeightCap(address lsd, uint256 marginalDeposit) public view returns(uint256) {
if(!useWeightCaps) {
return type(uint256).max;
}
uint256 weightCapBps = lsdConfigs[lsd].weightCapBps;
uint256 rate = getEthConversionRate(lsd);
uint256 marginalDepositInEth = marginalDeposit*rate/1e18;
uint256 v1VaultEthBalance = _getV1VaultEthBalance();
uint256 totalEthBalance = balanceInUnderlying() + v1VaultEthBalance + marginalDepositInEth;
uint256 weightCapInEth = totalEthBalance*weightCapBps/10000;
return 1e18*weightCapInEth/rate;
}
function getEffectiveCap(address lsd, uint256 marginalDeposit) public view returns(uint256) {
uint256 absoluteCap = getAbsoluteCap(lsd);
uint256 weightCap = getWeightCap(lsd, marginalDeposit);
if(weightCap < absoluteCap) {
return weightCap;
} else {
return absoluteCap;
}
}
function getTargetAmount(address lsd, uint256 marginalDeposit) public view returns(uint256) {
uint256 targetWeightBps = lsdConfigs[lsd].targetWeightBps;
uint256 rate = getEthConversionRate(lsd);
uint256 marginalDepositInEth = marginalDeposit*rate/1e18;
uint256 v1VaultEthBalance = _getV1VaultEthBalance();
uint256 totalEthBalance = balanceInUnderlying() + v1VaultEthBalance + marginalDepositInEth;
uint256 targetInEth = totalEthBalance* targetWeightBps /10000;
return 1e18*targetInEth/rate;
}
function _getV1VaultBalance(address lsd) internal view returns(uint256) {
uint256 v1VaultBalance = 0;
if(includeV1VaultAssets) {
v1VaultBalance = IERC20(lsd).balanceOf(v1VaultAddress);
}
return v1VaultBalance;
}
function _getV1VaultEthBalance() internal view returns(uint256) {
uint256 v1VaultEthBalance = 0;
if(includeV1VaultAssets) {
v1VaultEthBalance = ILSDVault(v1VaultAddress).balanceInUnderlying();
}
return v1VaultEthBalance;
}
function getCombinedVaultBalance(address lsd) public view returns(uint256) {
uint256 balance = IERC20(lsd).balanceOf(address(this));
return balance + _getV1VaultBalance(lsd);
}
function remainingRoomToCap(address lsd, uint256 marginalDeposit) public view returns(uint256) {
uint256 combinedBalance = getCombinedVaultBalance(lsd);
uint256 effectiveCap = getEffectiveCap(lsd, marginalDeposit);
if(combinedBalance > effectiveCap) {
return 0;
} else {
return (effectiveCap - combinedBalance);
}
}
function remainingRoomToCapInEthTerms(address lsd, uint256 marginalDepositEth) public view returns(uint256) {
uint256 rate = getEthConversionRate(lsd);
uint256 marginalDeposit = 1e18*marginalDepositEth/rate;
return remainingRoomToCap(lsd,marginalDeposit)*getEthConversionRate(lsd)/1e18;
}
function remainingRoomToTarget(address lsd, uint256 marginalDeposit) public view returns(uint256) {
uint256 combinedBalance = getCombinedVaultBalance(lsd);
uint256 target = getTargetAmount(lsd, marginalDeposit);
if(combinedBalance > target) {
return 0;
} else {
return (target - combinedBalance);
}
}
function remainingRoomToTargetInEthTerms(address lsd, uint256 marginalDepositEth) public view returns(uint256) {
uint256 rate = getEthConversionRate(lsd);
uint256 marginalDeposit = 1e18*marginalDepositEth/rate;
return remainingRoomToTarget(lsd,marginalDeposit)*rate/1e18;
}
function setRedeemFee(uint256 _redeemFee) external onlyOwner {
require(_redeemFee <= maxRedeemFee, "Redeem fee too high");
redeemFee = _redeemFee;
emit RedeemFeeUpdated(redeemFee);
}
function exit(uint256 amount) external nonReentrant {
require(migrated == false, "Already migrated, use new vault to exit");
require(block.timestamp > shanghaiTime, "Cannot exit until shanghaiTime");
require(!withdrawalsPaused || block.timestamp > withdrawalUnpauseTime, "Withdrawals are paused");
require(IERC20(unshETHAddress).balanceOf(msg.sender) >= amount, "Insufficient unshETH");
uint256 shareOfUnsheth = 1e18*amount/IERC20(unshETHAddress).totalSupply();
uint256 fee = shareOfUnsheth*redeemFee/10000;
IunshETH(unshETHAddress).minter_burn_from(msg.sender, amount);
for (uint256 i = 0; i < supportedLSDs.length; i = unchkIncr(i)) {
uint256 lsdBalance = IERC20(supportedLSDs[i]).balanceOf(address(this));
uint256 amountPerLsd = (shareOfUnsheth-fee)*lsdBalance/1e18;
IERC20(supportedLSDs[i]).safeTransfer(msg.sender, amountPerLsd);
}
}
function createTimelockProposal(TimelockFunctions _fn, address _proposedAddress) public onlyOwner {
require(_proposedAddress != address(0), "Cannot propose zero address");
uint256 unlockTime = block.timestamp + _TIMELOCK;
timelock[_fn] = TimelockProposal(_proposedAddress, unlockTime);
emit TimelockUpdateProposed(_fn, _proposedAddress, unlockTime);
}
function cancelTimelockProposal(TimelockFunctions _fn) public onlyOwner {
timelock[_fn] = TimelockProposal(address(0), 0);
emit TimelockUpdateCanceled(_fn);
}
function _completeTimelockProposal(TimelockFunctions _fn) internal onlyOwner {
timelock[_fn] = TimelockProposal(address(0), 0);
emit TimelockUpdateCompleted(_fn);
}
function updateUnshethZapAddress() external onlyOwner timelockUnlocked(TimelockFunctions.ZAP) {
unshethZapAddress = timelock[TimelockFunctions.ZAP].proposedAddress;
_completeTimelockProposal(TimelockFunctions.ZAP);
}
function updateDarknetAddress() external onlyOwner timelockUnlocked(TimelockFunctions.DARKNET) {
darknetAddress = timelock[TimelockFunctions.DARKNET].proposedAddress;
_completeTimelockProposal(TimelockFunctions.DARKNET);
}
function migrateVault() external onlyOwner postShanghai timelockUnlocked(TimelockFunctions.MIGRATE) {
require(IunshETH(unshETHAddress).timelock_address() == address(this), "LSDVault cannot change unshETH minter");
address proposedVaultAddress = timelock[TimelockFunctions.MIGRATE].proposedAddress;
for (uint256 i = 0; i < supportedLSDs.length; i = unchkIncr(i)) {
uint256 balance = IERC20(supportedLSDs[i]).balanceOf(address(this));
IERC20(supportedLSDs[i]).safeTransfer(proposedVaultAddress, balance);
}
IunshETH unshETH = IunshETH(unshETHAddress);
unshETH.addMinter(proposedVaultAddress);
unshETH.setTimelock(proposedVaultAddress);
unshETH.removeMinter(address(this));
migrated = true;
_completeTimelockProposal(TimelockFunctions.MIGRATE);
}
function setVdAmm() external onlyOwner postShanghai timelockUnlocked(TimelockFunctions.AMM) {
if(swapperAddress != address(0)) {
_setApprovals(swapperAddress, 0);
}
address proposedSwapper = timelock[TimelockFunctions.AMM].proposedAddress;
_setApprovals(proposedSwapper, type(uint256).max);
swapperAddress = proposedSwapper;
ammEnabled = true;
_completeTimelockProposal(TimelockFunctions.AMM);
}
function _setApprovals(address spender, uint256 limit) internal {
for (uint256 i = 0; i < supportedLSDs.length; i = unchkIncr(i)) {
TransferHelper.safeApprove(supportedLSDs[i], spender, limit);
}
}
function updateShanghaiTime(uint256 _newTime) external onlyOwnerOrAdmin {
require(_newTime < shanghaiTime + 4 weeks, "Cannot extend more than 4 weeks" );
require(_newTime > block.timestamp, "Cannot set shanghaiTime in the past" );
shanghaiTime = _newTime;
emit ShanghaiTimeUpdated(shanghaiTime);
}
function pauseDeposits() external onlyOwnerOrAdmin {
require(depositsPaused == false, "Already paused" );
depositsPaused = true;
emit DepositPauseToggled(depositsPaused);
}
function pauseWithdrawals(uint256 _unpauseTime) external onlyOwnerOrAdmin {
require(_unpauseTime <= block.timestamp + _TIMELOCK - 1 days, "Cannot pause withdrawals too long");
require(block.timestamp >= withdrawalUnpauseTime + 1 days, "Need 1 day cooldown before pausing again");
withdrawalUnpauseTime = _unpauseTime;
withdrawalsPaused = true;
emit WithdrawalsPaused(withdrawalUnpauseTime);
}
function unpauseWithdrawals() external onlyOwnerOrAdmin {
withdrawalsPaused = false;
emit WithdrawalsUnpaused();
}
function disableVdAmm() external onlyOwnerOrAdmin {
require(swapperAddress != address(0), "Vdamm is not set");
_setApprovals(swapperAddress, 0);
emit VdAmmDisabled(swapperAddress);
}
}
文件 5 的 9:Owned.sol
pragma solidity >=0.6.11;
contract Owned {
address public owner;
address public nominatedOwner;
constructor (address _owner) public {
require(_owner != address(0), "Owner address cannot be 0");
owner = _owner;
emit OwnerChanged(address(0), _owner);
}
function nominateNewOwner(address _owner) external onlyOwner {
nominatedOwner = _owner;
emit OwnerNominated(_owner);
}
function acceptOwnership() external {
require(msg.sender == nominatedOwner, "You must be nominated before you can accept ownership");
emit OwnerChanged(owner, nominatedOwner);
owner = nominatedOwner;
nominatedOwner = address(0);
}
modifier onlyOwner {
require(msg.sender == owner, "Only the contract owner may perform this action");
_;
}
event OwnerNominated(address newOwner);
event OwnerChanged(address oldOwner, address newOwner);
}
文件 6 的 9:ReentrancyGuard.sol
pragma solidity >=0.6.11;
abstract contract ReentrancyGuard {
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor () internal {
_status = _NOT_ENTERED;
}
modifier nonReentrant() {
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
_status = _ENTERED;
_;
_status = _NOT_ENTERED;
}
}
文件 7 的 9:SafeERC20.sol
pragma solidity >=0.6.11;
import "./IERC20.sol";
import "./SafeMath.sol";
import "./Address.sol";
library SafeERC20 {
using SafeMath for uint256;
using Address for address;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function safeApprove(IERC20 token, address spender, uint256 value) internal {
require((value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).add(value);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
文件 8 的 9:SafeMath.sol
pragma solidity >=0.6.11;
library SafeMath {
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
uint256 c = a / b;
return c;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
文件 9 的 9:TransferHelper.sol
pragma solidity >=0.6.11;
library TransferHelper {
function safeApprove(address token, address to, uint value) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: APPROVE_FAILED');
}
function safeTransfer(address token, address to, uint value) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FAILED');
}
function safeTransferFrom(address token, address from, address to, uint value) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FROM_FAILED');
}
function safeTransferETH(address to, uint value) internal {
(bool success,) = to.call{value:value}(new bytes(0));
require(success, 'TransferHelper: ETH_TRANSFER_FAILED');
}
}
{
"compilationTarget": {
"src/LSDVault.sol": "LSDVault"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 9999
},
"remappings": [
":@prb/math/=lib/prb-math/src/",
":@prb/test/=lib/prb-test/src/",
":Common/=lib/Common/",
":ERC20/=lib/ERC20/",
":Governance/=lib/Governance/",
":Math/=lib/Math/",
":Staking/=lib/Staking/",
":Utils/=lib/Utils/",
":communal/=lib/communal/",
":ds-test/=lib/forge-std/lib/ds-test/src/",
":forge-std/=lib/forge-std/src/",
":layerzerolabs/=lib/solidity-examples/",
":local/=src/",
":openzeppelin-contracts/=lib/openzeppelin-contracts/",
":openzeppelin/=lib/openzeppelin/",
":prb-math/=lib/prb-math/src/",
":prb-test/=lib/prb-math/lib/prb-test/src/",
":solidity-examples/=lib/solidity-examples/contracts/",
":solmate/=lib/solmate/src/",
":src/=lib/prb-math/src/"
]
}
[{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_darknetAddress","type":"address"},{"internalType":"address","name":"_unshethAddress","type":"address"},{"internalType":"address[]","name":"_lsds","type":"address[]"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"useAbsoluteCaps","type":"bool"}],"name":"AbsoluteCapsToggled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"admin","type":"address"}],"name":"AdminSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"paused","type":"bool"}],"name":"DepositPauseToggled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"includeV1Assets","type":"bool"}],"name":"IncludeV1VaultAssetsToggled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"lsd","type":"address"}],"name":"LSDAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"lsd","type":"address"},{"components":[{"internalType":"uint256","name":"targetWeightBps","type":"uint256"},{"internalType":"uint256","name":"weightCapBps","type":"uint256"},{"internalType":"uint256","name":"absoluteCapEth","type":"uint256"}],"indexed":false,"internalType":"struct LSDVault.LSDConfig","name":"config","type":"tuple"}],"name":"LSDConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"lsd","type":"address"}],"name":"LSDDisabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"lsd","type":"address"}],"name":"LSDEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerNominated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"redeemFee","type":"uint256"}],"name":"RedeemFeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newTime","type":"uint256"}],"name":"ShanghaiTimeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"enum LSDVault.TimelockFunctions","name":"_fn","type":"uint8"}],"name":"TimelockUpdateCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"enum LSDVault.TimelockFunctions","name":"_fn","type":"uint8"}],"name":"TimelockUpdateCompleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"enum LSDVault.TimelockFunctions","name":"_fn","type":"uint8"},{"indexed":false,"internalType":"address","name":"_newAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"_unlockTime","type":"uint256"}],"name":"TimelockUpdateProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"unshethAddress","type":"address"}],"name":"UnshethAddressSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"unshethZapAddress","type":"address"}],"name":"UnshethZapAddressSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"swapper","type":"address"}],"name":"VdAmmDisabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"useWeightCaps","type":"bool"}],"name":"WeightCapsToggled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"withdrawalUnpauseTime","type":"uint256"}],"name":"WithdrawalsPaused","type":"event"},{"anonymous":false,"inputs":[],"name":"WithdrawalsUnpaused","type":"event"},{"inputs":[],"name":"_TIMELOCK","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_lsd","type":"address"}],"name":"addLSD","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ammEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"balanceInUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum LSDVault.TimelockFunctions","name":"_fn","type":"uint8"}],"name":"cancelTimelockProposal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum LSDVault.TimelockFunctions","name":"_fn","type":"uint8"},{"internalType":"address","name":"_proposedAddress","type":"address"}],"name":"createTimelockProposal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"darknetAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"lsd","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"lsd","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"depositNoCapCheck","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"depositsPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_lsd","type":"address"}],"name":"disableLSD","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"disableVdAmm","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"enableAllLSDs","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_lsd","type":"address"}],"name":"enableLSD","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"exit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"lsd","type":"address"}],"name":"getAbsoluteCap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"lsd","type":"address"}],"name":"getCombinedVaultBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"lsd","type":"address"},{"internalType":"uint256","name":"marginalDeposit","type":"uint256"}],"name":"getEffectiveCap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"lsd","type":"address"}],"name":"getEthConversionRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"lsd","type":"address"}],"name":"getLsdIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"lsd","type":"address"}],"name":"getPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"lsd","type":"address"},{"internalType":"uint256","name":"marginalDeposit","type":"uint256"}],"name":"getTargetAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"lsd","type":"address"},{"internalType":"uint256","name":"marginalDeposit","type":"uint256"}],"name":"getWeightCap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"includeV1VaultAssets","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"lsd","type":"address"}],"name":"isLsdEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"lsdConfigs","outputs":[{"internalType":"uint256","name":"targetWeightBps","type":"uint256"},{"internalType":"uint256","name":"weightCapBps","type":"uint256"},{"internalType":"uint256","name":"absoluteCapEth","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"lsdIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxRedeemFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"migrateVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"migrated","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"nominateNewOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nominatedOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pauseDeposits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_unpauseTime","type":"uint256"}],"name":"pauseWithdrawals","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"redeemFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"lsd","type":"address"},{"internalType":"uint256","name":"marginalDeposit","type":"uint256"}],"name":"remainingRoomToCap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"lsd","type":"address"},{"internalType":"uint256","name":"marginalDepositEth","type":"uint256"}],"name":"remainingRoomToCapInEthTerms","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"lsd","type":"address"},{"internalType":"uint256","name":"marginalDeposit","type":"uint256"}],"name":"remainingRoomToTarget","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"lsd","type":"address"},{"internalType":"uint256","name":"marginalDepositEth","type":"uint256"}],"name":"remainingRoomToTargetInEthTerms","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_admin","type":"address"}],"name":"setAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_lsd","type":"address"},{"internalType":"uint256","name":"_targetWeightBps","type":"uint256"},{"internalType":"uint256","name":"_maxWeightBps","type":"uint256"},{"internalType":"uint256","name":"_maxEthCap","type":"uint256"}],"name":"setLSDConfigs","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_redeemFee","type":"uint256"}],"name":"setRedeemFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_unshethZapAddress","type":"address"}],"name":"setUnshethZap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setVdAmm","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"shanghaiTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakedETHperunshETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"supportedLSDs","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"swapperAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum LSDVault.TimelockFunctions","name":"","type":"uint8"}],"name":"timelock","outputs":[{"internalType":"address","name":"proposedAddress","type":"address"},{"internalType":"uint256","name":"unlockTime","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"toggleAbsoluteCaps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"toggleV1VaultAssetsForCaps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"toggleWeightCaps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpauseDeposits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpauseWithdrawals","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unshETHAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unshethZapAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"updateDarknetAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newTime","type":"uint256"}],"name":"updateShanghaiTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateUnshethZapAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"useAbsoluteCaps","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"useWeightCaps","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"v1VaultAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawalUnpauseTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawalsPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]