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
5 changes: 1 addition & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,7 @@ The following commands are available via the Smart Node client:
- `rocketpool minipool import-key, ik` - Import the externally-derived key for a minipool that was previously a solo validator, so the Smart Node's VC manages it instead of your externally-managed VC.
- `rocketpool minipool promote, p` - Promote a vacant minipool after the scrub check, completing a solo validator migration.
- `rocketpool minipool refund, r` - Refund ETH belonging to the node from minipools
- `rocketpool minipool begin-bond-reduction, bbr` - Begins the ETH bond reduction process for a minipool, taking it from 16 ETH down to 8 ETH (begins conversion of a 16 ETH minipool to an LEB8)
- `rocketpool minipool reduce-bond, rb` - Manually completes the ETH bond reduction process for a minipool from 16 ETH down to 8 ETH once it is eligible. Please run `begin-bond-reduction` first to start this process.
- `rocketpool minipool reduce-bond, rb` - Manually completes the ETH bond reduction process for a minipool from 16 ETH down to 8 ETH once it is eligible. Note that `begin-bond-reduction` has been removed after Saturn 1.
- `rocketpool minipool distribute-balance, d` - Distribute a minipool's ETH balance between your withdrawal address and the rETH holders.
- `rocketpool minipool exit, e` - Exit staking minipools from the beacon chain
- `rocketpool minipool close, c` - Withdraw any remaining balance from a minipool and close it
Expand Down Expand Up @@ -122,8 +121,6 @@ The following commands are available via the Smart Node client:
- `rocketpool node claim-rewards, c` - Claim available RPL and ETH rewards for any checkpoint you haven't claimed yet
- `rocketpool node withdraw-rpl, i` - Withdraw RPL staked against the node
- `rocketpool node withdraw-eth, h` - Withdraw ETH staked on behalf of the node
- `rocketpool node deposit, d` - Make a deposit and create a minipool (deprecated after Saturn 1)
- `rocketpool node create-vacant-minipool, cvm` - Create an empty minipool, which can be used to migrate an existing solo staking validator as part of the 0x00 to 0x01 withdrawal credentials upgrade (deprecated after Saturn 1)
- `rocketpool node send, n` - Send ETH or tokens from the node account to an address. ENS names supported. <token> can be 'rpl', 'eth', 'fsrpl' (for the old RPL v1 token), 'reth', or the address of an arbitrary token you want to send (including the 0x prefix).
- `rocketpool node initialize-fee-distributor, z` - Create the fee distributor contract for your node, so you can withdraw priority fees and MEV rewards after the merge
- `rocketpool node distribute-fees, b` - Distribute the priority fee and MEV rewards from your fee distributor to your withdrawal address and the rETH contract (based on your node's average commission)
Expand Down
22 changes: 0 additions & 22 deletions bindings/node/deposit.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,28 +24,6 @@ type NodeDeposit struct {

type Deposits []NodeDeposit

// Estimate the gas of Deposit
func EstimateDepositGas(rp *rocketpool.RocketPool, bondAmount *big.Int, useExpressTicket bool, validatorPubkey rptypes.ValidatorPubkey, validatorSignature rptypes.ValidatorSignature, depositDataRoot common.Hash, opts *bind.TransactOpts) (rocketpool.GasInfo, error) {
rocketNodeDeposit, err := getRocketNodeDeposit(rp, nil)
if err != nil {
return rocketpool.GasInfo{}, err
}
return rocketNodeDeposit.GetTransactionGasInfo(opts, "deposit", bondAmount, useExpressTicket, validatorPubkey[:], validatorSignature[:], depositDataRoot)
}

// Make a node deposit
func Deposit(rp *rocketpool.RocketPool, bondAmount *big.Int, useExpressTicket bool, validatorPubkey rptypes.ValidatorPubkey, validatorSignature rptypes.ValidatorSignature, depositDataRoot common.Hash, opts *bind.TransactOpts) (*types.Transaction, error) {
rocketNodeDeposit, err := getRocketNodeDeposit(rp, nil)
if err != nil {
return nil, err
}
tx, err := rocketNodeDeposit.Transact(opts, "deposit", bondAmount, useExpressTicket, validatorPubkey[:], validatorSignature[:], depositDataRoot)
if err != nil {
return nil, fmt.Errorf("error making node deposit: %w", err)
}
return tx, nil
}

// Estimate the gas of DepositMulti
func EstimateDepositMultiGas(rp *rocketpool.RocketPool, deposits Deposits, opts *bind.TransactOpts) (rocketpool.GasInfo, error) {
rocketNodeDeposit, err := getRocketNodeDeposit(rp, nil)
Expand Down
33 changes: 1 addition & 32 deletions rocketpool-cli/minipool/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,41 +193,10 @@ func RegisterCommands(app *cli.App, name string, aliases []string) {
},
},

{
Name: "begin-bond-reduction",
Aliases: []string{"bbr"},
Usage: "Begins the ETH bond reduction process for a minipool, taking it from 16 ETH down to 8 ETH (begins conversion of a 16 ETH minipool to an LEB8)",
UsageText: "rocketpool minipool begin-bond-reduction [options]",
Flags: []cli.Flag{
cli.StringFlag{
Name: "minipool, m",
Usage: "The minipool/s to begin the bond reduction for (address or 'all')",
},
},
Action: func(c *cli.Context) error {

// Validate args
if err := cliutils.ValidateArgCount(c, 0); err != nil {
return err
}

// Validate flags
if c.String("minipool") != "" && c.String("minipool") != "all" {
if _, err := cliutils.ValidateAddress("minipool address", c.String("minipool")); err != nil {
return err
}
}

// Run
return beginReduceBondAmount(c)

},
},

{
Name: "reduce-bond",
Aliases: []string{"rb"},
Usage: "Manually completes the ETH bond reduction process for a minipool from 16 ETH down to 8 ETH once it is eligible. Please run `begin-bond-reduction` first to start this process.",
Usage: "Manually completes the ETH bond reduction process for a minipool from 16 ETH down to 8 ETH once it is eligible. Note that `begin-bond-reduction` has been removed after Saturn 1.",
UsageText: "rocketpool minipool reduce-bond [options]",
Flags: []cli.Flag{
cli.StringFlag{
Expand Down
210 changes: 1 addition & 209 deletions rocketpool-cli/minipool/reduce-bond.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,227 +3,19 @@ package minipool
import (
"bytes"
"fmt"
"math/big"
"time"

"github.com/ethereum/go-ethereum/common"
rocketpoolapi "github.com/rocket-pool/smartnode/bindings/rocketpool"
"github.com/rocket-pool/smartnode/bindings/types"
"github.com/rocket-pool/smartnode/bindings/utils/eth"
"github.com/rocket-pool/smartnode/shared/services/gas"
"github.com/rocket-pool/smartnode/shared/services/rocketpool"
"github.com/rocket-pool/smartnode/shared/types/api"
cliutils "github.com/rocket-pool/smartnode/shared/utils/cli"
"github.com/rocket-pool/smartnode/shared/utils/cli/prompt"
"github.com/rocket-pool/smartnode/shared/utils/math"
"github.com/urfave/cli"
)

func beginReduceBondAmount(c *cli.Context) error {

// Get RP client
rp, err := rocketpool.NewClientFromCtx(c).WithReady()
if err != nil {
return err
}
defer rp.Close()

// Check the fee distributor
distribResponse, err := rp.IsFeeDistributorInitialized()
if err != nil {
return fmt.Errorf("error checking the node's fee distributor status: %w", err)
}
if !distribResponse.IsInitialized {
fmt.Println("Minipools cannot have their bonds reduced until your fee distributor has been initialized.\nPlease run `rocketpool node initialize-fee-distributor` first, then return here to reduce your bonds.")
return nil
}

// Check if bond reduction is enabled
bondReductionEnabledResponse, err := rp.GetBondReductionEnabled()
if err != nil {
return fmt.Errorf("error checking if bond reduction is enabled: %w", err)
}
if !bondReductionEnabledResponse.BondReductionEnabled {
fmt.Println("Bond reductions are currently disabled.")
return nil
}

// Get minipool statuses
status, err := rp.MinipoolStatus()
if err != nil {
return err
}

// Get the bond reduction variables
settingsResponse, err := rp.GetTNDAOMinipoolSettings()
if err != nil {
return err
}

// TODO POST-ATLAS: Ask the user how much they want the new bond to be; since there's only one option right now there's no point
fmt.Printf("This will allow you to begin the bond reduction process to reduce your 16 ETH bond for a minipool down to 8 ETH, awarding you 8 ETH in credit and allowing you to create a second minipool for free (plus gas costs).\n\nThere will be a %.0f-hour wait period after you start the process. After this wait period is over, you will have %.0f hours to complete the process. Your `node` container will do this automatically unless you have it disabled, in which case you must manually run `rocketpool minipool reduce-bond`.\n\n%sNOTE: If you don't run it during this window, your request will time out and you will have to start over.%s\n\n", (time.Duration(settingsResponse.BondReductionWindowStart) * time.Second).Hours(), (time.Duration(settingsResponse.BondReductionWindowLength) * time.Second).Hours(), colorYellow, colorReset)
newBondAmount := eth.EthToWei(8)

// Prompt for confirmation
if !(c.Bool("yes") || prompt.Confirm("Do you understand how the bond reduction process will work?")) {
fmt.Println("Cancelled.")
return nil
}

bondReductionTimeout := time.Duration(settingsResponse.BondReductionWindowStart+settingsResponse.BondReductionWindowLength) * time.Second

// Get reduceable minipools
reduceableMinipools := []api.MinipoolDetails{}
scrubbedMinipools := []api.MinipoolDetails{}

for _, minipool := range status.Minipools {
if minipool.ReduceBondCancelled {
scrubbedMinipools = append(scrubbedMinipools, minipool)
} else {
nodeDepositBalance := eth.WeiToEth(minipool.Node.DepositBalance)
if nodeDepositBalance == 16 &&
time.Since(minipool.ReduceBondTime) > bondReductionTimeout &&
minipool.Status.Status == types.Staking &&
!minipool.Finalised {
reduceableMinipools = append(reduceableMinipools, minipool)
}
}
}

// Print scrubs
if len(scrubbedMinipools) > 0 {
fmt.Printf("%sNOTE: The following minipools had a previous bond reducton attempt scrubbed by the Oracle DAO and are no longer reduceable:\n", colorYellow)
for _, mp := range scrubbedMinipools {
fmt.Printf("\t%s\n", mp.Address)
}
fmt.Printf("%s\n\n", colorReset)
}

if len(reduceableMinipools) == 0 {
fmt.Println("No minipools can have their bond reduced at this time.")
return nil
}

// Get selected minipools
var selectedMinipools []api.MinipoolDetails
if c.String("minipool") == "" {

// Prompt for minipool selection
options := make([]string, len(reduceableMinipools)+1)
options[0] = "All available minipools"
for mi, minipool := range reduceableMinipools {
options[mi+1] = fmt.Sprintf("%s (Current bond: %d ETH, commission: %.2f%%)", minipool.Address.Hex(), int(eth.WeiToEth(minipool.Node.DepositBalance)), minipool.Node.Fee*100)
}
selected, _ := prompt.Select("Please select a minipool to begin the ETH bond reduction for:", options)

// Get minipools
if selected == 0 {
selectedMinipools = reduceableMinipools
} else {
selectedMinipools = []api.MinipoolDetails{reduceableMinipools[selected-1]}
}

} else {

// Get matching minipools
if c.String("minipool") == "all" {
selectedMinipools = reduceableMinipools
} else {
selectedAddress := common.HexToAddress(c.String("minipool"))
for _, minipool := range reduceableMinipools {
if bytes.Equal(minipool.Address.Bytes(), selectedAddress.Bytes()) {
selectedMinipools = []api.MinipoolDetails{minipool}
break
}
}
if selectedMinipools == nil {
return fmt.Errorf("The minipool %s cannot have its bond reduced.", selectedAddress.Hex())
}
}

}

// Get the total gas limit estimate
var totalGas uint64 = 0
var totalSafeGas uint64 = 0
var gasInfo rocketpoolapi.GasInfo
totalBorrowRequest := big.NewInt(0)
for _, minipool := range selectedMinipools {
canResponse, err := rp.CanBeginReduceBondAmount(minipool.Address, newBondAmount)
if err != nil {
return fmt.Errorf("couldn't check if minipool %s could have its bond reduced: %s)", minipool.Address.Hex(), err.Error())
} else {
if !canResponse.CanReduce {
fmt.Printf("Cannot reduce bond for minipool %s:\n", minipool.Address.Hex())
if canResponse.BondReductionDisabled {
fmt.Println("Bond reductions are currently disabled.")
}
if canResponse.MinipoolVersionTooLow {
fmt.Println("The minipool version is too low. It must be upgraded first using `rocketpool minipool delegate-upgrade`.")
}
if canResponse.BalanceTooLow {
fmt.Printf("The minipool's validator balance on the Beacon Chain is too low (must be 32 ETH or higher, currently %.6f ETH).\n", math.RoundDown(float64(canResponse.Balance)/1e9, 6))
}
if canResponse.InvalidBeaconState {
fmt.Printf("The minipool's validator is not in a legal state on the Beacon Chain. It must be pending or active (current state: %s)\n", canResponse.BeaconState)
}
return nil
}
gasInfo = canResponse.GasInfo
totalGas += canResponse.GasInfo.EstGasLimit
totalSafeGas += canResponse.GasInfo.SafeGasLimit
totalBorrowRequest.Add(totalBorrowRequest, canResponse.BorrowRequest)
}
}
gasInfo.EstGasLimit = totalGas
gasInfo.SafeGasLimit = totalSafeGas

// Make sure there's enough collateral to cover all of the pending bond reductions
collateralResponse, err := rp.CheckCollateral()
if err != nil {
return fmt.Errorf("error checking the node's total collateral: %w", err)
}
totalBorrowAvailable := big.NewInt(0).Sub(collateralResponse.EthBorrowedLimit, collateralResponse.EthBorrowed)
totalBorrowAvailable.Sub(totalBorrowAvailable, collateralResponse.PendingBorrowAmount)
if totalBorrowAvailable.Cmp(totalBorrowRequest) < 0 {
fmt.Printf("You do not have enough RPL staked to support all of the selected bond reductions.\nYou can borrow %.6f more ETH, but are requesting %.6f ETH with these bond reductions.\nIn total, they would bring you below the minimum RPL staking requirement (including the RPL required for any pending bond reductions you've already started).\nYou will have to stake more RPL first.\n", eth.WeiToEth(totalBorrowAvailable), eth.WeiToEth(totalBorrowRequest))
return nil
}

// Assign max fees
err = gas.AssignMaxFeeAndLimit(gasInfo, rp, c.Bool("yes"))
if err != nil {
return err
}

// Prompt for confirmation
if !(c.Bool("yes") || prompt.Confirm(fmt.Sprintf("Are you sure you want to begin bond reduction for %d minipools from 16 ETH to 8 ETH?", len(selectedMinipools)))) {
fmt.Println("Cancelled.")
return nil
}

// Begin bond reduction
for _, minipool := range selectedMinipools {
response, err := rp.BeginReduceBondAmount(minipool.Address, newBondAmount)
if err != nil {
fmt.Printf("Could not begin bond reduction for minipool %s: %s.\n", minipool.Address.Hex(), err.Error())
continue
}

fmt.Printf("Beginning bond reduction for minipool %s...\n", minipool.Address.Hex())
cliutils.PrintTransactionHash(rp, response.TxHash)
if _, err = rp.WaitForTransaction(response.TxHash); err != nil {
fmt.Printf("Could not begin bond reduction for minipool %s: %s.\n", minipool.Address.Hex(), err.Error())
} else {
fmt.Printf("Successfully started bond reduction for minipool %s.\n", minipool.Address.Hex())
}
}

// Return
return nil

}

func reduceBondAmount(c *cli.Context) error {

// Get RP client
Expand All @@ -245,7 +37,7 @@ func reduceBondAmount(c *cli.Context) error {
return err
}

fmt.Println("NOTE: this function is used to complete the bond reduction process for a minipool. If you haven't started the process already, please run `rocketpool minipool begin-bond-reduction` first.")
fmt.Println("NOTE: this function is used to complete the bond reduction process for a minipool. Note that `rocketpool minipool begin-bond-reduction` has been removed after Saturn 1.")
fmt.Println()

// Get reduceable minipools
Expand Down
Loading
Loading