diff --git a/src/test/integration/IntegrationBase.t.sol b/src/test/integration/IntegrationBase.t.sol index 2442fcf95..a85a8b14e 100644 --- a/src/test/integration/IntegrationBase.t.sol +++ b/src/test/integration/IntegrationBase.t.sol @@ -9,253 +9,32 @@ import "src/test/integration/IntegrationDeployer.t.sol"; import "src/test/integration/Global.t.sol"; import "src/test/integration/users/User.sol"; -abstract contract Flags is Test { - - uint constant FLAG = 1; - - /// @dev Asset flags - /// These are used with _configRand to determine what assets are given - /// to a user when they are created. - uint constant NO_ASSETS = (FLAG << 0); // will have no assets - uint constant HOLDS_LST = (FLAG << 1); // will hold some random amount of LSTs - uint constant HOLDS_ETH = (FLAG << 2); // will hold some random amount of ETH - uint constant HOLDS_MIX = (FLAG << 3); // will hold a mix of LSTs and ETH - - /// @dev Withdrawal flags - /// These are used with _configRand to determine how a user conducts a withdrawal - uint constant FULL_WITHDRAW_SINGLE = (FLAG << 0); // stakers will withdraw all assets using a single queued withdrawal - uint constant FULL_WITHDRAW_MULTI = (FLAG << 1); // stakers will withdraw all assets using multiple queued withdrawals - uint constant PART_WITHDRAW_SINGLE = (FLAG << 2); // stakers will withdraw some, but not all assets - - /// Note: Thought about the following flags (but did not implement) - - /// - /// WithdrawerType (SELF_WITHDRAWER, OTHER_WITHDRAWER) - /// - especially with EPM share handling, this felt like it deserved its own test rather than a fuzzy state - /// CompletionType (AS_TOKENS, AS_SHARES) - /// - same reason as above - /// - /// DepositMethod (DEPOSIT_STD, DEPOSIT_WITH_SIG) - /// - could still do this! - /// WithdrawalMethod (QUEUE_WITHDRAWAL, UNDELEGATE, REDELEGATE) - /// - could still do this! - /// - This would trigger staker.queueWithdrawals to use either `queueWithdrawals` or `undelegate` under the hood - /// - "redelegate" would be like the above, but adding a new `delegateTo` step after undelegating - - bytes32 random; - - bytes stakerAssets; - bytes operatorAssets; - bytes withdrawTypes; - - function _configRand( - uint16 _randomSeed, - uint _stakerAssets, - uint _operatorAssets, - uint _withdrawType - ) internal { - // Using uint16 for the seed type so that if a test fails, it's easier - // to manually use the seed to replay the same test. - // TODO - should we expand the type? - emit log_named_uint("_configRand: set random seed to ", _randomSeed); - random = keccak256(abi.encodePacked(_randomSeed)); - - emit log_named_uint("staker assets: ", _stakerAssets); - emit log_named_uint("operator assets: ", _operatorAssets); - - emit log_named_uint("HOLDS_LST: ", HOLDS_LST); - - // Convert flag bitmaps to bytes of set bits for easy use with _randUint - stakerAssets = _bitmapToBytes(_stakerAssets); - operatorAssets = _bitmapToBytes(_operatorAssets); - withdrawTypes = _bitmapToBytes(_withdrawType); - - emit log("staker assets"); - for (uint i = 0; i < stakerAssets.length; i++) { - emit log_named_uint("- ", uint(uint8(stakerAssets[i]))); - } - - emit log("operator assets"); - for (uint i = 0; i < operatorAssets.length; i++) { - emit log_named_uint("- ", uint(uint8(operatorAssets[i]))); - } - - assertTrue(stakerAssets.length != 0, "_configRand: no staker assets selected"); - assertTrue(operatorAssets.length != 0, "_configRand: no operator assets selected"); - assertTrue(withdrawTypes.length != 0, "_configRand: no withdrawal type selected"); - } +abstract contract IntegrationBase is IntegrationDeployer { /** - * @dev Converts a bitmap into an array of bytes - * @dev Each byte in the input is processed as indicating a single bit to flip in the bitmap + * Gen/Init methods: */ - function _bitmapToBytes(uint bitmap) internal returns (bytes memory bytesArray) { - for (uint i = 0; i < 256; ++i) { - // Mask for i-th bit - uint mask = uint(1 << i); - - // emit log_named_uint("mask: ", mask); - - // If the i-th bit is flipped, add a byte to the return array - if (bitmap & mask != 0) { - emit log_named_uint("i: ", i); - bytesArray = bytes.concat(bytesArray, bytes1(uint8(1 << i))); - } - } - return bytesArray; - } - - /// @dev Uses `random` to return a random uint, with a range given by `min` and `max` (inclusive) - /// @return `min` <= result <= `max` - function _randUint(uint min, uint max) internal returns (uint) { - uint range = max - min + 1; - - // calculate the number of bits needed for the range - uint bitsNeeded = 0; - uint tempRange = range; - while (tempRange > 0) { - bitsNeeded++; - tempRange >>= 1; - } - - // create a mask for the required number of bits - // and extract the value from the hash - uint mask = (1 << bitsNeeded) - 1; - uint value = uint(random) & mask; - - // in case value is out of range, wrap around or retry - while (value >= range) { - value = (value - range) & mask; - } - - // Hash `random` with itself so the next value we generate is different - random = keccak256(abi.encodePacked(random)); - return min + value; - } -} - -abstract contract IntegrationBase is IntegrationDeployer, Flags { - - Global global; - - function setUp() public virtual override { - super.setUp(); - - global = new Global(); - } /** - * Gen/Init methods: + * @dev Create a new user according to configured random variants. + * This user is ready to deposit into some strategies and has some underlying token balances */ - - /// @dev Uses configured randomness to create a Staker-type user - /// @return The new User, the strategies the user has token balances for, and the - /// token balances themselves. function _newStaker() internal returns (User, IStrategy[] memory, uint[] memory) { - // Select assets for the staker - uint assetIdx = _randUint({ min: 0, max: stakerAssets.length - 1 }); - emit log_named_uint("_newStaker: rand idx: ", assetIdx); - - uint flag = uint(uint8(stakerAssets[assetIdx])); - - emit log_named_uint("_newStaker: asset flag is ", flag); - - User staker = new User(delegationManager, strategyManager, eigenPodManager, global); - IStrategy[] memory strategies; - uint[] memory tokenBalances; - - if (flag == NO_ASSETS) { - emit log("_newStaker: creating NO_ASSETS user"); - } else if (flag == HOLDS_LST) { - emit log("_newStaker: creating HOLDS_LST user"); - - strategies = new IStrategy[](1); - tokenBalances = new uint[](1); - - // TODO rand more than 1 strategy - uint idx = _randUint({ min: 0, max: _strategies.length - 1 }); - - IStrategy strategy = _strategies[idx]; - IERC20 underlyingToken = strategy.underlyingToken(); + (User staker, IStrategy[] memory strategies, uint[] memory tokenBalances) = _randUser(); - // TODO better rand token balance - uint balance = _randUint({ min: 1e6, max: 5e6 }); - - // award tokens - StdCheats.deal(address(underlyingToken), address(staker), balance); - - strategies[0] = strategy; - tokenBalances[0] = balance; - - emit log_named_string("_newStaker: user receiving token: ", IERC20Metadata(address(underlyingToken)).name()); - emit log_named_uint("_newStaker: awarded tokens to user: ", balance); - } else if (flag == HOLDS_ETH) { - emit log("_newStaker: creating HOLDS_ETH user"); - // Need to support native eth initialization - revert("unimplemented"); - } else if (flag == HOLDS_MIX) { - emit log("_newStaker: creating HOLDS_MIX user"); - // Need to support native eth initialization - revert("unimplemented"); - } else { - revert("_newStaker: invalid flag"); - } + assert_HasUnderlyingTokenBalances(staker, strategies, tokenBalances, "_newStaker: failed to award token balances"); return (staker, strategies, tokenBalances); } function _newOperator() internal returns (User, IStrategy[] memory, uint[] memory) { - // Select assets for the operator - uint assetIdx = _randUint({ min: 0, max: operatorAssets.length - 1 }); - emit log_named_uint("_newOperator: rand idx: ", assetIdx); - - uint flag = uint(uint8(operatorAssets[assetIdx])); - - emit log_named_uint("_newOperator: asset flag is ", flag); - - User operator = new User(delegationManager, strategyManager, eigenPodManager, global); - IStrategy[] memory strategies; - uint[] memory tokenBalances; - - if (flag == NO_ASSETS) { - emit log("_newOperator: creating NO_ASSETS user"); - } else if (flag == HOLDS_LST) { - emit log("_newOperator: creating HOLDS_LST user"); - - strategies = new IStrategy[](1); - tokenBalances = new uint[](1); - - // TODO rand more than 1 strategy - uint idx = _randUint({ min: 0, max: _strategies.length - 1 }); - - IStrategy strategy = _strategies[idx]; - IERC20 underlyingToken = strategy.underlyingToken(); + (User operator, IStrategy[] memory strategies, uint[] memory tokenBalances) = _randUser(); - // TODO better rand token balance - uint balance = _randUint({ min: 1e6, max: 5e6 }); - - // award tokens - StdCheats.deal(address(underlyingToken), address(operator), balance); - - strategies[0] = strategy; - tokenBalances[0] = balance; - - emit log_named_string("_newOperator: user receiving token: ", IERC20Metadata(address(underlyingToken)).name()); - emit log_named_uint("_newOperator: awarded tokens to user: ", balance); - } else if (flag == HOLDS_ETH) { - emit log("_newOperator: creating HOLDS_ETH user"); - // Need to support native eth initialization - revert("unimplemented"); - } else if (flag == HOLDS_MIX) { - emit log("_newOperator: creating HOLDS_MIX user"); - // Need to support native eth initialization - revert("unimplemented"); - } else { - revert("_newOperator: invalid flag"); - } - operator.registerAsOperator(); + // TODO - deposit into strats + + assert_HasUnderlyingTokenBalances(operator, strategies, tokenBalances, "_newOperator: failed to award token balances"); assertTrue(delegationManager.isOperator(address(operator)), "_newOperator: operator should be registered"); - // assert_HasNoDelegatableShares(operator, "_newOperator: new operator should not have delegatable shares"); return (operator, strategies, tokenBalances); } @@ -272,6 +51,27 @@ abstract contract IntegrationBase is IntegrationDeployer, Flags { assertEq(strategies.length, shares.length, "assert_HasNoDelegatableShares: return length mismatch"); } + function assert_HasUnderlyingTokenBalances( + User user, + IStrategy[] memory strategies, + uint[] memory expectedBalances, + string memory err + ) internal { + for (uint i = 0; i < strategies.length; i++) { + IStrategy strat = strategies[i]; + + uint expectedBalance = expectedBalances[i]; + uint tokenBalance; + if (strat == BEACONCHAIN_ETH_STRAT) { + tokenBalance = address(user).balance; + } else { + tokenBalance = strat.underlyingToken().balanceOf(address(user)); + } + + assertEq(expectedBalance, tokenBalance, err); + } + } + function assert_HasNoUnderlyingTokenBalance(User user, IStrategy[] memory strategies, string memory err) internal { for (uint i = 0; i < strategies.length; i++) { IStrategy strat = strategies[i]; diff --git a/src/test/integration/IntegrationDeployer.t.sol b/src/test/integration/IntegrationDeployer.t.sol index cd7f41a39..50b49c473 100644 --- a/src/test/integration/IntegrationDeployer.t.sol +++ b/src/test/integration/IntegrationDeployer.t.sol @@ -22,6 +22,8 @@ import "src/test/mocks/EmptyContract.sol"; import "src/test/mocks/ETHDepositMock.sol"; import "src/test/mocks/BeaconChainOracleMock.sol"; +import "src/test/integration/users/User.sol"; + abstract contract IntegrationDeployer is Test { Vm cheats = Vm(HEVM_ADDRESS); @@ -38,9 +40,20 @@ abstract contract IntegrationDeployer is Test { // Base strategy implementation in case we want to create more strategies later StrategyBase baseStrategyImplementation; - // Lists of strategies and corresponding underlying tokens + + // List of LST strategies IStrategy[] _strategies; - IERC20[] _underlyingTokens; + + // TODO - rename to time machine?? + Global global; + + // Lists of strategies used in the system + // + // When we select random user assets, we use the `assetType` to determine + // which of these lists to select user assets from. + IStrategy[] lstStrats; + IStrategy[] ethStrats; // only has one strat tbh + IStrategy[] mixedStrats; // just a combination of the above 2 lists // Mock Contracts to deploy ETHPOSDepositMock ethPOSDeposit; @@ -52,11 +65,73 @@ abstract contract IntegrationDeployer is Test { address eigenLayerReputedMultisig = address(this); // admin address address constant pauser = address(555); address constant unpauser = address(556); + + // Randomness state vars + + bytes32 random; + + // After calling `_configRand`, these are the allowed "variants" on users that will + // be returned from `_randUser`. + bytes assetTypes; + bytes signedTypes; + // Constants uint64 constant MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR = 32e9; uint64 constant GOERLI_GENESIS_TIME = 1616508000; IStrategy constant BEACONCHAIN_ETH_STRAT = IStrategy(0xbeaC0eeEeeeeEEeEeEEEEeeEEeEeeeEeeEEBEaC0); + uint constant MIN_BALANCE = 1e6; + uint constant MAX_BALANCE = 5e6; + + // Flags + + uint constant FLAG = 1; + + /// @dev Asset flags + /// These are used with _configRand to determine what assets are given + /// to a user when they are created. + uint constant NO_ASSETS = (FLAG << 0); // will have no assets + uint constant HOLDS_LST = (FLAG << 1); // will hold some random amount of LSTs + uint constant HOLDS_ETH = (FLAG << 2); // will hold some random amount of ETH + uint constant HOLDS_MIX = (FLAG << 3); // will hold a mix of LSTs and ETH + + /// @dev Signing flags + /// These are used with _configRand to determine whether a user will use "WithSignature"-type methods + uint constant NO_SIGNED_METHODS = (FLAG << 0); + uint constant SIGNED_METHODS = (FLAG << 1); + + // /// @dev Withdrawal flags + // /// These are used with _configRand to determine how a user conducts a withdrawal + // uint constant FULL_WITHDRAW_SINGLE = (FLAG << 0); // stakers will withdraw all assets using a single queued withdrawal + // uint constant FULL_WITHDRAW_MULTI = (FLAG << 1); // stakers will withdraw all assets using multiple queued withdrawals + // uint constant PART_WITHDRAW_SINGLE = (FLAG << 2); // stakers will withdraw some, but not all assets + + /// Note: Thought about the following flags (but did not implement) - + /// + /// WithdrawerType (SELF_WITHDRAWER, OTHER_WITHDRAWER) + /// - especially with EPM share handling, this felt like it deserved its own test rather than a fuzzy state + /// CompletionType (AS_TOKENS, AS_SHARES) + /// - same reason as above + /// + /// DepositMethod (DEPOSIT_STD, DEPOSIT_WITH_SIG) + /// - could still do this! + /// WithdrawalMethod (QUEUE_WITHDRAWAL, UNDELEGATE, REDELEGATE) + /// - could still do this! + /// - This would trigger staker.queueWithdrawals to use either `queueWithdrawals` or `undelegate` under the hood + /// - "redelegate" would be like the above, but adding a new `delegateTo` step after undelegating + + mapping(uint => string) assetTypeToStr; + mapping(uint => string) signedTypeToStr; + + constructor () { + assetTypeToStr[NO_ASSETS] = "NO_ASSETS"; + assetTypeToStr[HOLDS_LST] = "HOLDS_LST"; + assetTypeToStr[HOLDS_ETH] = "HOLDS_ETH"; + assetTypeToStr[HOLDS_MIX] = "HOLDS_MIX"; + + signedTypeToStr[NO_SIGNED_METHODS] = "NO_SIGNED_METHODS"; + signedTypeToStr[SIGNED_METHODS] = "SIGNED_METHODS"; + } function setUp() public virtual { // Deploy ProxyAdmin @@ -184,12 +259,17 @@ abstract contract IntegrationDeployer is Test { _newStrategyAndToken("Strategy1Token", "str1", 10e50, address(this)); // initialSupply, owner _newStrategyAndToken("Strategy2Token", "str2", 10e50, address(this)); // initialSupply, owner _newStrategyAndToken("Strategy3Token", "str3", 10e50, address(this)); // initialSupply, owner + + ethStrats.push(BEACONCHAIN_ETH_STRAT); + mixedStrats.push(BEACONCHAIN_ETH_STRAT); + + global = new Global(); } /// @dev Deploy a strategy and its underlying token, push to global lists of tokens/strategies, and whitelist /// strategy in strategyManager function _newStrategyAndToken(string memory tokenName, string memory tokenSymbol, uint initialSupply, address owner) internal { - IERC20 underlyingToken = new ERC20PresetFixedSupply(tokenName, tokenSymbol, 10e50, address(this)); + IERC20 underlyingToken = new ERC20PresetFixedSupply(tokenName, tokenSymbol, initialSupply, owner); StrategyBase strategy = StrategyBase( address( new TransparentUpgradeableProxy( @@ -201,12 +281,209 @@ abstract contract IntegrationDeployer is Test { ); // Push to list of tokens/strategies - _underlyingTokens.push(underlyingToken); _strategies.push(strategy); // Whitelist strategy IStrategy[] memory strategies = new IStrategy[](1); strategies[0] = strategy; strategyManager.addStrategiesToDepositWhitelist(strategies); + + // Add to lstStrats and mixedStrats + lstStrats.push(strategy); + mixedStrats.push(strategy); + } + + function _configRand( + uint16 _randomSeed, + uint _assetTypes, + uint _signedTypes + ) internal { + // Using uint16 for the seed type so that if a test fails, it's easier + // to manually use the seed to replay the same test. + // TODO - should we expand the type? + emit log_named_uint("_configRand: set random seed to: ", _randomSeed); + random = keccak256(abi.encodePacked(_randomSeed)); + + emit log_named_uint("_configRand: allowed asset types: ", _assetTypes); + + // Convert flag bitmaps to bytes of set bits for easy use with _randUint + assetTypes = _bitmapToBytes(_assetTypes); + signedTypes = _bitmapToBytes(_signedTypes); + + emit log("_configRand: users will be initialized with these asset types:"); + for (uint i = 0; i < assetTypes.length; i++) { + emit log(assetTypeToStr[uint(uint8(assetTypes[i]))]); + } + + emit log("_configRand: users will be initialized with these signed types:"); + for (uint i = 0; i < signedTypes.length; i++) { + emit log(signedTypeToStr[uint(uint8(signedTypes[i]))]); + } + + assertTrue(assetTypes.length != 0, "_configRand: no asset types selected"); + assertTrue(signedTypes.length != 0, "_configRand: no signed types selected"); + } + + /** + * @dev Create a new User with a random config using the range defined in `_configRand` + * + * Assets are pulled from `strategies` based on a random staker/operator `assetType` + */ + function _randUser() internal returns (User, IStrategy[] memory, uint[] memory) { + // For the new user, select what type of assets they'll have and whether + // they'll use `xWithSignature` methods. + // + // The values selected here are in the ranges configured via `_configRand` + uint assetType = _randAssetType(); + uint signedType = _randSignedType(); + + // Create User contract based on deposit type: + User user; + if (signedType == NO_SIGNED_METHODS) { + user = new User(delegationManager, strategyManager, eigenPodManager, global); + } else if (signedType == SIGNED_METHODS) { + // User will use `delegateToBySignature` and `depositIntoStrategyWithSignature` + user = User(new User_SignedMethods(delegationManager, strategyManager, eigenPodManager, global)); + } else { + revert("_newUser: unimplemented signedType"); + } + + // For the specific asset selection we made, get a random assortment of + // strategies and deal the user some corresponding underlying token balances + (IStrategy[] memory strategies, uint[] memory tokenBalances) = _dealRandAssets(user, assetType); + + _printUserInfo(assetType, signedType, strategies, tokenBalances); + + return (user, strategies, tokenBalances); + } + + /// @dev For a given `assetType`, select a random assortment of strategies and assets + /// NO_ASSETS - return will be empty + /// HOLDS_LST - `strategies` will be a random subset of initialized strategies + /// `tokenBalances` will be the user's balances in each token + /// HOLDS_ETH - `strategies` will only contain BEACON_CHAIN_ETH_STRAT, and + /// `tokenBalances` will contain the user's eth balance + /// HOLDS_MIX - random combination of `HOLDS_LST` and `HOLDS_ETH` + function _dealRandAssets(User user, uint assetType) internal returns (IStrategy[] memory, uint[] memory) { + + IStrategy[] memory strategies; + uint[] memory tokenBalances; + + if (assetType == NO_ASSETS) { + strategies = new IStrategy[](0); + tokenBalances = new uint[](0); + } else if (assetType == HOLDS_LST) { + + // Select a random number of assets + uint numAssets = _randUint({ min: 1, max: lstStrats.length }); + + strategies = new IStrategy[](numAssets); + tokenBalances = new uint[](numAssets); + + // For each asset, award the user a random balance of the underlying token + for (uint i = 0; i < numAssets; i++) { + IStrategy strat = lstStrats[i]; + IERC20 underlyingToken = strat.underlyingToken(); + + uint balance = _randUint({ min: MIN_BALANCE, max: MAX_BALANCE }); + StdCheats.deal(address(underlyingToken), address(user), balance); + + tokenBalances[i] = balance; + strategies[i] = strat; + } + + return (strategies, tokenBalances); + } else if (assetType == HOLDS_ETH) { + revert("_getRandAssets: HOLDS_ETH unimplemented"); + } else if (assetType == HOLDS_MIX) { + revert("_getRandAssets: HOLDS_MIX unimplemented"); + } else { + revert("_getRandAssets: assetType unimplemented"); + } + + return (strategies, tokenBalances); + } + + /// @dev Uses `random` to return a random uint, with a range given by `min` and `max` (inclusive) + /// @return `min` <= result <= `max` + function _randUint(uint min, uint max) internal returns (uint) { + uint range = max - min + 1; + + // calculate the number of bits needed for the range + uint bitsNeeded = 0; + uint tempRange = range; + while (tempRange > 0) { + bitsNeeded++; + tempRange >>= 1; + } + + // create a mask for the required number of bits + // and extract the value from the hash + uint mask = (1 << bitsNeeded) - 1; + uint value = uint(random) & mask; + + // in case value is out of range, wrap around or retry + while (value >= range) { + value = (value - range) & mask; + } + + // Hash `random` with itself so the next value we generate is different + random = keccak256(abi.encodePacked(random)); + return min + value; + } + + function _randAssetType() internal returns (uint) { + uint idx = _randUint({ min: 0, max: assetTypes.length - 1 }); + uint assetType = uint(uint8(assetTypes[idx])); + + return assetType; + } + + function _randSignedType() internal returns (uint) { + uint idx = _randUint({ min: 0, max: signedTypes.length - 1 }); + uint signedType = uint(uint8(signedTypes[idx])); + + return signedType; + } + + /** + * @dev Converts a bitmap into an array of bytes + * @dev Each byte in the input is processed as indicating a single bit to flip in the bitmap + */ + function _bitmapToBytes(uint bitmap) internal returns (bytes memory bytesArray) { + for (uint i = 0; i < 256; ++i) { + // Mask for i-th bit + uint mask = uint(1 << i); + + // emit log_named_uint("mask: ", mask); + + // If the i-th bit is flipped, add a byte to the return array + if (bitmap & mask != 0) { + bytesArray = bytes.concat(bytesArray, bytes1(uint8(1 << i))); + } + } + return bytesArray; + } + + function _printUserInfo( + uint assetType, + uint signedType, + IStrategy[] memory strategies, + uint[] memory tokenBalances + ) internal { + + emit log("Creating user:"); + emit log_named_string("assetType: ", assetTypeToStr[assetType]); + emit log_named_string("signedType: ", signedTypeToStr[signedType]); + + emit log_named_uint("num assets: ", strategies.length); + + for (uint i = 0; i < strategies.length; i++) { + IStrategy strat = strategies[i]; + IERC20 underlyingToken = strat.underlyingToken(); + + emit log_named_string("token name: ", IERC20Metadata(address(underlyingToken)).name()); + emit log_named_uint("token balance: ", tokenBalances[i]); + } } } \ No newline at end of file diff --git a/src/test/integration/Test.t.sol b/src/test/integration/Test.t.sol index 17c9846b9..f571ca741 100644 --- a/src/test/integration/Test.t.sol +++ b/src/test/integration/Test.t.sol @@ -15,9 +15,8 @@ contract Integration_Deposit_Delegate_Queue_Complete is IntegrationBase { // Creating new user objects / withdrawal params will use only these setups: _configRand({ _randomSeed: _random, - _stakerAssets: HOLDS_LST, - _operatorAssets: NO_ASSETS | HOLDS_LST, - _withdrawType: FULL_WITHDRAW_SINGLE /**| FULL_WITHDRAW_MULTI*/ + _assetTypes: HOLDS_LST, + _signedTypes: NO_SIGNED_METHODS /**| SIGNED_METHODS*/ }); /// 0. Create an operator and a staker with: diff --git a/src/test/integration/users/User.sol b/src/test/integration/users/User.sol index 35b71d1fc..66c100ee8 100644 --- a/src/test/integration/users/User.sol +++ b/src/test/integration/users/User.sol @@ -135,6 +135,16 @@ contract User is Test { } } +contract User_SignedMethods is User { + + constructor( + DelegationManager _delegationManager, + StrategyManager _strategyManager, + EigenPodManager _eigenPodManager, + Global _global + ) User(_delegationManager, _strategyManager, _eigenPodManager, _global) { } +} + // contract User_MixedAssets is User { // /// Since this is the base setup, we don't need anything else // }