Skip to content
Open
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
32 changes: 32 additions & 0 deletions bindings/node/deposit.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ import (
"github.com/rocket-pool/smartnode/bindings/utils/eth"
)

type NodeDeposit struct {
BondAmount *big.Int `json:"bondAmount"`
UseExpressTicket bool `json:"useExpressTicket"`
ValidatorPubkey []byte `json:"validatorPubkey"`
ValidatorSignature []byte `json:"validatorSignature"`
DepositDataRoot common.Hash `json:"depositDataRoot"`
}

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)
Expand All @@ -36,6 +46,28 @@ func Deposit(rp *rocketpool.RocketPool, bondAmount *big.Int, useExpressTicket bo
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)
if err != nil {
return rocketpool.GasInfo{}, err
}
return rocketNodeDeposit.GetTransactionGasInfo(opts, "depositMulti", deposits)
}

// Make multiple node deposits
func DepositMulti(rp *rocketpool.RocketPool, deposits Deposits, opts *bind.TransactOpts) (*types.Transaction, error) {
rocketNodeDeposit, err := getRocketNodeDeposit(rp, nil)
if err != nil {
return nil, err
}
tx, err := rocketNodeDeposit.Transact(opts, "depositMulti", deposits)
if err != nil {
return nil, fmt.Errorf("error making multiple node deposits: %w", err)
}
return tx, nil
}

