Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions mainnet-contracts/script/DeployPuffer.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import { IPufferOracleV2 } from "../src/interface/IPufferOracleV2.sol";
import { IRewardsCoordinator } from "../src/interface/EigenLayer/IRewardsCoordinator.sol";
import { AVSContractsRegistry } from "../src/AVSContractsRegistry.sol";
import { RewardsCoordinatorMock } from "../test/mocks/RewardsCoordinatorMock.sol";
import { PufferNoRestakingValidator } from "../src/PufferNoRestakingValidator.sol";
import { MockWithdrawalRequestPredeploy } from "../test/mocks/MockWithdrawalRequestPredeploy.sol";

/**
* @title DeployPuffer
Expand Down Expand Up @@ -56,6 +58,9 @@ contract DeployPuffer is BaseScript {
OperationsCoordinator operationsCoordinator;
ValidatorTicketPricer validatorTicketPricer;
AVSContractsRegistry aVSContractsRegistry;
PufferNoRestakingValidator noRestakingETHRecipient;
MockWithdrawalRequestPredeploy withdrawalRequestPredeploy;
ValidatorTicket validatorTicketImplementation;

address eigenPodManager;
address delegationManager;
Expand Down Expand Up @@ -101,7 +106,7 @@ contract DeployPuffer is BaseScript {
validatorTicketPricer = new ValidatorTicketPricer(PufferOracleV2(oracle), address(accessManager));

validatorTicketProxy = new ERC1967Proxy(address(new NoImplementation()), "");
ValidatorTicket validatorTicketImplementation = new ValidatorTicket({
validatorTicketImplementation = new ValidatorTicket({
guardianModule: payable(guardiansDeployment.guardianModule),
treasury: payable(treasury),
pufferVault: payable(pufferVault),
Expand Down Expand Up @@ -146,14 +151,24 @@ contract DeployPuffer is BaseScript {

aVSContractsRegistry = new AVSContractsRegistry(address(accessManager));

withdrawalRequestPredeploy = new MockWithdrawalRequestPredeploy();

noRestakingETHRecipient = new PufferNoRestakingValidator({
protocol: address(proxy),
accessManager: address(accessManager),
beaconDepositContract: getStakingContract(),
withdrawalRequestPredeploy: address(withdrawalRequestPredeploy)
});

// Puffer Service implementation
pufferProtocolImpl = new PufferProtocol({
pufferVault: PufferVaultV2(payable(pufferVault)),
validatorTicket: ValidatorTicket(address(validatorTicketProxy)),
guardianModule: GuardianModule(payable(guardiansDeployment.guardianModule)),
moduleManager: address(moduleManagerProxy),
oracle: IPufferOracleV2(oracle),
beaconDepositContract: getStakingContract()
beaconDepositContract: getStakingContract(),
noRestakingETHRecipient: noRestakingETHRecipient
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ contract DeployPufferModuleImplementation is DeployerHelper {
vm.startBroadcast();

PufferModule newImpl = new PufferModule({
protocol: PufferProtocol(_getPufferProtocol()),
protocol: PufferProtocol(payable(_getPufferProtocol())),
eigenPodManager: _getEigenPodManager(),
delegationManager: IDelegationManager(_getDelegationManager()),
moduleManager: PufferModuleManager(payable(_getPufferModuleManager())),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { stdJson } from "forge-std/StdJson.sol";
import { IPufferOracleV2 } from "../src/interface/IPufferOracleV2.sol";
import { GuardianModule } from "../src/GuardianModule.sol";
import { DeployerHelper } from "./DeployerHelper.s.sol";
import { PufferNoRestakingValidator } from "../src/PufferNoRestakingValidator.sol";

/**
* forge script script/DeployPufferProtocolImplementation.s.sol:DeployPufferProtocolImplementation --rpc-url=$RPC_URL --private-key $PK
Expand All @@ -29,7 +30,8 @@ contract DeployPufferProtocolImplementation is DeployerHelper {
guardianModule: GuardianModule(payable(_getGuardianModule())),
moduleManager: _getPufferModuleManager(),
oracle: IPufferOracleV2(_getPufferOracle()),
beaconDepositContract: _getBeaconDepositContract()
beaconDepositContract: _getBeaconDepositContract(),
noRestakingETHRecipient: PufferNoRestakingValidator(payable(_getPufferNoRestakingETHRecipient()))
})
);

Expand Down
21 changes: 21 additions & 0 deletions mainnet-contracts/script/DeployerHelper.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,27 @@ abstract contract DeployerHelper is Script {
revert("PufferDeployer not available for this chain");
}

function _getPufferNoRestakingETHRecipient() internal view returns (address) {
if (block.chainid == mainnet) {
//@todo update
return address(55);
}

revert("PufferNoRestakingETHRecipient not available for this chain");
}

function _getWithdrawalRequestPredeploy() internal view returns (address) {
if (block.chainid == mainnet) {
// https://etherscan.io/address/0x00000961Ef480Eb55e80D19ad83579A64c007002
return 0x00000961Ef480Eb55e80D19ad83579A64c007002;
} else if (block.chainid == holesky) {
// https://holesky.etherscan.io/address/0x00000961Ef480Eb55e80D19ad83579A64c007002
return 0x00000961Ef480Eb55e80D19ad83579A64c007002;
}

revert("WithdrawalRequestPredeploy not available for this chain");
}

function _getDAO() internal view returns (address) {
// ATM Ops multisig is the DAO
return _getOPSMultisig();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ contract GenerateBLSKeysAndRegisterValidators is Script {
if (block.chainid == 17000) {
// Holesky
protocolAddress = 0xE00c79408B9De5BaD2FDEbB1688997a68eC988CD;
pufferProtocol = PufferProtocol(protocolAddress);
pufferProtocol = PufferProtocol(payable(protocolAddress));
forkVersion = "0x01017000";
} else if (block.chainid == 1) {
// Mainnet
protocolAddress = 0xf7b6B32492c2e13799D921E84202450131bd238B;
pufferProtocol = PufferProtocol(protocolAddress);
pufferProtocol = PufferProtocol(payable(protocolAddress));
forkVersion = "0x00000000";
}

Expand Down
2 changes: 1 addition & 1 deletion mainnet-contracts/script/UpgradePufETH.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ contract UpgradePufETH is BaseScript {

function run(
PufferDeployment memory deployment,
BridgingDeployment memory bridgingDeployment,
BridgingDeployment memory,
address pufferOracle,
address revenueDepositor
) public broadcast {
Expand Down
6 changes: 6 additions & 0 deletions mainnet-contracts/src/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,9 @@ error InvalidAmount();
* @dev Signature "0x90b8ec18"
*/
error TransferFailed();

