Skip to content

Conversation

@nadir-akhtar
Copy link
Collaborator

@nadir-akhtar nadir-akhtar commented Jun 30, 2025

Motivation:

When developing the slashing release, two different functions for calculating slashed shares were ideated: calcSlashedAmount and scaleForBurning. The former is used to calculated slashed "operator shares," or shares currently delegated to an operator, and the latter for "queued shares," or shares in the withdrawal queue that are currently slashable.

Despite the functionality effectively being equivalent (determine how many of these shares are to be slashed), two different functions were maintained due to uncertainty as to how to deduplicate them.

During the redistribution release, manual analysis yielded the conclusion that there is a way to consolidate these two functions. In order to reduce disparate logic, this PR removes scaleForBurning and replaces it with calcSlashedAmount.

Context:

calcSlashedAmount and scaleForBurning both attempt to do the same thing: return how many shares to slash as a result of an operator's magnitude change. However, they each go about it in different ways:

  • calcSlashedAmount reduces an operator's shares by a certain proportion, specifically by newMaxMag / prevMaxMag, then returns that number of shares.
  • scaleForBurning calculates the effective shares at the current and expected max mags, then returns the difference.

Note that the "type" of share entering each equation is not the same: calcSlashedAmount takes in operator shares, whereas scaleForBurning takes in "scaled shares," i.e. shares that are scaled based on staker deposit scaling factors (DSFs), but do not change based on operator slashings.

Once this type discrepancy was evident, the next step was to determine how to convert from one type to the other. Choosing calcSlashedAmount as the natural base function, it became evident that "scaled shares" can be translated to operator shares by simply multiplying by the current operator magnitude. With that simple change, calcSlashedAmount can be used to calculate shares slashed from both operator shares as well as queued shares.

Math:

Variables

  • $s_n$ - The amount of shares in the storage of the {EP,S}M at time n
  • $k_n$ - The “staker deposit scaling factor” at time n. This is initialized to 1.
  • $m_n$ - The operator magnitude at time n
  • $op_{n}$ - The shares of the operator $op_{n}=s_{n}k_{n}m_{n}$
  • $ss_{n}$ - The scaled shares of the operator in storage. This is stored upon queueing of a withdrawal $ss_{n}=s_{n}k_{n}$. See scaleForQueueWithdrawal in SlashingLib

Scale For Burning
$ss_{n}(m_{n}-m_{n+1})$

= $s_nk_n(m_{n}-m_{n+1})$

= $s_nk_nm_{n}- s_nk_nm_{n+1}$

= $op_n - s_nk_nm_{n+1}$

Calc Slashed Amount

$op_{n} - op_{n}*\frac{m_{n+1}}{m_n}$

= $op_{n} - s_nk_nm_n*\frac{m_{n+1}}{m_n}$

= $op_{n} - s_nk_n{m_{n+1}}$

Modifications:

  • Removes scaleForBurning from SlashingLib.sol and replaces its use with calcSlashedAmount

Result:

  • Cleaned up code
  • Clearer logic

@nadir-akhtar nadir-akhtar force-pushed the nadir/remove-scale-for-burning branch 2 times, most recently from 35c94c2 to 061c318 Compare June 30, 2025 20:58
@nadir-akhtar nadir-akhtar requested a review from ypatil12 June 30, 2025 22:44
@nadir-akhtar nadir-akhtar assigned wadealexc and unassigned wadealexc Jun 30, 2025
@nadir-akhtar nadir-akhtar requested a review from wadealexc June 30, 2025 22:44
@nadir-akhtar nadir-akhtar force-pushed the nadir/remove-scale-for-burning branch 2 times, most recently from fe0efd1 to 170e3dc Compare June 30, 2025 23:23
@github-actions github-actions bot deleted the nadir/remove-scale-for-burning branch October 7, 2025 00:16
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.

Can we change the base to release-dev/slashing-ux-improvements? Also need to run make bindings after.

@nadir-akhtar nadir-akhtar changed the base branch from main to release-dev/slashing-ux-improvements December 1, 2025 20:32
@nadir-akhtar nadir-akhtar requested a review from ypatil12 December 1, 2025 21:11
@nadir-akhtar nadir-akhtar merged commit f76315e into release-dev/slashing-ux-improvements Dec 1, 2025
9 checks passed
ypatil12 added a commit that referenced this pull request Dec 1, 2025
**Motivation:**

When developing the slashing release, two different functions for
calculating slashed shares were ideated: `calcSlashedAmount` and
`scaleForBurning`. The former is used to calculated slashed "operator
shares," or shares currently delegated to an operator, and the latter
for "queued shares," or shares in the withdrawal queue that are
currently slashable.

Despite the functionality effectively being equivalent (determine how
many of these shares are to be slashed), two different functions were
maintained due to uncertainty as to how to deduplicate them.

During the redistribution release, manual analysis yielded the
conclusion that there is a way to consolidate these two functions. In
order to reduce disparate logic, this PR removes `scaleForBurning` and
replaces it with `calcSlashedAmount`.

_Context:_

`calcSlashedAmount` and `scaleForBurning` both attempt to do the same
thing: return how many shares to slash as a result of an operator's
magnitude change. However, they each go about it in different ways:
* `calcSlashedAmount` reduces an operator's shares by a certain
proportion, specifically by `newMaxMag / prevMaxMag`, then returns that
number of shares.
* `scaleForBurning` calculates the effective shares at the current and
expected max mags, then returns the difference.

Note that the "type" of share entering each equation is not the same:
`calcSlashedAmount` takes in operator shares, whereas `scaleForBurning`
takes in "scaled shares," i.e. shares that are scaled based on staker
deposit scaling factors (DSFs), but do _not_ change based on operator
slashings.

Once this type discrepancy was evident, the next step was to determine
how to convert from one type to the other. Choosing `calcSlashedAmount`
as the natural base function, it became evident that "scaled shares" can
be translated to operator shares by simply _multiplying by the current
operator magnitude_. With that simple change, `calcSlashedAmount` can be
used to calculate shares slashed from both operator shares as well as
queued shares.

Math:

**Variables** 
- $s_n$ - The amount of shares in the storage of the {EP,S}M at time n 
- $k_n$ - The “staker deposit scaling factor” at time n. This is
initialized to 1.
- $m_n$ - The operator magnitude at time n 
- $op_{n}$ - The shares of the operator $op_{n}=s_{n}k_{n}m_{n}$ 
- $ss_{n}$ - The scaled shares of the operator in storage. This is
stored upon queueing of a withdrawal $ss_{n}=s_{n}k_{n}$. See
`scaleForQueueWithdrawal` in `SlashingLib`

**Scale For Burning**
$ss_{n}(m_{n}-m_{n+1})$ 

= $s_nk_n(m_{n}-m_{n+1})$ 

= $s_nk_nm_{n}- s_nk_nm_{n+1}$ 

= $op_n - s_nk_nm_{n+1}$

**Calc Slashed Amount**

$op_{n} - op_{n}*\frac{m_{n+1}}{m_n}$ 

= $op_{n} - s_nk_nm_n*\frac{m_{n+1}}{m_n}$ 

= $op_{n} - s_nk_n{m_{n+1}}$

**Modifications:**

* Removes `scaleForBurning` from `SlashingLib.sol` and replaces its use
with `calcSlashedAmount`

**Result:**

* Cleaned up code
* Clearer logic

---------

Co-authored-by: Nadir Akhtar <[email protected]>
Co-authored-by: Yash Patil <[email protected]>
ypatil12 added a commit that referenced this pull request Dec 2, 2025
# v1.9.0 Slashing UX Improvements

## Release Manager

@ypatil12 @eigenmikem @0xClandestine

# Overview

The Slashing UX improvement release is a tech debt-focused release that
improves key parts of the Eigenlayer Core UX. This release will upgrade
every core contract.

The below release notes cover Core Contracts.

## Highlights

🚀 New Features

