Skip to content
Merged
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
95 changes: 95 additions & 0 deletions bindings/legacy/v1.3.1/protocol/node.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package protocol

import (
"fmt"
"math/big"
"sync"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"

"github.com/rocket-pool/smartnode/bindings/dao/protocol"
"github.com/rocket-pool/smartnode/bindings/rocketpool"
"github.com/rocket-pool/smartnode/bindings/types"
"github.com/rocket-pool/smartnode/bindings/utils/eth"
)

// Config
const (
NodeSettingsContractName string = "rocketDAOProtocolSettingsNode"
MinimumPerMinipoolStakeSettingPath string = "node.per.minipool.stake.minimum"
MaximumPerMinipoolStakeSettingPath string = "node.per.minipool.stake.maximum"
)

// The minimum RPL stake per minipool as a fraction of assigned user ETH
func GetMinimumPerMinipoolStake(rp *rocketpool.RocketPool, opts *bind.CallOpts) (float64, error) {
nodeSettingsContract, err := getNodeSettingsContract(rp, opts)
if err != nil {
return 0, err
}
value := new(*big.Int)
if err := nodeSettingsContract.Call(opts, value, "getMinimumPerMinipoolStake"); err != nil {
return 0, fmt.Errorf("error getting minimum RPL stake per minipool: %w", err)
}
return eth.WeiToEth(*value), nil
}
func ProposeMinimumPerMinipoolStake(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) {
return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", MinimumPerMinipoolStakeSettingPath), NodeSettingsContractName, MinimumPerMinipoolStakeSettingPath, value, blockNumber, treeNodes, opts)
}
func EstimateProposeMinimumPerMinipoolStakeGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) {
return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", MinimumPerMinipoolStakeSettingPath), NodeSettingsContractName, MinimumPerMinipoolStakeSettingPath, value, blockNumber, treeNodes, opts)
}

// The minimum RPL stake per minipool as a fraction of assigned user ETH
func GetMinimumPerMinipoolStakeRaw(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) {
nodeSettingsContract, err := getNodeSettingsContract(rp, opts)
if err != nil {
return nil, err
}
value := new(*big.Int)
if err := nodeSettingsContract.Call(opts, value, "getMinimumPerMinipoolStake"); err != nil {
return nil, fmt.Errorf("error getting minimum RPL stake per minipool: %w", err)
}
return *value, nil
}

// The maximum RPL stake per minipool as a fraction of assigned user ETH
func GetMaximumPerMinipoolStake(rp *rocketpool.RocketPool, opts *bind.CallOpts) (float64, error) {
nodeSettingsContract, err := getNodeSettingsContract(rp, opts)
if err != nil {
return 0, err
}
value := new(*big.Int)
if err := nodeSettingsContract.Call(opts, value, "getMaximumPerMinipoolStake"); err != nil {
return 0, fmt.Errorf("error getting maximum RPL stake per minipool: %w", err)
}
return eth.WeiToEth(*value), nil
}
func ProposeMaximumPerMinipoolStake(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) {
return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", MaximumPerMinipoolStakeSettingPath), NodeSettingsContractName, MaximumPerMinipoolStakeSettingPath, value, blockNumber, treeNodes, opts)
}
func EstimateProposeMaximumPerMinipoolStakeGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) {
return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", MaximumPerMinipoolStakeSettingPath), NodeSettingsContractName, MaximumPerMinipoolStakeSettingPath, value, blockNumber, treeNodes, opts)
}

// The maximum RPL stake per minipool as a fraction of assigned user ETH
func GetMaximumPerMinipoolStakeRaw(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) {
nodeSettingsContract, err := getNodeSettingsContract(rp, opts)
if err != nil {
return nil, err
}
value := new(*big.Int)
if err := nodeSettingsContract.Call(opts, value, "getMaximumPerMinipoolStake"); err != nil {
return nil, fmt.Errorf("error getting maximum RPL stake per minipool: %w", err)
}
return *value, nil
}

// Get contracts
var nodeSettingsContractLock sync.Mutex