/**
* @notice Thrown when the input is invalid
* @dev Signature "0xb4fa3fb3"
*/
error InvalidInput();
148 changes: 148 additions & 0 deletions mainnet-contracts/src/PufferNoRestakingValidator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.0 <0.9.0;

import { InvalidAddress, Unauthorized, InvalidInput } from "./Errors.sol";
import { IBeaconDepositContract } from "./interface/IBeaconDepositContract.sol";
import { PufferProtocol } from "./PufferProtocol.sol";
import { AccessManaged } from "@openzeppelin/contracts/access/manager/AccessManaged.sol";
import { IPufferNoRestakingValidator } from "./interface/IPufferNoRestakingValidator.sol";
import { Address } from "@openzeppelin/contracts/utils/Address.sol";

/**
* @title PufferNoRestakingValidator
* @author Puffer Finance
* @custom:security-contact [email protected]
*/
contract PufferNoRestakingValidator is IPufferNoRestakingValidator, AccessManaged {
using Address for address payable;

address public immutable BEACON_DEPOSIT_CONTRACT;
address public immutable WITHDRAWAL_REQUEST_PREDEPLOY;

PufferProtocol public immutable PUFFER_PROTOCOL;

/**
* The amount of ETH deposited to the Beacon Chain through this contract
*/
uint256 public depositedToBeaconChain;

/**
* The amount of ETH queued for withdrawal from the Beacon Chain through this contract
*/
uint256 public queuedWithdrawals;

constructor(
address protocol,
address accessManager,
address beaconDepositContract,
address withdrawalRequestPredeploy
) AccessManaged(accessManager) {
require(protocol != address(0), InvalidAddress());
require(beaconDepositContract != address(0), InvalidAddress());
require(withdrawalRequestPredeploy != address(0), InvalidAddress());
PUFFER_PROTOCOL = PufferProtocol(payable(protocol));
BEACON_DEPOSIT_CONTRACT = beaconDepositContract;
WITHDRAWAL_REQUEST_PREDEPLOY = withdrawalRequestPredeploy;
}

modifier onlyPufferProtocol() {
require(msg.sender == address(PUFFER_PROTOCOL), Unauthorized());
_;
}

/**
* @notice Receive function to allow the contract to receive ETH
*/
receive() external payable { }

/**
* @notice Start non restaking compounding validator
* @param pubKey The public key of the validator
* @param signature The signature of the validator
* @param depositDataRoot The deposit data root of the validator
*/
function startNonRestakingValidators(bytes calldata pubKey, bytes calldata signature, bytes32 depositDataRoot)
external
payable
onlyPufferProtocol
{
IBeaconDepositContract(BEACON_DEPOSIT_CONTRACT).deposit{ value: msg.value }({
pubkey: pubKey,
withdrawal_credentials: getWithdrawalCredentialsCompounding(),
signature: signature,
deposit_data_root: depositDataRoot
});

depositedToBeaconChain += msg.value;

emit ValidatorDeposited(pubKey, msg.value);
}

/**
* @notice Creates withdrawal requests for 0x02 validators
* @param pubkeys The public keys of the validators
* @param withdrawalAmounts The amounts of ETH to withdraw (in gwei) 0 = validator exit
* @dev Restricted to Puffer Paymaster
*/
function createWithdrawalRequest(bytes[] calldata pubkeys, uint256[] calldata withdrawalAmounts)
external
payable
restricted
{
require(pubkeys.length == withdrawalAmounts.length, InvalidInput());
require(pubkeys.length > 0, InvalidInput());

// Ensure withdrawalAmount is in gwei (1 gwei = 10^9 wei)
for (uint256 i = 0; i < withdrawalAmounts.length; i++) {
if (withdrawalAmounts[i] % 1 gwei != 0) {
revert WithdrawalAmountNotInGwei();
}
}

// Read the withdrawal fee from the withdrawal request predeploy
(bool feeReadOk, bytes memory feeData) = WITHDRAWAL_REQUEST_PREDEPLOY.staticcall("");
require(feeReadOk, FailedToReadWithdrawalFee());

// Create the withdrawal request
for (uint256 i = 0; i < pubkeys.length; i++) {
(bool withdrawalRequestOk,) = WITHDRAWAL_REQUEST_PREDEPLOY.call{
value: uint256(bytes32(feeData)) * pubkeys.length
}(abi.encodePacked(pubkeys[i], withdrawalAmounts[i]));
require(withdrawalRequestOk, FailedToCreateWithdrawalRequest());
// Convert from gwei to wei (1 gwei = 10^9 wei)
queuedWithdrawals += withdrawalAmounts[i] * 1 gwei;

emit WithdrawalRequested(pubkeys[i], withdrawalAmounts[i]);
}
}

/**
* @notice Exit the validators and send the ETH to the Puffer Vault
* @param amount The amount of ETH to return to the Puffer Vault
*/
function exitValidators(uint256 amount) external onlyPufferProtocol {
queuedWithdrawals -= amount;
payable(PUFFER_PROTOCOL.PUFFER_VAULT()).sendValue(amount);
}

/**
* @notice Transfer ETH to an address
* This function is used to transfer the earned rewards from this contract
* restricted to Puffer Team
* @param to The address to transfer the ETH to
* @param amount The amount of ETH to transfer
*/
function transferETH(address payable to, uint256 amount) external restricted {
// Only allow transfers of the Rewards to `to` address
require(amount > (address(this).balance - queuedWithdrawals), InvalidAmount());
payable(to).sendValue(amount);
}

/**
* @notice Get the withdrawal credentials for the compounding validators (this contract address)
* @return The withdrawal credentials
*/
function getWithdrawalCredentialsCompounding() public view returns (bytes memory) {
return abi.encodePacked(bytes1(uint8(2)), bytes11(0), address(this));
}
}
Loading