Skip to content

Commit

Permalink
refactor: adding slashAVS functionality to EigenLayerSystem
Browse files Browse the repository at this point in the history
  • Loading branch information
nican0r committed Jun 19, 2024
1 parent 2242c07 commit b26597b
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 2 deletions.
53 changes: 52 additions & 1 deletion src/test/recon/EigenLayerSystem.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@
pragma solidity ^0.8.12;

import {EigenLayerSetupV2} from "./EigenLayerSetupV2.sol";
import {IStrategy} from "../../contracts/interfaces/IStrategy.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "forge-std/Test.sol";

contract EigenLayerSystem is EigenLayerSetupV2, Test {
address immutable TOKEN_BURN_ADDRESS = address(0xDEADBEEF); // used to simulate token burning for tokens that don't allow transfer to 0 address

contract EigenLayerSystem is EigenLayerSetupV2 {
/// @notice simulates a native slashing event on a validator
/// @dev when calling this through a target function, need to prank as the pod's address to allow modifying balances in EigenPodManager
/// @param podOwner the owner of the pod being slashed
Expand All @@ -15,6 +20,52 @@ contract EigenLayerSystem is EigenLayerSetupV2 {
ethPOSDepositMock.slash(1 ether);
}

/// @notice simulates an AVS slashing event
/// @dev this assumes slashing amounts for an LST and native ETH can be different
function slashAVS(
address user,
address[] memory activeStrategies,
uint256 nativeSlashAmount,
uint256 lstSlashAmount
) public {
// Slash native ETH if user has any staked in an EigenPod
uint256 nativeEthShares = uint256(eigenPodManager.podOwnerShares(address(user)));
if (nativeEthShares > 0) {
// user can be slashed a max amount of their entire stake
nativeSlashAmount = nativeSlashAmount % nativeEthShares;

// shares are 1:1 with ETH in EigenPod so can slash the share amount directly
ethPOSDepositMock.slash(nativeSlashAmount);

// update the OperatorDelegator's share balance in EL by calling EigenPodManager as the pod
address podAddress = getPodForOwner(user);
vm.prank(podAddress);
eigenPodManager.recordBeaconChainETHBalanceUpdate(address(user), -int256(nativeSlashAmount));
}

// loop through strategies to slash if a user has any shares in them
for (uint256 i; i < activeStrategies.length; i++) {
IStrategy strategy = IStrategy(activeStrategies[i]);
uint256 lstShares = strategy.shares(address(user));

// Slash LST if user has any shares of the given LST strategy
if (lstShares > 0) {
uint256 slashingAmountLSTShares = lstSlashAmount % lstShares;
// convert share amount to slash to amount of collateral token
uint256 amountLSTToken = strategy.sharesToUnderlyingView(slashingAmountLSTShares);

// "burn" tokens in strategy to ensure they don't effect accounting
vm.prank(activeStrategies[i]);
IERC20 underlyingToken = strategy.underlyingToken();
underlyingToken.transfer(TOKEN_BURN_ADDRESS, amountLSTToken);

// remove shares to update user's accounting
vm.prank(address(delegation));
_removeSharesFromStrategyManager(address(user), address(activeStrategies[i]), slashingAmountLSTShares);
}
}
}

/// @notice returns the address of an EigenPod for an Owner, if one exists
function getPodForOwner(address owner) public view returns (address eigenPod) {
return address(eigenPodManager.getPod(address(owner)));
Expand Down
2 changes: 1 addition & 1 deletion src/test/recon/EigenLayerSystemTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {EigenLayerSystem} from "./EigenLayerSystem.sol";
import {MockERC20} from "../mocks/MockERC20.sol";
import "forge-std/Test.sol";

contract EigenLayerSystemTest is EigenLayerSystem, Test {
contract EigenLayerSystemTest is EigenLayerSystem {
MockERC20 stETH;
MockERC20 cbETH;

Expand Down

0 comments on commit b26597b

Please sign in to comment.