Skip to content

Conversation

@eigenmikem
Copy link
Contributor

Motivation:

Explain here the context, and why you're making that change. What is the problem you're trying to solve.

Modifications:

Describe the modifications you've done.

Result:

After your change, what will change.

Copy link
Collaborator

@ypatil12 ypatil12 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! General state machine + readability improvements. Big question is still around guarantees we need around staker delegation at the time of deposit.

require(!isBlacklisted[underlyingToken], BlacklistedToken());
require(address(durationVaultBeacon) != address(0), DurationVaultBeaconNotSet());

newVault = IDurationVaultStrategy(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want these to be deterministic deploys?

address newVaultAdmin
) external;

function vaultAdmin() external view returns (address);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of vaultAdmin, is it worth just inheriting Ownable? Reduces the surface area of code IMO.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want it to be transferable?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think we need it to be transferrable? Fine to just leave as an immutable at deploy

ypatil12
ypatil12 previously approved these changes Dec 3, 2025
address newVaultAdmin
) external;

function vaultAdmin() external view returns (address);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think we need it to be transferrable? Fine to just leave as an immutable at deploy

* @notice Locks the vault, preventing new deposits and withdrawals until maturity.
*/
function lock() external override onlyVaultAdmin {
require(_state == VaultState.Deposits, VaultAlreadyLocked());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want a startTime for the vault? Unsure what the user story is for when the vault can begin allocation

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having a unique start time per vault enables each vault to have a deterministic deployment... again unsure if that's a relevant user story

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We used to have defined deposit windows but removed to make things easier for AVSs. Insurance AVS can lock at an arbitrary time but it's expected they will say publicly

@ypatil12 ypatil12 dismissed their stale review December 3, 2025 14:36

Did not mean to approve

@eigenmikem eigenmikem changed the base branch from main to release-dev/duration-vaults December 4, 2025 15:02
Copy link
Collaborator

@ypatil12 ypatil12 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will review the integration tests a bit later

_;
}

constructor(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to set the operator fee here for rewards? Operators (and this strategy) by default get 10% of rewards. We don't have a way of claiming those rewards with the current implementation. Should the fee just be set to zero since presumably the operator and AVS are the (same?) entity? cc @non-fungible-nelson

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's set in configureOperatorIntegration

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha. Btw, it takes 7 days for this fee to become active on mainnet. We should document this as an edge case if the allocation state ends up being hit prior to the fee becoming active

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i guess allocation delay is 14 days , so this won't be possible?

Copy link
Collaborator

@ypatil12 ypatil12 Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the activationDelay:

/// @inheritdoc IRewardsCoordinator
function setOperatorPISplit(
address operator,
uint16 split
) external onlyWhenNotPaused(PAUSED_OPERATOR_PI_SPLIT) checkCanCall(operator) {
uint32 activatedAt = uint32(block.timestamp) + activationDelay;
uint16 oldSplit = _getOperatorSplit(_operatorPISplitBips[operator]);
_setOperatorSplit(_operatorPISplitBips[operator], split, activatedAt);
emit OperatorPISplitBipsSet(msg.sender, operator, activatedAt, oldSplit, split);
}
/// @inheritdoc IRewardsCoordinator
function setOperatorSetSplit(
address operator,
OperatorSet calldata operatorSet,
uint16 split
) external onlyWhenNotPaused(PAUSED_OPERATOR_SET_SPLIT) checkCanCall(operator) {

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, need to set the operator PI split to 0.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming this strategy doesn't get PI? Probably something to validate with product.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i mean , split is set on initialization and after 7 days it becomes active

  • allocation in the other hand will be active only after allocation delay (14 days after lock ) which we set in initialization as well ,so by the time of reward , the split will be active cause rewards only starts after allocation is active (correct?)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, good point. So we should be fine here :)

@@ -0,0 +1,466 @@
// SPDX-License-Identifier: BUSL-1.1
Copy link
Collaborator

@ypatil12 ypatil12 Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For sanity, in the upgrade folder can we add an upgrade test against a deposit that is before the upgrade for already existing strategies (Eigen strat, strategy factory, and tvl limits)

  1. Deposit into strategy, upgrade, queue withdrawal (tests remove shares), complete withdrawal as shares (tests add shares)

_transferOwnership(_initialOwner);
_setPausedStatus(_initialPausedStatus);
_setStrategyBeacon(_strategyBeacon);
if (address(_durationVaultBeacon) != address(0)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems unnecessary if we don't check strategyBeacon is address(0)?

@eigenmikem eigenmikem force-pushed the mike/duration-strategy branch from fb1301d to 8829ce2 Compare December 16, 2025 18:10
Comment on lines 213 to 215
function explanation() external pure virtual override returns (string memory) {
return "Base Strategy implementation to inherit from for more complex implementations";
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you may override the explanation function to return an explanation of the duration vault strategy, it's weird that it returns baseStrategy explanation

Comment on lines +59 to +61
delegationManager = _delegationManager;
allocationManager = _allocationManager;
rewardsCoordinator = _rewardsCoordinator;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add _disableInitializers() to the constructor for impl

Comment on lines 165 to 169
function getDurationVaults(
IERC20 token
) external view returns (IDurationVaultStrategy[] memory) {
return durationVaultsByToken[token];
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is the only way to fetch the duration vaults for a token, but it can lead to OOG issue for on-chain integrations, specially that anyone can increase the length of the array, by creating a new duration vaults.

consider making the durationVaultsByToken public for safe on-chain reads

Comment on lines +252 to +253
function allocationsActive() public view override returns (bool) {
return _state == VaultState.ALLOCATIONS;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note that this doesn't always means that allocation on AM is active, since we have a delay of minWithdrawalDelayBlocks()

/// Storage slots used: vaultAdmin (1) + duration/lockedAt/unlockAt/maturedAt/_state (packed, 1) +
/// metadataURI (1) + _operatorSet (2) + maxPerDeposit (1) + maxTotalDeposits (1) = 8.
/// Gap: 50 - 8 = 42.
uint256[42] private __gap;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

__gap should be 44 , (50 - 6 )

Comment on lines +78 to +87
assertTrue(
address(Env.impl.strategyManager()) != address(0), "StrategyManager implementation should be deployed"
);
assertTrue(
address(Env.impl.strategyManager().delegation()) == address(Env.proxy.delegationManager()),
"StrategyManager: delegationManager mismatch"
);

// Validate EigenStrategy
assertTrue(address(Env.impl.eigenStrategy()) != address(0), "EigenStrategy implementation should be deployed");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If deployment fails silently, Env.impl.* falls back to the old mainnet address from environment variables which won't be address(0) for most

  • better to add :
function _validateDeployedContracts() internal view {
+         assertEq(deploys().length, 7, "Deployed contracts should be 7");

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants