Skip to content

Commit

Permalink
Fix exit queue bug, reenable deposit with credit, remove separate cre…
Browse files Browse the repository at this point in the history
…dit balance, update interfaces
  • Loading branch information
kanewallmann committed Feb 10, 2025
1 parent 2eb88e5 commit f107ad3
Show file tree
Hide file tree
Showing 18 changed files with 185 additions and 140 deletions.
8 changes: 4 additions & 4 deletions contracts/contract/deposit/RocketDepositPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ contract RocketDepositPool is RocketBase, RocketDepositPoolInterface, RocketVaul
/// @notice Returns the credit balance for a given node operator
/// @param _nodeAddress Address of the node operator to query
function getNodeCreditBalance(address _nodeAddress) override public view returns (uint256) {
return getUint(keccak256(abi.encodePacked("deposit.pool.node.credit", _nodeAddress)));
return getUint(keccak256(abi.encodePacked("node.deposit.credit.balance", _nodeAddress)));
}

/// @notice Excess deposit pool balance (in excess of minipool queue capacity)
Expand Down Expand Up @@ -379,7 +379,7 @@ contract RocketDepositPool is RocketBase, RocketDepositPoolInterface, RocketVaul
// Add to node's credit for the amount supplied
RocketMegapoolDelegateInterface megapool = RocketMegapoolDelegateInterface(msg.sender);
address nodeAddress = megapool.getNodeAddress();
addUint(keccak256(abi.encodePacked("deposit.pool.node.credit", nodeAddress)), value.suppliedValue * milliToWei);
addUint(keccak256(abi.encodePacked("node.deposit.credit.balance", nodeAddress)), value.suppliedValue * milliToWei);
if (_expressQueue) {
// Refund express ticket
RocketNodeManagerInterface rocketNodeManager = RocketNodeManagerInterface(getContractAddress("rocketNodeManager"));
Expand All @@ -401,10 +401,10 @@ contract RocketDepositPool is RocketBase, RocketDepositPoolInterface, RocketVaul
/// @notice Allows node operator to withdraw any ETH credit they have as rETH
/// @param _amount Amount in ETH to withdraw
function withdrawCredit(uint256 _amount) override external onlyRegisteredNode(msg.sender) {
uint256 credit = getUint(keccak256(abi.encodePacked("deposit.pool.node.credit", msg.sender)));
uint256 credit = getUint(keccak256(abi.encodePacked("node.deposit.credit.balance", msg.sender)));
require(credit >= _amount, "Amount exceeds credit available");
// Account for balance changes
subUint(keccak256(abi.encodePacked("deposit.pool.node.credit", msg.sender)), _amount);
subUint(keccak256(abi.encodePacked("node.deposit.credit.balance", msg.sender)), _amount);
subUint("deposit.pool.node.balance", _amount);
// Mint rETH to node
// TODO: Do we need to check deposits are enabled, capacity is respected and apply a deposit fee?
Expand Down
18 changes: 12 additions & 6 deletions contracts/contract/megapool/RocketMegapoolDelegate.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import {IERC20} from "../../interface/util/IERC20.sol";
import {RocketMegapoolDelegateBase} from "./RocketMegapoolDelegateBase.sol";
import {RocketMegapoolStorageLayout} from "./RocketMegapoolStorageLayout.sol";

/// @title RocketMegapool
/// @notice This contract manages multiple validators. It serves as the target of Beacon Chain withdrawal credentials.
contract RocketMegapoolDelegate is RocketMegapoolDelegateBase, RocketMegapoolDelegateInterface {
// Constants
Expand Down Expand Up @@ -95,7 +94,9 @@ contract RocketMegapoolDelegate is RocketMegapoolDelegateBase, RocketMegapoolDel
RocketDepositPoolInterface rocketDepositPool = RocketDepositPoolInterface(getContractAddress("rocketDepositPool"));
rocketDepositPool.exitQueue(_validatorId, validator.expressUsed);
// Decrease total bond used for bond requirement calculations
nodeBond -= validator.lastRequestedBond;
nodeBond -= validator.lastRequestedBond * milliToWei;
// Increment inactive validator count
numInactiveValidators++;
// Update validator state
validator.inQueue = false;
validator.lastRequestedBond = 0;
Expand Down Expand Up @@ -329,6 +330,11 @@ contract RocketMegapoolDelegate is RocketMegapoolDelegateBase, RocketMegapoolDel
return numValidators;
}

/// @notice Returns the number of validators that are considered for bond requirement
function getActiveValidatorCount() override external view returns (uint32) {
return numValidators - numInactiveValidators;
}

/// @notice Returns information about a given validator
function getValidatorInfo(uint32 _validatorId) override external view returns (RocketMegapoolStorageLayout.ValidatorInfo memory) {
return validators[_validatorId];
Expand Down Expand Up @@ -366,13 +372,13 @@ contract RocketMegapoolDelegate is RocketMegapoolDelegateBase, RocketMegapoolDel
return _calculateRewards(_amount);
}

function _calculateRewards(uint256 _rewards) internal view returns (uint256 nodeAmount, uint256 voterAmount, uint256 rethAmount) {
function _calculateRewards(uint256 _rewards) internal view returns (uint256 nodeRewards, uint256 voterRewards, uint256 rethRewards) {
RocketNetworkRevenuesInterface rocketNetworkRevenues = RocketNetworkRevenuesInterface(getContractAddress("rocketNetworkRevenues"));
(, uint256 voterShare, uint256 rethShare) = rocketNetworkRevenues.calculateSplit(lastDistributionBlock);
uint256 borrowedPortion = _rewards * userCapital / (nodeCapital + userCapital);
rethAmount = rethShare * borrowedPortion / calcBase;
voterAmount = voterShare * borrowedPortion / calcBase;
nodeAmount = _rewards - rethAmount - voterAmount;
rethRewards = rethShare * borrowedPortion / calcBase;
voterRewards = voterShare * borrowedPortion / calcBase;
nodeRewards = _rewards - rethRewards - voterRewards;
}

function getPendingRewards() override public view returns (uint256) {
Expand Down
10 changes: 5 additions & 5 deletions contracts/contract/megapool/RocketMegapoolManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ contract RocketMegapoolManager is RocketBase, RocketMegapoolManagerInterface {

/// @notice Returns validator info for the given global megapool validator index
/// @param _index The index of the validator to query
function getValidatorInfo(uint256 _index) override external view returns (RocketMegapoolStorageLayout.ValidatorInfo memory, address, uint32) {
function getValidatorInfo(uint256 _index) override external view returns (RocketMegapoolStorageLayout.ValidatorInfo memory validatorInfo, address megapool, uint32 validatorId) {
uint256 encoded = getUint(keccak256(abi.encodePacked("megapool.validator.set", _index)));
address megapoolAddress = address(uint160(encoded >> 96));
uint32 validatorId = uint32(encoded);
megapool = address(uint160(encoded >> 96));
validatorId = uint32(encoded);

RocketMegapoolInterface rocketMegapool = RocketMegapoolInterface(megapoolAddress);
return (rocketMegapool.getValidatorInfo(validatorId), megapoolAddress, validatorId);
RocketMegapoolInterface rocketMegapool = RocketMegapoolInterface(megapool);
return (rocketMegapool.getValidatorInfo(validatorId), megapool, validatorId);
}
}
6 changes: 1 addition & 5 deletions contracts/contract/megapool/RocketMegapoolStorageLayout.sol
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,5 @@ abstract contract RocketMegapoolStorageLayout {
mapping(uint32 => ValidatorInfo) internal validators;
mapping(uint32 => PrestakeData) internal prestakeData;

// TODO: Move this to rocketNodeStaking
uint256 internal stakedRPL;
uint256 internal unstakedRPL;
uint256 internal lastUnstakeRequest;

uint32 internal numInactiveValidators; // Number of validators that are no longer contributing to bond requirement
}
6 changes: 2 additions & 4 deletions contracts/contract/network/RocketNetworkRevenues.sol
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,8 @@ contract RocketNetworkRevenues is RocketBase, RocketNetworkRevenuesInterface {

/// @notice Calculates the time-weighted average revenue split values between the supplied block number and now
/// @param _sinceBlock The starting block number for the calculation
function calculateSplit(uint256 _sinceBlock) external override view returns (uint256, uint256, uint256) {
function calculateSplit(uint256 _sinceBlock) external override view returns (uint256 nodeShare, uint256 voterShare, uint256 rethShare) {
RocketNetworkSnapshotsInterface rocketNetworkSnapshots = RocketNetworkSnapshotsInterface(getContractAddress("rocketNetworkSnapshots"));
uint256 nodeShare;
uint256 voterShare;
if (_sinceBlock == block.number) {
nodeShare = _getCurrentShare(rocketNetworkSnapshots, nodeShareKey);
voterShare = _getCurrentShare(rocketNetworkSnapshots, voterShareKey);
Expand All @@ -103,7 +101,7 @@ contract RocketNetworkRevenues is RocketBase, RocketNetworkRevenuesInterface {
voterShare *= shareScale;
}
uint256 rethCommission = nodeShare + voterShare;
uint256 rethShare = 1 ether - rethCommission;
rethShare = 1 ether - rethCommission;
return (nodeShare, voterShare, rethShare);
}

Expand Down
71 changes: 36 additions & 35 deletions contracts/contract/node/RocketNodeDeposit.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ import "../../interface/node/RocketNodeManagerInterface.sol";
import "../../interface/node/RocketNodeStakingInterface.sol";
import "../RocketBase.sol";
import {RocketMegapoolManagerInterface} from "../../interface/megapool/RocketMegapoolManagerInterface.sol";
import {RocketVaultWithdrawerInterface} from "../../interface/RocketVaultWithdrawerInterface.sol";

/// @notice Entry point for node operators to perform deposits for the creation of new validators on the network
contract RocketNodeDeposit is RocketBase, RocketNodeDepositInterface {
contract RocketNodeDeposit is RocketBase, RocketNodeDepositInterface, RocketVaultWithdrawerInterface {
// Constants
uint256 constant internal pubKeyLength = 48;
uint256 constant internal signatureLength = 96;
Expand All @@ -32,9 +33,12 @@ contract RocketNodeDeposit is RocketBase, RocketNodeDepositInterface {
version = 5;
}

/// @dev Accept incoming ETH from the deposit pool
/// @notice Accept incoming ETH from the deposit pool
receive() external payable onlyLatestContract("rocketDepositPool", msg.sender) {}

/// @notice Accept incoming ETH from the vault
function receiveVaultWithdrawalETH() external payable {}

/// @notice Returns the bond requirement for the given number of validators
function getBondRequirement(uint256 _numValidators) override public view returns (uint256) {
// Get contracts
Expand Down Expand Up @@ -133,7 +137,7 @@ contract RocketNodeDeposit is RocketBase, RocketNodeDepositInterface {
// Check amount
require(msg.value == _bondAmount, "Invalid value");
// Process the deposit
_deposit(_bondAmount, _useExpressTicket, _validatorPubkey, _validatorSignature, _depositDataRoot);
_deposit(_bondAmount, _useExpressTicket, _validatorPubkey, _validatorSignature, _depositDataRoot, msg.value);
}

/// @notice Accept a node deposit and create a new minipool under the node. Only accepts calls from registered nodes
Expand All @@ -143,46 +147,43 @@ contract RocketNodeDeposit is RocketBase, RocketNodeDepositInterface {
/// @param _validatorSignature Signature from the validator over the deposit data
/// @param _depositDataRoot The hash tree root of the deposit data (passed onto the deposit contract on pre stake)
function depositWithCredit(uint256 _bondAmount, bool _useExpressTicket, bytes calldata _validatorPubkey, bytes calldata _validatorSignature, bytes32 _depositDataRoot) override external payable onlyLatestContract("rocketNodeDeposit", address(this)) onlyRegisteredNode(msg.sender) {
revert("Not implemented");
// {
// uint256 balanceToUse = 0;
// uint256 creditToUse = 0;
// uint256 shortFall = _bondAmount - msg.value;
// uint256 credit = getNodeUsableCredit(msg.sender);
// uint256 balance = getNodeEthBalance(msg.sender);
// // Check credit
// require(credit + balance >= shortFall, "Insufficient credit");
// // Calculate amounts to use
// creditToUse = shortFall;
// if (credit < shortFall) {
// balanceToUse = shortFall - credit;
// creditToUse = credit;
// }
// // Update balances
// if (balanceToUse > 0) {
// subUint(keccak256(abi.encodePacked("node.eth.balance", msg.sender)), balanceToUse);
// // Withdraw the funds
// RocketVaultInterface rocketVault = RocketVaultInterface(getContractAddress("rocketVault"));
// rocketVault.withdrawEther(balanceToUse);
// }
// if (creditToUse > 0) {
// subUint(keccak256(abi.encodePacked("node.deposit.credit.balance", msg.sender)), creditToUse);
// }
// }
// // Process the deposit
// _deposit(_bondAmount, _useExpressTicket, _validatorPubkey, _validatorSignature, _depositDataRoot);
uint256 balanceToUse = 0;
uint256 creditToUse = 0;
uint256 shortFall = _bondAmount - msg.value;
uint256 credit = getNodeUsableCredit(msg.sender);
uint256 balance = getNodeEthBalance(msg.sender);
// Check credit
require(credit + balance >= shortFall, "Insufficient credit");
// Calculate amounts to use
creditToUse = shortFall;
if (credit < shortFall) {
balanceToUse = shortFall - credit;
creditToUse = credit;
}
// Update balances
if (balanceToUse > 0) {
subUint(keccak256(abi.encodePacked("node.eth.balance", msg.sender)), balanceToUse);
// Withdraw the funds
RocketVaultInterface rocketVault = RocketVaultInterface(getContractAddress("rocketVault"));
rocketVault.withdrawEther(balanceToUse);
}
if (creditToUse > 0) {
subUint(keccak256(abi.encodePacked("node.deposit.credit.balance", msg.sender)), creditToUse);
}
// Process the deposit
_deposit(_bondAmount, _useExpressTicket, _validatorPubkey, _validatorSignature, _depositDataRoot, msg.value + balanceToUse);
}

/// @dev Internal logic to process a deposit
function _deposit(uint256 _bondAmount, bool _useExpressTicket, bytes calldata _validatorPubkey, bytes calldata _validatorSignature, bytes32 _depositDataRoot) private {
function _deposit(uint256 _bondAmount, bool _useExpressTicket, bytes calldata _validatorPubkey, bytes calldata _validatorSignature, bytes32 _depositDataRoot, uint256 _value) private {
// Validate arguments
validateBytes(_validatorPubkey, pubKeyLength);
validateBytes(_validatorSignature, signatureLength);
// Check pre-conditions
checkVotingInitialised();
checkDepositsEnabled();
// Emit deposit received event
emit DepositReceived(msg.sender, msg.value, block.timestamp);
emit DepositReceived(msg.sender, _value, block.timestamp);
// Get or deploy a megapool for the caller
RocketMegapoolFactoryInterface rocketMegapoolFactory = RocketMegapoolFactoryInterface(getContractAddress("rocketMegapoolFactory"));
RocketMegapoolInterface megapool = RocketMegapoolInterface(rocketMegapoolFactory.getOrDeployContract(msg.sender));
Expand All @@ -195,7 +196,7 @@ contract RocketNodeDeposit is RocketBase, RocketNodeDepositInterface {
rocketMegapoolManager.addValidator(address(megapool), megapool.getValidatorCount());
// Send node operator's bond to the deposit pool
RocketDepositPoolInterface rocketDepositPool = RocketDepositPoolInterface(getContractAddress("rocketDepositPool"));
rocketDepositPool.nodeDeposit{value: msg.value}(_bondAmount);
rocketDepositPool.nodeDeposit{value: _value}(_bondAmount);
// Attempt to assign 1 megapool
rocketDepositPool.maybeAssignOneDeposit();
}
Expand Down Expand Up @@ -230,7 +231,7 @@ contract RocketNodeDeposit is RocketBase, RocketNodeDepositInterface {

/// @dev Checks the bond requirements for a node deposit
function checkBondRequirement(RocketMegapoolInterface _megapool, uint256 _bondAmount) internal {
uint256 totalBondRequired = getBondRequirement(_megapool.getValidatorCount());
uint256 totalBondRequired = getBondRequirement(_megapool.getActiveValidatorCount());
uint256 currentBond = _megapool.getNodeBond();
uint256 requiredBond = totalBondRequired - currentBond;
require(_bondAmount == requiredBond, "Bond requirement not met");
Expand Down
6 changes: 3 additions & 3 deletions contracts/contract/util/LinkedListStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -233,9 +233,9 @@ contract LinkedListStorage is RocketBase, LinkedListStorageInterface {
}

/// @notice Returns the supplied number of entries starting at the supplied index
function scan(bytes32 _namespace, uint256 _start, uint256 _count) override external view returns (DepositQueueValue[] memory, uint256) {
DepositQueueValue[] memory entries = new DepositQueueValue[](_count);
uint256 nextIndex = _start;
function scan(bytes32 _namespace, uint256 _start, uint256 _count) override external view returns (DepositQueueValue[] memory entries, uint256 nextIndex) {
entries = new DepositQueueValue[](_count);
nextIndex = _start;
uint256 total = 0;

// If nextIndex is 0, begin scan at the start of the list
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ interface RocketMegapoolDelegateInterface is RocketMegapoolDelegateBaseInterface
function repayDebt() external payable;

function getValidatorCount() external view returns (uint32);
function getActiveValidatorCount() external view returns (uint32);
function getValidatorInfo(uint32 _validatorId) external view returns (RocketMegapoolStorageLayout.ValidatorInfo memory);
function getAssignedValue() external view returns (uint256);
function getDebt() external view returns (uint256);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ import "../../contract/megapool/RocketMegapoolStorageLayout.sol";
interface RocketMegapoolManagerInterface {
function getValidatorCount() external view returns (uint256);
function addValidator(address _megapoolAddress, uint32 _validatorId) external;
function getValidatorInfo(uint256 _index) external view returns (RocketMegapoolStorageLayout.ValidatorInfo memory validatorInfo, address megapoolAddress, uint32 validatorId);
function getValidatorInfo(uint256 _index) external view returns (RocketMegapoolStorageLayout.ValidatorInfo memory validatorInfo, address megapool, uint32 validatorId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ interface RocketNetworkRevenuesInterface {
function getCurrentVoterShare() external view returns (uint256);
function setNodeShare(uint256 _newShare) external;
function setVoterShare(uint256 _newShare) external;
function calculateSplit(uint256 _sinceBlock) external view returns (uint256, uint256, uint256);
function calculateSplit(uint256 _sinceBlock) external view returns (uint256 nodeShare, uint256 voterShare, uint256 rethShare);
}
2 changes: 1 addition & 1 deletion contracts/interface/util/LinkedListStorageInterface.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,5 @@ interface LinkedListStorageInterface {
function enqueueItem(bytes32 _namespace, DepositQueueValue memory _value) external;
function dequeueItem(bytes32 _namespace) external returns (DepositQueueValue memory);
function removeItem(bytes32 _namespace, DepositQueueKey memory _key) external;
function scan(bytes32 _namespace, uint256 _startIndex, uint256 _count) external view returns (DepositQueueValue[] memory, uint256 nextIndex);
function scan(bytes32 _namespace, uint256 _startIndex, uint256 _count) external view returns (DepositQueueValue[] memory entries, uint256 nextIndex);
}
2 changes: 1 addition & 1 deletion test-upgrade/rocket-upgrade-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ describe('Test Upgrade', () => {
await executeUpgrade();

await deployMegapool({ from: node });
await nodeDeposit(false, false, { value: '4'.ether, from: node });
await nodeDeposit(node);

const megapool = await getMegapoolForNode(node);

Expand Down
Loading

0 comments on commit f107ad3

Please sign in to comment.