-
Notifications
You must be signed in to change notification settings - Fork 331
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: burn erc20s on slashing #881
base: slashing-magnitudes
Are you sure you want to change the base?
Conversation
@@ -26,6 +26,9 @@ abstract contract StrategyManagerStorage is IStrategyManager { | |||
// index for flag that pauses deposits when set | |||
uint8 internal constant PAUSED_DEPOSITS = 0; | |||
|
|||
/// @notice default address for burning slashed shares and transferring underlying tokens | |||
address public constant DEFAULT_BURN_ADDRESS = address(0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we do some analysis on if this is ok for LSTs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's for sure not, placeholder value for now. Personally a fan of 0xdeadbeef
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
91a806b
0xDeaDBeef
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should at least do 0x000000000000000000000000000000000000dEaD
or address(0)
since other protocols use that. Working with Matt Nelson to get feedback
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like we cannot do address(0) given that stETH's contract
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
0x E 1 6 E 4 (eigen)
* withdrawal snapshot is probably "recent", defined as being among the last sqrt(N) withdrawal snapshots where N is the number of | ||
* withdrawal snapshots. | ||
*/ | ||
function getAtProbablyRecentBlock( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just do upperLookup imo. is this any different?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's the same logic but named more clearly for blocknumber usage. I can change this back though to be more consistent with DefaultWadHistory.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changing the name makes it clearer to me imo
@@ -685,6 +699,26 @@ contract DelegationManager is | |||
_beaconChainSlashingFactor[staker] = bsf; | |||
} | |||
|
|||
/// @dev Calculate amount of withdrawable shares from an operator for a strategy that is still in the queue | |||
/// and therefore slashable. | |||
function _getSlashableSharesInQueue(address operator, IStrategy strategy) internal view returns (uint256) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we expose a public view func for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IStrategy strategy, | ||
uint256 queueWithdrawnShares | ||
) internal { | ||
uint256 currCumulativeWithdrawalShares = uint256(_cumulativeWithdrawalsHistory[operator][strategy].latest()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this PR looks nice!
my main feedback is that we should store cumulative scaled shares queued for withdrawal, not shares.
Upon burning we should multiply the scaled shares in the withdrawal queue by the slashed magnitude. This would make it so that view functions for introspection into the withdrawal queue automatically update to reflect correct values after slashing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
138eef7
Added, had to change calcSlashedAmount back to old interface but it is more consistent with the way scaledShares are also calculated.
|
1 similar comment
|
// Staker was delegated and remains slashable during the withdrawal delay period | ||
// Cumulative withdrawn scaled shares are updated for the strategy, this is for accounting | ||
// purposes for burning shares if slashed | ||
_updateCumulativeScaledShares(operator, strategies[i], scaledShares[i]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we don't want to do this for beaconchain ETH, because this value is sorta useless since it doesn't take slashing factors into account
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, yes this only for SM Strategies
080a510
to
859359e
Compare
|
Synchronous Burning
One solution for burning slashed shares is to simply take the existing logic, take the sharesToDecrement in
calcSlashedAmount
and just burn that amount. But for all shares that were withdrawn and are still slashable forMIN_WITHDRAWAL_DELAY_BLOCKS
, they would have to be slashed and burned when those withdrawals are completed.Instead, completed withdrawals don't burn and we calculate all the shares to burn upon slashing. We burn synchronously all those slashabe shares in the queue by reading all the cumulative withdrawn shares from the time of slashing and
MIN_WITHDRAWAL_DELAY_BLOCKS
into the past. Here is where checkpointed values come into play again.Changes:
Snapshots.sol
: Added back OZ CheckpointsUpgradeable library for blocknumbers to the Snapshots library. The reasoning is because we want to keep track of cumulative queue withdrawn shares from an operator for a specific strategy and not using a default WAD value like we use for magnitudes.DelegationManager.sol
: renamed functiondecreaseAndBurnOperatorShares
which will now read all the slashable shares queue withdrawn from an operator for a specific strategy, calculate amount of shares to burn, and callStrategyManager.burnShares
.StrategyManager.sol
: burnShares function is exact same as the withdraw function but with the destination address being theDEFAULT_BURN_ADDRESS
(address 0).SlashingLib.sol
: ChangedcalcSlashedAmount
back to taking in as input prevMaxMagnitude and newMaxMagnitude because the way that burned shares are calculated with cumulative scaled shares.Note: The call trace begins from
ALM.slashOperator -> DM.decreaseAndBurnOperatorShares -> SM.burnShares -> Strategy.withdraw -> underlyingToken.transfer
Each of these calls are permissioned by the core contracts in the protocol with the exception of slashOperator which is only callable by the AVS. We may want to additionally consider for a reentrancy guard onslashOperator
as a result.TODO: