-
-
Save 3docSec/a4bc6254f709a6218907a3de370ae84e to your computer and use it in GitHub Desktop.
LessDupes team's Renzo test setup
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.8.0; | |
// original code | |
// https://github.com/optionality/clone-factory/blob/master/contracts/CloneFactory.sol | |
contract MinimalProxy { | |
function clone(address target) external returns (address result) { | |
// convert address to 20 bytes | |
bytes20 targetBytes = bytes20(target); | |
// actual code // | |
// 3d602d80600a3d3981f3363d3d373d3d3d363d73bebebebebebebebebebebebebebebebebebebebe5af43d82803e903d91602b57fd5bf3 | |
// creation code // | |
// copy runtime code into memory and return it | |
// 3d602d80600a3d3981f3 | |
// runtime code // | |
// code to delegatecall to address | |
// 363d3d373d3d3d363d73 address 5af43d82803e903d91602b57fd5bf3 | |
assembly { | |
/* | |
reads the 32 bytes of memory starting at pointer stored in 0x40 | |
In solidity, the 0x40 slot in memory is special: it contains the "free memory pointer" | |
which points to the end of the currently allocated memory. | |
*/ | |
let clone := mload(0x40) | |
// store 32 bytes to memory starting at "clone" | |
mstore( | |
clone, | |
0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000 | |
) | |
/* | |
| 20 bytes | | |
0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000 | |
^ | |
pointer | |
*/ | |
// store 32 bytes to memory starting at "clone" + 20 bytes | |
// 0x14 = 20 | |
mstore(add(clone, 0x14), targetBytes) | |
/* | |
| 20 bytes | 20 bytes | | |
0x3d602d80600a3d3981f3363d3d373d3d3d363d73bebebebebebebebebebebebebebebebebebebebe | |
^ | |
pointer | |
*/ | |
// store 32 bytes to memory starting at "clone" + 40 bytes | |
// 0x28 = 40 | |
mstore( | |
add(clone, 0x28), | |
0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000 | |
) | |
/* | |
| 20 bytes | 20 bytes | 15 bytes | | |
0x3d602d80600a3d3981f3363d3d373d3d3d363d73bebebebebebebebebebebebebebebebebebebebe5af43d82803e903d91602b57fd5bf3 | |
*/ | |
// create new contract | |
// send 0 Ether | |
// code starts at pointer stored in "clone" | |
// code size 0x37 (55 bytes) | |
result := create(0, clone, 0x37) | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity ^0.8.19; | |
import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; | |
contract MockAggrV3 is AggregatorV3Interface { | |
uint8 _decimals; | |
int256 _answer; | |
constructor(uint8 decimals, int256 answer) { | |
_decimals = decimals; | |
_answer = answer; | |
} | |
function decimals() external view returns (uint8) { | |
return _decimals; | |
} | |
function description() external view returns (string memory) { | |
return "Mock aggregator"; | |
} | |
function version() external view returns (uint256) { | |
return 1; | |
} | |
function getRoundData( | |
uint80 _roundId | |
) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) { | |
return (100, _answer, block.timestamp, block.timestamp, 100); | |
} | |
function latestRoundData() | |
external | |
view | |
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) { | |
return (100, _answer, block.timestamp, block.timestamp, 100); | |
} | |
function setAnswer(int256 answer) external { | |
_answer = answer; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity ^0.8.19; | |
import "contracts/EigenLayer/interfaces/IDelegationManager.sol"; | |
// stolen from https://github.com/Layr-Labs/eigenlayer-contracts/blob/dev/src/test/mocks/DelegationManagerMock.sol | |
contract MockDelegationManager is IDelegationManager { | |
IStrategyManager _strategyManager; | |
mapping(address => bool) public isOperator; | |
mapping(address => mapping(IStrategy => uint256)) public operatorShares; | |
constructor(IStrategyManager strategyManager) { | |
_strategyManager = strategyManager; | |
} | |
function setIsOperator(address operator, bool _isOperatorReturnValue) external { | |
isOperator[operator] = _isOperatorReturnValue; | |
} | |
/// @notice returns the total number of shares in `strategy` that are delegated to `operator`. | |
function setOperatorShares(address operator, IStrategy strategy, uint256 shares) external { | |
operatorShares[operator][strategy] = shares; | |
} | |
mapping (address => address) public delegatedTo; | |
function registerAsOperator(OperatorDetails calldata /*registeringOperatorDetails*/, string calldata /*metadataURI*/) external pure {} | |
function updateOperatorMetadataURI(string calldata /*metadataURI*/) external pure {} | |
function updateAVSMetadataURI(string calldata /*metadataURI*/) external pure {} | |
function delegateTo(address operator, SignatureWithExpiry memory /*approverSignatureAndExpiry*/, bytes32 /*approverSalt*/) external { | |
delegatedTo[msg.sender] = operator; | |
} | |
function modifyOperatorDetails(OperatorDetails calldata /*newOperatorDetails*/) external pure {} | |
function delegateToBySignature( | |
address /*staker*/, | |
address /*operator*/, | |
SignatureWithExpiry memory /*stakerSignatureAndExpiry*/, | |
SignatureWithExpiry memory /*approverSignatureAndExpiry*/, | |
bytes32 /*approverSalt*/ | |
) external pure {} | |
function undelegate(address staker) external returns (bytes32[] memory withdrawalRoot) { | |
delegatedTo[staker] = address(0); | |
return withdrawalRoot; | |
} | |
function increaseDelegatedShares(address /*staker*/, IStrategy /*strategy*/, uint256 /*shares*/) external pure {} | |
function decreaseDelegatedShares( | |
address /*staker*/, | |
IStrategy /*strategy*/, | |
uint256 /*shares*/ | |
) external pure {} | |
function operatorDetails(address operator) external pure returns (OperatorDetails memory) { | |
OperatorDetails memory returnValue = OperatorDetails({ | |
earningsReceiver: operator, | |
delegationApprover: operator, | |
stakerOptOutWindowBlocks: 0 | |
}); | |
return returnValue; | |
} | |
function earningsReceiver(address operator) external pure returns (address) { | |
return operator; | |
} | |
function delegationApprover(address operator) external pure returns (address) { | |
return operator; | |
} | |
function stakerOptOutWindowBlocks(address /*operator*/) external pure returns (uint256) { | |
return 0; | |
} | |
function minWithdrawalDelayBlocks() external pure returns (uint256) { | |
return 0; | |
} | |
/** | |
* @notice Minimum delay enforced by this contract per Strategy for completing queued withdrawals. Measured in blocks, and adjustable by this contract's owner, | |
* up to a maximum of `MAX_WITHDRAWAL_DELAY_BLOCKS`. Minimum value is 0 (i.e. no delay enforced). | |
*/ | |
function strategyWithdrawalDelayBlocks(IStrategy /*strategy*/) external pure returns (uint256) { | |
return 0; | |
} | |
function getOperatorShares( | |
address operator, | |
IStrategy[] memory strategies | |
) external view returns (uint256[] memory) {} | |
function getWithdrawalDelay(IStrategy[] calldata /*strategies*/) public pure returns (uint256) { | |
return 0; | |
} | |
function isDelegated(address staker) external view returns (bool) { | |
return (delegatedTo[staker] != address(0)); | |
} | |
function isNotDelegated(address /*staker*/) external pure returns (bool) {} | |
// function isOperator(address /*operator*/) external pure returns (bool) {} | |
function stakerNonce(address /*staker*/) external pure returns (uint256) {} | |
function delegationApproverSaltIsSpent(address /*delegationApprover*/, bytes32 /*salt*/) external pure returns (bool) {} | |
function calculateCurrentStakerDelegationDigestHash(address /*staker*/, address /*operator*/, uint256 /*expiry*/) external view returns (bytes32) {} | |
function calculateStakerDelegationDigestHash(address /*staker*/, uint256 /*stakerNonce*/, address /*operator*/, uint256 /*expiry*/) external view returns (bytes32) {} | |
function calculateDelegationApprovalDigestHash( | |
address /*staker*/, | |
address /*operator*/, | |
address /*_delegationApprover*/, | |
bytes32 /*approverSalt*/, | |
uint256 /*expiry*/ | |
) external view returns (bytes32) {} | |
function calculateStakerDigestHash(address /*staker*/, address /*operator*/, uint256 /*expiry*/) | |
external pure returns (bytes32 stakerDigestHash) {} | |
function calculateApproverDigestHash(address /*staker*/, address /*operator*/, uint256 /*expiry*/) | |
external pure returns (bytes32 approverDigestHash) {} | |
function calculateOperatorAVSRegistrationDigestHash(address /*operator*/, address /*avs*/, bytes32 /*salt*/, uint256 /*expiry*/) | |
external pure returns (bytes32 digestHash) {} | |
function DOMAIN_TYPEHASH() external view returns (bytes32) {} | |
function STAKER_DELEGATION_TYPEHASH() external view returns (bytes32) {} | |
function DELEGATION_APPROVAL_TYPEHASH() external view returns (bytes32) {} | |
function OPERATOR_AVS_REGISTRATION_TYPEHASH() external view returns (bytes32) {} | |
function domainSeparator() external view returns (bytes32) {} | |
function cumulativeWithdrawalsQueued(address staker) external view returns (uint256) {} | |
function calculateWithdrawalRoot(Withdrawal memory withdrawal) external pure returns (bytes32) {} | |
function registerOperatorToAVS(address operator, ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature) external {} | |
function deregisterOperatorFromAVS(address operator) external {} | |
function operatorSaltIsSpent(address avs, bytes32 salt) external view returns (bool) {} | |
function queueWithdrawals( | |
QueuedWithdrawalParams[] calldata queuedWithdrawalParams | |
) external returns (bytes32[] memory) { | |
for(uint i; i < queuedWithdrawalParams.length; i++) { | |
QueuedWithdrawalParams memory w = queuedWithdrawalParams[i]; | |
for (uint j; j < w.strategies.length; j++) { | |
_strategyManager.removeShares(w.withdrawer, w.strategies[i], w.shares[i]); | |
} | |
} | |
bytes32[] memory ret = new bytes32[](1); | |
ret[0] = keccak256("what r u looking at?"); | |
return ret; | |
} | |
function completeQueuedWithdrawal( | |
Withdrawal calldata withdrawal, | |
IERC20[] calldata tokens, | |
uint256 middlewareTimesIndex, | |
bool receiveAsTokens | |
) external {} | |
function completeQueuedWithdrawals( | |
Withdrawal[] calldata withdrawals, | |
IERC20[][] calldata tokens, | |
uint256[] calldata middlewareTimesIndexes, | |
bool[] calldata receiveAsTokens | |
) external {} | |
function migrateQueuedWithdrawals(IStrategyManager.DeprecatedStruct_QueuedWithdrawal[] memory withdrawalsToQueue) external {} | |
// onlyDelegationManager functions in StrategyManager | |
function addShares( | |
IStrategyManager strategyManager, | |
address staker, | |
IERC20 token, | |
IStrategy strategy, | |
uint256 shares | |
) external { | |
strategyManager.addShares(staker, token, strategy, shares); | |
} | |
function removeShares( | |
IStrategyManager strategyManager, | |
address staker, | |
IStrategy strategy, | |
uint256 shares | |
) external { | |
strategyManager.removeShares(staker, strategy, shares); | |
} | |
function withdrawSharesAsTokens( | |
IStrategyManager strategyManager, | |
address recipient, | |
IStrategy strategy, | |
uint256 shares, | |
IERC20 token | |
) external { | |
strategyManager.withdrawSharesAsTokens(recipient, strategy, shares, token); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: BUSL-1.1 | |
pragma solidity ^0.8.9; | |
import "contracts/EigenLayer/interfaces/IEigenPodManager.sol"; | |
// stripped down version from here -> https://github.com/Layr-Labs/eigenlayer-contracts/blob/dev/src/test/mocks/EigenPodManagerMock.sol | |
contract MockEigenPodManager is IEigenPodManager { | |
IStrategy public constant beaconChainETHStrategy = IStrategy(0xbeaC0eeEeeeeEEeEeEEEEeeEEeEeeeEeeEEBEaC0); | |
IBeacon public eigenPodBeacon; | |
IETHPOSDeposit public ethPOS; | |
mapping(address => int256) public podShares; | |
function slasher() external view returns(ISlasher) {} | |
function createPod() external returns(address) {} | |
function stake(bytes calldata /*pubkey*/, bytes calldata /*signature*/, bytes32 /*depositDataRoot*/) external payable {} | |
function recordBeaconChainETHBalanceUpdate(address /*podOwner*/, int256 /*sharesDelta*/) external pure {} | |
function updateBeaconChainOracle(IBeaconChainOracle /*newBeaconChainOracle*/) external pure {} | |
function ownerToPod(address /*podOwner*/) external pure returns(IEigenPod) { | |
return IEigenPod(address(0)); | |
} | |
function getPod(address podOwner) external pure returns(IEigenPod) { | |
return IEigenPod(podOwner); | |
} | |
function beaconChainOracle() external pure returns(IBeaconChainOracle) { | |
return IBeaconChainOracle(address(0)); | |
} | |
function getBlockRootAtTimestamp(uint64 /*timestamp*/) external pure returns(bytes32) { | |
return bytes32(0); | |
} | |
function strategyManager() external pure returns(IStrategyManager) { | |
return IStrategyManager(address(0)); | |
} | |
function hasPod(address /*podOwner*/) external pure returns (bool) { | |
return false; | |
} | |
function pause(uint256 /*newPausedStatus*/) external{} | |
function pauseAll() external{} | |
function paused() external pure returns (uint256) { | |
return 0; | |
} | |
function paused(uint8 /*index*/) external pure returns (bool) { | |
return false; | |
} | |
function setPauserRegistry(IPauserRegistry /*newPauserRegistry*/) external {} | |
function pauserRegistry() external pure returns (IPauserRegistry) { | |
return IPauserRegistry(address(0)); | |
} | |
function unpause(uint256 /*newPausedStatus*/) external{} | |
function podOwnerShares(address podOwner) external view returns (int256) { | |
return podShares[podOwner]; | |
} | |
function setPodOwnerShares(address podOwner, int256 shares) external { | |
podShares[podOwner] = shares; | |
} | |
function addShares(address /*podOwner*/, uint256 shares) external pure returns (uint256) { | |
// this is the "increase in delegateable tokens" | |
return (shares); | |
} | |
function withdrawSharesAsTokens(address podOwner, address destination, uint256 shares) external {} | |
function removeShares(address podOwner, uint256 shares) external {} | |
function numPods() external view returns (uint256) {} | |
function denebForkTimestamp() external pure returns (uint64) { | |
return type(uint64).max; | |
} | |
function setDenebForkTimestamp(uint64 timestamp) external{} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity ^0.8.19; | |
import "contracts/EigenLayer/interfaces/IStrategyManager.sol"; | |
import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | |
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; | |
import "@openzeppelin/contracts/proxy/utils/Initializable.sol"; | |
// stripped down version of this -> https://github.com/Layr-Labs/eigenlayer-contracts/blob/dev/src/contracts/strategies/StrategyBase.sol | |
contract MockStrategy is IStrategy, Initializable { | |
using SafeERC20 for IERC20; | |
uint8 internal constant PAUSED_DEPOSITS = 0; | |
uint8 internal constant PAUSED_WITHDRAWALS = 1; | |
/** | |
* @notice virtual shares used as part of the mitigation of the common 'share inflation' attack vector. | |
* Constant value chosen to reasonably reduce attempted share inflation by the first depositor, while still | |
* incurring reasonably small losses to depositors | |
*/ | |
uint256 internal constant SHARES_OFFSET = 1e3; | |
/** | |
* @notice virtual balance used as part of the mitigation of the common 'share inflation' attack vector | |
* Constant value chosen to reasonably reduce attempted share inflation by the first depositor, while still | |
* incurring reasonably small losses to depositors | |
*/ | |
uint256 internal constant BALANCE_OFFSET = 1e3; | |
/// @notice EigenLayer's StrategyManager contract | |
IStrategyManager public immutable strategyManager; | |
/// @notice The underlying token for shares in this Strategy | |
IERC20 public underlyingToken; | |
/// @notice The total number of extant shares in this Strategy | |
uint256 public totalShares; | |
/// @notice Simply checks that the `msg.sender` is the `strategyManager`, which is an address stored immutably at construction. | |
modifier onlyStrategyManager() { | |
require(msg.sender == address(strategyManager), "StrategyBase.onlyStrategyManager"); | |
_; | |
} | |
/// @notice Since this contract is designed to be initializable, the constructor simply sets `strategyManager`, the only immutable variable. | |
constructor(IStrategyManager _strategyManager) { | |
strategyManager = _strategyManager; | |
_disableInitializers(); | |
} | |
function initialize(address _underlyingToken) public virtual initializer { | |
_initializeStrategyBase(IERC20(_underlyingToken)); | |
} | |
/// @notice Sets the `underlyingToken` and `pauserRegistry` for the strategy. | |
function _initializeStrategyBase( | |
IERC20 _underlyingToken | |
) internal onlyInitializing { | |
underlyingToken = _underlyingToken; | |
} | |
/** | |
* @notice Used to deposit tokens into this Strategy | |
* @param token is the ERC20 token being deposited | |
* @param amount is the amount of token being deposited | |
* @dev This function is only callable by the strategyManager contract. It is invoked inside of the strategyManager's | |
* `depositIntoStrategy` function, and individual share balances are recorded in the strategyManager as well. | |
* @dev Note that the assumption is made that `amount` of `token` has already been transferred directly to this contract | |
* (as performed in the StrategyManager's deposit functions). In particular, setting the `underlyingToken` of this contract | |
* to be a fee-on-transfer token will break the assumption that the amount this contract *received* of the token is equal to | |
* the amount that was input when the transfer was performed (i.e. the amount transferred 'out' of the depositor's balance). | |
* @dev Note that any validation of `token` is done inside `_beforeDeposit`. This can be overridden if needed. | |
* @return newShares is the number of new shares issued at the current exchange ratio. | |
*/ | |
function deposit( | |
IERC20 token, | |
uint256 amount | |
) external virtual override onlyStrategyManager returns (uint256 newShares) { | |
// call hook to allow for any pre-deposit logic | |
_beforeDeposit(token, amount); | |
// copy `totalShares` value to memory, prior to any change | |
uint256 priorTotalShares = totalShares; | |
/** | |
* @notice calculation of newShares *mirrors* `underlyingToShares(amount)`, but is different since the balance of `underlyingToken` | |
* has already been increased due to the `strategyManager` transferring tokens to this strategy prior to calling this function | |
*/ | |
// account for virtual shares and balance | |
uint256 virtualShareAmount = priorTotalShares + SHARES_OFFSET; | |
uint256 virtualTokenBalance = _tokenBalance() + BALANCE_OFFSET; | |
// calculate the prior virtual balance to account for the tokens that were already transferred to this contract | |
uint256 virtualPriorTokenBalance = virtualTokenBalance - amount; | |
newShares = (amount * virtualShareAmount) / virtualPriorTokenBalance; | |
// extra check for correctness / against edge case where share rate can be massively inflated as a 'griefing' sort of attack | |
require(newShares != 0, "StrategyBase.deposit: newShares cannot be zero"); | |
// update total share amount to account for deposit | |
totalShares = (priorTotalShares + newShares); | |
return newShares; | |
} | |
/** | |
* @notice Used to withdraw tokens from this Strategy, to the `recipient`'s address | |
* @param recipient is the address to receive the withdrawn funds | |
* @param token is the ERC20 token being transferred out | |
* @param amountShares is the amount of shares being withdrawn | |
* @dev This function is only callable by the strategyManager contract. It is invoked inside of the strategyManager's | |
* other functions, and individual share balances are recorded in the strategyManager as well. | |
* @dev Note that any validation of `token` is done inside `_beforeWithdrawal`. This can be overridden if needed. | |
*/ | |
function withdraw( | |
address recipient, | |
IERC20 token, | |
uint256 amountShares | |
) external virtual override onlyStrategyManager { | |
// call hook to allow for any pre-withdrawal logic | |
_beforeWithdrawal(recipient, token, amountShares); | |
// copy `totalShares` value to memory, prior to any change | |
uint256 priorTotalShares = totalShares; | |
require( | |
amountShares <= priorTotalShares, | |
"StrategyBase.withdraw: amountShares must be less than or equal to totalShares" | |
); | |
/** | |
* @notice calculation of amountToSend *mirrors* `sharesToUnderlying(amountShares)`, but is different since the `totalShares` has already | |
* been decremented. Specifically, notice how we use `priorTotalShares` here instead of `totalShares`. | |
*/ | |
// account for virtual shares and balance | |
uint256 virtualPriorTotalShares = priorTotalShares + SHARES_OFFSET; | |
uint256 virtualTokenBalance = _tokenBalance() + BALANCE_OFFSET; | |
// calculate ratio based on virtual shares and balance, being careful to multiply before dividing | |
uint256 amountToSend = (virtualTokenBalance * amountShares) / virtualPriorTotalShares; | |
// Decrease the `totalShares` value to reflect the withdrawal | |
totalShares = priorTotalShares - amountShares; | |
_afterWithdrawal(recipient, token, amountToSend); | |
} | |
/** | |
* @notice Called in the external `deposit` function, before any logic is executed. Expected to be overridden if strategies want such logic. | |
* @param token The token being deposited | |
* @param amount The amount of `token` being deposited | |
*/ | |
function _beforeDeposit(IERC20 token, uint256 amount) internal virtual { | |
require(token == underlyingToken, "StrategyBase.deposit: Can only deposit underlyingToken"); | |
} | |
/** | |
* @notice Called in the external `withdraw` function, before any logic is executed. Expected to be overridden if strategies want such logic. | |
* @param recipient The address that will receive the withdrawn tokens | |
* @param token The token being withdrawn | |
* @param amountShares The amount of shares being withdrawn | |
*/ | |
function _beforeWithdrawal(address recipient, IERC20 token, uint256 amountShares) internal virtual { | |
require(token == underlyingToken, "StrategyBase.withdraw: Can only withdraw the strategy token"); | |
} | |
/** | |
* @notice Transfers tokens to the recipient after a withdrawal is processed | |
* @dev Called in the external `withdraw` function after all logic is executed | |
* @param recipient The destination of the tokens | |
* @param token The ERC20 being transferred | |
* @param amountToSend The amount of `token` to transfer | |
*/ | |
function _afterWithdrawal(address recipient, IERC20 token, uint256 amountToSend) internal virtual { | |
token.safeTransfer(recipient, amountToSend); | |
} | |
/** | |
* @notice Currently returns a brief string explaining the strategy's goal & purpose, but for more complex | |
* strategies, may be a link to metadata that explains in more detail. | |
*/ | |
function explanation() external pure virtual override returns (string memory) { | |
return "Base Strategy implementation to inherit from for more complex implementations"; | |
} | |
/** | |
* @notice Used to convert a number of shares to the equivalent amount of underlying tokens for this strategy. | |
* @notice In contrast to `sharesToUnderlying`, this function guarantees no state modifications | |
* @param amountShares is the amount of shares to calculate its conversion into the underlying token | |
* @return The amount of underlying tokens corresponding to the input `amountShares` | |
* @dev Implementation for these functions in particular may vary significantly for different strategies | |
*/ | |
function sharesToUnderlyingView(uint256 amountShares) public view virtual override returns (uint256) { | |
// account for virtual shares and balance | |
uint256 virtualTotalShares = totalShares + SHARES_OFFSET; | |
uint256 virtualTokenBalance = _tokenBalance() + BALANCE_OFFSET; | |
// calculate ratio based on virtual shares and balance, being careful to multiply before dividing | |
return (virtualTokenBalance * amountShares) / virtualTotalShares; | |
} | |
/** | |
* @notice Used to convert a number of shares to the equivalent amount of underlying tokens for this strategy. | |
* @notice In contrast to `sharesToUnderlyingView`, this function **may** make state modifications | |
* @param amountShares is the amount of shares to calculate its conversion into the underlying token | |
* @return The amount of underlying tokens corresponding to the input `amountShares` | |
* @dev Implementation for these functions in particular may vary significantly for different strategies | |
*/ | |
function sharesToUnderlying(uint256 amountShares) public view virtual override returns (uint256) { | |
return sharesToUnderlyingView(amountShares); | |
} | |
/** | |
* @notice Used to convert an amount of underlying tokens to the equivalent amount of shares in this strategy. | |
* @notice In contrast to `underlyingToShares`, this function guarantees no state modifications | |
* @param amountUnderlying is the amount of `underlyingToken` to calculate its conversion into strategy shares | |
* @return The amount of shares corresponding to the input `amountUnderlying` | |
* @dev Implementation for these functions in particular may vary significantly for different strategies | |
*/ | |
function underlyingToSharesView(uint256 amountUnderlying) public view virtual returns (uint256) { | |
// account for virtual shares and balance | |
uint256 virtualTotalShares = totalShares + SHARES_OFFSET; | |
uint256 virtualTokenBalance = _tokenBalance() + BALANCE_OFFSET; | |
// calculate ratio based on virtual shares and balance, being careful to multiply before dividing | |
return (amountUnderlying * virtualTotalShares) / virtualTokenBalance; | |
} | |
/** | |
* @notice Used to convert an amount of underlying tokens to the equivalent amount of shares in this strategy. | |
* @notice In contrast to `underlyingToSharesView`, this function **may** make state modifications | |
* @param amountUnderlying is the amount of `underlyingToken` to calculate its conversion into strategy shares | |
* @return The amount of shares corresponding to the input `amountUnderlying` | |
* @dev Implementation for these functions in particular may vary significantly for different strategies | |
*/ | |
function underlyingToShares(uint256 amountUnderlying) external view virtual returns (uint256) { | |
return underlyingToSharesView(amountUnderlying); | |
} | |
/** | |
* @notice convenience function for fetching the current underlying value of all of the `user`'s shares in | |
* this strategy. In contrast to `userUnderlying`, this function guarantees no state modifications | |
*/ | |
function userUnderlyingView(address user) external view virtual returns (uint256) { | |
return sharesToUnderlyingView(shares(user)); | |
} | |
/** | |
* @notice convenience function for fetching the current underlying value of all of the `user`'s shares in | |
* this strategy. In contrast to `userUnderlyingView`, this function **may** make state modifications | |
*/ | |
function userUnderlying(address user) external virtual returns (uint256) { | |
return sharesToUnderlying(shares(user)); | |
} | |
/** | |
* @notice convenience function for fetching the current total shares of `user` in this strategy, by | |
* querying the `strategyManager` contract | |
*/ | |
function shares(address user) public view virtual returns (uint256) { | |
return strategyManager.stakerStrategyShares(user, IStrategy(address(this))); | |
} | |
/// @notice Internal function used to fetch this contract's current balance of `underlyingToken`. | |
// slither-disable-next-line dead-code | |
function _tokenBalance() internal view virtual returns (uint256) { | |
return underlyingToken.balanceOf(address(this)); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity ^0.8.19; | |
import "contracts/EigenLayer/interfaces/IStrategyManager.sol"; | |
// stolen from https://github.com/Layr-Labs/eigenlayer-contracts/blob/dev/src/test/mocks/StrategyManagerMock.sol | |
contract MockStrategyManager is IStrategyManager { | |
IDelegationManager public delegation; | |
IEigenPodManager public eigenPodManager; | |
ISlasher public slasher; | |
address public strategyWhitelister; | |
mapping(address => IStrategy[]) public strategiesToReturn; | |
mapping(address => uint256[]) public sharesToReturn; | |
mapping(address => mapping(IStrategy => uint)) public strategyShares; | |
/// @notice Mapping: staker => cumulative number of queued withdrawals they have ever initiated. only increments (doesn't decrement) | |
mapping(address => uint256) public cumulativeWithdrawalsQueued; | |
mapping(IStrategy => bool) public thirdPartyTransfersForbidden; | |
function stakerStrategyList(address staker, uint256 index) external view returns (IStrategy) { | |
return strategiesToReturn[staker][index]; | |
} | |
function setAddresses(IDelegationManager _delegation, IEigenPodManager _eigenPodManager, ISlasher _slasher) external | |
{ | |
delegation = _delegation; | |
slasher = _slasher; | |
eigenPodManager = _eigenPodManager; | |
} | |
function depositIntoStrategy(IStrategy strategy, IERC20 token, uint256 amount) | |
external | |
returns (uint256) { | |
token.transferFrom(msg.sender, address(strategy), amount); | |
uint shares = strategy.deposit(token, amount); | |
strategyShares[msg.sender][strategy] += shares; | |
} | |
function depositBeaconChainETH(address staker, uint256 amount) external{} | |
function recordBeaconChainETHBalanceUpdate(address overcommittedPodOwner, uint256 beaconChainETHStrategyIndex, int256 sharesDelta) | |
external{} | |
function depositIntoStrategyWithSignature( | |
IStrategy strategy, | |
IERC20 token, | |
uint256 amount, | |
address staker, | |
uint256 expiry, | |
bytes memory signature | |
) | |
external | |
returns (uint256 shares) {} | |
/// @notice Returns the current shares of `user` in `strategy` | |
function stakerStrategyShares(address user, IStrategy strategy) external view returns (uint256 shares) { | |
return strategyShares[user][strategy]; | |
} | |
/** | |
* @notice mocks the return value of getDeposits | |
* @param staker staker whose deposits are being mocked | |
* @param _strategiesToReturn strategies to return in getDeposits | |
* @param _sharesToReturn shares to return in getDeposits | |
*/ | |
function setDeposits(address staker, IStrategy[] calldata _strategiesToReturn, uint256[] calldata _sharesToReturn) external { | |
require(_strategiesToReturn.length == _sharesToReturn.length, "StrategyManagerMock: length mismatch"); | |
strategiesToReturn[staker] = _strategiesToReturn; | |
sharesToReturn[staker] = _sharesToReturn; | |
} | |
function setThirdPartyTransfersForbidden(IStrategy strategy, bool value) external { | |
emit UpdatedThirdPartyTransfersForbidden(strategy, value); | |
thirdPartyTransfersForbidden[strategy] = value; | |
} | |
/** | |
* @notice Get all details on the staker's deposits and corresponding shares | |
* @return (staker's strategies, shares in these strategies) | |
*/ | |
function getDeposits(address staker) external view returns (IStrategy[] memory, uint256[] memory) { | |
return (strategiesToReturn[staker], sharesToReturn[staker]); | |
} | |
/// @notice Returns the array of strategies in which `staker` has nonzero shares | |
function stakerStrats(address staker) external view returns (IStrategy[] memory) {} | |
uint256 public stakerStrategyListLengthReturnValue; | |
/// @notice Simple getter function that returns `stakerStrategyList[staker].length`. | |
function stakerStrategyListLength(address /*staker*/) external view returns (uint256) { | |
return stakerStrategyListLengthReturnValue; | |
} | |
function setStakerStrategyListLengthReturnValue(uint256 valueToSet) public { | |
stakerStrategyListLengthReturnValue = valueToSet; | |
} | |
function removeShares(address staker, IStrategy strategy, uint256 shares) external { | |
strategyShares[staker][strategy] -= shares; | |
strategy.withdraw(staker, strategy.underlyingToken(), shares); | |
} | |
function addShares(address staker, IERC20 token, IStrategy strategy, uint256 shares) external {} | |
function withdrawSharesAsTokens(address recipient, IStrategy strategy, uint256 shares, IERC20 token) external {} | |
/// @notice returns the enshrined beaconChainETH Strategy | |
function beaconChainETHStrategy() external view returns (IStrategy) {} | |
// function withdrawalDelayBlocks() external view returns (uint256) {} | |
function addStrategiesToDepositWhitelist( | |
IStrategy[] calldata /*strategiesToWhitelist*/, | |
bool[] calldata /*thirdPartyTransfersForbiddenValues*/ | |
) external pure {} | |
function removeStrategiesFromDepositWhitelist(IStrategy[] calldata /*strategiesToRemoveFromWhitelist*/) external pure {} | |
function migrateQueuedWithdrawal(DeprecatedStruct_QueuedWithdrawal memory queuedWithdrawal) external returns (bool, bytes32) {} | |
function calculateWithdrawalRoot(DeprecatedStruct_QueuedWithdrawal memory queuedWithdrawal) external pure returns (bytes32) {} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity ^0.8.19; | |
import "forge-std/console.sol"; | |
import "forge-std/Test.sol"; | |
import {MinimalProxy} from "./MinimalProxy.sol"; | |
import {MockAggrV3} from "./MockAggrV3.sol"; | |
import {MockEigenPodManager} from "./MockEigenPodManager.sol"; | |
import {MockERC20} from "solmate/test/utils/mocks/MockERC20.sol"; | |
import {MockStrategy} from "./MockStrategy.sol"; | |
import {MockStrategyManager} from "./MockStrategyManager.sol"; | |
import {MockDelegationManager} from "./MockDelegationManager.sol"; | |
import {DepositQueue} from "contracts/Deposits/DepositQueue.sol"; | |
import {EzEthToken} from "contracts/token/EzEthToken.sol"; | |
import {OperatorDelegator, IERC20} from "contracts/delegation/OperatorDelegator.sol"; | |
import {RenzoOracle} from "contracts/Oracle/RenzoOracle.sol"; | |
import {RestakeManager, IOperatorDelegator} from "contracts/RestakeManager.sol"; | |
import {RewardHandler} from "contracts/rewards/RewardHandler.sol"; | |
import {RoleManager} from "contracts/Permissions/RoleManager.sol"; | |
import {WithdrawQueue, WithdrawQueueStorageV1, IWithdrawQueue} from "contracts/Withdraw/WithdrawQueue.sol"; | |
contract Setup is Test { | |
MinimalProxy proxyFactory; | |
address OWNER = address(uint160(uint256(keccak256("OWNER")))); | |
DepositQueue depositQueue; | |
EzEthToken ezETH; | |
OperatorDelegator operatorDelegator1; | |
OperatorDelegator operatorDelegator2; | |
RenzoOracle renzoOracle; | |
RestakeManager restakeManager; | |
RewardHandler rewardHandler; | |
RoleManager roleManager; | |
WithdrawQueue withdrawQueue; | |
MockERC20 stETH; | |
MockERC20 cbETH; | |
MockAggrV3 stEthPriceOracle; | |
MockAggrV3 cbEthPriceOracle; | |
MockEigenPodManager eigenPodManager; | |
MockStrategy stEthStrategy; | |
MockStrategy cbEthStrategy; | |
MockStrategyManager strategyManager; | |
MockDelegationManager delegationManager; | |
function setUp() public { | |
vm.warp(1714563220); | |
proxyFactory = new MinimalProxy(); | |
deployRoleManager(); | |
deployTokens(); | |
deployOracle(); | |
deployStrategyManagers(); | |
deployRestakeManager(); | |
deployOperatorDelegators(); | |
restakeManager.addOperatorDelegator( | |
IOperatorDelegator(address(operatorDelegator1)), | |
70_00 // 70% | |
); | |
restakeManager.addOperatorDelegator( | |
IOperatorDelegator(address(operatorDelegator2)), | |
30_00 // 30% | |
); | |
restakeManager.addCollateralToken(IERC20(address(stETH))); | |
restakeManager.addCollateralToken(IERC20(address(cbETH))); | |
} | |
function deployRoleManager() internal { | |
RoleManager impl = new RoleManager(); | |
roleManager = RoleManager(proxyFactory.clone(address(impl))); | |
roleManager.initialize(OWNER); | |
vm.startPrank(OWNER); | |
roleManager.grantRole(roleManager.RESTAKE_MANAGER_ADMIN(), OWNER); | |
// Allow owner to set oracle addresses | |
roleManager.grantRole(roleManager.OPERATOR_DELEGATOR_ADMIN(), OWNER); | |
roleManager.grantRole(roleManager.ORACLE_ADMIN(), OWNER); | |
roleManager.grantRole(roleManager.RESTAKE_MANAGER_ADMIN(), OWNER); | |
roleManager.grantRole(roleManager.WITHDRAW_QUEUE_ADMIN(), OWNER); | |
roleManager.grantRole(roleManager.NATIVE_ETH_RESTAKE_ADMIN(), OWNER); | |
} | |
function deployTokens() internal { | |
EzEthToken impl = new EzEthToken(); | |
ezETH = EzEthToken(proxyFactory.clone(address(impl))); | |
ezETH.initialize(roleManager); | |
stETH = new MockERC20("Staked ETH", "stETH", 18); // https://etherscan.io/token/0xae7ab96520de3a18e5e111b5eaab095312d7fe84#readProxyContract | |
vm.label(address(stETH), "stETH"); | |
cbETH = new MockERC20("Coinbase ETH", "cbETH", 18); // https://etherscan.io/token/0xbe9895146f7af43049ca1c1ae358b0541ea49704#readProxyContract | |
vm.label(address(cbETH), "cbETH"); | |
} | |
function deployOracle() internal { | |
RenzoOracle impl = new RenzoOracle(); | |
renzoOracle = RenzoOracle(proxyFactory.clone(address(impl))); | |
renzoOracle.initialize(roleManager); | |
stEthPriceOracle = new MockAggrV3( | |
18, | |
1e18 | |
); // values verified here https://etherscan.io/address/0x86392dC19c0b719886221c78AB11eb8Cf5c52812 | |
cbEthPriceOracle = new MockAggrV3( | |
18, | |
1.1e18 | |
); // values verified here https://etherscan.io/address/0xF017fcB346A1885194689bA23Eff2fE6fA5C483b | |
renzoOracle.setOracleAddress( | |
IERC20(address(stETH)), | |
stEthPriceOracle | |
); | |
renzoOracle.setOracleAddress( | |
IERC20(address(cbETH)), | |
cbEthPriceOracle | |
); | |
} | |
function deployStrategyManagers() internal { | |
strategyManager = new MockStrategyManager(); | |
delegationManager = new MockDelegationManager(strategyManager); | |
MockStrategy impl = new MockStrategy(strategyManager); | |
stEthStrategy = MockStrategy(proxyFactory.clone(address(impl))); | |
stEthStrategy.initialize(address(stETH)); | |
cbEthStrategy = MockStrategy(proxyFactory.clone(address(impl))); | |
cbEthStrategy.initialize(address(cbETH)); | |
} | |
function deployRestakeManager() internal { | |
{ | |
DepositQueue impl = new DepositQueue(); | |
depositQueue = DepositQueue(payable(proxyFactory.clone(address(impl)))); | |
depositQueue.initialize(roleManager); | |
} | |
{ | |
RestakeManager impl = new RestakeManager(); | |
restakeManager = RestakeManager(proxyFactory.clone(address(impl))); | |
restakeManager.initialize( | |
roleManager, | |
ezETH, | |
renzoOracle, | |
strategyManager, | |
delegationManager, | |
depositQueue | |
); | |
} | |
{ | |
WithdrawQueue impl = new WithdrawQueue(); | |
withdrawQueue = WithdrawQueue(proxyFactory.clone(address(impl))); | |
WithdrawQueueStorageV1.TokenWithdrawBuffer[] memory buffers = new WithdrawQueueStorageV1.TokenWithdrawBuffer[](1); | |
buffers[0] = | |
WithdrawQueueStorageV1.TokenWithdrawBuffer( | |
0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, | |
10_000 // bufferAmount | |
); | |
withdrawQueue.initialize( | |
roleManager, | |
restakeManager, | |
ezETH, | |
renzoOracle, | |
1 weeks, // guess of WITHDRAW_COOLDOWN | |
buffers | |
); | |
depositQueue.setWithdrawQueue(IWithdrawQueue(address(withdrawQueue))); | |
depositQueue.setRestakeManager(restakeManager); | |
roleManager.grantRole(roleManager.RX_ETH_MINTER_BURNER(), address(restakeManager)); | |
roleManager.grantRole(roleManager.RX_ETH_MINTER_BURNER(), address(withdrawQueue)); | |
} | |
{ | |
RewardHandler impl = new RewardHandler(); | |
rewardHandler = RewardHandler(payable(proxyFactory.clone(address(impl)))); | |
rewardHandler.initialize( | |
roleManager, | |
address(depositQueue) | |
// TODO eigenPodManager | |
); | |
} | |
} | |
function deployOperatorDelegators() internal { | |
{ | |
eigenPodManager = new MockEigenPodManager(); | |
} | |
OperatorDelegator impl = new OperatorDelegator(); | |
{ | |
operatorDelegator1 = OperatorDelegator(payable(proxyFactory.clone(address(impl)))); | |
operatorDelegator1.initialize( | |
roleManager, | |
strategyManager, | |
restakeManager, | |
delegationManager, | |
eigenPodManager | |
); | |
operatorDelegator1.setTokenStrategy( | |
IERC20(address(stETH)), | |
stEthStrategy | |
); | |
operatorDelegator1.setTokenStrategy( | |
IERC20(address(cbETH)), | |
cbEthStrategy | |
); | |
} | |
{ | |
operatorDelegator2 = OperatorDelegator(payable(proxyFactory.clone(address(impl)))); | |
operatorDelegator2.initialize( | |
roleManager, | |
strategyManager, | |
restakeManager, | |
delegationManager, | |
eigenPodManager | |
); | |
operatorDelegator2.setTokenStrategy( | |
IERC20(address(stETH)), | |
stEthStrategy | |
); | |
operatorDelegator2.setTokenStrategy( | |
IERC20(address(cbETH)), | |
cbEthStrategy | |
); | |
} | |
} | |
} | |
Author
3docSec
commented
May 8, 2024

Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment