Skip to content

Commit

Permalink
Merge branch 'master' into m2-upgrade-shadow-fork-testing
Browse files Browse the repository at this point in the history
  • Loading branch information
ypatil12 committed Oct 16, 2023
2 parents e3c4728 + e7bf6b2 commit 98c26b6
Show file tree
Hide file tree
Showing 8 changed files with 312 additions and 288 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
<a name="introduction"/></a>

# EigenLayer
<p align="center"><b><font size="+1">
🚧 The Slasher contract is under active development and its interface expected to change. We recommend writing slashing logic without integrating with the Slasher at this point in time. 🚧
</font></b><p>
EigenLayer (formerly 'EigenLayr') is a set of smart contracts deployed on Ethereum that enable restaking of assets to secure new services.
At present, this repository contains *both* the contracts for EigenLayer *and* a set of general "middleware" contracts, designed to be reusable across different applications built on top of EigenLayer.

Expand Down
3 changes: 3 additions & 0 deletions certora/specs/core/DelegationManager.spec
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ methods {
function decreaseDelegatedShares(address,address,uint256) external;
function increaseDelegatedShares(address,address,uint256) external;

// external calls from DelegationManager to ServiceManager
function _.updateStakes(address[]) external => NONDET;

// external calls to Slasher
function _.isFrozen(address) external => DISPATCHER(true);
function _.canWithdraw(address,uint32,uint256) external => DISPATCHER(true);
Expand Down
3 changes: 3 additions & 0 deletions certora/specs/core/StrategyManager.spec
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ methods {
function _.decreaseDelegatedShares(address,address,uint256) external => DISPATCHER(true);
function _.increaseDelegatedShares(address,address,uint256) external => DISPATCHER(true);

// external calls from DelegationManager to ServiceManager
function _.updateStakes(address[]) external => NONDET;

// external calls to Slasher
function _.isFrozen(address) external => DISPATCHER(true);
function _.canWithdraw(address,uint32,uint256) external => DISPATCHER(true);
Expand Down
3 changes: 3 additions & 0 deletions certora/specs/pods/EigenPodManager.spec
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ methods {
function _.decreaseDelegatedShares(address,address,uint256) external;
function _.increaseDelegatedShares(address,address,uint256) external;

// external calls from DelegationManager to ServiceManager
function _.updateStakes(address[]) external => NONDET;

// external calls to Slasher
function _.isFrozen(address) external => DISPATCHER(true);
function _.canWithdraw(address,uint32,uint256) external => DISPATCHER(true);
Expand Down
17 changes: 11 additions & 6 deletions docs/outdated/AVS-Guide.md → docs/experimental/AVS-Guide.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
[middleware-folder-link]: https://github.com/Layr-Labs/eigenlayer-contracts/tree/master/src/contracts/middleware
[middleware-guide-link]: https://github.com/Layr-Labs/eigenlayer-contracts/blob/master/docs/AVS-Guide.md#quick-start-guide-to-build-avs-contracts
# Purpose
This document aims to describe and summarize how actively validated services (AVSs) building on EigenLayer interact with the core EigenLayer protocol. Currently, this doc explains how AVS developers can use the APIs for:
This document aims to describe and summarize how actively validated services (AVSs) building on EigenLayer interact with the core EigenLayer protocol. Currently, this doc explains how AVS developers can use the current** APIs for:
- enabling operators to opt-in to the AVS,
- enabling operators to opt-out (withdraw stake) from the AVS,
- enabling operators to continuously update their commitments to middlewares, and
- enabling AVS to freeze operators for the purpose of slashing (the corresponding unfreeze actions are determined by the veto committee).

<p align="center"><b><font size="+1">
🚧 ** The Slasher contract is under active development and its interface expected to change. We recommend writing slashing logic without integrating with the Slasher at this point in time. 🚧
</font></b><p>

We are currently in the process of implementing the API for payment flow from AVSs to operators in EigenLayer. Details of this API will be added to this document in the near future.

The following figure summarizes scope of this document:
![Doc Outline](./images/middleware_outline_doc.png)
![Doc Outline](../images/middleware_outline_doc.png)


# Introduction
In designing EigenLayer, the EigenLabs team aspired to make minimal assumptions about the structure of AVSs built on top of it. If you are getting started looking at EigenLayer's codebase, the `Slasher.sol` contains most of the logic that actually mediates the interactions between EigenLayer and AVSs. Additionally, there is a general-purpose [/middleware/ folder][middleware-folder-link], which contains code that can be extended, used directly, or consulted as a reference in building an AVS on top of EigenLayer. Note that there will be a single, EigenLayer-owned, `Slasher.sol` contract, but all the `middleware` contracts are AVS-specific and need to be deployed separately by AVS teams.
Expand Down Expand Up @@ -47,7 +52,7 @@ In order for any EigenLayer operator to be able to opt-in to an AVS, EigenLayer
2. Next, the operator needs to register with the AVS on chain via an AVS-specific registry contract (see [this][middleware-guide-link] section for examples). To integrate with EigenLayer, the AVS's Registry contract provides a registration endpoint that calls on the AVS's `ServiceManager.recordFirstStakeUpdate(..)` which in turn calls `Slasher.recordFirstStakeUpdate(..)`. On successful execution of this function call, the event `MiddlewareTimesAdded(..)` is emitted and the operator has to start serving the tasks from the AVS.

The following figure illustrates the above flow:
![Operator opting-in](./images/operator_opting.png)
![Operator opting-in](../images/operator_opting.png)

### *Staker Delegation to an Operator: Which Opts-In to AVSs*

Expand All @@ -70,19 +75,19 @@ Let us illustrate the usage of this facility with an example: A staker has deleg
- The AVS provider now is aware of the change in stake, and the staker can eventually complete their withdrawal. Refer [here](https://github.com/Layr-Labs/eigenlayer-contracts/blob/master/docs/EigenLayer-withdrawal-flow.md) for more details

The following figure illustrates the above flow:
![Stake update](./images/staker_withdrawing.png)
![Stake update](../images/staker_withdrawing.png)

### *Deregistering from AVS*
In order for any EigenLayer operator to be able to de-register from an AVS, EigenLayer provides the interface `Slasher.recordLastStakeUpdateAndRevokeSlashingAbility(..)`. Essentially, in order for an operator to deregister from an AVS, the operator has to call `Slasher.recordLastStakeUpdateAndRevokeSlashingAbility(..)` via the AVS's ServiceManager contract. It is important to note that the latest block number until which the operator is required to serve tasks for the service must be known by the service and included in the ServiceManager's call to `Slasher.recordLastStakeUpdateAndRevokeSlashingAbility`.

The following figure illustrates the above flow in which the operator calls the `deregister(..)` function in a sample Registry contract.
![Operator deregistering](./images/operator_deregister.png)
![Operator deregistering](../images/operator_deregister.png)

### *Slashing*
As mentioned above, EigenLayer is built to support slashing as a result of an on-chain-checkable, objectively attributable action. In order for an AVS to be able to slash an operator in an objective manner, the AVS needs to deploy a DisputeResolution contract which anyone can call to raise a challenge against an EigenLayer operator for its adversarial action. On successful challenge, the DisputeResolution contract calls `ServiceManager.freezeOperator(..)`; the ServiceManager in turn calls `Slasher.freezeOperator(..)` to freeze the operator in EigenLayer. EigenLayer's Slasher contract emits a `OperatorFrozen(..)` event whenever an operator is (successfully) frozen

The following figure illustrates the above flow:
![Slashing](./images/slashing.png)
![Slashing](../images/slashing.png)


## Quick Start Guide to Build AVS Contracts:
Expand Down
20 changes: 11 additions & 9 deletions src/contracts/core/DelegationManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -245,9 +245,6 @@ contract DelegationManager is Initializable, OwnableUpgradeable, Pausable, Deleg
(IStrategy[] memory strategies, uint256[] memory shares)
= getDelegatableShares(staker);

// push the operator's new stake to the StakeRegistry
_pushOperatorStakeUpdate(operator);

// emit an event if this action was not initiated by the staker themselves
if (msg.sender != staker) {
emit StakerForceUndelegated(staker, operator);
Expand Down Expand Up @@ -607,16 +604,19 @@ contract DelegationManager is Initializable, OwnableUpgradeable, Pausable, Deleg
podOwner: staker,
shares: withdrawal.shares[i]
});
currentOperator = delegatedTo[staker];
address podOwnerOperator = delegatedTo[staker];
// Similar to `isDelegated` logic
if (currentOperator != address(0)) {
if (podOwnerOperator != address(0)) {
_increaseOperatorShares({
operator: currentOperator,
operator: podOwnerOperator,
// the 'staker' here is the address receiving new shares
staker: staker,
strategy: withdrawal.strategies[i],
shares: increaseInDelegateableShares
});

// push the operator's new stake to the StakeRegistry
_pushOperatorStakeUpdate(podOwnerOperator);
}
} else {
strategyManager.addShares(msg.sender, withdrawal.strategies[i], withdrawal.shares[i]);
Expand Down Expand Up @@ -687,9 +687,6 @@ contract DelegationManager is Initializable, OwnableUpgradeable, Pausable, Deleg
strategy: strategies[i],
shares: shares[i]
});

// push the operator's new stake to the StakeRegistry
_pushOperatorStakeUpdate(operator);
}

// Remove active shares from EigenPodManager/StrategyManager
Expand All @@ -709,6 +706,11 @@ contract DelegationManager is Initializable, OwnableUpgradeable, Pausable, Deleg
unchecked { ++i; }
}

// Push the operator's new stake to the StakeRegistry
if (operator != address(0)) {
_pushOperatorStakeUpdate(operator);
}

// Create queue entry and increment withdrawal nonce
uint256 nonce = cumulativeWithdrawalsQueued[staker];
cumulativeWithdrawalsQueued[staker]++;
Expand Down
30 changes: 30 additions & 0 deletions src/test/mocks/DelegationManagerMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity =0.8.12;

import "forge-std/Test.sol";
import "../../contracts/interfaces/IDelegationManager.sol";
import "../../contracts/interfaces/IStrategyManager.sol";


contract DelegationManagerMock is IDelegationManager, Test {
Expand Down Expand Up @@ -137,4 +138,33 @@ contract DelegationManagerMock is IDelegationManager, Test {
) external {}

function migrateQueuedWithdrawals(IStrategyManager.DeprecatedStruct_QueuedWithdrawal[] memory withdrawalsToQueue) external {}

// onlyDelegationManager functions in StrategyManager
function addShares(
IStrategyManager strategyManager,
address staker,
IStrategy strategy,
uint256 shares
) external {
strategyManager.addShares(staker, 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);
}
}
Loading

0 comments on commit 98c26b6

Please sign in to comment.