- The `AllocationManager` has been split into two contracts to address
size limitations of the contract. The main contract handles
state-mutating operations, while `AllocationManagerView` handles all
read-only view functions. **This is not a breaking change for
introspection as previous introspection calls fallback to `delegateCall`
into the `AllocationManagerView` contract.**. For more information, see
the [contract
architecture](../docs/core/AllocationManager.md#contract-architecture).
- The `ProtocolRegistry` is a new contract that stores all proxy
contract addresses, global semver, and has the ability to pause the
entire protocol. This contract will be deployed on all chains.
- Two new `createOperatorSets` functions (for redistributing and non
redistributing operatorSets) have been added that take in a slasher
address. This address is the *only* address that can slash an
operatorSet. Changing the address is behind a
`ALLOCATION_CONFIGURATION_DELAY` (17.5 days on mainnet).

⛔ Breaking Changes

- The slasher permissions are set and stored in the `AllocationManager`
instead of the `PermissionController`. Only one address can slash an
operatorSet; the address is initially set upon creation of the
operatorSet. OperatorSets created prior to this release will have their
slasher migrated based on the following rules:
- If there is no slasher set or the slasher in the
`PermissionController` is the 0 address, the AVS address will be set as
the slasher
- If there are multiple slashers set in the `PermissionController`, the
first address will be set as the slasher
- Semver (`SemverMixin.sol`) has been removed from all contracts, except
from those that inherit the `SignatureUtilsMixin`. The version of the
core protocol can be introspected via the `ProtocolRegistry`.

📌 Deprecations

The old `createOperatorSets` functions in the leftmost column will be
deprecated in Q2 2026 in favor of the newly specified functions. The old
functions do not pass in a slasher. The new functions do pass in a
slasher. If the old function is used, the slasher of the operatorSet is
set to the avs address.

| Function | MigrateTo | Notes |
| -------- | -------- | -------- |
| `createOperatorSets(avs, CreateSetParams[])` |
`createOperatorSets(address avs, CreateSetParamsV2[])` | New function
takes in a slasher address |
| `createRedistributingOperatorSets(avs, CreateSetParams[],
redistributionRecipients[])` | `createRedistributingOperatorSets(avs,
CreateSetParamsV2[], redistributionRecipients[])` | New function takes
in a slasher address |

🔧 Improvements

- Added a non-revert `_canCall` in the `PermissionControllerMixin` for
space savings. This function is used in the `AllocationManager` and
`DelegationManager`.
- The allocation delay for a newly created operator is active
immediately. This allows operators to make allocations instantly after
registering in the core.
- The internal `SlashingLib.scaleForBurning` function has been
deprecated in favor of `SlashingLib.calcSlashedAmount`, standardizing
the calculation of slashed shares across the withdrawal queue and
storage. See [PR
#1502](#1502) for
more information.


# Changelog

- feat: re-enable forge fmt + foundry v1.5.0 [PR
#1669](#1669)
- feat: substitute calcSlashedAmount for scaleForBurning [PR
#1502](#1502)
- fix: `v1.9.0` upgrade script [PR
#1666](#1666)
- feat: `v1.9.0` upgrade scripts + reusable upgrade helpers [PR
#1665](#1665)
- chore: update interface natspec for DM [PR
#1664](#1664)
- feat: slashing commitments [PR
#1645](#1645)
- feat: remove semver + minor optimizations [PR
#1654](#1654)
- feat: split `AllocationManager` [PR
#1643](#1643)
- feat: add protocol registry [PR
#1655](#1655)
- feat: instant alloc delay from dm [PR
#1646](#1646)
- chore: remove holesky [PR
#1662](#1662)
- chore: hardcode foundry ci to v1.3.5 [PR
#1658](#1658)
- chore: hardcode foundry to v1.3.5 in ci [PR
#1657](#1657)
- feat(audit): publish Hourglass + Multichain + RMS audit reports [PR
#1644](#1644)
- docs: add transport frequency for multichain [PR
#1642](#1642)
- chore: update readMe for multichain/hourglass [PR
#1637](#1637)
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.

4 participants