// Estimate the gas to WithdrawETH
func EstimateWithdrawEthGas(rp *rocketpool.RocketPool, nodeAccount common.Address, ethAmount *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) {
rocketNodeDeposit, err := getRocketNodeDeposit(rp, nil)
Expand Down
9 changes: 7 additions & 2 deletions rocketpool-cli/megapool/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ func RegisterCommands(app *cli.App, name string, aliases []string) {
{
Name: "deposit",
Aliases: []string{"d"},
Usage: "Make a deposit and create a new validator on the megapool",
UsageText: "rocketpool node deposit [options]",
Usage: "Make a deposit and create a new validator on the megapool. Optionally specify count to make multiple deposits.",
UsageText: "rocketpool megapool deposit [options]",
Flags: []cli.Flag{
cli.BoolFlag{
Name: "yes, y",
Expand All @@ -41,6 +41,11 @@ func RegisterCommands(app *cli.App, name string, aliases []string) {
Name: "use-express-ticket, e",
Usage: "Use an express ticket to create a new validator",
},
cli.UintFlag{
Name: "count, c",
Usage: "Number of deposits to make",
Value: 0,
},
},
Action: func(c *cli.Context) error {

Expand Down
144 changes: 90 additions & 54 deletions rocketpool-cli/megapool/deposit.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package megapool
import (
"fmt"
"math/big"
"strconv"

"github.com/rocket-pool/smartnode/bindings/utils/eth"
"github.com/urfave/cli"
Expand All @@ -18,13 +19,11 @@ import (

// Config
const (
colorReset string = "\033[0m"
colorRed string = "\033[31m"
colorGreen string = "\033[32m"
colorYellow string = "\033[33m"
smoothingPoolLink string = "https://docs.rocketpool.net/guides/redstone/whats-new.html#smoothing-pool"
signallingAddressLink string = "https://docs.rocketpool.net/guides/houston/participate#setting-your-snapshot-signalling-address"
maxAlertItems int = 3
colorReset string = "\033[0m"
colorRed string = "\033[31m"
colorGreen string = "\033[32m"
colorYellow string = "\033[33m"
maxCount uint64 = 35
)

func nodeMegapoolDeposit(c *cli.Context) error {
Expand Down Expand Up @@ -64,24 +63,6 @@ func nodeMegapoolDeposit(c *cli.Context) error {
return nil
}

/*
// Check if the fee distributor has been initialized
isInitializedResponse, err := rp.IsFeeDistributorInitialized()
if err != nil {
return err
}
if !isInitializedResponse.IsInitialized {
fmt.Println("Your fee distributor has not been initialized yet so you cannot create a new validator.\nPlease run `rocketpool node initialize-fee-distributor` to initialize it first.")
return nil
}

// Post a warning about fee distribution
if !(c.Bool("yes") || prompt.Confirm(fmt.Sprintf("%sNOTE: By creating a new validator, your node will automatically claim and distribute any balance you have in your fee distributor contract. If you don't want to claim your balance at this time, you should not create a new minipool.%s\nWould you like to continue?", colorYellow, colorReset))) {
fmt.Println("Cancelled.")
return nil
}
*/

useExpressTicket := false

var wg errgroup.Group
Expand Down Expand Up @@ -118,7 +99,19 @@ func nodeMegapoolDeposit(c *cli.Context) error {
return err
}

if !(c.Bool("yes") || prompt.Confirm(fmt.Sprintf("%sNOTE: You are about to create a new megapool validator with a %.0f ETH deposit.%s\nWould you like to continue?", colorYellow, amount, colorReset))) {
count := c.Uint64("count")

// If the count was not provided, prompt the user for the number of deposits
for count == 0 || count > maxCount {
countStr := prompt.Prompt(fmt.Sprintf("How many validators would you like to create? (max: %d)", maxCount), "^\\d+$", "Invalid number.")
count, err = strconv.ParseUint(countStr, 10, 64)
if err != nil {
fmt.Println("Invalid number. Please try again.")
continue
}
}

if !(c.Bool("yes") || prompt.Confirm(fmt.Sprintf("%sNOTE: You are about to create %d new megapool validators, each with a %.0f ETH deposit (total: %.0f ETH).%s\nWould you like to continue?", colorYellow, count, amount, amount*float64(count), colorReset))) {
fmt.Println("Cancelled.")
return nil
}
Expand All @@ -139,7 +132,7 @@ func nodeMegapoolDeposit(c *cli.Context) error {
fmt.Printf("You have %d express tickets available.", expressTicketCount)
fmt.Println()
// Prompt for confirmation
if c.Bool("yes") || prompt.Confirm("Would you like to use an express ticket?") {
if c.Bool("yes") || prompt.Confirm("Would you like to use your express tickets?") {
useExpressTicket = true
}
}
Expand All @@ -149,12 +142,12 @@ func nodeMegapoolDeposit(c *cli.Context) error {
minNodeFee := 0.0

// Check deposit can be made
canDeposit, err := rp.CanNodeDeposit(amountWei, minNodeFee, big.NewInt(0), useExpressTicket)
canDeposit, err := rp.CanNodeDeposits(count, amountWei, minNodeFee, big.NewInt(0), useExpressTicket)
if err != nil {
return err
}
if !canDeposit.CanDeposit {
fmt.Println("Cannot make node deposit:")
fmt.Printf("Cannot make %d node deposits:\n", count)
if canDeposit.NodeHasDebt {
fmt.Println("The node has debt. You must repay the debt before creating a new validator. Use the `rocketpool megapool repay-debt` command to repay the debt.")
}
Expand All @@ -165,7 +158,11 @@ func nodeMegapoolDeposit(c *cli.Context) error {
if canDeposit.InsufficientBalance {
nodeBalance := eth.WeiToEth(canDeposit.NodeBalance)
creditBalance := eth.WeiToEth(canDeposit.CreditBalance)
fmt.Printf("The node's balance of %.6f ETH and credit balance of %.6f ETH are not enough to create a megapool validator with a %.1f ETH bond.", nodeBalance, creditBalance, amount)
if count > 1 {
fmt.Printf("The node's balance of %.6f ETH and credit balance of %.6f ETH are not enough to create %d megapool validators with a %.1f ETH bond each (total: %.1f ETH).", nodeBalance, creditBalance, count, amount, amount*float64(count))
} else {
fmt.Printf("The node's balance of %.6f ETH and credit balance of %.6f ETH are not enough to create a megapool validator with a %.1f ETH bond.", nodeBalance, creditBalance, amount)
}
}
if canDeposit.InvalidAmount {
fmt.Println("The deposit amount is invalid.")
Expand All @@ -177,12 +174,13 @@ func nodeMegapoolDeposit(c *cli.Context) error {
}

useCreditBalance := false
totalAmountWei := big.NewInt(0).Mul(amountWei, big.NewInt(int64(count)))
fmt.Printf("You currently have %.2f ETH in your credit balance plus ETH staked on your behalf.\n", eth.WeiToEth(canDeposit.CreditBalance))
if canDeposit.CreditBalance.Cmp(big.NewInt(0)) > 0 {
if canDeposit.CanUseCredit {
useCreditBalance = true
// Get how much credit to use
remainingAmount := big.NewInt(0).Sub(amountWei, canDeposit.CreditBalance)
remainingAmount := big.NewInt(0).Sub(totalAmountWei, canDeposit.CreditBalance)
if remainingAmount.Cmp(big.NewInt(0)) > 0 {
fmt.Printf("This deposit will use all %.6f ETH from your credit balance plus ETH staked on your behalf and %.6f ETH from your node.\n\n", eth.WeiToEth(canDeposit.CreditBalance), eth.WeiToEth(remainingAmount))
} else {
Expand All @@ -192,6 +190,7 @@ func nodeMegapoolDeposit(c *cli.Context) error {
fmt.Printf("%sNOTE: Your credit balance *cannot* currently be used to create a new megapool validator; there is not enough ETH in the staking pool to cover the initial deposit on your behalf (it needs at least 1 ETH but only has %.2f ETH).%s\nIf you want to continue creating this megapool validator now, you will have to pay for the full bond amount.\n\n", colorYellow, eth.WeiToEth(canDeposit.DepositBalance), colorReset)
}
}

// Prompt for confirmation
if !(c.Bool("yes") || prompt.Confirm("Would you like to continue?")) {
fmt.Println("Cancelled.")
Expand Down Expand Up @@ -227,40 +226,77 @@ func nodeMegapoolDeposit(c *cli.Context) error {
}

// Prompt for confirmation

if !(c.Bool("yes") || prompt.Confirm(fmt.Sprintf(
"You are about to deposit %.6f ETH to create a new megapool validator.\n"+
"%sARE YOU SURE YOU WANT TO DO THIS? Exiting this validator and retrieving your capital cannot be done until the validator has been *active* on the Beacon Chain for 256 epochs (approx. 27 hours).%s\n",
"You are about to deposit %.6f ETH to create %d new megapool validators (%.6f ETH total).\n"+
"%sARE YOU SURE YOU WANT TO DO THIS? Exiting these validators and retrieving your capital cannot be done until each validator has been *active* on the Beacon Chain for 256 epochs (approx. 27 hours).%s\n",
math.RoundDown(eth.WeiToEth(amountWei), 6),
count,
math.RoundDown(eth.WeiToEth(amountWei), 6)*float64(count),
colorYellow,
colorReset))) {
fmt.Println("Cancelled.")
return nil
}

// Make deposit
response, err := rp.NodeDeposit(amountWei, minNodeFee, big.NewInt(0), useCreditBalance, useExpressTicket, true)
if err != nil {
return err
}
// Make deposit(s)
if count == 1 {
// Single deposit
response, err := rp.NodeDeposit(amountWei, minNodeFee, big.NewInt(0), useCreditBalance, useExpressTicket, true)
if err != nil {
return err
}

// Log and wait for the megapool validator deposit
fmt.Printf("Creating megapool validator...\n")
cliutils.PrintTransactionHash(rp, response.TxHash)
_, err = rp.WaitForTransaction(response.TxHash)
if err != nil {
return err
}
// Log and wait for the megapool validator deposit
fmt.Printf("Creating megapool validator...\n")
cliutils.PrintTransactionHash(rp, response.TxHash)
_, err = rp.WaitForTransaction(response.TxHash)
if err != nil {
return err
}

// Log & return
fmt.Printf("The node deposit of %.6f ETH was made successfully!\n", math.RoundDown(eth.WeiToEth(amountWei), 6))
fmt.Printf("The validator pubkey is: %s\n\n", response.ValidatorPubkey.Hex())
// Log & return
fmt.Printf("The node deposit of %.6f ETH was made successfully!\n", math.RoundDown(eth.WeiToEth(amountWei), 6))
fmt.Printf("The validator pubkey is: %s\n\n", response.ValidatorPubkey.Hex())

fmt.Println("The new megapool validator has been created.")
fmt.Println("Once your validator progresses through the queue, ETH will be assigned and a 1 ETH prestake submitted.")
fmt.Printf("After the prestake, your node will automatically perform a stake transaction, to complete the progress.")
fmt.Println("")
fmt.Println("To check the status of your validators use `rocketpool megapool validators`")
fmt.Println("To monitor the stake transaction use `rocketpool service logs node`")
fmt.Println("The new megapool validator has been created.")
fmt.Println("Once your validator progresses through the queue, ETH will be assigned and a 1 ETH prestake submitted.")
fmt.Printf("After the prestake, your node will automatically perform a stake transaction, to complete the progress.")
fmt.Println("")
fmt.Println("To check the status of your validators use `rocketpool megapool validators`")
fmt.Println("To monitor the stake transaction use `rocketpool service logs node`")
} else {
// Multiple deposits
responses, err := rp.NodeDeposits(count, amountWei, minNodeFee, big.NewInt(0), useCreditBalance, useExpressTicket, true)
if err != nil {
return err
}

// Log and wait for the megapool validator deposits
fmt.Printf("Creating %d megapool validators in a single transaction...\n", count)
cliutils.PrintTransactionHash(rp, responses.TxHash)
_, err = rp.WaitForTransaction(responses.TxHash)
if err != nil {
return err
}

// Log & return
fmt.Printf("The node deposits of %.6f ETH each (%.6f ETH total) were made successfully!\n",
math.RoundDown(eth.WeiToEth(amountWei), 6),
math.RoundDown(eth.WeiToEth(amountWei), 6)*float64(count))
fmt.Printf("Validator pubkeys:\n")
for i, pubkey := range responses.ValidatorPubkeys {
fmt.Printf(" %d. %s\n", i+1, pubkey.Hex())
}
fmt.Println()

fmt.Printf("The %d new megapool validators have been created.\n", count)
fmt.Println("Once your validators progress through the queue, ETH will be assigned and a 1 ETH prestake submitted for each.")
fmt.Printf("After the prestake, your node will automatically perform a stake transaction for each validator, to complete the progress.")
fmt.Println("")
fmt.Println("To check the status of your validators use `rocketpool megapool validators`")
fmt.Println("To monitor the stake transactions use `rocketpool service logs node`")
}

return nil

Expand Down
30 changes: 19 additions & 11 deletions rocketpool/api/node/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -936,14 +936,15 @@ func RegisterSubcommands(command *cli.Command, name string, aliases []string) {

{
Name: "can-deposit",
Usage: "Check whether the node can make a deposit",
UsageText: "rocketpool api node can-deposit amount min-fee salt use-express-ticket",
Usage: "Check whether the node can make a deposit. Optionally specify count to check multiple deposits.",
UsageText: "rocketpool api node can-deposit amount min-fee salt use-express-ticket count",
Action: func(c *cli.Context) error {

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

amountWei, err := cliutils.ValidatePositiveWeiAmount("deposit amount", c.Args().Get(0))
if err != nil {
return err
Expand All @@ -958,29 +959,34 @@ func RegisterSubcommands(command *cli.Command, name string, aliases []string) {
return err
}

useExpressTicketString := c.Args().Get(3)
useExpressTicket, err := cliutils.ValidateBool("use-express-ticket", useExpressTicketString)
useExpressTicket, err := cliutils.ValidateBool("use-express-ticket", c.Args().Get(3))
if err != nil {
return err
}

count, err := cliutils.ValidateUint("count", c.Args().Get(4))
if err != nil {
return err
}

// Run
api.PrintResponse(canNodeDeposit(c, amountWei, minNodeFee, salt, useExpressTicket))
api.PrintResponse(canNodeDeposits(c, count, amountWei, minNodeFee, salt, useExpressTicket))
return nil

},
},
{
Name: "deposit",
Aliases: []string{"d"},
Usage: "Make a deposit and create a minipool, or just make and sign the transaction (when submit = false)",
UsageText: "rocketpool api node deposit amount min-node-fee salt use-credit-balance use-express-ticket submit",
Usage: "Make a deposit and create a minipool, or just make and sign the transaction (when submit = false). Optionally specify count to make multiple deposits.",
UsageText: "rocketpool api node deposit amount min-node-fee salt use-credit-balance use-express-ticket submit count",
Action: func(c *cli.Context) error {

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

amountWei, err := cliutils.ValidatePositiveWeiAmount("deposit amount", c.Args().Get(0))
if err != nil {
return err
Expand Down Expand Up @@ -1012,15 +1018,17 @@ func RegisterSubcommands(command *cli.Command, name string, aliases []string) {
return err
}

// Check if count is provided
count, err := cliutils.ValidateUint("count", c.Args().Get(6))
if err != nil {
return err
}

// Run
response, err := nodeDeposit(c, amountWei, minNodeFee, salt, useCreditBalance, useExpressTicket, submit)
response, err := nodeDeposits(c, count, amountWei, minNodeFee, salt, useCreditBalance, useExpressTicket, submit)
if submit {
api.PrintResponse(response, err)
} // else nodeDeposit already printed the encoded transaction
} // else nodeDeposits already printed the encoded transaction
return nil

},
Expand Down
Loading
Loading