Skip to content

Commit

Permalink
feat: staker upgrade to include protocol fees
Browse files Browse the repository at this point in the history
  • Loading branch information
sandybradley committed Nov 1, 2023
1 parent 25149b6 commit 2aaac71
Show file tree
Hide file tree
Showing 9 changed files with 123 additions and 8 deletions.
15 changes: 15 additions & 0 deletions nix/shell.nix
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,21 @@
-vvvvv
'';
}
{
category = "deployments";
name = "deployStaker";
help = "Deploy the Staker contract";
command = ''
forge script $PRJ_ROOT/script/DeployStaker.s.sol:DeployStakerScript \
--chain-id 1 \
--rpc-url $RPC_MAINNET \
--broadcast \
--private-key $PRIVATE_KEY \
--verify \
--etherscan-api-key $ETHERSCAN_API_KEY \
-vvvvv
'';
}
{
category = "deployments";
name = "deploy-goerli";
Expand Down
2 changes: 1 addition & 1 deletion script/Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ contract DeployScript is Script {
// MevEthShareVault initialShareVault = new MevEthShareVault(authority, address(mevEth), authority);
address initialShareVault = safe;
// deploy staking module
IStakingModule initialStakingModule = new WagyuStaker(authority, beaconDepositContract, address(mevEth));
IStakingModule initialStakingModule = new WagyuStaker(authority, beaconDepositContract, address(mevEth), authority);
// initialise mevETH
mevEth.init(address(initialShareVault), address(initialStakingModule));

Expand Down
41 changes: 41 additions & 0 deletions script/DeployStaker.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;

import "forge-std/Script.sol";
import { MevEth } from "src/MevEth.sol";
import { IAuth } from "src/interfaces/IAuth.sol";
import { WagyuStaker } from "src/WagyuStaker.sol";
import { AuthManager } from "src/libraries/AuthManager.sol";
import { IStakingModule } from "src/interfaces/IStakingModule.sol";

contract DeployStakerScript is Script {
error UnknownChain();

function run() public {
address authority = 0x617c8dE5BdE54ffbb8d92716CC947858cA38f582;
uint256 chainId;
address beaconDepositContract;
address weth;
assembly {
chainId := chainid()
}
if (chainId == 1) {
// Eth mainnet
beaconDepositContract = 0x00000000219ab540356cBB839Cbe05303d7705Fa;
weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
} else if (chainId == 5) {
// Goerli
beaconDepositContract = 0xff50ed3d0ec03aC01D4C79aAd74928BFF48a7b2b;
weth = 0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6;
} else {
revert UnknownChain();
}

vm.startBroadcast();

// deploy staking module
IStakingModule initialStakingModule = new WagyuStaker(authority, beaconDepositContract, address(mevEth), authority);

vm.stopBroadcast();
}
}
31 changes: 29 additions & 2 deletions src/WagyuStaker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,14 @@ contract WagyuStaker is Auth, IStakingModule {
uint128 totalValidatorExitsPaid;
}

/// @notice Record of total deposits, withdraws, rewards paid and validators exited
/// @notice Record of total deposits, withdraws, rewards and fees paid and validators exited
Record public record;
/// @notice The number of validators on the consensus layer registered under this contract
uint256 public validators;
/// @notice The address of the MevEth contract
address public mevEth;
/// @notice The address that protocol fees are sent to.
address public protocolFeeTo;
/// @notice Validator deposit size.
uint256 public constant override VALIDATOR_DEPOSIT_SIZE = 32 ether;
/// @notice The Canonical Address of the BeaconChainDepositContract
Expand All @@ -53,14 +55,20 @@ contract WagyuStaker is Auth, IStakingModule {
event ValidatorWithdraw(address sender, uint256 amount);
/// @notice Event emitted when the mevEth address is updated.
event MevEthUpdated(address indexed meveth);
/// @notice Event emitted when the protocolFeeTo address is updated.
event ProtocolFeeToUpdated(address indexed newProtocolFeeTo);
/// @notice Event emitted when the protocol fees are sent to the protocolFeeTo address.
event FeesSent(uint256 indexed feesSent);

/// @notice Construction sets authority, MevEth, and deposit contract addresses
/// @param _authority The address of the controlling admin authority
/// @param _depositContract The address of the beacon deposit contract
/// @param _mevEth The address of the mevETH contract
constructor(address _authority, address _depositContract, address _mevEth) Auth(_authority) {
/// @param _protocolFeeTo The address that protocol fees are sent to.
constructor(address _authority, address _depositContract, address _mevEth, address _protocolFeeTo) Auth(_authority) {
mevEth = _mevEth;
BEACON_CHAIN_DEPOSIT_CONTRACT = IBeaconDepositContract(_depositContract);
protocolFeeTo = _protocolFeeTo;
}

/// @notice Function to deposit funds into the BEACON_CHAIN_DEPOSIT_CONTRACT, and register a validator
Expand Down Expand Up @@ -111,6 +119,25 @@ contract WagyuStaker is Auth, IStakingModule {
emit RewardsPaid(rewards);
}

/// @notice Function to collect the fees owed to the prorotocol.
function sendFees(uint256 fees) external onlyOperator {
unchecked {
record.totalWithdrawn += uint128(fees);
}

SafeTransferLib.safeTransferETH(protocolFeeTo, fees);

emit FeesSent(fees);
}

function setProtocolFeeTo(address newProtocolFeeTo) external onlyAdmin {
if (newProtocolFeeTo == address(0)) {
revert MevEthErrors.ZeroAddress();
}
protocolFeeTo = newProtocolFeeTo;
emit ProtocolFeeToUpdated(newProtocolFeeTo);
}

function registerExit() external {
// Only the MevEth contract can call this function
if (msg.sender != mevEth) {
Expand Down
2 changes: 1 addition & 1 deletion test/MevEthTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ contract MevEthTest is Test {
// assign share vault as proxy to multisig
address initialShareVault = address(safe);

address initialStakingModule = address(IStakingModule(address(new WagyuStaker(SamBacha, address(depositContract), address(mevEth)))));
address initialStakingModule = address(IStakingModule(address(new WagyuStaker(SamBacha, address(depositContract), address(mevEth), SamBacha))));

AuthManager authManager = new AuthManager(SamBacha, address(mevEth), address(initialShareVault), address(initialStakingModule));

Expand Down
4 changes: 2 additions & 2 deletions test/unit/Admin.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,7 @@ contract MevAdminTest is MevEthTest {

// Create new share vault and staking module
address initialShareVault = address(new MevEthShareVault(SamBacha, address(mevEth), SamBacha));
address initialStakingModule = address(IStakingModule(address(new WagyuStaker(SamBacha, address(depositContract), address(mevEth)))));
address initialStakingModule = address(IStakingModule(address(new WagyuStaker(SamBacha, address(depositContract), address(mevEth), SamBacha))));
assert(!mevEth.initialized());

// Initialize the MevEth contract
Expand All @@ -623,7 +623,7 @@ contract MevAdminTest is MevEthTest {

// Create new share vault and staking module
address initialShareVault = address(new MevEthShareVault(SamBacha, address(mevEth), SamBacha));
address initialStakingModule = address(IStakingModule(address(new WagyuStaker(SamBacha, address(depositContract), address(mevEth)))));
address initialStakingModule = address(IStakingModule(address(new WagyuStaker(SamBacha, address(depositContract), address(mevEth), SamBacha))));

// Expect an unauthorized revert
vm.expectRevert(Auth.Unauthorized.selector);
Expand Down
2 changes: 1 addition & 1 deletion test/unit/CreamRedeem.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ contract CreamRedeemTest is MevEthTest {
address constant CRETH2_HOLDER = 0x36cc7B13029B5DEe4034745FB4F24034f3F2ffc6;

function setUp() public override {
MAINNET_FORK_ID = vm.createSelectFork(RPC_ETH_MAINNET);
MAINNET_FORK_ID = vm.createSelectFork(RPC_ETH_MAINNET, 18_368_945);

vm.selectFork(MAINNET_FORK_ID);
// deploy mevEth (mainnet)
Expand Down
2 changes: 1 addition & 1 deletion test/unit/Validator.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ contract ValidatorTest is MevEthTest {
function testUpdateToWagyuStakingModule() public {
// Update the staking module to the WagyuStaker and create a new validator
address depositContract = address(new DepositContract());
IStakingModule wagyuStakingModule = IStakingModule(address(new WagyuStaker(SamBacha, depositContract, address(mevEth))));
IStakingModule wagyuStakingModule = IStakingModule(address(new WagyuStaker(SamBacha, depositContract, address(mevEth), SamBacha)));
_updateStakingModule(wagyuStakingModule);

uint256 depositSize = mevEth.stakingModule().VALIDATOR_DEPOSIT_SIZE();
Expand Down
32 changes: 32 additions & 0 deletions test/unit/WagyuStaker.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,38 @@ contract WagyuStakerTest is MevEthTest {
assertEq(address(wagyuStaker).balance - totalDeposited, amount);
}

function testSendFees(uint128 fees) public {
vm.assume(fees > 0);
vm.deal(address(this), fees);
payable(wagyuStaker).transfer(fees);

vm.prank(SamBacha);
vm.expectEmit(true, false, false, false, address(wagyuStaker));
emit FeesSent(fees);
wagyuStaker.sendFees(fees);

assertEq(address(wagyuStaker).balance, 0);
assertEq(wagyuStaker.protocolFeeTo().balance, fees);
}

function testNegativeSendFees(uint128 fees) public {
vm.assume(fees > 0);
vm.assume(fees < 100_000_000_000_000_000_000_000_000);
vm.deal(address(this), fees);
payable(wagyuStaker).transfer(fees);

vm.expectRevert(Auth.Unauthorized.selector);
wagyuStaker.sendFees(fees);

address newProtocolFeeTo = address(0);
vm.prank(SamBacha);
vm.expectRevert();
wagyuStaker.setProtocolFeeTo(newProtocolFeeTo);

assertEq(address(wagyuStaker).balance, fees);
assertEq(wagyuStaker.protocolFeeTo().balance, 0);
}

function testSetNewMevEth(address newMevEth) public {
vm.assume(newMevEth != address(0));

Expand Down

0 comments on commit 2aaac71

Please sign in to comment.