func getNodeSettingsContract(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) {
nodeSettingsContractLock.Lock()
defer nodeSettingsContractLock.Unlock()
return rp.GetContract(NodeSettingsContractName, opts)
}
26 changes: 13 additions & 13 deletions bindings/node/staking.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,19 +111,6 @@ func GetNodeUnstakingRPL(rp *rocketpool.RocketPool, nodeAddress common.Address,
return *unstakingRpl, nil
}

// Get a node's maximum RPL stake to collateralize their minipools
func GetNodeMaximumRPLStakeForMinipools(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) {
rocketNodeStaking, err := getRocketNodeStaking(rp, opts)
if err != nil {
return nil, err
}
nodeMaximumRplStake := new(*big.Int)
if err := rocketNodeStaking.Call(opts, nodeMaximumRplStake, "getNodeMaximumRPLStakeForMinipools", nodeAddress); err != nil {
return nil, fmt.Errorf("error getting maximum node RPL stake for minipools: %w", err)
}
return *nodeMaximumRplStake, nil
}

// Get the time a node last staked RPL
func GetNodeRPLStakedTime(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (uint64, error) {
rocketNodeStaking, err := getRocketNodeStaking(rp, opts)
Expand Down Expand Up @@ -202,6 +189,19 @@ func GetNodeMinipoolETHBorrowed(rp *rocketpool.RocketPool, nodeAddress common.Ad
return *nodeMinipoolETHBorrowed, nil
}

// Get the minimum amount of legacy staked RPL a node must have after unstaking
func GetNodeMinimumLegacyRPLStake(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) {
rocketNodeStaking, err := getRocketNodeStaking(rp, opts)
if err != nil {
return nil, err
}
nodeMinimumLegacyRplStake := new(*big.Int)
if err := rocketNodeStaking.Call(opts, nodeMinimumLegacyRplStake, "getNodeMinimumLegacyRPLStake", nodeAddress); err != nil {
return nil, fmt.Errorf("error getting node minimum legacy rpl stake: %w", err)
}
return *nodeMinimumLegacyRplStake, nil
}

// Get the amount of ETH the node has bonded
func GetNodeEthBonded(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) {
rocketNodeStaking, err := getRocketNodeStaking(rp, opts)
Expand Down
59 changes: 13 additions & 46 deletions bindings/settings/protocol/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ const (
SmoothingPoolRegistrationEnabledSettingPath string = "node.smoothing.pool.registration.enabled"
NodeDepositEnabledSettingPath string = "node.deposit.enabled"
VacantMinipoolsEnabledSettingPath string = "node.vacant.minipools.enabled"
MinimumPerMinipoolStakeSettingPath string = "node.per.minipool.stake.minimum"
MaximumPerMinipoolStakeSettingPath string = "node.per.minipool.stake.maximum"
MinimumLegacyRplStakePath string = "node.minimum.legacy.staked.rpl"
ReducedBondSettingPath string = "reduced.bond"
NodeUnstakingPeriodSettingPath string = "node.unstaking.period"
)
Expand Down Expand Up @@ -103,66 +102,34 @@ func EstimateProposeVacantMinipoolsEnabledGas(rp *rocketpool.RocketPool, value b
return protocol.EstimateProposeSetBoolGas(rp, fmt.Sprintf("set %s", VacantMinipoolsEnabledSettingPath), NodeSettingsContractName, VacantMinipoolsEnabledSettingPath, value, blockNumber, treeNodes, opts)
}

// The minimum RPL stake per minipool as a fraction of assigned user ETH
func GetMinimumPerMinipoolStake(rp *rocketpool.RocketPool, opts *bind.CallOpts) (float64, error) {
// The amount of legacy staked RPL required by a node after unstaking as percentage of their borrowed ETH
func GetMinimumLegacyRPLStake(rp *rocketpool.RocketPool, opts *bind.CallOpts) (float64, error) {
nodeSettingsContract, err := getNodeSettingsContract(rp, opts)
if err != nil {
return 0, err
}
value := new(*big.Int)
if err := nodeSettingsContract.Call(opts, value, "getMinimumPerMinipoolStake"); err != nil {
return 0, fmt.Errorf("error getting minimum RPL stake per minipool: %w", err)
if err := nodeSettingsContract.Call(opts, value, "getMinimumLegacyRPLStake"); err != nil {
return 0, fmt.Errorf("error getting minimum legacy RPL stake per node: %w", err)
}
return eth.WeiToEth(*value), nil
}
func ProposeMinimumPerMinipoolStake(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) {
return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", MinimumPerMinipoolStakeSettingPath), NodeSettingsContractName, MinimumPerMinipoolStakeSettingPath, value, blockNumber, treeNodes, opts)
func ProposeMinimumLecacyRPLStake(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) {
return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", MinimumLegacyRplStakePath), NodeSettingsContractName, MinimumLegacyRplStakePath, value, blockNumber, treeNodes, opts)
}
func EstimateProposeMinimumPerMinipoolStakeGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) {
return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", MinimumPerMinipoolStakeSettingPath), NodeSettingsContractName, MinimumPerMinipoolStakeSettingPath, value, blockNumber, treeNodes, opts)
func EstimateProposeMinimumLecacyRPLStakeGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) {
return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", MinimumLegacyRplStakePath), NodeSettingsContractName, MinimumLegacyRplStakePath, value, blockNumber, treeNodes, opts)
}

// The minimum RPL stake per minipool as a fraction of assigned user ETH
func GetMinimumPerMinipoolStakeRaw(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) {
// The amount of legacy staked RPL required by a node after unstaking as percentage of their borrowed ETH
func GetMinimumLegacyRPLStakeRaw(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) {
nodeSettingsContract, err := getNodeSettingsContract(rp, opts)
if err != nil {
return nil, err
}
value := new(*big.Int)
if err := nodeSettingsContract.Call(opts, value, "getMinimumPerMinipoolStake"); err != nil {
return nil, fmt.Errorf("error getting minimum RPL stake per minipool: %w", err)
}
return *value, nil
}

// The maximum RPL stake per minipool as a fraction of assigned user ETH
func GetMaximumPerMinipoolStake(rp *rocketpool.RocketPool, opts *bind.CallOpts) (float64, error) {
nodeSettingsContract, err := getNodeSettingsContract(rp, opts)
if err != nil {
return 0, err
}
value := new(*big.Int)
if err := nodeSettingsContract.Call(opts, value, "getMaximumPerMinipoolStake"); err != nil {
return 0, fmt.Errorf("error getting maximum RPL stake per minipool: %w", err)
}
return eth.WeiToEth(*value), nil
}
func ProposeMaximumPerMinipoolStake(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) {
return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", MaximumPerMinipoolStakeSettingPath), NodeSettingsContractName, MaximumPerMinipoolStakeSettingPath, value, blockNumber, treeNodes, opts)
}
func EstimateProposeMaximumPerMinipoolStakeGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) {
return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", MaximumPerMinipoolStakeSettingPath), NodeSettingsContractName, MaximumPerMinipoolStakeSettingPath, value, blockNumber, treeNodes, opts)
}

// The maximum RPL stake per minipool as a fraction of assigned user ETH
func GetMaximumPerMinipoolStakeRaw(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) {
nodeSettingsContract, err := getNodeSettingsContract(rp, opts)
if err != nil {
return nil, err
}
value := new(*big.Int)
if err := nodeSettingsContract.Call(opts, value, "getMaximumPerMinipoolStake"); err != nil {
return nil, fmt.Errorf("error getting maximum RPL stake per minipool: %w", err)
if err := nodeSettingsContract.Call(opts, value, "getMinimumLegacyRPLStake"); err != nil {
return nil, fmt.Errorf("error getting raw minimum legacy RPL stake per node: %w", err)
}
return *value, nil
}
Expand Down
11 changes: 8 additions & 3 deletions bindings/utils/state/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type NetworkDetails struct {
RplPrice *big.Int `json:"rpl_price"`
MinCollateralFraction *big.Int `json:"min_collateral_fraction"`
MaxCollateralFraction *big.Int `json:"max_collateral_fraction"`
MinimumLegacyRplStakeFraction *big.Int `json:"minimum_legacy_rpl_stake_fraction"`
IntervalDuration time.Duration `json:"interval_duration"`
IntervalStart time.Time `json:"interval_start"`
NodeOperatorRewardsPercent *big.Int `json:"node_operator_rewards_percent"`
Expand Down Expand Up @@ -114,8 +115,6 @@ func NewNetworkDetails(rp *rocketpool.RocketPool, contracts *NetworkContracts, i

// Multicall getters
contracts.Multicaller.AddCall(contracts.RocketNetworkPrices, &details.RplPrice, "getRPLPrice")
contracts.Multicaller.AddCall(contracts.RocketDAOProtocolSettingsNode, &details.MinCollateralFraction, "getMinimumPerMinipoolStake")
contracts.Multicaller.AddCall(contracts.RocketDAOProtocolSettingsNode, &details.MaxCollateralFraction, "getMaximumPerMinipoolStake")
contracts.Multicaller.AddCall(contracts.RocketRewardsPool, &rewardIndex, "getRewardIndex")
contracts.Multicaller.AddCall(contracts.RocketRewardsPool, &intervalStart, "getClaimIntervalTimeStart")
contracts.Multicaller.AddCall(contracts.RocketRewardsPool, &intervalDuration, "getClaimIntervalTime")
Expand Down Expand Up @@ -155,13 +154,19 @@ func NewNetworkDetails(rp *rocketpool.RocketPool, contracts *NetworkContracts, i
contracts.Multicaller.AddCall(contracts.RocketDAOProtocolSettingsNetwork, &pricesSubmissionFrequency, "getSubmitPricesFrequency")
contracts.Multicaller.AddCall(contracts.RocketDAOProtocolSettingsNetwork, &balancesSubmissionFrequency, "getSubmitBalancesFrequency")

// Exists in Houston but to be removed in Saturn
if !isSaturnDeployed {
contracts.Multicaller.AddCall(contracts.RocketDAOProtocolSettingsNode, &details.MinCollateralFraction, "getMinimumPerMinipoolStake")
contracts.Multicaller.AddCall(contracts.RocketDAOProtocolSettingsNode, &details.MaxCollateralFraction, "getMaximumPerMinipoolStake")
}

// Saturn
if isSaturnDeployed {
contracts.Multicaller.AddCall(contracts.RocketDAOProtocolSettingsNetwork, &details.MegapoolRevenueSplitSettings.NodeOperatorCommissionShare, "getNodeShare")
contracts.Multicaller.AddCall(contracts.RocketDAOProtocolSettingsNetwork, &details.MegapoolRevenueSplitSettings.NodeOperatorCommissionAdder, "getNodeShareSecurityCouncilAdder")
contracts.Multicaller.AddCall(contracts.RocketDAOProtocolSettingsNetwork, &details.MegapoolRevenueSplitSettings.VoterCommissionShare, "getVoterShare")
contracts.Multicaller.AddCall(contracts.RocketDAOProtocolSettingsNetwork, &details.MegapoolRevenueSplitSettings.PdaoCommissionShare, "getProtocolDAOShare")

contracts.Multicaller.AddCall(contracts.RocketDAOProtocolSettingsNode, &details.MinimumLegacyRplStakeFraction, "getMinimumLegacyRPLStake")
contracts.Multicaller.AddCall(contracts.RocketNetworkRevenues, &details.MegapoolRevenueSplitTimeWeightedAverages.NodeShare, "getCurrentNodeShare")
contracts.Multicaller.AddCall(contracts.RocketNetworkRevenues, &details.MegapoolRevenueSplitTimeWeightedAverages.VoterShare, "getCurrentVoterShare")
contracts.Multicaller.AddCall(contracts.RocketNetworkRevenues, &details.MegapoolRevenueSplitTimeWeightedAverages.PdaoShare, "getCurrentProtocolDAOShare")
Expand Down
2 changes: 2 additions & 0 deletions bindings/utils/state/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ func GetNativeNodeDetails(rp *rocketpool.RocketPool, contracts *NetworkContracts
// Do some postprocessing on the node data
details.DistributorBalance = distributorBalance

// TODO effectiveRPLStake and MinimumRPLStake are deprecated in Saturn
// Fix the effective stake
if details.EffectiveRPLStake.Cmp(details.MinimumRPLStake) == -1 {
details.EffectiveRPLStake.SetUint64(0)
Expand Down Expand Up @@ -211,6 +212,7 @@ func GetAllNativeNodeDetails(rp *rocketpool.RocketPool, contracts *NetworkContra
details := &nodeDetails[i]
details.DistributorBalance = balances[i]

// TODO effectiveRPLStake and MinimumRPLStake are deprecated in Saturn
// Fix the effective stake
if details.EffectiveRPLStake.Cmp(details.MinimumRPLStake) == -1 {
details.EffectiveRPLStake.SetUint64(0)
Expand Down
4 changes: 2 additions & 2 deletions rocketpool-cli/node/stake-rpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -330,8 +330,8 @@ func nodeStakeRpl(c *cli.Context) error {
} else {
if !(c.Bool("yes") || prompt.Confirm(fmt.Sprintf("Are you sure you want to stake %.6f RPL? You will not be able to unstake this RPL until you exit your validators and close your minipools, or reach %.6f staked RPL (%.0f%% of bonded eth)!",
math.RoundDown(eth.WeiToEth(amountWei), 6),
math.RoundDown(eth.WeiToEth(status.MaximumRplStake), 6),
status.MaximumStakeFraction*100))) {
math.RoundDown(eth.WeiToEth(status.RplStakeThreshold), 6),
status.RplStakeThresholdFraction*100))) {
fmt.Println("Cancelled.")
return nil
}
Expand Down
8 changes: 6 additions & 2 deletions rocketpool-cli/node/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,10 @@ func getStatus(c *cli.Context) error {
if status.RplStakeLegacy != nil && status.RplStakeLegacy.Cmp(big.NewInt(0)) != 0 {
fmt.Printf("The node has %6f legacy staked RPL.\n", math.RoundDown(eth.WeiToEth(status.RplStakeLegacy), 6))
fmt.Printf("The node has a total stake (legacy minipool RPL plus megapool RPL) of %.6f RPL.\n", math.RoundDown(eth.WeiToEth(status.RplStake), 6))
if status.RplStakeLegacy.Cmp(status.RplStakeThreshold) > 1 {
fmt.Printf(
"You can withdraw down to %.6f Legacy RPL (%.0f%% of borrowed eth)\n", math.RoundDown(eth.WeiToEth(status.RplStakeThreshold), 6), (status.RplStakeThresholdFraction)*100)
}
}
var unstakingPeriodEnd time.Time
if status.UnstakingRPL.Cmp(big.NewInt(0)) > 0 {
Expand All @@ -356,10 +360,10 @@ func getStatus(c *cli.Context) error {
} else {
// Withdrawal limit pre-saturn 1
rplTotalStake := math.RoundDown(eth.WeiToEth(status.RplStake), 6)
rplWithdrawalLimit := math.RoundDown(eth.WeiToEth(status.MaximumRplStake), 6)
rplWithdrawalLimit := math.RoundDown(eth.WeiToEth(status.RplStakeThreshold), 6)
if rplTotalStake > rplWithdrawalLimit {
fmt.Printf(
"You can withdraw down to %.6f RPL (%.0f%% of bonded eth)\n", math.RoundDown(eth.WeiToEth(status.MaximumRplStake), 6), (status.MaximumStakeFraction)*100)
"You can withdraw down to %.6f RPL (%.0f%% of bonded eth)\n", math.RoundDown(eth.WeiToEth(status.RplStakeThreshold), 6), (status.RplStakeThresholdFraction)*100)
}
}

Expand Down
Loading
Loading