Skip to content

Commit

Permalink
feat: add fair hook for NetworkRestakeDelegator
Browse files Browse the repository at this point in the history
  • Loading branch information
1kresh committed Sep 19, 2024
1 parent 7f5e1bb commit d4fcf0c
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 0 deletions.
54 changes: 54 additions & 0 deletions src/contracts/NetworkRestakeFairHook.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

import {INetworkRestakeFairHook} from "../interfaces/INetworkRestakeFairHook.sol";

import {IBaseSlasher} from "@symbioticfi/core/src/interfaces/slasher/IBaseSlasher.sol";
import {IEntity} from "@symbioticfi/core/src/interfaces/common/IEntity.sol";
import {IVault} from "@symbioticfi/core/src/interfaces/vault/IVault.sol";
import {IDelegatorHook} from "@symbioticfi/core/src/interfaces/delegator/IDelegatorHook.sol";
import {INetworkRestakeDelegator} from "@symbioticfi/core/src/interfaces/delegator/INetworkRestakeDelegator.sol";

import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";

contract NetworkRestakeFairHook is INetworkRestakeFairHook {
using Math for uint256;

/**
* @inheritdoc IDelegatorHook
*/
function onSlash(
bytes32 subnetwork,
address operator,
uint256 slashedAmount,
uint48 captureTimestamp,
bytes calldata data
) external {
if (IEntity(msg.sender).TYPE() != 0) {
revert NotNetworkRestakeDelegator();
}

if (slashedAmount == 0) {
return;
}

address vault = INetworkRestakeDelegator(msg.sender).vault();
address slasher = IVault(vault).slasher();

uint256 prevSlashableStake = INetworkRestakeDelegator(msg.sender).stakeAt(
subnetwork, operator, captureTimestamp, new bytes(0)
)
- (
(IBaseSlasher(slasher).cumulativeSlash(subnetwork, operator) - slashedAmount)
- IBaseSlasher(slasher).cumulativeSlashAt(subnetwork, operator, captureTimestamp, new bytes(0))
);

INetworkRestakeDelegator(msg.sender).setOperatorNetworkShares(
subnetwork,
operator,
(prevSlashableStake - slashedAmount).mulDiv(
INetworkRestakeDelegator(msg.sender).operatorNetworkShares(subnetwork, operator), prevSlashableStake
)
);
}
}
8 changes: 8 additions & 0 deletions src/interfaces/INetworkRestakeFairHook.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

import {IDelegatorHook} from "@symbioticfi/core/src/interfaces/delegator/IDelegatorHook.sol";

interface INetworkRestakeFairHook is IDelegatorHook {
error NotNetworkRestakeDelegator();
}
86 changes: 86 additions & 0 deletions test/NetworkRestakeFairHook.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.8.25;

import {POCBaseTest} from "@symbioticfi/core/test/POCBase.t.sol";

import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {Subnetwork} from "@symbioticfi/core/src/contracts/libraries/Subnetwork.sol";

import {NetworkRestakeFairHook} from "../src/contracts/NetworkRestakeFairHook.sol";

contract NetworkRestakeFairHookTest is POCBaseTest {
using Math for uint256;
using Subnetwork for bytes32;
using Subnetwork for address;

function setUp() public override {
super.setUp();
}

function test_SlashWithHook(
uint256 depositAmount,
uint256 operatorNetworkShares1,
uint256 slashAmount1,
uint256 slashAmount2
) public {
depositAmount = bound(depositAmount, 1, 100 * 10 ** 18);
operatorNetworkShares1 = bound(operatorNetworkShares1, 1, type(uint256).max / 2);
slashAmount1 = bound(slashAmount1, 1, type(uint256).max);
slashAmount2 = bound(slashAmount2, 1, type(uint256).max);
vm.assume(slashAmount1 < Math.min(depositAmount, Math.min(type(uint256).max, operatorNetworkShares1)));

uint256 blockTimestamp = block.timestamp * block.timestamp / block.timestamp * block.timestamp / block.timestamp;
blockTimestamp = blockTimestamp + 1_720_700_948;
vm.warp(blockTimestamp);

address hook = address(new NetworkRestakeFairHook());

vm.startPrank(alice);
delegator1.setHook(hook);
delegator1.grantRole(delegator1.OPERATOR_NETWORK_SHARES_SET_ROLE(), hook);
vm.stopPrank();

address network = alice;
_registerNetwork(network, alice);
_setMaxNetworkLimit(address(delegator1), network, 0, type(uint256).max);

_registerOperator(alice);

_optInOperatorVault(vault1, alice);

_optInOperatorNetwork(alice, address(network));

_deposit(vault1, alice, depositAmount);

_setNetworkLimitNetwork(delegator1, alice, network, type(uint256).max);

_setOperatorNetworkShares(delegator1, alice, network, alice, operatorNetworkShares1);

assertEq(delegator1.networkLimit(network.subnetwork(0)), type(uint256).max);
assertEq(delegator1.totalOperatorNetworkShares(network.subnetwork(0)), operatorNetworkShares1);
assertEq(delegator1.operatorNetworkShares(network.subnetwork(0), alice), operatorNetworkShares1);

blockTimestamp = blockTimestamp + 1;
vm.warp(blockTimestamp);

uint256 slashableStake = slasher1.slashableStake(network.subnetwork(0), alice, uint48(blockTimestamp - 1), "");
uint256 slashedAmount = _slash(slasher1, alice, network, alice, slashAmount1, uint48(blockTimestamp - 1), "");

uint256 operatorShares =
operatorNetworkShares1 - slashedAmount.mulDiv(operatorNetworkShares1, slashableStake, Math.Rounding.Ceil);

assertEq(delegator1.networkLimit(network.subnetwork(0)), type(uint256).max);
assertEq(delegator1.totalOperatorNetworkShares(network.subnetwork(0)), operatorShares);
assertEq(delegator1.operatorNetworkShares(network.subnetwork(0), alice), operatorShares);

slashableStake = slasher1.slashableStake(network.subnetwork(0), alice, uint48(blockTimestamp - 1), "");
slashedAmount = _slash(slasher1, alice, network, alice, slashAmount2, uint48(blockTimestamp - 1), "");

operatorShares = operatorShares - slashedAmount.mulDiv(operatorShares, slashableStake, Math.Rounding.Ceil);

assertEq(delegator1.networkLimit(network.subnetwork(0)), type(uint256).max);
assertEq(delegator1.totalOperatorNetworkShares(network.subnetwork(0)), operatorShares);
assertEq(delegator1.operatorNetworkShares(network.subnetwork(0), alice), operatorShares);
}
}

0 comments on commit d4fcf0c

Please sign in to comment.