Skip to content

Instantly share code, notes, and snippets.

@3docSec
Last active May 8, 2024 14:08
Show Gist options
  • Save 3docSec/a4bc6254f709a6218907a3de370ae84e to your computer and use it in GitHub Desktop.
Save 3docSec/a4bc6254f709a6218907a3de370ae84e to your computer and use it in GitHub Desktop.
LessDupes team's Renzo test setup
// 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)
}
}
}
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;
}
}
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);
}
}
// 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{}
}
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));
}
}
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) {}
}
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
);
}
}
}
@3docSec
Copy link
Author

3docSec commented May 8, 2024

image

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